[PATCH perf-tools-next v2 3/4] perf trace: Enhance BTF type formatting to symbolise kernel function pointers
Aaron Tomlin <[email protected]>
| Newsgroups | org.kernel.vger.linux-perf-users,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
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 <[email protected]> --- tools/perf/builtin-trace.c | 65 +++++++++++++++++---- tools/perf/tests/shell/trace_btf_general.sh | 2 +- 2 files changed, 55 insertions(+), 12 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 85abae09d328..cb3cb877a22e 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -979,21 +979,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) { + bool has_ptr = false; + + while (type) { + if (btf_is_ptr(type)) { + has_ptr = true; + type = btf__type_by_id(btf, type->type); + } else if (btf_is_typedef(type) || btf_is_mod(type)) { + type = btf__type_by_id(btf, type->type); + } else { + break; + } + } + return has_ptr && 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 "); if (type == NULL) return; - type += 5; // skip "enum " to get the enumeration name + 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) @@ -1028,8 +1068,7 @@ static bool syscall_arg__strtoul_btf_type(char *bf, size_t size, struct syscall_ return false; if (arg->fmt->type == NULL) { - // See if this is an enum - syscall_arg_fmt__cache_btf_enum(arg->fmt, btf, type); + syscall_arg_fmt__cache_btf_type(arg->fmt, btf, type); } // Now let's see if we have a BTF type resolved @@ -1108,7 +1147,7 @@ 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; @@ -1116,8 +1155,7 @@ static size_t trace__btf_scnprintf(struct trace *trace, struct syscall_arg *arg, 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); + syscall_arg_fmt__cache_btf_type(arg_fmt, trace->btf, type); } // Did we manage to find a BTF type for the syscall/tracepoint argument? @@ -1128,13 +1166,17 @@ static size_t trace__btf_scnprintf(struct trace *trace, struct syscall_arg *arg, 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)) 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; @@ -2567,7 +2609,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