Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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 <howardchu95@gmail.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Günther Noack <gnoack@google.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Mickaël Salaün <mic@digikod.net>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: https://lore.kernel.org/lkml/20240613022757.3589783-1-howardchu95@gmail.com
Link: https://lore.kernel.org/lkml/ZnXAhFflUl_LV1QY@x1 # [1]
Link: https://lore.kernel.org/r/20240624181345.124764-3-howardchu95@gmail.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Howard Chu and committed by
Arnaldo Carvalho de Melo
45a0c928 e4fc196f

+106 -4
+106 -4
tools/perf/builtin-trace.c
··· 19 19 #ifdef HAVE_LIBBPF_SUPPORT 20 20 #include <bpf/bpf.h> 21 21 #include <bpf/libbpf.h> 22 + #include <bpf/btf.h> 22 23 #ifdef HAVE_BPF_SKEL 23 24 #include "bpf_skel/augmented_raw_syscalls.skel.h" 24 25 #endif ··· 111 110 const char *name; 112 111 u16 nr_entries; // for arrays 113 112 bool show_zero; 113 + bool is_enum; 114 + #ifdef HAVE_LIBBPF_SUPPORT 115 + const struct btf_type *type; 116 + #endif 114 117 }; 115 118 116 119 struct syscall_fmt { ··· 144 139 } syscalls; 145 140 #ifdef HAVE_BPF_SKEL 146 141 struct augmented_raw_syscalls_bpf *skel; 142 + #endif 143 + #ifdef HAVE_LIBBPF_SUPPORT 144 + struct btf *btf; 147 145 #endif 148 146 struct record_opts opts; 149 147 struct evlist *evlist; ··· 211 203 u64 last; 212 204 } oe; 213 205 }; 206 + 207 + static void trace__load_vmlinux_btf(struct trace *trace __maybe_unused) 208 + { 209 + #ifdef HAVE_LIBBPF_SUPPORT 210 + if (trace->btf != NULL) 211 + return; 212 + 213 + trace->btf = btf__load_vmlinux_btf(); 214 + if (verbose > 0) { 215 + fprintf(trace->output, trace->btf ? "vmlinux BTF loaded\n" : 216 + "Failed to load vmlinux BTF\n"); 217 + } 218 + #endif 219 + } 214 220 215 221 struct tp_field { 216 222 int offset; ··· 909 887 910 888 #define SCA_GETRANDOM_FLAGS syscall_arg__scnprintf_getrandom_flags 911 889 890 + #ifdef HAVE_LIBBPF_SUPPORT 891 + static int syscall_arg_fmt__cache_btf_enum(struct syscall_arg_fmt *arg_fmt, struct btf *btf, char *type) 892 + { 893 + int id; 894 + 895 + // Already cached? 896 + if (arg_fmt->type != NULL) 897 + return 0; 898 + 899 + type = strstr(type, "enum "); 900 + if (type == NULL) 901 + return -1; 902 + 903 + type += 5; // skip "enum " to get the enumeration name 904 + 905 + id = btf__find_by_name(btf, type); 906 + if (id < 0) 907 + return -1; 908 + 909 + arg_fmt->type = btf__type_by_id(btf, id); 910 + return arg_fmt->type == NULL ? -1 : 0; 911 + } 912 + 913 + static size_t btf_enum_scnprintf(const struct btf_type *type, struct btf *btf, char *bf, size_t size, int val) 914 + { 915 + struct btf_enum *be = btf_enum(type); 916 + const int nr_entries = btf_vlen(type); 917 + 918 + for (int i = 0; i < nr_entries; ++i, ++be) { 919 + if (be->val == val) { 920 + return scnprintf(bf, size, "%s", 921 + btf__name_by_offset(btf, be->name_off)); 922 + } 923 + } 924 + 925 + return 0; 926 + } 927 + 928 + static size_t trace__btf_enum_scnprintf(struct trace *trace, struct syscall_arg_fmt *arg_fmt, char *bf, 929 + size_t size, int val, char *type) 930 + { 931 + if (trace->btf == NULL) 932 + return 0; 933 + 934 + if (syscall_arg_fmt__cache_btf_enum(arg_fmt, trace->btf, type) < 0) 935 + return 0; 936 + 937 + return btf_enum_scnprintf(arg_fmt->type, trace->btf, bf, size, val); 938 + } 939 + #else // HAVE_LIBBPF_SUPPORT 940 + static size_t trace__btf_enum_scnprintf(struct trace *trace __maybe_unused, struct syscall_arg_fmt *arg_fmt __maybe_unused, 941 + char *bf __maybe_unused, size_t size __maybe_unused, int val __maybe_unused, 942 + char *type __maybe_unused) 943 + { 944 + return 0; 945 + } 946 + #endif // HAVE_LIBBPF_SUPPORT 947 + 912 948 #define STRARRAY(name, array) \ 913 949 { .scnprintf = SCA_STRARRAY, \ 914 950 .strtoul = STUL_STRARRAY, \ ··· 1318 1238 bool is_exit; 1319 1239 bool is_open; 1320 1240 bool nonexistent; 1241 + bool use_btf; 1321 1242 struct tep_format_field *args; 1322 1243 const char *name; 1323 1244 const struct syscall_fmt *fmt; ··· 1825 1744 } 1826 1745 1827 1746 static struct tep_format_field * 1828 - syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field *field) 1747 + syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field *field, 1748 + bool *use_btf) 1829 1749 { 1830 1750 struct tep_format_field *last_field = NULL; 1831 1751 int len; ··· 1838 1756 continue; 1839 1757 1840 1758 len = strlen(field->name); 1759 + arg->is_enum = false; 1841 1760 1842 1761 if (strcmp(field->type, "const char *") == 0 && 1843 1762 ((len >= 4 && strcmp(field->name + len - 4, "name") == 0) || ··· 1865 1782 * 7 unsigned long 1866 1783 */ 1867 1784 arg->scnprintf = SCA_FD; 1785 + } else if (strstr(field->type, "enum") && use_btf != NULL) { 1786 + *use_btf = arg->is_enum = true; 1868 1787 } else { 1869 1788 const struct syscall_arg_fmt *fmt = 1870 1789 syscall_arg_fmt__find_by_name(field->name); ··· 1883 1798 1884 1799 static int syscall__set_arg_fmts(struct syscall *sc) 1885 1800 { 1886 - struct tep_format_field *last_field = syscall_arg_fmt__init_array(sc->arg_fmt, sc->args); 1801 + struct tep_format_field *last_field = syscall_arg_fmt__init_array(sc->arg_fmt, sc->args, 1802 + &sc->use_btf); 1887 1803 1888 1804 if (last_field) 1889 1805 sc->args_size = last_field->offset + last_field->size; ··· 1897 1811 char tp_name[128]; 1898 1812 struct syscall *sc; 1899 1813 const char *name = syscalltbl__name(trace->sctbl, id); 1814 + int err; 1900 1815 1901 1816 #ifdef HAVE_SYSCALL_TABLE_SUPPORT 1902 1817 if (trace->syscalls.table == NULL) { ··· 1970 1883 sc->is_exit = !strcmp(name, "exit_group") || !strcmp(name, "exit"); 1971 1884 sc->is_open = !strcmp(name, "open") || !strcmp(name, "openat"); 1972 1885 1973 - return syscall__set_arg_fmts(sc); 1886 + err = syscall__set_arg_fmts(sc); 1887 + 1888 + /* after calling syscall__set_arg_fmts() we'll know whether use_btf is true */ 1889 + if (sc->use_btf) 1890 + trace__load_vmlinux_btf(trace); 1891 + 1892 + return err; 1974 1893 } 1975 1894 1976 1895 static int evsel__init_tp_arg_scnprintf(struct evsel *evsel) ··· 1984 1891 struct syscall_arg_fmt *fmt = evsel__syscall_arg_fmt(evsel); 1985 1892 1986 1893 if (fmt != NULL) { 1987 - syscall_arg_fmt__init_array(fmt, evsel->tp_format->format.fields); 1894 + syscall_arg_fmt__init_array(fmt, evsel->tp_format->format.fields, NULL); 1988 1895 return 0; 1989 1896 } 1990 1897 ··· 2195 2102 2196 2103 if (trace->show_arg_names) 2197 2104 printed += scnprintf(bf + printed, size - printed, "%s: ", field->name); 2105 + 2106 + if (sc->arg_fmt[arg.idx].is_enum) { 2107 + size_t p = trace__btf_enum_scnprintf(trace, &sc->arg_fmt[arg.idx], bf + printed, 2108 + size - printed, val, field->type); 2109 + if (p) { 2110 + printed += p; 2111 + continue; 2112 + } 2113 + } 2198 2114 2199 2115 printed += syscall_arg_fmt__scnprintf_val(&sc->arg_fmt[arg.idx], 2200 2116 bf + printed, size - printed, &arg, val);