Skip to content

Commit 45a0c92

Browse files
Sbermacmel
authored andcommitted
perf trace: BTF-based enum pretty printing for syscall args
In this patch, BTF is used to turn enum value to the corresponding name. There is only one system call that uses enum value as its argument, that is `landlock_add_rule()`. The vmlinux btf is loaded lazily, when user decided to trace the `landlock_add_rule` syscall. But if one decide to run `perf trace` without any arguments, the behaviour is to trace `landlock_add_rule`, so vmlinux btf will be loaded by default. The laziest behaviour is to load vmlinux btf when a `landlock_add_rule` syscall hits. But I think you could lose some samples when loading vmlinux btf at run time, for it can delay the handling of other samples. I might need your precious opinions on this... before: ``` perf $ ./perf trace -e landlock_add_rule 0.000 ( 0.008 ms): ldlck-test/438194 landlock_add_rule(rule_type: 2) = -1 EBADFD (File descriptor in bad state) 0.010 ( 0.001 ms): ldlck-test/438194 landlock_add_rule(rule_type: 1) = -1 EBADFD (File descriptor in bad state) ``` after: ``` perf $ ./perf trace -e landlock_add_rule 0.000 ( 0.029 ms): ldlck-test/438194 landlock_add_rule(rule_type: LANDLOCK_RULE_NET_PORT) = -1 EBADFD (File descriptor in bad state) 0.036 ( 0.004 ms): ldlck-test/438194 landlock_add_rule(rule_type: LANDLOCK_RULE_PATH_BENEATH) = -1 EBADFD (File descriptor in bad state) ``` Committer notes: Made it build with NO_LIBBPF=1, simplified btf_enum_fprintf(), see [1] for the discussion. Signed-off-by: Howard Chu <[email protected]> Tested-by: Arnaldo Carvalho de Melo <[email protected]> Cc: Adrian Hunter <[email protected]> Cc: Alexander Shishkin <[email protected]> Cc: Günther Noack <[email protected]> Cc: Ian Rogers <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Jiri Olsa <[email protected]> Cc: Kan Liang <[email protected]> Cc: Mark Rutland <[email protected]> Cc: Mickaël Salaün <[email protected]> Cc: Namhyung Kim <[email protected]> Cc: Peter Zijlstra <[email protected]> Link: https://lore.kernel.org/lkml/[email protected] Link: https://lore.kernel.org/lkml/ZnXAhFflUl_LV1QY@x1 # [1] Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
1 parent e4fc196 commit 45a0c92

File tree

1 file changed

+106
-4
lines changed

1 file changed

+106
-4
lines changed

tools/perf/builtin-trace.c

Lines changed: 106 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#ifdef HAVE_LIBBPF_SUPPORT
2020
#include <bpf/bpf.h>
2121
#include <bpf/libbpf.h>
22+
#include <bpf/btf.h>
2223
#ifdef HAVE_BPF_SKEL
2324
#include "bpf_skel/augmented_raw_syscalls.skel.h"
2425
#endif
@@ -110,6 +111,10 @@ struct syscall_arg_fmt {
110111
const char *name;
111112
u16 nr_entries; // for arrays
112113
bool show_zero;
114+
bool is_enum;
115+
#ifdef HAVE_LIBBPF_SUPPORT
116+
const struct btf_type *type;
117+
#endif
113118
};
114119

115120
struct syscall_fmt {
@@ -139,6 +144,9 @@ struct trace {
139144
} syscalls;
140145
#ifdef HAVE_BPF_SKEL
141146
struct augmented_raw_syscalls_bpf *skel;
147+
#endif
148+
#ifdef HAVE_LIBBPF_SUPPORT
149+
struct btf *btf;
142150
#endif
143151
struct record_opts opts;
144152
struct evlist *evlist;
@@ -204,6 +212,20 @@ struct trace {
204212
} oe;
205213
};
206214

215+
static void trace__load_vmlinux_btf(struct trace *trace __maybe_unused)
216+
{
217+
#ifdef HAVE_LIBBPF_SUPPORT
218+
if (trace->btf != NULL)
219+
return;
220+
221+
trace->btf = btf__load_vmlinux_btf();
222+
if (verbose > 0) {
223+
fprintf(trace->output, trace->btf ? "vmlinux BTF loaded\n" :
224+
"Failed to load vmlinux BTF\n");
225+
}
226+
#endif
227+
}
228+
207229
struct tp_field {
208230
int offset;
209231
union {
@@ -887,6 +909,64 @@ static size_t syscall_arg__scnprintf_getrandom_flags(char *bf, size_t size,
887909

888910
#define SCA_GETRANDOM_FLAGS syscall_arg__scnprintf_getrandom_flags
889911

912+
#ifdef HAVE_LIBBPF_SUPPORT
913+
static int syscall_arg_fmt__cache_btf_enum(struct syscall_arg_fmt *arg_fmt, struct btf *btf, char *type)
914+
{
915+
int id;
916+
917+
// Already cached?
918+
if (arg_fmt->type != NULL)
919+
return 0;
920+
921+
type = strstr(type, "enum ");
922+
if (type == NULL)
923+
return -1;
924+
925+
type += 5; // skip "enum " to get the enumeration name
926+
927+
id = btf__find_by_name(btf, type);
928+
if (id < 0)
929+
return -1;
930+
931+
arg_fmt->type = btf__type_by_id(btf, id);
932+
return arg_fmt->type == NULL ? -1 : 0;
933+
}
934+
935+
static size_t btf_enum_scnprintf(const struct btf_type *type, struct btf *btf, char *bf, size_t size, int val)
936+
{
937+
struct btf_enum *be = btf_enum(type);
938+
const int nr_entries = btf_vlen(type);
939+
940+
for (int i = 0; i < nr_entries; ++i, ++be) {
941+
if (be->val == val) {
942+
return scnprintf(bf, size, "%s",
943+
btf__name_by_offset(btf, be->name_off));
944+
}
945+
}
946+
947+
return 0;
948+
}
949+
950+
static size_t trace__btf_enum_scnprintf(struct trace *trace, struct syscall_arg_fmt *arg_fmt, char *bf,
951+
size_t size, int val, char *type)
952+
{
953+
if (trace->btf == NULL)
954+
return 0;
955+
956+
if (syscall_arg_fmt__cache_btf_enum(arg_fmt, trace->btf, type) < 0)
957+
return 0;
958+
959+
return btf_enum_scnprintf(arg_fmt->type, trace->btf, bf, size, val);
960+
}
961+
#else // HAVE_LIBBPF_SUPPORT
962+
static size_t trace__btf_enum_scnprintf(struct trace *trace __maybe_unused, struct syscall_arg_fmt *arg_fmt __maybe_unused,
963+
char *bf __maybe_unused, size_t size __maybe_unused, int val __maybe_unused,
964+
char *type __maybe_unused)
965+
{
966+
return 0;
967+
}
968+
#endif // HAVE_LIBBPF_SUPPORT
969+
890970
#define STRARRAY(name, array) \
891971
{ .scnprintf = SCA_STRARRAY, \
892972
.strtoul = STUL_STRARRAY, \
@@ -1238,6 +1318,7 @@ struct syscall {
12381318
bool is_exit;
12391319
bool is_open;
12401320
bool nonexistent;
1321+
bool use_btf;
12411322
struct tep_format_field *args;
12421323
const char *name;
12431324
const struct syscall_fmt *fmt;
@@ -1744,7 +1825,8 @@ static const struct syscall_arg_fmt *syscall_arg_fmt__find_by_name(const char *n
17441825
}
17451826

17461827
static struct tep_format_field *
1747-
syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field *field)
1828+
syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field *field,
1829+
bool *use_btf)
17481830
{
17491831
struct tep_format_field *last_field = NULL;
17501832
int len;
@@ -1756,6 +1838,7 @@ syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field
17561838
continue;
17571839

17581840
len = strlen(field->name);
1841+
arg->is_enum = false;
17591842

17601843
if (strcmp(field->type, "const char *") == 0 &&
17611844
((len >= 4 && strcmp(field->name + len - 4, "name") == 0) ||
@@ -1782,6 +1865,8 @@ syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field
17821865
* 7 unsigned long
17831866
*/
17841867
arg->scnprintf = SCA_FD;
1868+
} else if (strstr(field->type, "enum") && use_btf != NULL) {
1869+
*use_btf = arg->is_enum = true;
17851870
} else {
17861871
const struct syscall_arg_fmt *fmt =
17871872
syscall_arg_fmt__find_by_name(field->name);
@@ -1798,7 +1883,8 @@ syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field
17981883

17991884
static int syscall__set_arg_fmts(struct syscall *sc)
18001885
{
1801-
struct tep_format_field *last_field = syscall_arg_fmt__init_array(sc->arg_fmt, sc->args);
1886+
struct tep_format_field *last_field = syscall_arg_fmt__init_array(sc->arg_fmt, sc->args,
1887+
&sc->use_btf);
18021888

18031889
if (last_field)
18041890
sc->args_size = last_field->offset + last_field->size;
@@ -1811,6 +1897,7 @@ static int trace__read_syscall_info(struct trace *trace, int id)
18111897
char tp_name[128];
18121898
struct syscall *sc;
18131899
const char *name = syscalltbl__name(trace->sctbl, id);
1900+
int err;
18141901

18151902
#ifdef HAVE_SYSCALL_TABLE_SUPPORT
18161903
if (trace->syscalls.table == NULL) {
@@ -1883,15 +1970,21 @@ static int trace__read_syscall_info(struct trace *trace, int id)
18831970
sc->is_exit = !strcmp(name, "exit_group") || !strcmp(name, "exit");
18841971
sc->is_open = !strcmp(name, "open") || !strcmp(name, "openat");
18851972

1886-
return syscall__set_arg_fmts(sc);
1973+
err = syscall__set_arg_fmts(sc);
1974+
1975+
/* after calling syscall__set_arg_fmts() we'll know whether use_btf is true */
1976+
if (sc->use_btf)
1977+
trace__load_vmlinux_btf(trace);
1978+
1979+
return err;
18871980
}
18881981

18891982
static int evsel__init_tp_arg_scnprintf(struct evsel *evsel)
18901983
{
18911984
struct syscall_arg_fmt *fmt = evsel__syscall_arg_fmt(evsel);
18921985

18931986
if (fmt != NULL) {
1894-
syscall_arg_fmt__init_array(fmt, evsel->tp_format->format.fields);
1987+
syscall_arg_fmt__init_array(fmt, evsel->tp_format->format.fields, NULL);
18951988
return 0;
18961989
}
18971990

@@ -2103,6 +2196,15 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size,
21032196
if (trace->show_arg_names)
21042197
printed += scnprintf(bf + printed, size - printed, "%s: ", field->name);
21052198

2199+
if (sc->arg_fmt[arg.idx].is_enum) {
2200+
size_t p = trace__btf_enum_scnprintf(trace, &sc->arg_fmt[arg.idx], bf + printed,
2201+
size - printed, val, field->type);
2202+
if (p) {
2203+
printed += p;
2204+
continue;
2205+
}
2206+
}
2207+
21062208
printed += syscall_arg_fmt__scnprintf_val(&sc->arg_fmt[arg.idx],
21072209
bf + printed, size - printed, &arg, val);
21082210
}

0 commit comments

Comments
 (0)