* [PATCH perf-tools-next v3 1/4] perf trace: Introduce kernel symbol beautifier for virtual addresses
2026-08-20 21:10 [PATCH perf-tools-next v3 0/4] perf trace: Symbolise kernel virtual addresses and function pointers Aaron Tomlin
@ 2026-08-20 21:10 ` Aaron Tomlin
2026-08-20 21:19 ` sashiko-bot
2026-08-20 21:10 ` [PATCH perf-tools-next v3 2/4] perf trace: Auto-assign kernel symbol beautifier to function pointer fields Aaron Tomlin
` (2 subsequent siblings)
3 siblings, 1 reply; 10+ messages in thread
From: Aaron Tomlin @ 2026-08-20 21:10 UTC (permalink / raw)
To: peterz, mingo, acme, namhyung
Cc: mark.rutland, alexander.shishkin, jolsa, irogers, adrian.hunter,
james.clark, howardchu95, atomlin, neelx, chjohnst, sean, steve,
rishil1999, linux-perf-users, linux-kernel
Currently, when 'perf trace' formats tracepoint payloads or system call
arguments containing raw kernel virtual addresses (e.g., a work item
function pointer work_func_t in workqueue:workqueue_execute_start),
it prints them as raw hexadecimal values (e.g., 0xffffffff81234567).
This impairs readability when tracing kernel execution flows.
Introduce a dedicated kernel symbol beautifier,
syscall_arg__scnprintf_ksym (i.e., SCA_KSYM), to resolve kernel
virtual addresses to human-readable symbol names and offsets
(e.g., "flush_to_ldisc").
The beautifier looks up the virtual address in the machine kernel maps via
machine__find_kernel_symbol(). If a valid kernel symbol is found, the
symbol name and offset are printed without requiring --libtraceevent; if
the address is zero, "NULL" is rendered; otherwise, it gracefully falls
back to hexadecimal formatting.
Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
---
tools/perf/builtin-trace.c | 29 +++++++++++++++++++++++++++++
tools/perf/trace/beauty/beauty.h | 3 +++
2 files changed, 32 insertions(+)
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index c3c7f1f85c53..003048946503 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -750,6 +750,35 @@ size_t syscall_arg__scnprintf_ptr(char *bf, size_t size, struct syscall_arg *arg
return syscall_arg__scnprintf_hex(bf, size, arg);
}
+size_t syscall_arg__scnprintf_ksym(char *bf, size_t size, struct syscall_arg *arg)
+{
+ if (arg->val == 0)
+ return scnprintf(bf, size, "NULL");
+
+ if (arg->trace && arg->trace->host) {
+ struct map *map = NULL;
+ struct symbol *sym = machine__find_kernel_symbol(arg->trace->host,
+ arg->val, &map);
+
+ if (sym) {
+ u64 start = map__unmap_ip(map, sym->start);
+ u64 offset = arg->val - start;
+ size_t printed;
+
+ if (offset == 0)
+ printed = scnprintf(bf, size, "%s", sym->name);
+ else
+ printed = scnprintf(bf, size, "%s+0x%" PRIx64,
+ sym->name, offset);
+ map__put(map);
+ return printed;
+ }
+ map__put(map);
+ }
+
+ return syscall_arg__scnprintf_hex(bf, size, arg);
+}
+
size_t syscall_arg__scnprintf_int(char *bf, size_t size, struct syscall_arg *arg)
{
return scnprintf(bf, size, "%d", arg->val);
diff --git a/tools/perf/trace/beauty/beauty.h b/tools/perf/trace/beauty/beauty.h
index 58a3206481ae..0f4801c61a5b 100644
--- a/tools/perf/trace/beauty/beauty.h
+++ b/tools/perf/trace/beauty/beauty.h
@@ -160,6 +160,9 @@ size_t syscall_arg__scnprintf_hex(char *bf, size_t size, struct syscall_arg *arg
size_t syscall_arg__scnprintf_ptr(char *bf, size_t size, struct syscall_arg *arg);
#define SCA_PTR syscall_arg__scnprintf_ptr
+size_t syscall_arg__scnprintf_ksym(char *bf, size_t size, struct syscall_arg *arg);
+#define SCA_KSYM syscall_arg__scnprintf_ksym
+
size_t syscall_arg__scnprintf_int(char *bf, size_t size, struct syscall_arg *arg);
#define SCA_INT syscall_arg__scnprintf_int
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH perf-tools-next v3 1/4] perf trace: Introduce kernel symbol beautifier for virtual addresses
2026-08-20 21:10 ` [PATCH perf-tools-next v3 1/4] perf trace: Introduce kernel symbol beautifier for virtual addresses Aaron Tomlin
@ 2026-08-20 21:19 ` sashiko-bot
0 siblings, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-08-20 21:19 UTC (permalink / raw)
To: Aaron Tomlin; +Cc: linux-perf-users
> Currently, when 'perf trace' formats tracepoint payloads or system call
> arguments containing raw kernel virtual addresses (e.g., a work item
> function pointer work_func_t in workqueue:workqueue_execute_start),
> it prints them as raw hexadecimal values (e.g., 0xffffffff81234567).
> This impairs readability when tracing kernel execution flows.
>
> Introduce a dedicated kernel symbol beautifier,
> syscall_arg__scnprintf_ksym (i.e., SCA_KSYM), to resolve kernel
> virtual addresses to human-readable symbol names and offsets
> (e.g., "flush_to_ldisc").
>
> The beautifier looks up the virtual address in the machine kernel maps via
> machine__find_kernel_symbol(). If a valid kernel symbol is found, the
> symbol name and offset are printed without requiring --libtraceevent; if
> the address is zero, "NULL" is rendered; otherwise, it gracefully falls
> back to hexadecimal formatting.
>
> Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260820211100.649142-1-atomlin@atomlin.com?part=1
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH perf-tools-next v3 2/4] perf trace: Auto-assign kernel symbol beautifier to function pointer fields
2026-08-20 21:10 [PATCH perf-tools-next v3 0/4] perf trace: Symbolise kernel virtual addresses and function pointers Aaron Tomlin
2026-08-20 21:10 ` [PATCH perf-tools-next v3 1/4] perf trace: Introduce kernel symbol beautifier for virtual addresses Aaron Tomlin
@ 2026-08-20 21:10 ` Aaron Tomlin
2026-08-20 21:26 ` sashiko-bot
2026-08-20 21:10 ` [PATCH perf-tools-next v3 3/4] perf trace: Enhance BTF type formatting to symbolise kernel function pointers Aaron Tomlin
2026-08-20 21:11 ` [PATCH perf-tools-next v3 4/4] perf tests: Add shell test for kernel symbol beautifier Aaron Tomlin
3 siblings, 1 reply; 10+ messages in thread
From: Aaron Tomlin @ 2026-08-20 21:10 UTC (permalink / raw)
To: peterz, mingo, acme, namhyung
Cc: mark.rutland, alexander.shishkin, jolsa, irogers, adrian.hunter,
james.clark, howardchu95, atomlin, neelx, chjohnst, sean, steve,
rishil1999, linux-perf-users, linux-kernel
Tracepoint fields that convey kernel function pointers, callbacks, and
call sites, such as "function", "func", "fn", "callback", "action",
"handler", "caller", "location", "callsite", and "call_site" are
currently formatted as generic hexadecimal pointers by default.
Enhance syscall_arg_fmt__init_array() to automatically detect function
pointer fields by type signature (e.g., typedefs ending with "_func_t"
or "_fn", or C function pointer types containing "(*)") and assign
SCA_KSYM as their default beautifier.
Additionally, register common function pointer and callback field names
within the sorted syscall_arg_fmts__by_name lookup table. This ensures
tracepoint arguments such as workqueue:workqueue_execute_start.function
and csd:csd_function.func are symbolised automatically without requiring
explicit per-event configuration. For example:
❯ sudo tools/perf/perf trace --event workqueue:workqueue_execute_end --max-events 2 --show-cpu
0.000 [000] kworker/u32:15/236682 workqueue:workqueue_execute_end(work: 0xffffffffab2f1420, function: toggle_allocation_gate)
0.132 [000] kworker/u32:15/236682 workqueue:workqueue_execute_end(work: 0xffff8ac2c1adc010, function: flush_to_ldisc)
Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
---
tools/perf/builtin-trace.c | 71 +++++++++++++++++++-------------------
1 file changed, 35 insertions(+), 36 deletions(-)
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 003048946503..bb8f4fd9ba24 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -2096,6 +2096,17 @@ static int syscall__alloc_arg_fmts(struct syscall *sc, int nr_args)
}
static const struct syscall_arg_fmt syscall_arg_fmts__by_name[] = {
+ { .name = "action", .scnprintf = SCA_KSYM, },
+ { .name = "call_site", .scnprintf = SCA_KSYM, },
+ { .name = "callback", .scnprintf = SCA_KSYM, },
+ { .name = "caller", .scnprintf = SCA_KSYM, },
+ { .name = "callsite", .scnprintf = SCA_KSYM, },
+ { .name = "cb", .scnprintf = SCA_KSYM, },
+ { .name = "fn", .scnprintf = SCA_KSYM, },
+ { .name = "func", .scnprintf = SCA_KSYM, },
+ { .name = "function", .scnprintf = SCA_KSYM, },
+ { .name = "handler", .scnprintf = SCA_KSYM, },
+ { .name = "location", .scnprintf = SCA_KSYM, },
{ .name = "msr", .scnprintf = SCA_X86_MSR, .strtoul = STUL_X86_MSR, },
{ .name = "vector", .scnprintf = SCA_X86_IRQ_VECTORS, .strtoul = STUL_X86_IRQ_VECTORS, },
};
@@ -2198,31 +2209,10 @@ syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field
((len >= 4 && strcmp(field->name + len - 4, "name") == 0) ||
strstr(field->name, "path") != NULL)) {
arg->scnprintf = SCA_FILENAME;
- } else if ((field->flags & TEP_FIELD_IS_POINTER) || strstr(field->name, "addr") ||
- field_has_hex_fmt(field, len))
- arg->scnprintf = SCA_PTR;
- else if (strcmp(field->type, "pid_t") == 0)
- arg->scnprintf = SCA_PID;
- else if (strcmp(field->type, "umode_t") == 0)
- arg->scnprintf = SCA_MODE_T;
- else if ((field->flags & TEP_FIELD_IS_ARRAY) && strstr(field->type, "char")) {
- arg->scnprintf = SCA_CHAR_ARRAY;
- arg->nr_entries = field->arraylen;
- } else if ((strcmp(field->type, "int") == 0 ||
- strcmp(field->type, "unsigned int") == 0 ||
- strcmp(field->type, "long") == 0) &&
- len >= 2 && strcmp(field->name + len - 2, "fd") == 0) {
- /*
- * /sys/kernel/tracing/events/syscalls/sys_enter*
- * grep -E 'field:.*fd;' .../format|sed -r 's/.*field:([a-z ]+) [a-z_]*fd.+/\1/g'|sort|uniq -c
- * 65 int
- * 23 unsigned int
- * 7 unsigned long
- */
- arg->scnprintf = SCA_FD;
- } else if (strstr(field->type, "enum") && use_btf != NULL) {
- *use_btf = true;
- arg->strtoul = STUL_BTF_TYPE;
+ } else if (field->type && (strstr(field->type, "(*)") != NULL ||
+ strstr(field->type, "_func_t") != NULL ||
+ strstr(field->type, "_fn") != NULL)) {
+ arg->scnprintf = SCA_KSYM;
} else {
const struct syscall_arg_fmt *fmt =
syscall_arg_fmt__find_by_name(field->name);
@@ -2230,6 +2220,24 @@ syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field
if (fmt) {
arg->scnprintf = fmt->scnprintf;
arg->strtoul = fmt->strtoul;
+ } else if ((field->flags & TEP_FIELD_IS_POINTER) || strstr(field->name, "addr") ||
+ field_has_hex_fmt(field, len)) {
+ arg->scnprintf = SCA_PTR;
+ } else if (strcmp(field->type, "pid_t") == 0) {
+ arg->scnprintf = SCA_PID;
+ } else if (strcmp(field->type, "umode_t") == 0) {
+ arg->scnprintf = SCA_MODE_T;
+ } else if ((field->flags & TEP_FIELD_IS_ARRAY) && strstr(field->type, "char")) {
+ arg->scnprintf = SCA_CHAR_ARRAY;
+ arg->nr_entries = field->arraylen;
+ } else if ((strcmp(field->type, "int") == 0 ||
+ strcmp(field->type, "unsigned int") == 0 ||
+ strcmp(field->type, "long") == 0) &&
+ len >= 2 && strcmp(field->name + len - 2, "fd") == 0) {
+ arg->scnprintf = SCA_FD;
+ } else if (strstr(field->type, "enum") && use_btf != NULL) {
+ *use_btf = true;
+ arg->strtoul = STUL_BTF_TYPE;
}
}
}
@@ -3297,12 +3305,6 @@ static unsigned char bitmap_byte(const unsigned long *mask, int byte_idx)
return b_val;
}
-static bool trace__field_is_ip(const char *name)
-{
- return !strcmp(name, "__probe_ip") ||
- !strcmp(name, "caller_ip") ||
- !strcmp(name, "call_site");
-}
static size_t trace__fprintf_tp_fields(struct trace *trace, struct perf_sample *sample,
struct thread *thread, void *augmented_args, int augmented_args_size)
@@ -3404,14 +3406,11 @@ static size_t trace__fprintf_tp_fields(struct trace *trace, struct perf_sample *
* Suppress it by default to avoid cluttering the output.
* If verbose mode is enabled, ensure it is formatted as a
* hexadecimal memory address rather than a signed integer.
- *
- * caller_ip and call_site are also expected to be instruction
- * pointers and should always be represented in hexadecimal.
*/
is_probe_ip = evsel__is_probe(evsel) && !strcmp(field->name, "__probe_ip");
- if (is_probe_ip || trace__field_is_ip(field->name)) {
- if (is_probe_ip && !verbose)
+ if (is_probe_ip) {
+ if (!verbose)
continue;
printed += scnprintf(bf + printed, size - printed,
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH perf-tools-next v3 2/4] perf trace: Auto-assign kernel symbol beautifier to function pointer fields
2026-08-20 21:10 ` [PATCH perf-tools-next v3 2/4] perf trace: Auto-assign kernel symbol beautifier to function pointer fields Aaron Tomlin
@ 2026-08-20 21:26 ` sashiko-bot
2026-08-21 0:29 ` Aaron Tomlin
0 siblings, 1 reply; 10+ messages in thread
From: sashiko-bot @ 2026-08-20 21:26 UTC (permalink / raw)
To: Aaron Tomlin; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Mapping highly generic field names like 'action', 'cb', and 'caller' globally to `SCA_KSYM` short-circuits type-based logic, leaking perf userspace memory addresses instead of strings and breaking integers/enums.
- [Medium] Removal of `trace__field_is_ip()` and omission of `caller_ip` from the new lookup table causes kernel instruction pointers to be formatted as unreadable decimal integers.
--
--- Patch [2]: [PATCH perf-tools-next v3 2/4] perf trace: Auto-assign kernel symbol beautifier to function pointer fields ---
commit d3017d506b83550526e2692582e2544d0b006d0f
Author: Aaron Tomlin <atomlin@atomlin.com>
perf trace: Auto-assign kernel symbol beautifier to function pointer fields
This patch enhances perf trace to automatically format kernel function
pointer fields using the SCA_KSYM beautifier. It detects these fields
by type signature or by matching common function pointer and callback
field names in a lookup table.
> diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
> index 0030489465032..bb8f4fd9ba244 100644
> --- a/tools/perf/builtin-trace.c
> +++ b/tools/perf/builtin-trace.c
> @@ -2096,6 +2096,17 @@ static int syscall__alloc_arg_fmts(struct syscall *sc, int nr_args)
> }
>
> static const struct syscall_arg_fmt syscall_arg_fmts__by_name[] = {
> + { .name = "action", .scnprintf = SCA_KSYM, },
> + { .name = "call_site", .scnprintf = SCA_KSYM, },
> + { .name = "callback", .scnprintf = SCA_KSYM, },
> + { .name = "caller", .scnprintf = SCA_KSYM, },
> + { .name = "callsite", .scnprintf = SCA_KSYM, },
> + { .name = "cb", .scnprintf = SCA_KSYM, },
[ ... ]
> @@ -2230,6 +2220,24 @@ syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field
> if (fmt) {
> arg->scnprintf = fmt->scnprintf;
> arg->strtoul = fmt->strtoul;
> + } else if ((field->flags & TEP_FIELD_IS_POINTER) || strstr(field->name, "addr") ||
> + field_has_hex_fmt(field, len)) {
> + arg->scnprintf = SCA_PTR;
> + } else if (strcmp(field->type, "pid_t") == 0) {
> + arg->scnprintf = SCA_PID;
> + } else if (strcmp(field->type, "umode_t") == 0) {
> + arg->scnprintf = SCA_MODE_T;
> + } else if ((field->flags & TEP_FIELD_IS_ARRAY) && strstr(field->type, "char")) {
> + arg->scnprintf = SCA_CHAR_ARRAY;
> + arg->nr_entries = field->arraylen;
[Severity: High]
Does checking syscall_arg_fmt__find_by_name() earlier in this branch,
before the type-based checks like string arrays or BTF enums, short-circuit
those checks for generic field names?
If a field is named "caller", "action", or "cb" but is actually a dynamic
string or integer, it seems this early name match will blindly assign
SCA_KSYM.
Would this cause strings to be printed as raw kernel addresses by passing a
userspace perf pointer to SCA_KSYM, permanently obfuscating the trace data?
[ ... ]
> @@ -3297,12 +3305,6 @@ static unsigned char bitmap_byte(const unsigned long *mask, int byte_idx)
> return b_val;
> }
>
> -static bool trace__field_is_ip(const char *name)
> -{
> - return !strcmp(name, "__probe_ip") ||
> - !strcmp(name, "caller_ip") ||
> - !strcmp(name, "call_site");
> -}
[Severity: Medium]
Since trace__field_is_ip() is removed here, should "caller_ip" have been
added to the new syscall_arg_fmts__by_name lookup table?
Because "caller_ip" is typed as unsigned long (not a pointer), lacks "addr"
in its name, and its TP_printk format strings do not match the strict
=0x/=%p checks in field_has_hex_fmt(), it evades all heuristics.
Will this cause kernel instruction pointers like "caller_ip" (used heavily
in XFS and OCFS2 tracepoints) to be formatted as unreadable base-10 decimal
integers?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260820211100.649142-1-atomlin@atomlin.com?part=2
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH perf-tools-next v3 2/4] perf trace: Auto-assign kernel symbol beautifier to function pointer fields
2026-08-20 21:26 ` sashiko-bot
@ 2026-08-21 0:29 ` Aaron Tomlin
0 siblings, 0 replies; 10+ messages in thread
From: Aaron Tomlin @ 2026-08-21 0:29 UTC (permalink / raw)
To: namhyung, irogers, acme; +Cc: linux-perf-users
On Thu, Aug 20, 2026 at 09:26:12PM +0000, sashiko-bot@kernel.org wrote:
> commit d3017d506b83550526e2692582e2544d0b006d0f
> Author: Aaron Tomlin <atomlin@atomlin.com>
>
> perf trace: Auto-assign kernel symbol beautifier to function pointer fields
>
> This patch enhances perf trace to automatically format kernel function
> pointer fields using the SCA_KSYM beautifier. It detects these fields
> by type signature or by matching common function pointer and callback
> field names in a lookup table.
>
> > diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
> > index 0030489465032..bb8f4fd9ba244 100644
> > --- a/tools/perf/builtin-trace.c
> > +++ b/tools/perf/builtin-trace.c
> > @@ -2096,6 +2096,17 @@ static int syscall__alloc_arg_fmts(struct syscall *sc, int nr_args)
> > }
> >
> > static const struct syscall_arg_fmt syscall_arg_fmts__by_name[] = {
> > + { .name = "action", .scnprintf = SCA_KSYM, },
> > + { .name = "call_site", .scnprintf = SCA_KSYM, },
> > + { .name = "callback", .scnprintf = SCA_KSYM, },
> > + { .name = "caller", .scnprintf = SCA_KSYM, },
> > + { .name = "callsite", .scnprintf = SCA_KSYM, },
> > + { .name = "cb", .scnprintf = SCA_KSYM, },
>
> [ ... ]
>
> > @@ -2230,6 +2220,24 @@ syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field
> > if (fmt) {
> > arg->scnprintf = fmt->scnprintf;
> > arg->strtoul = fmt->strtoul;
> > + } else if ((field->flags & TEP_FIELD_IS_POINTER) || strstr(field->name, "addr") ||
> > + field_has_hex_fmt(field, len)) {
> > + arg->scnprintf = SCA_PTR;
> > + } else if (strcmp(field->type, "pid_t") == 0) {
> > + arg->scnprintf = SCA_PID;
> > + } else if (strcmp(field->type, "umode_t") == 0) {
> > + arg->scnprintf = SCA_MODE_T;
> > + } else if ((field->flags & TEP_FIELD_IS_ARRAY) && strstr(field->type, "char")) {
> > + arg->scnprintf = SCA_CHAR_ARRAY;
> > + arg->nr_entries = field->arraylen;
>
> [Severity: High]
> Does checking syscall_arg_fmt__find_by_name() earlier in this branch,
> before the type-based checks like string arrays or BTF enums, short-circuit
> those checks for generic field names?
>
> If a field is named "caller", "action", or "cb" but is actually a dynamic
> string or integer, it seems this early name match will blindly assign
> SCA_KSYM.
>
> Would this cause strings to be printed as raw kernel addresses by passing a
> userspace perf pointer to SCA_KSYM, permanently obfuscating the trace data?
I agree. This is a valid concern.
To resolve, I will guard the SCA_KSYM assignment in
syscall_arg_fmt__init_array(). Moving forward, a name-based lookup now
assigns SCA_KSYM only if the field is an actual pointer
(TEP_FIELD_IS_POINTER) or a pointer-sized non-array scalar (field->size ==
sizeof(void *) && !(field->flags & TEP_FIELD_IS_ARRAY)). If a field fails
this guard, it falls through to subsequent type checks (i.e.,
SCA_CHAR_ARRAY, BTF enum inspection, SCA_FD, etc.). Non-SCA_KSYM entries in
the lookup table (e.g., "msr" and "vector") continue to pass through
unhindered. This should work.
> [ ... ]
>
> > @@ -3297,12 +3305,6 @@ static unsigned char bitmap_byte(const unsigned long *mask, int byte_idx)
> > return b_val;
> > }
> >
> > -static bool trace__field_is_ip(const char *name)
> > -{
> > - return !strcmp(name, "__probe_ip") ||
> > - !strcmp(name, "caller_ip") ||
> > - !strcmp(name, "call_site");
> > -}
>
> [Severity: Medium]
> Since trace__field_is_ip() is removed here, should "caller_ip" have been
> added to the new syscall_arg_fmts__by_name lookup table?
>
> Because "caller_ip" is typed as unsigned long (not a pointer), lacks "addr"
> in its name, and its TP_printk format strings do not match the strict
> =0x/=%p checks in field_has_hex_fmt(), it evades all heuristics.
>
> Will this cause kernel instruction pointers like "caller_ip" (used heavily
> in XFS and OCFS2 tracepoints) to be formatted as unreadable base-10 decimal
> integers?
Apologies, "caller_ip" was accidently omitted!
In the next iteration, I have added "caller_ip" to
syscall_arg_fmts__by_name[] in strict alphabetical order to ensure
bsearch() integrity. Furthermore, I have relaxed field_has_hex_fmt() to
tolerate delimiters preceding %p/%# format specifiers, ensuring tracepoint
formats utilising %pS/%ps are properly recognised too.
Kind regards,
--
Aaron Tomlin
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH perf-tools-next v3 3/4] perf trace: Enhance BTF type formatting to symbolise kernel function pointers
2026-08-20 21:10 [PATCH perf-tools-next v3 0/4] perf trace: Symbolise kernel virtual addresses and function pointers Aaron Tomlin
2026-08-20 21:10 ` [PATCH perf-tools-next v3 1/4] perf trace: Introduce kernel symbol beautifier for virtual addresses Aaron Tomlin
2026-08-20 21:10 ` [PATCH perf-tools-next v3 2/4] perf trace: Auto-assign kernel symbol beautifier to function pointer fields Aaron Tomlin
@ 2026-08-20 21:10 ` Aaron Tomlin
2026-08-20 21:25 ` sashiko-bot
2026-08-20 21:11 ` [PATCH perf-tools-next v3 4/4] perf tests: Add shell test for kernel symbol beautifier Aaron Tomlin
3 siblings, 1 reply; 10+ messages in thread
From: Aaron Tomlin @ 2026-08-20 21:10 UTC (permalink / raw)
To: peterz, mingo, acme, namhyung
Cc: mark.rutland, alexander.shishkin, jolsa, irogers, adrian.hunter,
james.clark, howardchu95, atomlin, neelx, chjohnst, sean, steve,
rishil1999, linux-perf-users, linux-kernel
When BTF (BPF Type Format) metadata is loaded from vmlinux, 'perf trace'
can inspect the precise C types of tracepoint and system call parameters.
However, function pointer arguments are currently not recognised during
BTF pretty-printing and default to hexadecimal output.
Introduce btf_is_func_ptr() to inspect BTF type hierarchies
(i.e., traversing pointers, typedefs, and type modifiers) to determine
whether a parameter resolves to a function prototype
(BTF_KIND_FUNC_PROTO).
Generalise BTF type caching via syscall_arg_fmt__cache_btf_type() to
handle structs, unions, enums, and function pointers alike. When a field
is identified as a kernel function pointer, trace__btf_scnprintf()
routes its value to syscall_arg__scnprintf_ksym(), enabling automatic
zero-config symbolisation of kernel function pointers whenever BTF is
available.
Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
---
tools/perf/builtin-trace.c | 87 ++++++++++++++++-----
tools/perf/tests/shell/trace_btf_general.sh | 2 +-
2 files changed, 68 insertions(+), 21 deletions(-)
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index bb8f4fd9ba24..4182906449a9 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -122,6 +122,7 @@ struct syscall_arg_fmt {
#ifdef HAVE_LIBBPF_SUPPORT
const struct btf_type *type;
int type_id; /* used in btf_dump */
+ bool btf_type_cached;
#endif
};
@@ -979,21 +980,61 @@ static size_t syscall_arg__scnprintf_getrandom_flags(char *bf, size_t size,
#define SCA_GETRANDOM_FLAGS syscall_arg__scnprintf_getrandom_flags
#ifdef HAVE_LIBBPF_SUPPORT
-static void syscall_arg_fmt__cache_btf_enum(struct syscall_arg_fmt *arg_fmt, struct btf *btf, char *type)
+static bool btf_is_func_ptr(const struct btf *btf, const struct btf_type *type)
{
+ while (type) {
+ if (btf_is_ptr(type) || btf_is_typedef(type) || btf_is_mod(type))
+ type = btf__type_by_id(btf, type->type);
+ else
+ break;
+ }
+ return type && btf_is_func_proto(type);
+}
+
+static void syscall_arg_fmt__cache_btf_type(struct syscall_arg_fmt *arg_fmt,
+ struct btf *btf, const char *type)
+{
+ char name[128];
+ const char *pos;
+ size_t len = 0;
int id;
- type = strstr(type, "enum ");
+ arg_fmt->btf_type_cached = true;
+
if (type == NULL)
return;
- type += 5; // skip "enum " to get the enumeration name
+ /* Pointers to enums are memory addresses, not scalar enums */
+ if (strstr(type, "enum ") && strchr(type, '*'))
+ return;
+
+ if ((pos = strstr(type, "enum ")) != NULL)
+ pos += 5;
+ else if ((pos = strstr(type, "struct ")) != NULL)
+ pos += 7;
+ else if ((pos = strstr(type, "union ")) != NULL)
+ pos += 6;
+ else
+ pos = type;
+
+ while (isspace(*pos))
+ pos++;
- id = btf__find_by_name(btf, type);
+ while ((isalnum(pos[len]) || pos[len] == '_') && len < sizeof(name) - 1) {
+ name[len] = pos[len];
+ len++;
+ }
+ name[len] = '\0';
+
+ if (len == 0)
+ return;
+
+ id = btf__find_by_name(btf, name);
if (id < 0)
return;
arg_fmt->type = btf__type_by_id(btf, id);
+ arg_fmt->type_id = id;
}
static bool syscall_arg__strtoul_btf_enum(char *bf, size_t size, struct syscall_arg *arg, u64 *val)
@@ -1027,10 +1068,8 @@ static bool syscall_arg__strtoul_btf_type(char *bf, size_t size, struct syscall_
if (btf == NULL)
return false;
- if (arg->fmt->type == NULL) {
- // See if this is an enum
- syscall_arg_fmt__cache_btf_enum(arg->fmt, btf, type);
- }
+ if (!arg->fmt->btf_type_cached)
+ syscall_arg_fmt__cache_btf_type(arg->fmt, btf, type);
// Now let's see if we have a BTF type resolved
bt = arg->fmt->type;
@@ -1038,19 +1077,22 @@ static bool syscall_arg__strtoul_btf_type(char *bf, size_t size, struct syscall_
return false;
// If it is an enum:
- if (btf_is_enum(arg->fmt->type))
+ if (btf_is_enum(arg->fmt->type)) {
+ if (type && strchr(type, '*'))
+ return false;
return syscall_arg__strtoul_btf_enum(bf, size, arg, val);
+ }
return false;
}
-static size_t btf_enum_scnprintf(const struct btf_type *type, struct btf *btf, char *bf, size_t size, int val)
+static size_t btf_enum_scnprintf(const struct btf_type *type, struct btf *btf, char *bf, size_t size, unsigned long val)
{
struct btf_enum *be = btf_enum(type);
const unsigned int nr_entries = btf_vlen(type);
for (unsigned int i = 0; i < nr_entries; ++i, ++be) {
- if (be->val == val) {
+ if ((unsigned long)(__u32)be->val == val || (unsigned long)be->val == val) {
return scnprintf(bf, size, "%s",
btf__name_by_offset(btf, be->name_off));
}
@@ -1108,33 +1150,37 @@ static size_t btf_struct_scnprintf(const struct btf_type *type, struct btf *btf,
}
static size_t trace__btf_scnprintf(struct trace *trace, struct syscall_arg *arg, char *bf,
- size_t size, int val, char *type)
+ size_t size, unsigned long val, char *type)
{
struct syscall_arg_fmt *arg_fmt = arg->fmt;
if (trace->btf == NULL)
return 0;
- if (arg_fmt->type == NULL) {
- // Check if this is an enum and if we have the BTF type for it.
- syscall_arg_fmt__cache_btf_enum(arg_fmt, trace->btf, type);
- }
+ if (!arg_fmt->btf_type_cached)
+ syscall_arg_fmt__cache_btf_type(arg_fmt, trace->btf, type);
// Did we manage to find a BTF type for the syscall/tracepoint argument?
if (arg_fmt->type == NULL)
return 0;
- if (btf_is_enum(arg_fmt->type))
+ if (btf_is_enum(arg_fmt->type)) {
+ if (type && strchr(type, '*'))
+ return 0;
return btf_enum_scnprintf(arg_fmt->type, trace->btf, bf, size, val);
- else if (btf_is_struct(arg_fmt->type) || btf_is_union(arg_fmt->type))
+ } else if (btf_is_struct(arg_fmt->type) || btf_is_union(arg_fmt->type))
return btf_struct_scnprintf(arg_fmt->type, trace->btf, bf, size, arg);
+ else if (btf_is_func_ptr(trace->btf, arg_fmt->type)) {
+ arg->val = val;
+ return syscall_arg__scnprintf_ksym(bf, size, arg);
+ }
return 0;
}
#else // HAVE_LIBBPF_SUPPORT
static size_t trace__btf_scnprintf(struct trace *trace __maybe_unused, struct syscall_arg *arg __maybe_unused,
- char *bf __maybe_unused, size_t size __maybe_unused, int val __maybe_unused,
+ char *bf __maybe_unused, size_t size __maybe_unused, unsigned long val __maybe_unused,
char *type __maybe_unused)
{
return 0;
@@ -2566,7 +2612,8 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size,
default_scnprintf = sc->arg_fmt[arg.idx].scnprintf;
- if (trace->force_btf || default_scnprintf == NULL || default_scnprintf == SCA_PTR) {
+ if (trace->force_btf || default_scnprintf == NULL ||
+ default_scnprintf == SCA_PTR || default_scnprintf == SCA_KSYM) {
btf_printed = trace__btf_scnprintf(trace, &arg, bf + printed,
size - printed, val, field->type);
if (btf_printed) {
diff --git a/tools/perf/tests/shell/trace_btf_general.sh b/tools/perf/tests/shell/trace_btf_general.sh
index 7a94a5743924..255468eff128 100755
--- a/tools/perf/tests/shell/trace_btf_general.sh
+++ b/tools/perf/tests/shell/trace_btf_general.sh
@@ -49,7 +49,7 @@ trace_test_buffer() {
trace_test_struct_btf() {
echo "Testing perf trace's struct augmentation"
output="$(perf trace --sort-events -e clock_nanosleep --force-btf --max-events=1 -- sleep 1 2>&1)"
- if ! echo "$output" | grep -q -E "^sleep/[0-9]+ clock_nanosleep\(0, 0, \{1,.*\}, 0x[0-9a-f]+\) += +[0-9]+$"
+ if ! echo "$output" | grep -q -E "sleep/[0-9]+ clock_nanosleep\(.*(struct __kernel_timespec|\{1,).*\) = [0-9]+"
then
printf "BTF struct augmentation test failed, output:\n$output\n"
err=1
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH perf-tools-next v3 3/4] perf trace: Enhance BTF type formatting to symbolise kernel function pointers
2026-08-20 21:10 ` [PATCH perf-tools-next v3 3/4] perf trace: Enhance BTF type formatting to symbolise kernel function pointers Aaron Tomlin
@ 2026-08-20 21:25 ` sashiko-bot
0 siblings, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-08-20 21:25 UTC (permalink / raw)
To: Aaron Tomlin; +Cc: linux-perf-users
> When BTF (BPF Type Format) metadata is loaded from vmlinux, 'perf trace'
> can inspect the precise C types of tracepoint and system call parameters.
> However, function pointer arguments are currently not recognised during
> BTF pretty-printing and default to hexadecimal output.
>
> Introduce btf_is_func_ptr() to inspect BTF type hierarchies
> (i.e., traversing pointers, typedefs, and type modifiers) to determine
> whether a parameter resolves to a function prototype
> (BTF_KIND_FUNC_PROTO).
>
> Generalise BTF type caching via syscall_arg_fmt__cache_btf_type() to
> handle structs, unions, enums, and function pointers alike. When a field
> is identified as a kernel function pointer, trace__btf_scnprintf()
> routes its value to syscall_arg__scnprintf_ksym(), enabling automatic
> zero-config symbolisation of kernel function pointers whenever BTF is
> available.
>
> Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260820211100.649142-1-atomlin@atomlin.com?part=3
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH perf-tools-next v3 4/4] perf tests: Add shell test for kernel symbol beautifier
2026-08-20 21:10 [PATCH perf-tools-next v3 0/4] perf trace: Symbolise kernel virtual addresses and function pointers Aaron Tomlin
` (2 preceding siblings ...)
2026-08-20 21:10 ` [PATCH perf-tools-next v3 3/4] perf trace: Enhance BTF type formatting to symbolise kernel function pointers Aaron Tomlin
@ 2026-08-20 21:11 ` Aaron Tomlin
2026-08-20 21:27 ` sashiko-bot
3 siblings, 1 reply; 10+ messages in thread
From: Aaron Tomlin @ 2026-08-20 21:11 UTC (permalink / raw)
To: peterz, mingo, acme, namhyung
Cc: mark.rutland, alexander.shishkin, jolsa, irogers, adrian.hunter,
james.clark, howardchu95, atomlin, neelx, chjohnst, sean, steve,
rishil1999, linux-perf-users, linux-kernel
Add a dedicated shell test script, trace_ksym_beautifier.sh, to verify
that 'perf trace' properly symbolises kernel virtual addresses and
function pointers using both the default kallsyms beautifier (SCA_KSYM)
and BTF type routing i.e., --force-btf.
Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
---
.../perf/tests/shell/trace_ksym_beautifier.sh | 43 +++++++++++++++++++
1 file changed, 43 insertions(+)
create mode 100755 tools/perf/tests/shell/trace_ksym_beautifier.sh
diff --git a/tools/perf/tests/shell/trace_ksym_beautifier.sh b/tools/perf/tests/shell/trace_ksym_beautifier.sh
new file mode 100755
index 000000000000..30a8429669dd
--- /dev/null
+++ b/tools/perf/tests/shell/trace_ksym_beautifier.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+# perf trace kernel symbol beautifier tests
+# SPDX-License-Identifier: GPL-2.0
+
+err=0
+
+# shellcheck source=lib/probe.sh
+. "$(dirname "$0")"/lib/probe.sh
+skip_if_no_perf_trace || exit 2
+[ "$(id -u)" = 0 ] || exit 2
+
+test_ksym_kallsyms() {
+ echo "Testing perf trace kernel symbol beautifier (default kallsyms)"
+ output="$(perf trace -e kmem:kmalloc --max-events=1 2>&1)"
+ if ! echo "$output" | grep -q -E "call_site: [a-zA-Z_][a-zA-Z0-9_]*" || echo "$output" | grep -q -E "call_site: 0x[0-9a-fA-F]+"
+ then
+ printf "Default kallsyms function symbolization failed, output:\n%s\n" "$output"
+ err=1
+ fi
+}
+
+test_ksym_btf() {
+ echo "Testing perf trace kernel symbol beautifier (BTF)"
+ if [ ! -f /sys/kernel/btf/vmlinux ]; then
+ echo "Skipping BTF test due to missing vmlinux BTF"
+ return
+ fi
+
+ output="$(perf trace -e kmem:kmalloc --force-btf --max-events=1 2>&1)"
+ if ! echo "$output" | grep -q -E "call_site: [a-zA-Z_][a-zA-Z0-9_]*" || echo "$output" | grep -q -E "call_site: 0x[0-9a-fA-F]+"
+ then
+ printf "BTF function symbolization failed, output:\n%s\n" "$output"
+ err=1
+ fi
+}
+
+test_ksym_kallsyms
+
+if [ $err = 0 ]; then
+ test_ksym_btf
+fi
+
+exit $err
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH perf-tools-next v3 4/4] perf tests: Add shell test for kernel symbol beautifier
2026-08-20 21:11 ` [PATCH perf-tools-next v3 4/4] perf tests: Add shell test for kernel symbol beautifier Aaron Tomlin
@ 2026-08-20 21:27 ` sashiko-bot
0 siblings, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-08-20 21:27 UTC (permalink / raw)
To: Aaron Tomlin; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The BTF type routing test does not actually test BTF symbolization, as it silently falls back to the default kallsyms beautifier.
--
--- Patch [4]: [PATCH perf-tools-next v3 4/4] perf tests: Add shell test for kernel symbol beautifier ---
commit c8c43ea37c4e0f26131dd9e871af4241cd478b77
Author: Aaron Tomlin <atomlin@atomlin.com>
perf tests: Add shell test for kernel symbol beautifier
This commit adds a shell test script, trace_ksym_beautifier.sh, to verify
that perf trace symbolises kernel virtual addresses and function pointers
using both the default kallsyms beautifier and BTF type routing.
> diff --git a/tools/perf/tests/shell/trace_ksym_beautifier.sh b/tools/perf/tests/shell/trace_ksym_beautifier.sh
> new file mode 100755
> index 0000000000000..30a8429669dd5
> --- /dev/null
> +++ b/tools/perf/tests/shell/trace_ksym_beautifier.sh
[ ... ]
> +test_ksym_btf() {
> + echo "Testing perf trace kernel symbol beautifier (BTF)"
> + if [ ! -f /sys/kernel/btf/vmlinux ]; then
> + echo "Skipping BTF test due to missing vmlinux BTF"
> + return
> + fi
> +
> + output="$(perf trace -e kmem:kmalloc --force-btf --max-events=1 2>&1)"
[Severity: Medium]
Does this actually test the BTF type routing as intended?
Looking at the kmem:kmalloc tracepoint, the call_site field is defined as an
unsigned long, rather than a function pointer. When trace__btf_scnprintf()
evaluates this, it determines it is an integer rather than a function
pointer, and returns 0.
This causes perf to silently fall back to the default SCA_KSYM beautifier.
Consequently, the test passes via the fallback path rather than truly
exercising the BTF routing.
Would it be better to use a tracepoint with a field explicitly typed as a
function pointer for the --force-btf test?
> + if ! echo "$output" | grep -q -E "call_site: [a-zA-Z_][a-zA-Z0-9_]*" || echo "$output" | grep -q -E "call_site: 0x[0-9a-fA-F]+"
> + then
> + printf "BTF function symbolization failed, output:\n%s\n" "$output"
> + err=1
> + fi
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260820211100.649142-1-atomlin@atomlin.com?part=4
^ permalink raw reply [flat|nested] 10+ messages in thread