* [PATCH v1 1/5] perf trace: Fix iteration of syscall ids in syscalltbl->entries
2024-06-18 15:26 [PATCH v1 0/5] perf trace: Augment enum arguments with BTF Howard Chu
@ 2024-06-18 15:26 ` Howard Chu
2024-06-18 15:26 ` [PATCH v1 2/5] perf trace: Augment enum syscall arguments with BTF Howard Chu
` (3 subsequent siblings)
4 siblings, 0 replies; 8+ messages in thread
From: Howard Chu @ 2024-06-18 15:26 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, Adrian Hunter, Kan Liang,
linux-kernel, linux-perf-users, Arnaldo Carvalho de Melo
This is a bug found when implementing pretty-printing for the
landlock_add_rule system call, I decided to send this patch separately
because this is a serious bug that should be fixed fast.
I wrote a test program to do landlock_add_rule syscall in a loop,
yet perf trace -e landlock_add_rule freezes, giving no output.
This bug is introduced by the false understanding of the variable "key"
below:
```
for (key = 0; key < trace->sctbl->syscalls.nr_entries; ++key) {
struct syscall *sc = trace__syscall_info(trace, NULL, key);
...
}
```
The code above seems right at the beginning, but when looking at
syscalltbl.c, I found these lines:
```
for (i = 0; i <= syscalltbl_native_max_id; ++i)
if (syscalltbl_native[i])
++nr_entries;
entries = tbl->syscalls.entries = malloc(sizeof(struct syscall) * nr_entries);
...
for (i = 0, j = 0; i <= syscalltbl_native_max_id; ++i) {
if (syscalltbl_native[i]) {
entries[j].name = syscalltbl_native[i];
entries[j].id = i;
++j;
}
}
```
meaning the key is merely an index to traverse the syscall table,
instead of the actual syscall id for this particular syscall.
So if one uses key to do trace__syscall_info(trace, NULL, key), because
key only goes up to trace->sctbl->syscalls.nr_entries, for example, on
my X86_64 machine, this number is 373, it will end up neglecting all
the rest of the syscall, in my case, everything after `rseq`, because
the traversal will stop at 373, and `rseq` is the last syscall whose id
is lower than 373
in tools/perf/arch/x86/include/generated/asm/syscalls_64.c:
```
...
[334] = "rseq",
[424] = "pidfd_send_signal",
...
```
The reason why the key is scrambled but perf trace works well is that
key is used in trace__syscall_info(trace, NULL, key) to do
trace->syscalls.table[id], this makes sure that the struct syscall returned
actually has an id the same value as key, making the later bpf_prog
matching all correct.
After fixing this bug, I can do perf trace on 38 more syscalls, and
because more syscalls are visible, we get 8 more syscalls that can be
augmented.
before:
perf $ perf trace -vv --max-events=1 |& grep Reusing
Reusing "open" BPF sys_enter augmenter for "stat"
Reusing "open" BPF sys_enter augmenter for "lstat"
Reusing "open" BPF sys_enter augmenter for "access"
Reusing "connect" BPF sys_enter augmenter for "accept"
Reusing "sendto" BPF sys_enter augmenter for "recvfrom"
Reusing "connect" BPF sys_enter augmenter for "bind"
Reusing "connect" BPF sys_enter augmenter for "getsockname"
Reusing "connect" BPF sys_enter augmenter for "getpeername"
Reusing "open" BPF sys_enter augmenter for "execve"
Reusing "open" BPF sys_enter augmenter for "truncate"
Reusing "open" BPF sys_enter augmenter for "chdir"
Reusing "open" BPF sys_enter augmenter for "mkdir"
Reusing "open" BPF sys_enter augmenter for "rmdir"
Reusing "open" BPF sys_enter augmenter for "creat"
Reusing "open" BPF sys_enter augmenter for "link"
Reusing "open" BPF sys_enter augmenter for "unlink"
Reusing "open" BPF sys_enter augmenter for "symlink"
Reusing "open" BPF sys_enter augmenter for "readlink"
Reusing "open" BPF sys_enter augmenter for "chmod"
Reusing "open" BPF sys_enter augmenter for "chown"
Reusing "open" BPF sys_enter augmenter for "lchown"
Reusing "open" BPF sys_enter augmenter for "mknod"
Reusing "open" BPF sys_enter augmenter for "statfs"
Reusing "open" BPF sys_enter augmenter for "pivot_root"
Reusing "open" BPF sys_enter augmenter for "chroot"
Reusing "open" BPF sys_enter augmenter for "acct"
Reusing "open" BPF sys_enter augmenter for "swapon"
Reusing "open" BPF sys_enter augmenter for "swapoff"
Reusing "open" BPF sys_enter augmenter for "delete_module"
Reusing "open" BPF sys_enter augmenter for "setxattr"
Reusing "open" BPF sys_enter augmenter for "lsetxattr"
Reusing "openat" BPF sys_enter augmenter for "fsetxattr"
Reusing "open" BPF sys_enter augmenter for "getxattr"
Reusing "open" BPF sys_enter augmenter for "lgetxattr"
Reusing "openat" BPF sys_enter augmenter for "fgetxattr"
Reusing "open" BPF sys_enter augmenter for "listxattr"
Reusing "open" BPF sys_enter augmenter for "llistxattr"
Reusing "open" BPF sys_enter augmenter for "removexattr"
Reusing "open" BPF sys_enter augmenter for "lremovexattr"
Reusing "fsetxattr" BPF sys_enter augmenter for "fremovexattr"
Reusing "open" BPF sys_enter augmenter for "mq_open"
Reusing "open" BPF sys_enter augmenter for "mq_unlink"
Reusing "fsetxattr" BPF sys_enter augmenter for "add_key"
Reusing "fremovexattr" BPF sys_enter augmenter for "request_key"
Reusing "fremovexattr" BPF sys_enter augmenter for "inotify_add_watch"
Reusing "fremovexattr" BPF sys_enter augmenter for "mkdirat"
Reusing "fremovexattr" BPF sys_enter augmenter for "mknodat"
Reusing "fremovexattr" BPF sys_enter augmenter for "fchownat"
Reusing "fremovexattr" BPF sys_enter augmenter for "futimesat"
Reusing "fremovexattr" BPF sys_enter augmenter for "newfstatat"
Reusing "fremovexattr" BPF sys_enter augmenter for "unlinkat"
Reusing "fremovexattr" BPF sys_enter augmenter for "linkat"
Reusing "open" BPF sys_enter augmenter for "symlinkat"
Reusing "fremovexattr" BPF sys_enter augmenter for "readlinkat"
Reusing "fremovexattr" BPF sys_enter augmenter for "fchmodat"
Reusing "fremovexattr" BPF sys_enter augmenter for "faccessat"
Reusing "fremovexattr" BPF sys_enter augmenter for "utimensat"
Reusing "connect" BPF sys_enter augmenter for "accept4"
Reusing "fremovexattr" BPF sys_enter augmenter for "name_to_handle_at"
Reusing "fremovexattr" BPF sys_enter augmenter for "renameat2"
Reusing "open" BPF sys_enter augmenter for "memfd_create"
Reusing "fremovexattr" BPF sys_enter augmenter for "execveat"
Reusing "fremovexattr" BPF sys_enter augmenter for "statx"
after
perf $ perf trace -vv --max-events=1 |& grep Reusing
Reusing "open" BPF sys_enter augmenter for "stat"
Reusing "open" BPF sys_enter augmenter for "lstat"
Reusing "open" BPF sys_enter augmenter for "access"
Reusing "connect" BPF sys_enter augmenter for "accept"
Reusing "sendto" BPF sys_enter augmenter for "recvfrom"
Reusing "connect" BPF sys_enter augmenter for "bind"
Reusing "connect" BPF sys_enter augmenter for "getsockname"
Reusing "connect" BPF sys_enter augmenter for "getpeername"
Reusing "open" BPF sys_enter augmenter for "execve"
Reusing "open" BPF sys_enter augmenter for "truncate"
Reusing "open" BPF sys_enter augmenter for "chdir"
Reusing "open" BPF sys_enter augmenter for "mkdir"
Reusing "open" BPF sys_enter augmenter for "rmdir"
Reusing "open" BPF sys_enter augmenter for "creat"
Reusing "open" BPF sys_enter augmenter for "link"
Reusing "open" BPF sys_enter augmenter for "unlink"
Reusing "open" BPF sys_enter augmenter for "symlink"
Reusing "open" BPF sys_enter augmenter for "readlink"
Reusing "open" BPF sys_enter augmenter for "chmod"
Reusing "open" BPF sys_enter augmenter for "chown"
Reusing "open" BPF sys_enter augmenter for "lchown"
Reusing "open" BPF sys_enter augmenter for "mknod"
Reusing "open" BPF sys_enter augmenter for "statfs"
Reusing "open" BPF sys_enter augmenter for "pivot_root"
Reusing "open" BPF sys_enter augmenter for "chroot"
Reusing "open" BPF sys_enter augmenter for "acct"
Reusing "open" BPF sys_enter augmenter for "swapon"
Reusing "open" BPF sys_enter augmenter for "swapoff"
Reusing "open" BPF sys_enter augmenter for "delete_module"
Reusing "open" BPF sys_enter augmenter for "setxattr"
Reusing "open" BPF sys_enter augmenter for "lsetxattr"
Reusing "openat" BPF sys_enter augmenter for "fsetxattr"
Reusing "open" BPF sys_enter augmenter for "getxattr"
Reusing "open" BPF sys_enter augmenter for "lgetxattr"
Reusing "openat" BPF sys_enter augmenter for "fgetxattr"
Reusing "open" BPF sys_enter augmenter for "listxattr"
Reusing "open" BPF sys_enter augmenter for "llistxattr"
Reusing "open" BPF sys_enter augmenter for "removexattr"
Reusing "open" BPF sys_enter augmenter for "lremovexattr"
Reusing "fsetxattr" BPF sys_enter augmenter for "fremovexattr"
Reusing "open" BPF sys_enter augmenter for "mq_open"
Reusing "open" BPF sys_enter augmenter for "mq_unlink"
Reusing "fsetxattr" BPF sys_enter augmenter for "add_key"
Reusing "fremovexattr" BPF sys_enter augmenter for "request_key"
Reusing "fremovexattr" BPF sys_enter augmenter for "inotify_add_watch"
Reusing "fremovexattr" BPF sys_enter augmenter for "mkdirat"
Reusing "fremovexattr" BPF sys_enter augmenter for "mknodat"
Reusing "fremovexattr" BPF sys_enter augmenter for "fchownat"
Reusing "fremovexattr" BPF sys_enter augmenter for "futimesat"
Reusing "fremovexattr" BPF sys_enter augmenter for "newfstatat"
Reusing "fremovexattr" BPF sys_enter augmenter for "unlinkat"
Reusing "fremovexattr" BPF sys_enter augmenter for "linkat"
Reusing "open" BPF sys_enter augmenter for "symlinkat"
Reusing "fremovexattr" BPF sys_enter augmenter for "readlinkat"
Reusing "fremovexattr" BPF sys_enter augmenter for "fchmodat"
Reusing "fremovexattr" BPF sys_enter augmenter for "faccessat"
Reusing "fremovexattr" BPF sys_enter augmenter for "utimensat"
Reusing "connect" BPF sys_enter augmenter for "accept4"
Reusing "fremovexattr" BPF sys_enter augmenter for "name_to_handle_at"
Reusing "fremovexattr" BPF sys_enter augmenter for "renameat2"
Reusing "open" BPF sys_enter augmenter for "memfd_create"
Reusing "fremovexattr" BPF sys_enter augmenter for "execveat"
Reusing "fremovexattr" BPF sys_enter augmenter for "statx"
TL;DR:
These are the new syscalls that can be augmented
Reusing "openat" BPF sys_enter augmenter for "open_tree"
Reusing "openat" BPF sys_enter augmenter for "openat2"
Reusing "openat" BPF sys_enter augmenter for "mount_setattr"
Reusing "openat" BPF sys_enter augmenter for "move_mount"
Reusing "open" BPF sys_enter augmenter for "fsopen"
Reusing "openat" BPF sys_enter augmenter for "fspick"
Reusing "openat" BPF sys_enter augmenter for "faccessat2"
Reusing "openat" BPF sys_enter augmenter for "fchmodat2"
as for the perf trace output:
before
perf $ perf trace -e faccessat2 --max-events=1
[no output]
after
perf $ ./perf trace -e faccessat2 --max-events=1
0.000 ( 0.037 ms): waybar/958 faccessat2(dfd: 40, filename: "uevent") = 0
P.S. The reason why this bug was not found in the past five years is
probably because it only happens to the newer syscalls whose id is
greater, for instance, faccessat2 of id 439, which not a lot of people
care about when using perf trace.
Commiter notes:
That and the fact that the BPF code was hidden before having to use -e,
that got changed kinda recently when we switched to using BPF skels for
augmenting syscalls in 'perf trace':
⬢[acme@toolbox perf-tools-next]$ git log --oneline tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c
a9f4c6c999008c92 perf trace: Collect sys_nanosleep first argument
29d16de26df17e94 perf augmented_raw_syscalls.bpf: Move 'struct timespec64' to vmlinux.h
5069211e2f0b47e7 perf trace: Use the right bpf_probe_read(_str) variant for reading user data
33b725ce7b988756 perf trace: Avoid compile error wrt redefining bool
7d9642311b6d9d31 perf bpf augmented_raw_syscalls: Add an assert to make sure sizeof(augmented_arg->value) is a power of two.
262b54b6c9396823 perf bpf augmented_raw_syscalls: Add an assert to make sure sizeof(saddr) is a power of two.
1836480429d173c0 perf bpf_skel augmented_raw_syscalls: Cap the socklen parameter using &= sizeof(saddr)
cd2cece61ac5f900 perf trace: Tidy comments related to BPF + syscall augmentation
5e6da6be3082f77b perf trace: Migrate BPF augmentation to use a skeleton
⬢[acme@toolbox perf-tools-next]$
⬢[acme@toolbox perf-tools-next]$ git show --oneline --pretty=reference 5e6da6be3082f77b | head -1
5e6da6be3082f77b (perf trace: Migrate BPF augmentation to use a skeleton, 2023-08-10)
⬢[acme@toolbox perf-tools-next]$
I.e. from August, 2023.
One had as well to ask for BUILD_BPF_SKEL=1, which now is default if all
it needs is available on the system.
I simplified the code to not expose the 'struct syscall' outside of
tools/perf/util/syscalltbl.c, instead providing a function to go from
the index to the syscall id:
int syscalltbl__id_at_idx(struct syscalltbl *tbl, int idx);
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Link: https://lore.kernel.org/lkml/ZmhlAxbVcAKoPTg8@x1
Signed-off-by: Howard Chu <howardchu95@gmail.com>
---
tools/perf/builtin-trace.c | 14 +++++++-------
tools/perf/util/syscalltbl.c | 7 +++++++
tools/perf/util/syscalltbl.h | 1 +
3 files changed, 15 insertions(+), 7 deletions(-)
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index c42bc608954e..c4fa8191253d 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -3354,8 +3354,6 @@ static int trace__bpf_prog_sys_exit_fd(struct trace *trace, int id)
static struct bpf_program *trace__find_usable_bpf_prog_entry(struct trace *trace, struct syscall *sc)
{
struct tep_format_field *field, *candidate_field;
- int id;
-
/*
* We're only interested in syscalls that have a pointer:
*/
@@ -3367,7 +3365,8 @@ static struct bpf_program *trace__find_usable_bpf_prog_entry(struct trace *trace
return NULL;
try_to_find_pair:
- for (id = 0; id < trace->sctbl->syscalls.nr_entries; ++id) {
+ for (int i = 0; i < trace->sctbl->syscalls.nr_entries; ++i) {
+ int id = syscalltbl__id_at_idx(trace->sctbl, i);
struct syscall *pair = trace__syscall_info(trace, NULL, id);
struct bpf_program *pair_prog;
bool is_candidate = false;
@@ -3456,10 +3455,10 @@ static int trace__init_syscalls_bpf_prog_array_maps(struct trace *trace)
{
int map_enter_fd = bpf_map__fd(trace->skel->maps.syscalls_sys_enter);
int map_exit_fd = bpf_map__fd(trace->skel->maps.syscalls_sys_exit);
- int err = 0, key;
+ int err = 0;
- for (key = 0; key < trace->sctbl->syscalls.nr_entries; ++key) {
- int prog_fd;
+ for (int i = 0; i < trace->sctbl->syscalls.nr_entries; ++i) {
+ int prog_fd, key = syscalltbl__id_at_idx(trace->sctbl, i);
if (!trace__syscall_enabled(trace, key))
continue;
@@ -3505,7 +3504,8 @@ static int trace__init_syscalls_bpf_prog_array_maps(struct trace *trace)
* first and second arg (this one on the raw_syscalls:sys_exit prog
* array tail call, then that one will be used.
*/
- for (key = 0; key < trace->sctbl->syscalls.nr_entries; ++key) {
+ for (int i = 0; i < trace->sctbl->syscalls.nr_entries; ++i) {
+ int key = syscalltbl__id_at_idx(trace->sctbl, i);
struct syscall *sc = trace__syscall_info(trace, NULL, key);
struct bpf_program *pair_prog;
int prog_fd;
diff --git a/tools/perf/util/syscalltbl.c b/tools/perf/util/syscalltbl.c
index 63be7b58761d..0dd26b991b3f 100644
--- a/tools/perf/util/syscalltbl.c
+++ b/tools/perf/util/syscalltbl.c
@@ -123,6 +123,13 @@ int syscalltbl__id(struct syscalltbl *tbl, const char *name)
return sc ? sc->id : -1;
}
+int syscalltbl__id_at_idx(struct syscalltbl *tbl, int idx)
+{
+ struct syscall *syscalls = tbl->syscalls.entries;
+
+ return idx < tbl->syscalls.nr_entries ? syscalls[idx].id : -1;
+}
+
int syscalltbl__strglobmatch_next(struct syscalltbl *tbl, const char *syscall_glob, int *idx)
{
int i;
diff --git a/tools/perf/util/syscalltbl.h b/tools/perf/util/syscalltbl.h
index a41d2ca9e4ae..2b53b7ed25a6 100644
--- a/tools/perf/util/syscalltbl.h
+++ b/tools/perf/util/syscalltbl.h
@@ -16,6 +16,7 @@ void syscalltbl__delete(struct syscalltbl *tbl);
const char *syscalltbl__name(const struct syscalltbl *tbl, int id);
int syscalltbl__id(struct syscalltbl *tbl, const char *name);
+int syscalltbl__id_at_idx(struct syscalltbl *tbl, int idx);
int syscalltbl__strglobmatch_first(struct syscalltbl *tbl, const char *syscall_glob, int *idx);
int syscalltbl__strglobmatch_next(struct syscalltbl *tbl, const char *syscall_glob, int *idx);
--
2.45.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v1 2/5] perf trace: Augment enum syscall arguments with BTF
2024-06-18 15:26 [PATCH v1 0/5] perf trace: Augment enum arguments with BTF Howard Chu
2024-06-18 15:26 ` [PATCH v1 1/5] perf trace: Fix iteration of syscall ids in syscalltbl->entries Howard Chu
@ 2024-06-18 15:26 ` Howard Chu
2024-06-18 15:26 ` [PATCH v1 3/5] perf trace: Augment enum tracepoint " Howard Chu
` (2 subsequent siblings)
4 siblings, 0 replies; 8+ messages in thread
From: Howard Chu @ 2024-06-18 15:26 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, Adrian Hunter, Kan Liang,
linux-kernel, linux-perf-users
This is a feature implemented on the basis of the previous bug fix
https://lore.kernel.org/linux-perf-users/d18a9606-ac9f-4ca7-afaf-fcf4c951cb90@web.de/T/#t
In this patch, BTF is used to turn enum value to the corresponding
enum variable 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 decides to run `perf trace`
without any arguments, the behaviour is to trace `landlock_add_rule`,
so vmlinux btf will be loaded by default.
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)
```
Tested-by: Arnaldo Carvalho de Melo <acme@kernel.org>
Suggested-by: Arnaldo Carvalho de Melo <acme@kernel.org>
Reviewed-by: Arnaldo Carvalho de Melo <acme@kernel.org>
Signed-off-by: Howard Chu <howardchu95@gmail.com>
---
tools/perf/builtin-trace.c | 96 ++++++++++++++++++++++++++++++++++++--
1 file changed, 91 insertions(+), 5 deletions(-)
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index c4fa8191253d..d93f34e9af74 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -19,6 +19,7 @@
#ifdef HAVE_LIBBPF_SUPPORT
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
+#include <bpf/btf.h>
#ifdef HAVE_BPF_SKEL
#include "bpf_skel/augmented_raw_syscalls.skel.h"
#endif
@@ -110,6 +111,11 @@ struct syscall_arg_fmt {
const char *name;
u16 nr_entries; // for arrays
bool show_zero;
+ bool is_enum;
+ struct {
+ void *entries;
+ u16 nr_entries;
+ } btf_entry;
};
struct syscall_fmt {
@@ -140,6 +146,7 @@ struct trace {
#ifdef HAVE_BPF_SKEL
struct augmented_raw_syscalls_bpf *skel;
#endif
+ struct btf *btf;
struct record_opts opts;
struct evlist *evlist;
struct machine *host;
@@ -897,6 +904,56 @@ static size_t syscall_arg__scnprintf_getrandom_flags(char *bf, size_t size,
.strtoul = STUL_STRARRAY_FLAGS, \
.parm = &strarray__##array, }
+static int btf_enum_find_entry(struct btf *btf, char *type, struct syscall_arg_fmt *arg_fmt)
+{
+ const struct btf_type *bt;
+ char enum_prefix[][16] = { "enum", "const enum" }, *ep;
+ int id;
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(enum_prefix); i++) {
+ ep = enum_prefix[i];
+ if (strlen(type) > strlen(ep) + 1 && strstarts(type, ep))
+ type += strlen(ep) + 1;
+ }
+
+ id = btf__find_by_name(btf, type);
+ if (id < 0)
+ return -1;
+
+ bt = btf__type_by_id(btf, id);
+ if (bt == NULL)
+ return -1;
+
+ arg_fmt->btf_entry.entries = btf_enum(bt);
+ arg_fmt->btf_entry.nr_entries = btf_vlen(bt);
+
+ return 0;
+}
+
+static size_t btf_enum_scnprintf(char *bf, size_t size, int val, struct btf *btf, char *type,
+ struct syscall_arg_fmt *arg_fmt)
+{
+ struct btf_enum *be;
+ int i;
+
+ /* if btf_entry is NULL, find and save it to arg_fmt */
+ if (arg_fmt->btf_entry.entries == NULL)
+ if (btf_enum_find_entry(btf, type, arg_fmt))
+ return 0;
+
+ be = (struct btf_enum *)arg_fmt->btf_entry.entries;
+
+ for (i = 0; i < arg_fmt->btf_entry.nr_entries; ++i, ++be) {
+ if (be->val == val) {
+ return scnprintf(bf, size, "%s",
+ btf__name_by_offset(btf, be->name_off));
+ }
+ }
+
+ return 0;
+}
+
#include "trace/beauty/arch_errno_names.c"
#include "trace/beauty/eventfd.c"
#include "trace/beauty/futex_op.c"
@@ -1699,6 +1756,15 @@ static void trace__symbols__exit(struct trace *trace)
symbol__exit();
}
+static void trace__load_vmlinux_btf(struct trace *trace)
+{
+ trace->btf = btf__load_vmlinux_btf();
+ if (verbose > 0) {
+ fprintf(trace->output, trace->btf ? "vmlinux BTF loaded\n" :
+ "Failed to load vmlinux BTF\n");
+ }
+}
+
static int syscall__alloc_arg_fmts(struct syscall *sc, int nr_args)
{
int idx;
@@ -1744,7 +1810,7 @@ static const struct syscall_arg_fmt *syscall_arg_fmt__find_by_name(const char *n
}
static struct tep_format_field *
-syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field *field)
+syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field *field, bool *use_btf)
{
struct tep_format_field *last_field = NULL;
int len;
@@ -1782,6 +1848,8 @@ syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field
* 7 unsigned long
*/
arg->scnprintf = SCA_FD;
+ } else if (strstr(field->type, "enum") && use_btf != NULL) {
+ *use_btf = arg->is_enum = true;
} else {
const struct syscall_arg_fmt *fmt =
syscall_arg_fmt__find_by_name(field->name);
@@ -1796,9 +1864,10 @@ syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field
return last_field;
}
-static int syscall__set_arg_fmts(struct syscall *sc)
+static int syscall__set_arg_fmts(struct syscall *sc, bool *use_btf)
{
- struct tep_format_field *last_field = syscall_arg_fmt__init_array(sc->arg_fmt, sc->args);
+ struct tep_format_field *last_field = syscall_arg_fmt__init_array(sc->arg_fmt, sc->args,
+ use_btf);
if (last_field)
sc->args_size = last_field->offset + last_field->size;
@@ -1810,7 +1879,9 @@ static int trace__read_syscall_info(struct trace *trace, int id)
{
char tp_name[128];
struct syscall *sc;
+ int err;
const char *name = syscalltbl__name(trace->sctbl, id);
+ bool use_btf = false;
#ifdef HAVE_SYSCALL_TABLE_SUPPORT
if (trace->syscalls.table == NULL) {
@@ -1883,7 +1954,12 @@ static int trace__read_syscall_info(struct trace *trace, int id)
sc->is_exit = !strcmp(name, "exit_group") || !strcmp(name, "exit");
sc->is_open = !strcmp(name, "open") || !strcmp(name, "openat");
- return syscall__set_arg_fmts(sc);
+ err = syscall__set_arg_fmts(sc, &use_btf);
+
+ if (use_btf && trace->btf == NULL)
+ trace__load_vmlinux_btf(trace);
+
+ return err;
}
static int evsel__init_tp_arg_scnprintf(struct evsel *evsel)
@@ -1891,7 +1967,7 @@ static int evsel__init_tp_arg_scnprintf(struct evsel *evsel)
struct syscall_arg_fmt *fmt = evsel__syscall_arg_fmt(evsel);
if (fmt != NULL) {
- syscall_arg_fmt__init_array(fmt, evsel->tp_format->format.fields);
+ syscall_arg_fmt__init_array(fmt, evsel->tp_format->format.fields, NULL);
return 0;
}
@@ -2103,6 +2179,16 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size,
if (trace->show_arg_names)
printed += scnprintf(bf + printed, size - printed, "%s: ", field->name);
+ if (sc->arg_fmt[arg.idx].is_enum && trace->btf) {
+ size_t p = btf_enum_scnprintf(bf + printed, size - printed, val,
+ trace->btf, field->type,
+ &sc->arg_fmt[arg.idx]);
+ if (p) {
+ printed += p;
+ continue;
+ }
+ }
+
printed += syscall_arg_fmt__scnprintf_val(&sc->arg_fmt[arg.idx],
bf + printed, size - printed, &arg, val);
}
--
2.45.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v1 3/5] perf trace: Augment enum tracepoint arguments with BTF
2024-06-18 15:26 [PATCH v1 0/5] perf trace: Augment enum arguments with BTF Howard Chu
2024-06-18 15:26 ` [PATCH v1 1/5] perf trace: Fix iteration of syscall ids in syscalltbl->entries Howard Chu
2024-06-18 15:26 ` [PATCH v1 2/5] perf trace: Augment enum syscall arguments with BTF Howard Chu
@ 2024-06-18 15:26 ` Howard Chu
2024-06-18 15:26 ` [PATCH v1 4/5] perf trace: Filter enum arguments with enum names Howard Chu
2024-06-18 15:26 ` [PATCH v1 5/5] perf trace: Add test for enum augmentation Howard Chu
4 siblings, 0 replies; 8+ messages in thread
From: Howard Chu @ 2024-06-18 15:26 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, Adrian Hunter, Kan Liang,
linux-kernel, linux-perf-users
Before:
perf $ ./perf trace -e timer:hrtimer_start --max-events=1
0.000 :0/0 timer:hrtimer_start(hrtimer: 0xffff974466c25f18, function: 0xffffffff89da5be0, expires: 377432432256753, softexpires: 377432432256753, mode: 10)
After:
perf $ ./perf trace -e timer:hrtimer_start --max-events=1
0.000 :0/0 timer:hrtimer_start(hrtimer: 0xffff974466d25f18, function: 0xffffffff89da5be0, expires: 488283834504945, softexpires: 488283834504945, mode: HRTIMER_MODE_ABS_PINNED_HARD)
HRTIMER_MODE_ABS_PINNED_HARD is:
perf $ pahole hrtimer_mode
enum hrtimer_mode {
HRTIMER_MODE_ABS = 0,
HRTIMER_MODE_REL = 1,
HRTIMER_MODE_PINNED = 2,
HRTIMER_MODE_SOFT = 4,
HRTIMER_MODE_HARD = 8,
HRTIMER_MODE_ABS_PINNED = 2,
HRTIMER_MODE_REL_PINNED = 3,
HRTIMER_MODE_ABS_SOFT = 4,
HRTIMER_MODE_REL_SOFT = 5,
HRTIMER_MODE_ABS_PINNED_SOFT = 6,
HRTIMER_MODE_REL_PINNED_SOFT = 7,
HRTIMER_MODE_ABS_HARD = 8,
HRTIMER_MODE_REL_HARD = 9,
HRTIMER_MODE_ABS_PINNED_HARD = 10,
HRTIMER_MODE_REL_PINNED_HARD = 11,
};
Can also be tested by
./perf trace -e pagemap:mm_lru_insertion,timer:hrtimer_start,timer:hrtimer_init,skb:kfree_skb --max-events=10
(Chose these 4 events because they happen quite frequently.)
However some enum arguments may not be contained in vmlinux BTF. To see
what enum arguments are supported, use:
vmlinux_dir $ bpftool btf dump file /sys/kernel/btf/vmlinux > vmlinux
vmlinux_dir $ while read l; do grep "ENUM '$l'" vmlinux; done < <(grep field:enum /sys/kernel/tracing/events/*/*/format | awk '{print $3}' | sort | uniq) | awk '{print $3}' | sed "s/'\(.*\)'/\1/g"
dev_pm_qos_req_type
error_detector
hrtimer_mode
i2c_slave_event
ieee80211_bss_type
lru_list
migrate_mode
nl80211_auth_type
nl80211_band
nl80211_iftype
numa_vmaskip_reason
pm_qos_req_action
pwm_polarity
skb_drop_reason
thermal_trip_type
xen_lazy_mode
xen_mc_extend_args
xen_mc_flush_reason
zone_type
And what tracepoints have these enum types as their arguments:
vmlinux_dir $ while read l; do grep "ENUM '$l'" vmlinux; done < <(grep field:enum /sys/kernel/tracing/events/*/*/format | awk '{print $3}' | sort | uniq) | awk '{print $3}' | sed "s/'\(.*\)'/\1/g" > good_enums
vmlinux_dir $ cat good_enums
dev_pm_qos_req_type
error_detector
hrtimer_mode
i2c_slave_event
ieee80211_bss_type
lru_list
migrate_mode
nl80211_auth_type
nl80211_band
nl80211_iftype
numa_vmaskip_reason
pm_qos_req_action
pwm_polarity
skb_drop_reason
thermal_trip_type
xen_lazy_mode
xen_mc_extend_args
xen_mc_flush_reason
zone_type
vmlinux_dir $ grep -f good_enums -l /sys/kernel/tracing/events/*/*/format
/sys/kernel/tracing/events/cfg80211/cfg80211_chandef_dfs_required/format
/sys/kernel/tracing/events/cfg80211/cfg80211_ch_switch_notify/format
/sys/kernel/tracing/events/cfg80211/cfg80211_ch_switch_started_notify/format
/sys/kernel/tracing/events/cfg80211/cfg80211_get_bss/format
/sys/kernel/tracing/events/cfg80211/cfg80211_ibss_joined/format
/sys/kernel/tracing/events/cfg80211/cfg80211_inform_bss_frame/format
/sys/kernel/tracing/events/cfg80211/cfg80211_radar_event/format
/sys/kernel/tracing/events/cfg80211/cfg80211_ready_on_channel_expired/format
/sys/kernel/tracing/events/cfg80211/cfg80211_ready_on_channel/format
/sys/kernel/tracing/events/cfg80211/cfg80211_reg_can_beacon/format
/sys/kernel/tracing/events/cfg80211/cfg80211_return_bss/format
/sys/kernel/tracing/events/cfg80211/cfg80211_tx_mgmt_expired/format
/sys/kernel/tracing/events/cfg80211/rdev_add_virtual_intf/format
/sys/kernel/tracing/events/cfg80211/rdev_auth/format
/sys/kernel/tracing/events/cfg80211/rdev_change_virtual_intf/format
/sys/kernel/tracing/events/cfg80211/rdev_channel_switch/format
/sys/kernel/tracing/events/cfg80211/rdev_connect/format
/sys/kernel/tracing/events/cfg80211/rdev_inform_bss/format
/sys/kernel/tracing/events/cfg80211/rdev_libertas_set_mesh_channel/format
/sys/kernel/tracing/events/cfg80211/rdev_mgmt_tx/format
/sys/kernel/tracing/events/cfg80211/rdev_remain_on_channel/format
/sys/kernel/tracing/events/cfg80211/rdev_return_chandef/format
/sys/kernel/tracing/events/cfg80211/rdev_return_int_survey_info/format
/sys/kernel/tracing/events/cfg80211/rdev_set_ap_chanwidth/format
/sys/kernel/tracing/events/cfg80211/rdev_set_monitor_channel/format
/sys/kernel/tracing/events/cfg80211/rdev_set_radar_background/format
/sys/kernel/tracing/events/cfg80211/rdev_start_ap/format
/sys/kernel/tracing/events/cfg80211/rdev_start_radar_detection/format
/sys/kernel/tracing/events/cfg80211/rdev_tdls_channel_switch/format
/sys/kernel/tracing/events/compaction/mm_compaction_defer_compaction/format
/sys/kernel/tracing/events/compaction/mm_compaction_deferred/format
/sys/kernel/tracing/events/compaction/mm_compaction_defer_reset/format
/sys/kernel/tracing/events/compaction/mm_compaction_finished/format
/sys/kernel/tracing/events/compaction/mm_compaction_kcompactd_wake/format
/sys/kernel/tracing/events/compaction/mm_compaction_suitable/format
/sys/kernel/tracing/events/compaction/mm_compaction_wakeup_kcompactd/format
/sys/kernel/tracing/events/error_report/error_report_end/format
/sys/kernel/tracing/events/i2c_slave/i2c_slave/format
/sys/kernel/tracing/events/migrate/mm_migrate_pages/format
/sys/kernel/tracing/events/migrate/mm_migrate_pages_start/format
/sys/kernel/tracing/events/pagemap/mm_lru_insertion/format
/sys/kernel/tracing/events/power/dev_pm_qos_add_request/format
/sys/kernel/tracing/events/power/dev_pm_qos_remove_request/format
/sys/kernel/tracing/events/power/dev_pm_qos_update_request/format
/sys/kernel/tracing/events/power/pm_qos_update_flags/format
/sys/kernel/tracing/events/power/pm_qos_update_target/format
/sys/kernel/tracing/events/pwm/pwm_apply/format
/sys/kernel/tracing/events/pwm/pwm_get/format
/sys/kernel/tracing/events/sched/sched_skip_vma_numa/format
/sys/kernel/tracing/events/skb/kfree_skb/format
/sys/kernel/tracing/events/thermal/thermal_zone_trip/format
/sys/kernel/tracing/events/timer/hrtimer_init/format
/sys/kernel/tracing/events/timer/hrtimer_start/format
/sys/kernel/tracing/events/xen/xen_mc_batch/format
/sys/kernel/tracing/events/xen/xen_mc_extend_args/format
/sys/kernel/tracing/events/xen/xen_mc_flush_reason/format
/sys/kernel/tracing/events/xen/xen_mc_issue/format
Tested-by: Arnaldo Carvalho de Melo <acme@kernel.org>
Suggested-by: Arnaldo Carvalho de Melo <acme@kernel.org>
Reviewed-by: Arnaldo Carvalho de Melo <acme@kernel.org>
Signed-off-by: Howard Chu <howardchu95@gmail.com>
---
tools/perf/builtin-trace.c | 29 ++++++++++++++++++++++-------
1 file changed, 22 insertions(+), 7 deletions(-)
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index d93f34e9af74..bd16679fb4c0 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -1962,12 +1962,12 @@ static int trace__read_syscall_info(struct trace *trace, int id)
return err;
}
-static int evsel__init_tp_arg_scnprintf(struct evsel *evsel)
+static int evsel__init_tp_arg_scnprintf(struct evsel *evsel, bool *use_btf)
{
struct syscall_arg_fmt *fmt = evsel__syscall_arg_fmt(evsel);
if (fmt != NULL) {
- syscall_arg_fmt__init_array(fmt, evsel->tp_format->format.fields, NULL);
+ syscall_arg_fmt__init_array(fmt, evsel->tp_format->format.fields, use_btf);
return 0;
}
@@ -2171,7 +2171,8 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size,
* property isn't set.
*/
if (val == 0 && !trace->show_zeros &&
- !(sc->arg_fmt && sc->arg_fmt[arg.idx].show_zero))
+ !(sc->arg_fmt && sc->arg_fmt[arg.idx].show_zero) &&
+ !(sc->arg_fmt && sc->arg_fmt[arg.idx].is_enum))
continue;
printed += scnprintf(bf + printed, size - printed, "%s", printed ? ", " : "");
@@ -2877,7 +2878,7 @@ static size_t trace__fprintf_tp_fields(struct trace *trace, struct evsel *evsel,
val = syscall_arg_fmt__mask_val(arg, &syscall_arg, val);
/* Suppress this argument if its value is zero and show_zero property isn't set. */
- if (val == 0 && !trace->show_zeros && !arg->show_zero)
+ if (val == 0 && !trace->show_zeros && !arg->show_zero && !arg->is_enum)
continue;
printed += scnprintf(bf + printed, size - printed, "%s", printed ? ", " : "");
@@ -2885,6 +2886,15 @@ static size_t trace__fprintf_tp_fields(struct trace *trace, struct evsel *evsel,
if (trace->show_arg_names)
printed += scnprintf(bf + printed, size - printed, "%s: ", field->name);
+ if (arg->is_enum && trace->btf) {
+ size_t p = btf_enum_scnprintf(bf + printed, size - printed, val, trace->btf,
+ field->type, arg);
+ if (p) {
+ printed += p;
+ continue;
+ }
+ }
+
printed += syscall_arg_fmt__scnprintf_val(arg, bf + printed, size - printed, &syscall_arg, val);
}
@@ -4537,7 +4547,7 @@ static void evsel__set_syscall_arg_fmt(struct evsel *evsel, const char *name)
}
}
-static int evlist__set_syscall_tp_fields(struct evlist *evlist)
+static int evlist__set_syscall_tp_fields(struct evlist *evlist, bool *use_btf)
{
struct evsel *evsel;
@@ -4546,7 +4556,7 @@ static int evlist__set_syscall_tp_fields(struct evlist *evlist)
continue;
if (strcmp(evsel->tp_format->system, "syscalls")) {
- evsel__init_tp_arg_scnprintf(evsel);
+ evsel__init_tp_arg_scnprintf(evsel, use_btf);
continue;
}
@@ -5024,11 +5034,16 @@ int cmd_trace(int argc, const char **argv)
}
if (trace.evlist->core.nr_entries > 0) {
+ bool use_btf = false;
+
evlist__set_default_evsel_handler(trace.evlist, trace__event_handler);
- if (evlist__set_syscall_tp_fields(trace.evlist)) {
+ if (evlist__set_syscall_tp_fields(trace.evlist, &use_btf)) {
perror("failed to set syscalls:* tracepoint fields");
goto out;
}
+
+ if (use_btf && trace.btf == NULL)
+ trace__load_vmlinux_btf(&trace);
}
if (trace.sort_events) {
--
2.45.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v1 4/5] perf trace: Filter enum arguments with enum names
2024-06-18 15:26 [PATCH v1 0/5] perf trace: Augment enum arguments with BTF Howard Chu
` (2 preceding siblings ...)
2024-06-18 15:26 ` [PATCH v1 3/5] perf trace: Augment enum tracepoint " Howard Chu
@ 2024-06-18 15:26 ` Howard Chu
2024-06-18 15:26 ` [PATCH v1 5/5] perf trace: Add test for enum augmentation Howard Chu
4 siblings, 0 replies; 8+ messages in thread
From: Howard Chu @ 2024-06-18 15:26 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, Adrian Hunter, Kan Liang,
linux-kernel, linux-perf-users
Before:
perf $ ./perf trace -e timer:hrtimer_start --filter='mode!=HRTIMER_MODE_ABS_PINNED_HARD' --max-events=1
No resolver (strtoul) for "mode" in "timer:hrtimer_start", can't set filter "(mode!=HRTIMER_MODE_ABS_PINNED_HARD) && (common_pid != 281988)"
After:
perf $ ./perf trace -e timer:hrtimer_start --filter='mode!=HRTIMER_MODE_ABS_PINNED_HARD' --max-events=1
0.000 :0/0 timer:hrtimer_start(hrtimer: 0xffff9498a6ca5f18, function: 0xffffffffa77a5be0, expires: 12351248764875, softexpires: 12351248764875, mode: HRTIMER_MODE_ABS)
&& and ||:
perf $ ./perf trace -e timer:hrtimer_start --filter='mode != HRTIMER_MODE_ABS_PINNED_HARD && mode != HRTIMER_MODE_ABS' --max-events=1
0.000 Hyprland/534 timer:hrtimer_start(hrtimer: 0xffff9497801a84d0, function: 0xffffffffc04cdbe0, expires: 12639434638458, softexpires: 12639433638458, mode: HRTIMER_MODE_REL)
perf $ ./perf trace -e timer:hrtimer_start --filter='mode == HRTIMER_MODE_REL || mode == HRTIMER_MODE_PINNED' --max-events=1
0.000 ldlck-test/60639 timer:hrtimer_start(hrtimer: 0xffffb16404ee7bf8, function: 0xffffffffa7790420, expires: 12772614418016, softexpires: 12772614368016, mode: HRTIMER_MODE_REL)
Switching it up, using both enum name and integer value(--filter='mode == HRTIMER_MODE_ABS_PINNED_HARD || mode == 0'):
perf $ ./perf trace -e timer:hrtimer_start --filter='mode == HRTIMER_MODE_ABS_PINNED_HARD || mode == 0' --max-events=3
0.000 :0/0 timer:hrtimer_start(hrtimer: 0xffff9498a6ca5f18, function: 0xffffffffa77a5be0, expires: 12601748739825, softexpires: 12601748739825, mode: HRTIMER_MODE_ABS_PINNED_HARD)
0.036 :0/0 timer:hrtimer_start(hrtimer: 0xffff9498a6ca5f18, function: 0xffffffffa77a5be0, expires: 12518758748124, softexpires: 12518758748124, mode: HRTIMER_MODE_ABS_PINNED_HARD)
0.172 tmux: server/41881 timer:hrtimer_start(hrtimer: 0xffffb164081e7838, function: 0xffffffffa7790420, expires: 12518768255836, softexpires: 12518768205836, mode: HRTIMER_MODE_ABS)
P.S.
perf $ pahole hrtimer_mode
enum hrtimer_mode {
HRTIMER_MODE_ABS = 0,
HRTIMER_MODE_REL = 1,
HRTIMER_MODE_PINNED = 2,
HRTIMER_MODE_SOFT = 4,
HRTIMER_MODE_HARD = 8,
HRTIMER_MODE_ABS_PINNED = 2,
HRTIMER_MODE_REL_PINNED = 3,
HRTIMER_MODE_ABS_SOFT = 4,
HRTIMER_MODE_REL_SOFT = 5,
HRTIMER_MODE_ABS_PINNED_SOFT = 6,
HRTIMER_MODE_REL_PINNED_SOFT = 7,
HRTIMER_MODE_ABS_HARD = 8,
HRTIMER_MODE_REL_HARD = 9,
HRTIMER_MODE_ABS_PINNED_HARD = 10,
HRTIMER_MODE_REL_PINNED_HARD = 11,
};
Tested-by: Arnaldo Carvalho de Melo <acme@kernel.org>
Suggested-by: Arnaldo Carvalho de Melo <acme@kernel.org>
Reviewed-by: Arnaldo Carvalho de Melo <acme@kernel.org>
Signed-off-by: Howard Chu <howardchu95@gmail.com>
---
tools/perf/builtin-trace.c | 89 ++++++++++++++++++++++++++++++++------
1 file changed, 76 insertions(+), 13 deletions(-)
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index bd16679fb4c0..1148c3edee97 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -904,11 +904,36 @@ static size_t syscall_arg__scnprintf_getrandom_flags(char *bf, size_t size,
.strtoul = STUL_STRARRAY_FLAGS, \
.parm = &strarray__##array, }
-static int btf_enum_find_entry(struct btf *btf, char *type, struct syscall_arg_fmt *arg_fmt)
+#define SCA_GETRANDOM_FLAGS syscall_arg__scnprintf_getrandom_flags
+
+static const struct btf_type *btf_find_type(struct btf *btf, char *type)
{
const struct btf_type *bt;
+ int id = btf__find_by_name(btf, type);
+
+ if (id < 0)
+ return NULL;
+
+ bt = btf__type_by_id(btf, id);
+ if (bt == NULL)
+ return NULL;
+
+ return bt;
+}
+
+struct btf_parm {
+ struct btf *btf;
+ char *type;
+};
+
+static bool syscall_arg__strtoul_btf_enum(char *bf, size_t size, struct syscall_arg *arg, u64 *val)
+{
+ struct btf_parm *bparm = arg->parm;
+ struct btf *btf = bparm->btf;
+ char *type = bparm->type;
char enum_prefix[][16] = { "enum", "const enum" }, *ep;
- int id;
+ struct btf_enum *be;
+ const struct btf_type *bt;
size_t i;
for (i = 0; i < ARRAY_SIZE(enum_prefix); i++) {
@@ -917,11 +942,38 @@ static int btf_enum_find_entry(struct btf *btf, char *type, struct syscall_arg_f
type += strlen(ep) + 1;
}
- id = btf__find_by_name(btf, type);
- if (id < 0)
- return -1;
+ bt = btf_find_type(btf, type);
+ if (bt == NULL)
+ return false;
- bt = btf__type_by_id(btf, id);
+ for (be = btf_enum(bt), i = 0; i < btf_vlen(bt); ++i, ++be) {
+ const char *name = btf__name_by_offset(btf, be->name_off);
+ int max_len = max(size, strlen(name));
+
+ if (strncmp(name, bf, max_len) == 0) {
+ *val = be->val;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+#define STUL_BTF_ENUM syscall_arg__strtoul_btf_enum
+
+static int btf_enum_find_entry(struct btf *btf, char *type, struct syscall_arg_fmt *arg_fmt)
+{
+ char enum_prefix[][16] = { "enum", "const enum" }, *ep;
+ const struct btf_type *bt;
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(enum_prefix); i++) {
+ ep = enum_prefix[i];
+ if (strlen(type) > strlen(ep) + 1 && strstarts(type, ep))
+ type += strlen(ep) + 1;
+ }
+
+ bt = btf_find_type(btf, type);
if (bt == NULL)
return -1;
@@ -1850,6 +1902,7 @@ syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field
arg->scnprintf = SCA_FD;
} else if (strstr(field->type, "enum") && use_btf != NULL) {
*use_btf = arg->is_enum = true;
+ arg->strtoul = STUL_BTF_ENUM;
} else {
const struct syscall_arg_fmt *fmt =
syscall_arg_fmt__find_by_name(field->name);
@@ -3776,7 +3829,8 @@ static int ordered_events__deliver_event(struct ordered_events *oe,
return __trace__deliver_event(trace, event->event);
}
-static struct syscall_arg_fmt *evsel__find_syscall_arg_fmt_by_name(struct evsel *evsel, char *arg)
+static struct syscall_arg_fmt *evsel__find_syscall_arg_fmt_by_name(struct evsel *evsel, char *arg,
+ char **type)
{
struct tep_format_field *field;
struct syscall_arg_fmt *fmt = __evsel__syscall_arg_fmt(evsel);
@@ -3785,8 +3839,10 @@ static struct syscall_arg_fmt *evsel__find_syscall_arg_fmt_by_name(struct evsel
return NULL;
for (field = evsel->tp_format->format.fields; field; field = field->next, ++fmt)
- if (strcmp(field->name, arg) == 0)
+ if (strcmp(field->name, arg) == 0) {
+ *type = field->type;
return fmt;
+ }
return NULL;
}
@@ -3824,14 +3880,14 @@ static int trace__expand_filter(struct trace *trace __maybe_unused, struct evsel
struct syscall_arg_fmt *fmt;
int left_size = tok - left,
right_size = right_end - right;
- char arg[128];
+ char arg[128], *type;
while (isspace(left[left_size - 1]))
--left_size;
scnprintf(arg, sizeof(arg), "%.*s", left_size, left);
- fmt = evsel__find_syscall_arg_fmt_by_name(evsel, arg);
+ fmt = evsel__find_syscall_arg_fmt_by_name(evsel, arg, &type);
if (fmt == NULL) {
pr_err("\"%s\" not found in \"%s\", can't set filter \"%s\"\n",
arg, evsel->name, evsel->filter);
@@ -3843,9 +3899,16 @@ static int trace__expand_filter(struct trace *trace __maybe_unused, struct evsel
if (fmt->strtoul) {
u64 val;
- struct syscall_arg syscall_arg = {
- .parm = fmt->parm,
- };
+ struct syscall_arg syscall_arg;
+ struct btf_parm bparm;
+
+ if (fmt->is_enum) {
+ bparm.btf = trace->btf;
+ bparm.type = type;
+ syscall_arg.parm = &bparm;
+ } else {
+ syscall_arg.parm = fmt->parm;
+ }
if (fmt->strtoul(right, right_size, &syscall_arg, &val)) {
char *n, expansion[19];
--
2.45.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v1 5/5] perf trace: Add test for enum augmentation
2024-06-18 15:26 [PATCH v1 0/5] perf trace: Augment enum arguments with BTF Howard Chu
` (3 preceding siblings ...)
2024-06-18 15:26 ` [PATCH v1 4/5] perf trace: Filter enum arguments with enum names Howard Chu
@ 2024-06-18 15:26 ` Howard Chu
2024-06-18 17:56 ` Arnaldo Carvalho de Melo
4 siblings, 1 reply; 8+ messages in thread
From: Howard Chu @ 2024-06-18 15:26 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, Adrian Hunter, Kan Liang,
linux-kernel, linux-perf-users
Check for vmlinux's existence in sysfs as prerequisite.
Compile and run a script which calls landlock_add_rule syscall,
trace the syscall to judge if the output is desirable.
Trace the non-syscall tracepoint 'timer:hrtimer_init' and
'timer:hrtimer_start', see if the 'mode' argument is augmented,
the 'mode' enum argument has the prefix of 'HRTIMER_MODE_'
in its name.
Signed-off-by: Howard Chu <howardchu95@gmail.com>
---
tools/perf/tests/shell/trace_btf_enum.sh | 104 +++++++++++++++++++++++
1 file changed, 104 insertions(+)
create mode 100755 tools/perf/tests/shell/trace_btf_enum.sh
diff --git a/tools/perf/tests/shell/trace_btf_enum.sh b/tools/perf/tests/shell/trace_btf_enum.sh
new file mode 100755
index 000000000000..14c73b0b594d
--- /dev/null
+++ b/tools/perf/tests/shell/trace_btf_enum.sh
@@ -0,0 +1,104 @@
+#!/bin/sh
+# perf trace enum augmentation tests
+# SPDX-License-Identifier: GPL-2.0
+
+err=0
+set -e
+
+syscall="landlock_add_rule"
+non_syscall="timer:hrtimer_init,timer:hrtimer_start"
+
+landlock_script=$(mktemp /tmp/landlock-XXXXX.c)
+landlock_ex=$(echo $landlock_script | sed -E 's/(.*).c$/\1/g')
+
+landlock_fd=24
+landlock_flags=25
+
+. "$(dirname $0)"/lib/probe.sh
+skip_if_no_perf_trace || exit 2
+
+enum_aug_prereq() {
+ echo "Checking perf trace enum augmentation prerequisites"
+ if ! ls /sys/kernel/btf/vmlinux 1>/dev/null 2>&1
+ then
+ echo "trace+enum test [Skipped missing vmlinux BTF support]"
+ err=2
+ return
+ fi
+}
+
+prepare_landlock_script() {
+ echo "Preparing script for ${syscall} syscall"
+
+ cat > $landlock_script << EOF
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <linux/landlock.h>
+#include <sys/syscall.h>
+
+int main()
+{
+ int fd = ${landlock_fd};
+ int flags = ${landlock_flags};
+ struct landlock_path_beneath_attr path_beneath_attr = {
+ .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE,
+ };
+ struct landlock_net_port_attr net_port_attr = {
+ .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP,
+ .port = 443,
+ };
+
+ syscall(SYS_landlock_add_rule, fd, LANDLOCK_RULE_PATH_BENEATH,
+ &path_beneath_attr, flags);
+
+ syscall(SYS_landlock_add_rule, fd, LANDLOCK_RULE_NET_PORT,
+ &net_port_attr, flags);
+
+ return 0;
+}
+EOF
+
+ gcc $landlock_script -o $landlock_ex
+}
+
+trace_landlock() {
+ echo "Tracing syscall ${syscall}"
+ if perf trace -e $syscall $landlock_ex 2>&1 | \
+ grep -q -E ".*landlock_add_rule\(ruleset_fd: ${landlock_fd}, rule_type: (LANDLOCK_RULE_PATH_BENEATH|LANDLOCK_RULE_NET_PORT), rule_attr: 0x[a-f0-9]+, flags: ${landlock_flags}\) = -1.*"
+ then
+ err=0
+ else
+ err=1
+ fi
+}
+
+trace_non_syscall() {
+ echo "Tracing non-syscall tracepoint ${non-syscall}"
+ if perf trace -e $non_syscall --max-events=1 2>&1 | \
+ grep -q -E '.*timer:hrtimer_.*\(.*mode: HRTIMER_MODE_.*\)$'
+ then
+ err=0
+ else
+ err=1
+ fi
+}
+
+cleanup() {
+ rm -f $landlock_script $landlock_ex
+}
+
+enum_aug_prereq
+
+prepare_landlock_script
+
+if [ $err = 0 ]; then
+ trace_landlock
+fi
+
+if [ $err = 0 ]; then
+ trace_non_syscall
+fi
+
+cleanup
+
+exit $err
--
2.45.2
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v1 5/5] perf trace: Add test for enum augmentation
2024-06-18 15:26 ` [PATCH v1 5/5] perf trace: Add test for enum augmentation Howard Chu
@ 2024-06-18 17:56 ` Arnaldo Carvalho de Melo
2024-06-18 18:00 ` Howard Chu
0 siblings, 1 reply; 8+ messages in thread
From: Arnaldo Carvalho de Melo @ 2024-06-18 17:56 UTC (permalink / raw)
To: Howard Chu
Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, Adrian Hunter, Kan Liang,
linux-kernel, linux-perf-users
On Tue, Jun 18, 2024 at 11:26:52PM +0800, Howard Chu wrote:
> Check for vmlinux's existence in sysfs as prerequisite.
That is good, well done, but then we need to check if gcc is installed,
and it may not be and we want to test this feature even so.
So please take a look at git log tools/perf/tests/workloads/, then add a
tools/perf/tests/workloads/landlock_add_rule.c and finally you'll be
able to replace the inline landlock .c file + gcc to build it and
instead use:
perf test -w landlock_add_rule
As your workload.
Then you update the last patch in your series, test it all and resend
the whole series with a v2, with the description of v1 and v2 in the
cover letter.
Thanks,
- Arnaldo
> Compile and run a script which calls landlock_add_rule syscall,
> trace the syscall to judge if the output is desirable.
>
> Trace the non-syscall tracepoint 'timer:hrtimer_init' and
> 'timer:hrtimer_start', see if the 'mode' argument is augmented,
> the 'mode' enum argument has the prefix of 'HRTIMER_MODE_'
> in its name.
>
> Signed-off-by: Howard Chu <howardchu95@gmail.com>
> ---
> tools/perf/tests/shell/trace_btf_enum.sh | 104 +++++++++++++++++++++++
> 1 file changed, 104 insertions(+)
> create mode 100755 tools/perf/tests/shell/trace_btf_enum.sh
>
> diff --git a/tools/perf/tests/shell/trace_btf_enum.sh b/tools/perf/tests/shell/trace_btf_enum.sh
> new file mode 100755
> index 000000000000..14c73b0b594d
> --- /dev/null
> +++ b/tools/perf/tests/shell/trace_btf_enum.sh
> @@ -0,0 +1,104 @@
> +#!/bin/sh
> +# perf trace enum augmentation tests
> +# SPDX-License-Identifier: GPL-2.0
> +
> +err=0
> +set -e
> +
> +syscall="landlock_add_rule"
> +non_syscall="timer:hrtimer_init,timer:hrtimer_start"
> +
> +landlock_script=$(mktemp /tmp/landlock-XXXXX.c)
> +landlock_ex=$(echo $landlock_script | sed -E 's/(.*).c$/\1/g')
> +
> +landlock_fd=24
> +landlock_flags=25
> +
> +. "$(dirname $0)"/lib/probe.sh
> +skip_if_no_perf_trace || exit 2
> +
> +enum_aug_prereq() {
> + echo "Checking perf trace enum augmentation prerequisites"
> + if ! ls /sys/kernel/btf/vmlinux 1>/dev/null 2>&1
> + then
> + echo "trace+enum test [Skipped missing vmlinux BTF support]"
> + err=2
> + return
> + fi
> +}
> +
> +prepare_landlock_script() {
> + echo "Preparing script for ${syscall} syscall"
> +
> + cat > $landlock_script << EOF
> +#define _GNU_SOURCE
> +#include <unistd.h>
> +#include <linux/landlock.h>
> +#include <sys/syscall.h>
> +
> +int main()
> +{
> + int fd = ${landlock_fd};
> + int flags = ${landlock_flags};
> + struct landlock_path_beneath_attr path_beneath_attr = {
> + .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE,
> + };
> + struct landlock_net_port_attr net_port_attr = {
> + .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP,
> + .port = 443,
> + };
> +
> + syscall(SYS_landlock_add_rule, fd, LANDLOCK_RULE_PATH_BENEATH,
> + &path_beneath_attr, flags);
> +
> + syscall(SYS_landlock_add_rule, fd, LANDLOCK_RULE_NET_PORT,
> + &net_port_attr, flags);
> +
> + return 0;
> +}
> +EOF
> +
> + gcc $landlock_script -o $landlock_ex
> +}
> +
> +trace_landlock() {
> + echo "Tracing syscall ${syscall}"
> + if perf trace -e $syscall $landlock_ex 2>&1 | \
> + grep -q -E ".*landlock_add_rule\(ruleset_fd: ${landlock_fd}, rule_type: (LANDLOCK_RULE_PATH_BENEATH|LANDLOCK_RULE_NET_PORT), rule_attr: 0x[a-f0-9]+, flags: ${landlock_flags}\) = -1.*"
> + then
> + err=0
> + else
> + err=1
> + fi
> +}
> +
> +trace_non_syscall() {
> + echo "Tracing non-syscall tracepoint ${non-syscall}"
> + if perf trace -e $non_syscall --max-events=1 2>&1 | \
> + grep -q -E '.*timer:hrtimer_.*\(.*mode: HRTIMER_MODE_.*\)$'
> + then
> + err=0
> + else
> + err=1
> + fi
> +}
> +
> +cleanup() {
> + rm -f $landlock_script $landlock_ex
> +}
> +
> +enum_aug_prereq
> +
> +prepare_landlock_script
> +
> +if [ $err = 0 ]; then
> + trace_landlock
> +fi
> +
> +if [ $err = 0 ]; then
> + trace_non_syscall
> +fi
> +
> +cleanup
> +
> +exit $err
> --
> 2.45.2
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v1 5/5] perf trace: Add test for enum augmentation
2024-06-18 17:56 ` Arnaldo Carvalho de Melo
@ 2024-06-18 18:00 ` Howard Chu
0 siblings, 0 replies; 8+ messages in thread
From: Howard Chu @ 2024-06-18 18:00 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, Adrian Hunter, Kan Liang,
linux-kernel, linux-perf-users
Thank you Arnaldo, on it.
Thanks,
Howard
On Wed, Jun 19, 2024 at 1:56 AM Arnaldo Carvalho de Melo
<acme@kernel.org> wrote:
>
> On Tue, Jun 18, 2024 at 11:26:52PM +0800, Howard Chu wrote:
> > Check for vmlinux's existence in sysfs as prerequisite.
>
> That is good, well done, but then we need to check if gcc is installed,
> and it may not be and we want to test this feature even so.
>
> So please take a look at git log tools/perf/tests/workloads/, then add a
> tools/perf/tests/workloads/landlock_add_rule.c and finally you'll be
> able to replace the inline landlock .c file + gcc to build it and
> instead use:
>
> perf test -w landlock_add_rule
>
> As your workload.
>
> Then you update the last patch in your series, test it all and resend
> the whole series with a v2, with the description of v1 and v2 in the
> cover letter.
>
> Thanks,
>
> - Arnaldo
>
> > Compile and run a script which calls landlock_add_rule syscall,
> > trace the syscall to judge if the output is desirable.
> >
> > Trace the non-syscall tracepoint 'timer:hrtimer_init' and
> > 'timer:hrtimer_start', see if the 'mode' argument is augmented,
> > the 'mode' enum argument has the prefix of 'HRTIMER_MODE_'
> > in its name.
> >
> > Signed-off-by: Howard Chu <howardchu95@gmail.com>
> > ---
> > tools/perf/tests/shell/trace_btf_enum.sh | 104 +++++++++++++++++++++++
> > 1 file changed, 104 insertions(+)
> > create mode 100755 tools/perf/tests/shell/trace_btf_enum.sh
> >
> > diff --git a/tools/perf/tests/shell/trace_btf_enum.sh b/tools/perf/tests/shell/trace_btf_enum.sh
> > new file mode 100755
> > index 000000000000..14c73b0b594d
> > --- /dev/null
> > +++ b/tools/perf/tests/shell/trace_btf_enum.sh
> > @@ -0,0 +1,104 @@
> > +#!/bin/sh
> > +# perf trace enum augmentation tests
> > +# SPDX-License-Identifier: GPL-2.0
> > +
> > +err=0
> > +set -e
> > +
> > +syscall="landlock_add_rule"
> > +non_syscall="timer:hrtimer_init,timer:hrtimer_start"
> > +
> > +landlock_script=$(mktemp /tmp/landlock-XXXXX.c)
> > +landlock_ex=$(echo $landlock_script | sed -E 's/(.*).c$/\1/g')
> > +
> > +landlock_fd=24
> > +landlock_flags=25
> > +
> > +. "$(dirname $0)"/lib/probe.sh
> > +skip_if_no_perf_trace || exit 2
> > +
> > +enum_aug_prereq() {
> > + echo "Checking perf trace enum augmentation prerequisites"
> > + if ! ls /sys/kernel/btf/vmlinux 1>/dev/null 2>&1
> > + then
> > + echo "trace+enum test [Skipped missing vmlinux BTF support]"
> > + err=2
> > + return
> > + fi
> > +}
> > +
> > +prepare_landlock_script() {
> > + echo "Preparing script for ${syscall} syscall"
> > +
> > + cat > $landlock_script << EOF
> > +#define _GNU_SOURCE
> > +#include <unistd.h>
> > +#include <linux/landlock.h>
> > +#include <sys/syscall.h>
> > +
> > +int main()
> > +{
> > + int fd = ${landlock_fd};
> > + int flags = ${landlock_flags};
> > + struct landlock_path_beneath_attr path_beneath_attr = {
> > + .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE,
> > + };
> > + struct landlock_net_port_attr net_port_attr = {
> > + .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP,
> > + .port = 443,
> > + };
> > +
> > + syscall(SYS_landlock_add_rule, fd, LANDLOCK_RULE_PATH_BENEATH,
> > + &path_beneath_attr, flags);
> > +
> > + syscall(SYS_landlock_add_rule, fd, LANDLOCK_RULE_NET_PORT,
> > + &net_port_attr, flags);
> > +
> > + return 0;
> > +}
> > +EOF
> > +
> > + gcc $landlock_script -o $landlock_ex
> > +}
> > +
> > +trace_landlock() {
> > + echo "Tracing syscall ${syscall}"
> > + if perf trace -e $syscall $landlock_ex 2>&1 | \
> > + grep -q -E ".*landlock_add_rule\(ruleset_fd: ${landlock_fd}, rule_type: (LANDLOCK_RULE_PATH_BENEATH|LANDLOCK_RULE_NET_PORT), rule_attr: 0x[a-f0-9]+, flags: ${landlock_flags}\) = -1.*"
> > + then
> > + err=0
> > + else
> > + err=1
> > + fi
> > +}
> > +
> > +trace_non_syscall() {
> > + echo "Tracing non-syscall tracepoint ${non-syscall}"
> > + if perf trace -e $non_syscall --max-events=1 2>&1 | \
> > + grep -q -E '.*timer:hrtimer_.*\(.*mode: HRTIMER_MODE_.*\)$'
> > + then
> > + err=0
> > + else
> > + err=1
> > + fi
> > +}
> > +
> > +cleanup() {
> > + rm -f $landlock_script $landlock_ex
> > +}
> > +
> > +enum_aug_prereq
> > +
> > +prepare_landlock_script
> > +
> > +if [ $err = 0 ]; then
> > + trace_landlock
> > +fi
> > +
> > +if [ $err = 0 ]; then
> > + trace_non_syscall
> > +fi
> > +
> > +cleanup
> > +
> > +exit $err
> > --
> > 2.45.2
> >
^ permalink raw reply [flat|nested] 8+ messages in thread