From: Aaron Tomlin <atomlin@atomlin.com>
To: peterz@infradead.org, mingo@redhat.com, acme@kernel.org,
namhyung@kernel.org
Cc: mark.rutland@arm.com, alexander.shishkin@linux.intel.com,
jolsa@kernel.org, irogers@google.com, adrian.hunter@intel.com,
james.clark@linaro.org, howardchu95@gmail.com,
atomlin@atomlin.com, neelx@suse.com, chjohnst@mail.com,
sean@ashe.io, steve@abita.co, rishil1999@outlook.com,
linux-perf-users@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH perf-tools-next v5 3/4] perf trace: Enhance BTF type formatting to symbolise kernel function pointers
Date: Sat, 22 Aug 2026 17:35:57 -0400 [thread overview]
Message-ID: <20260822213558.704018-4-atomlin@atomlin.com> (raw)
In-Reply-To: <20260822213558.704018-1-atomlin@atomlin.com>
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 | 100 ++++++++++++++++++++++++++++---------
1 file changed, 77 insertions(+), 23 deletions(-)
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 10fbea7ed4db..4f8b4f2cf677 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++;
+
+ while ((isalnum(pos[len]) || pos[len] == '_') && len < sizeof(name) - 1) {
+ name[len] = pos[len];
+ len++;
+ }
+ name[len] = '\0';
- id = btf__find_by_name(btf, type);
+ 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));
}
@@ -1077,14 +1119,19 @@ static size_t btf_struct_scnprintf(const struct btf_type *type, struct btf *btf,
.bf = bf,
.size = size,
};
- struct augmented_arg *augmented_arg = arg->augmented.args;
+ struct augmented_arg *augmented_arg;
int type_id = arg->fmt->type_id, consumed;
struct btf_dump *btf_dump;
LIBBPF_OPTS(btf_dump_opts, dump_opts);
LIBBPF_OPTS(btf_dump_type_data_opts, dump_data_opts);
- if (arg == NULL || arg->augmented.args == NULL)
+ if (arg == NULL || arg->augmented.args == NULL || arg->augmented.size <= 0 ||
+ arg->fmt == NULL || !arg->fmt->from_user)
+ return 0;
+
+ augmented_arg = arg->augmented.args;
+ if (augmented_arg->size <= 0)
return 0;
dump_data_opts.compact = true;
@@ -1095,8 +1142,10 @@ static size_t btf_struct_scnprintf(const struct btf_type *type, struct btf *btf,
return 0;
/* pretty print the struct data here */
- if (btf_dump__dump_type_data(btf_dump, type_id, arg->augmented.args->value, type->size, &dump_data_opts) == 0)
+ if (btf_dump__dump_type_data(btf_dump, type_id, augmented_arg->value, type->size, &dump_data_opts) == 0) {
+ btf_dump__free(btf_dump);
return 0;
+ }
consumed = sizeof(*augmented_arg) + augmented_arg->size;
arg->augmented.args = ((void *)arg->augmented.args) + consumed;
@@ -1108,33 +1157,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;
@@ -2584,7 +2637,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) {
--
2.55.0
next prev parent reply other threads:[~2026-08-22 21:36 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-22 21:35 [PATCH perf-tools-next v5 0/4] perf trace: Symbolise kernel virtual addresses and function pointers Aaron Tomlin
2026-08-22 21:35 ` [PATCH perf-tools-next v5 1/4] perf trace: Introduce kernel symbol beautifier for virtual addresses Aaron Tomlin
2026-08-22 21:45 ` sashiko-bot
2026-08-22 21:35 ` [PATCH perf-tools-next v5 2/4] perf trace: Auto-assign kernel symbol beautifier to function pointer fields Aaron Tomlin
2026-08-22 21:48 ` sashiko-bot
2026-08-23 22:28 ` Aaron Tomlin
2026-08-22 21:35 ` Aaron Tomlin [this message]
2026-08-22 21:51 ` [PATCH perf-tools-next v5 3/4] perf trace: Enhance BTF type formatting to symbolise kernel function pointers sashiko-bot
2026-08-24 10:02 ` Aaron Tomlin
2026-08-22 21:35 ` [PATCH perf-tools-next v5 4/4] perf tests: Add shell test for kernel symbol beautifier Aaron Tomlin
2026-08-22 21:48 ` sashiko-bot
2026-08-24 11:28 ` Aaron Tomlin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260822213558.704018-4-atomlin@atomlin.com \
--to=atomlin@atomlin.com \
--cc=acme@kernel.org \
--cc=adrian.hunter@intel.com \
--cc=alexander.shishkin@linux.intel.com \
--cc=chjohnst@mail.com \
--cc=howardchu95@gmail.com \
--cc=irogers@google.com \
--cc=james.clark@linaro.org \
--cc=jolsa@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-perf-users@vger.kernel.org \
--cc=mark.rutland@arm.com \
--cc=mingo@redhat.com \
--cc=namhyung@kernel.org \
--cc=neelx@suse.com \
--cc=peterz@infradead.org \
--cc=rishil1999@outlook.com \
--cc=sean@ashe.io \
--cc=steve@abita.co \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.