linux-perf-users.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/5] perf trace: Augment enum arguments with BTF
@ 2024-06-19  8:20 Howard Chu
  2024-06-19  8:20 ` [PATCH v2 1/5] perf trace: Fix iteration of syscall ids in syscalltbl->entries Howard Chu
                   ` (5 more replies)
  0 siblings, 6 replies; 30+ messages in thread
From: Howard Chu @ 2024-06-19  8:20 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, Adrian Hunter, Kan Liang,
	linux-kernel, linux-perf-users

changes in v2:
- Move inline landlock_add_rule c code to tests/workloads
- Rename 'enum_aug_prereq' to 'check_vmlinux'

Augment enum arguments in perf trace, including syscall arguments and
non-syscall tracepoint arguments. The augmentation is implemented using
BTF.

This patch series also includes a bug fix by Arnaldo Carvalho de Melo 
<acme@redhat.com>, which makes more syscalls to be traceable by perf trace.

Test is included.

Howard Chu (5):
  perf trace: Fix iteration of syscall ids in syscalltbl->entries
  perf trace: Augment enum syscall arguments with BTF
  perf trace: Augment enum tracepoint arguments with BTF
  perf trace: Filter enum arguments with enum names
  perf trace: Add test for enum augmentation

 tools/perf/builtin-trace.c                    | 214 ++++++++++++++++--
 tools/perf/tests/builtin-test.c               |   1 +
 tools/perf/tests/shell/trace_btf_enum.sh      |  57 +++++
 tools/perf/tests/tests.h                      |   1 +
 tools/perf/tests/workloads/Build              |   1 +
 .../perf/tests/workloads/landlock_add_rule.c  |  32 +++
 tools/perf/util/syscalltbl.c                  |   7 +
 tools/perf/util/syscalltbl.h                  |   1 +
 8 files changed, 289 insertions(+), 25 deletions(-)
 create mode 100755 tools/perf/tests/shell/trace_btf_enum.sh
 create mode 100644 tools/perf/tests/workloads/landlock_add_rule.c

-- 
2.45.2


^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v2 1/5] perf trace: Fix iteration of syscall ids in syscalltbl->entries
  2024-06-19  8:20 [PATCH v2 0/5] perf trace: Augment enum arguments with BTF Howard Chu
@ 2024-06-19  8:20 ` Howard Chu
  2024-06-19  8:20 ` [PATCH v2 2/5] perf trace: Augment enum syscall arguments with BTF Howard Chu
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 30+ messages in thread
From: Howard Chu @ 2024-06-19  8:20 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] 30+ messages in thread

* [PATCH v2 2/5] perf trace: Augment enum syscall arguments with BTF
  2024-06-19  8:20 [PATCH v2 0/5] perf trace: Augment enum arguments with BTF Howard Chu
  2024-06-19  8:20 ` [PATCH v2 1/5] perf trace: Fix iteration of syscall ids in syscalltbl->entries Howard Chu
@ 2024-06-19  8:20 ` Howard Chu
  2024-06-19 13:44   ` Arnaldo Carvalho de Melo
  2024-06-19  8:20 ` [PATCH v2 3/5] perf trace: Augment enum tracepoint " Howard Chu
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 30+ messages in thread
From: Howard Chu @ 2024-06-19  8:20 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] 30+ messages in thread

* [PATCH v2 3/5] perf trace: Augment enum tracepoint arguments with BTF
  2024-06-19  8:20 [PATCH v2 0/5] perf trace: Augment enum arguments with BTF Howard Chu
  2024-06-19  8:20 ` [PATCH v2 1/5] perf trace: Fix iteration of syscall ids in syscalltbl->entries Howard Chu
  2024-06-19  8:20 ` [PATCH v2 2/5] perf trace: Augment enum syscall arguments with BTF Howard Chu
@ 2024-06-19  8:20 ` Howard Chu
  2024-06-19 13:46   ` Arnaldo Carvalho de Melo
  2024-06-19  8:20 ` [PATCH v2 4/5] perf trace: Filter enum arguments with enum names Howard Chu
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 30+ messages in thread
From: Howard Chu @ 2024-06-19  8:20 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] 30+ messages in thread

* [PATCH v2 4/5] perf trace: Filter enum arguments with enum names
  2024-06-19  8:20 [PATCH v2 0/5] perf trace: Augment enum arguments with BTF Howard Chu
                   ` (2 preceding siblings ...)
  2024-06-19  8:20 ` [PATCH v2 3/5] perf trace: Augment enum tracepoint " Howard Chu
@ 2024-06-19  8:20 ` Howard Chu
  2024-06-19 13:48   ` Arnaldo Carvalho de Melo
  2024-06-19  8:20 ` [PATCH v2 5/5] perf trace: Add test for enum augmentation Howard Chu
  2024-06-19 13:55 ` [PATCH v2 0/5] perf trace: Augment enum arguments with BTF Arnaldo Carvalho de Melo
  5 siblings, 1 reply; 30+ messages in thread
From: Howard Chu @ 2024-06-19  8:20 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] 30+ messages in thread

* [PATCH v2 5/5] perf trace: Add test for enum augmentation
  2024-06-19  8:20 [PATCH v2 0/5] perf trace: Augment enum arguments with BTF Howard Chu
                   ` (3 preceding siblings ...)
  2024-06-19  8:20 ` [PATCH v2 4/5] perf trace: Filter enum arguments with enum names Howard Chu
@ 2024-06-19  8:20 ` Howard Chu
  2024-06-19 13:51   ` Arnaldo Carvalho de Melo
                     ` (2 more replies)
  2024-06-19 13:55 ` [PATCH v2 0/5] perf trace: Augment enum arguments with BTF Arnaldo Carvalho de Melo
  5 siblings, 3 replies; 30+ messages in thread
From: Howard Chu @ 2024-06-19  8:20 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.

Add landlock_add_rule.c workload. Trace landlock_add_rule syscall to see
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.

Suggested-by: Arnaldo Carvalho de Melo <acme@kernel.org>
Signed-off-by: Howard Chu <howardchu95@gmail.com>
---
 tools/perf/tests/builtin-test.c               |  1 +
 tools/perf/tests/shell/trace_btf_enum.sh      | 57 +++++++++++++++++++
 tools/perf/tests/tests.h                      |  1 +
 tools/perf/tests/workloads/Build              |  1 +
 .../perf/tests/workloads/landlock_add_rule.c  | 32 +++++++++++
 5 files changed, 92 insertions(+)
 create mode 100755 tools/perf/tests/shell/trace_btf_enum.sh
 create mode 100644 tools/perf/tests/workloads/landlock_add_rule.c

diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index c3d84b67ca8e..e83200415ad1 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -152,6 +152,7 @@ static struct test_workload *workloads[] = {
 	&workload__sqrtloop,
 	&workload__brstack,
 	&workload__datasym,
+	&workload__landlock_add_rule,
 };
 
 static int num_subtests(const struct test_suite *t)
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..4861983553ab
--- /dev/null
+++ b/tools/perf/tests/shell/trace_btf_enum.sh
@@ -0,0 +1,57 @@
+#!/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"
+
+TESTPROG="perf test -w landlock_add_rule"
+
+. "$(dirname $0)"/lib/probe.sh
+skip_if_no_perf_trace || exit 2
+
+check_vmlinux() {
+  echo "Checking if vmlinux exists"
+  if ! ls /sys/kernel/btf/vmlinux 1>/dev/null 2>&1
+  then
+    echo "trace+enum test [Skipped missing vmlinux BTF support]"
+    err=2
+  fi
+}
+
+trace_landlock() {
+  echo "Tracing syscall ${syscall}"
+  if perf trace -e $syscall $TESTPROG 2>&1 | \
+     grep -q -E ".*landlock_add_rule\(ruleset_fd: 11, rule_type: (LANDLOCK_RULE_PATH_BENEATH|LANDLOCK_RULE_NET_PORT), rule_attr: 0x[a-f0-9]+, flags: 45\) = -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
+}
+
+check_vmlinux
+
+if [ $err = 0 ]; then
+  trace_landlock
+fi
+
+if [ $err = 0 ]; then
+  trace_non_syscall
+fi
+
+exit $err
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index 3aa7701ee0e9..69126299bb08 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -205,6 +205,7 @@ DECLARE_WORKLOAD(leafloop);
 DECLARE_WORKLOAD(sqrtloop);
 DECLARE_WORKLOAD(brstack);
 DECLARE_WORKLOAD(datasym);
+DECLARE_WORKLOAD(landlock_add_rule);
 
 extern const char *dso_to_test;
 extern const char *test_objdump_path;
diff --git a/tools/perf/tests/workloads/Build b/tools/perf/tests/workloads/Build
index a1f34d5861e3..5b12b93ecffa 100644
--- a/tools/perf/tests/workloads/Build
+++ b/tools/perf/tests/workloads/Build
@@ -6,6 +6,7 @@ perf-y += leafloop.o
 perf-y += sqrtloop.o
 perf-y += brstack.o
 perf-y += datasym.o
+perf-y += landlock_add_rule.o
 
 CFLAGS_sqrtloop.o         = -g -O0 -fno-inline -U_FORTIFY_SOURCE
 CFLAGS_leafloop.o         = -g -O0 -fno-inline -fno-omit-frame-pointer -U_FORTIFY_SOURCE
diff --git a/tools/perf/tests/workloads/landlock_add_rule.c b/tools/perf/tests/workloads/landlock_add_rule.c
new file mode 100644
index 000000000000..529b5f1ea5a7
--- /dev/null
+++ b/tools/perf/tests/workloads/landlock_add_rule.c
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#include <linux/compiler.h>
+#include <uapi/asm-generic/unistd.h> // for __NR_landlock_add_rule
+#include <unistd.h>
+#include <linux/landlock.h>
+#include "../tests.h"
+
+static int landlock_add_rule(int argc __maybe_unused, const char **argv __maybe_unused)
+{
+	int fd = 11;
+	int flags = 45;
+
+	struct landlock_path_beneath_attr path_beneath_attr = {
+	    .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE,
+	    .parent_fd = 14,
+	};
+
+	struct landlock_net_port_attr net_port_attr = {
+	    .port = 19,
+	    .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP,
+	};
+
+	syscall(__NR_landlock_add_rule, fd, LANDLOCK_RULE_PATH_BENEATH,
+		&path_beneath_attr, flags);
+
+	syscall(__NR_landlock_add_rule, fd, LANDLOCK_RULE_NET_PORT,
+		&net_port_attr, flags);
+
+	return 0;
+}
+
+DEFINE_WORKLOAD(landlock_add_rule);
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 2/5] perf trace: Augment enum syscall arguments with BTF
  2024-06-19  8:20 ` [PATCH v2 2/5] perf trace: Augment enum syscall arguments with BTF Howard Chu
@ 2024-06-19 13:44   ` Arnaldo Carvalho de Melo
  2024-06-19 17:59     ` Howard Chu
  0 siblings, 1 reply; 30+ messages in thread
From: Arnaldo Carvalho de Melo @ 2024-06-19 13:44 UTC (permalink / raw)
  To: Howard Chu
  Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, Adrian Hunter, Kan Liang,
	linux-kernel, linux-perf-users

On Wed, Jun 19, 2024 at 04:20:39PM +0800, Howard Chu wrote:
> 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))

No need for the strlen test? I.e. plain using strstarts() should be
enough?

> +			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)

a pr_debug() stating that something that tracefs says should be in BTF
and isn't found there seems to be of value here.

> +		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;

	struct btf_enum *be = (struct btf_enum *)arg_fmt->btf_entry.entries;

 arg_fmt->btf_entry.entries is (void *), so we don't need the (struct
btf_enum *) cast here, removing it makes the code more compact. And
since we move the declaration to the same line, the info about its type
is there as well.

> +
> +	for (i = 0; i < arg_fmt->btf_entry.nr_entries; ++i, ++be) {

         
	for (int i = 0; i < arg_fmt->btf_entry.nr_entries; ++i, ++be) {

⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' tools/perf/ | wc -l
99
⬢[acme@toolbox perf-tools-next]$

Doing it this way makes the code more compact and is allowed even in
kernel code since some time ago:

⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' drivers/ | wc -l
294
⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' kernel/ | wc -l
12
⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' net/ | wc -l
3
⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' mm/ | wc -l
21
⬢[acme@toolbox perf-tools-next]$

> +		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;

Here you have to check if use_btf is NULL, as you, in this patch, later
call syscall_arg_fmt__init_array(arg, field, NULL) in
evsel__init_tp_arg_scnprintf(), probably you tested it all at the end of
the patch series, we have to make sure that it works after each patch,
so that we keep the codebase bisectable.

>  		} 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	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 3/5] perf trace: Augment enum tracepoint arguments with BTF
  2024-06-19  8:20 ` [PATCH v2 3/5] perf trace: Augment enum tracepoint " Howard Chu
@ 2024-06-19 13:46   ` Arnaldo Carvalho de Melo
  2024-06-19 18:00     ` Howard Chu
  0 siblings, 1 reply; 30+ messages in thread
From: Arnaldo Carvalho de Melo @ 2024-06-19 13:46 UTC (permalink / raw)
  To: Howard Chu
  Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, Adrian Hunter, Kan Liang,
	linux-kernel, linux-perf-users

On Wed, Jun 19, 2024 at 04:20:40PM +0800, Howard Chu wrote:
> 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);

Can we defer loading btf to when one of those tracepoints is hit?

>  	}
>  
>  	if (trace.sort_events) {
> -- 
> 2.45.2

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 4/5] perf trace: Filter enum arguments with enum names
  2024-06-19  8:20 ` [PATCH v2 4/5] perf trace: Filter enum arguments with enum names Howard Chu
@ 2024-06-19 13:48   ` Arnaldo Carvalho de Melo
  2024-06-19 18:18     ` Howard Chu
  0 siblings, 1 reply; 30+ messages in thread
From: Arnaldo Carvalho de Melo @ 2024-06-19 13:48 UTC (permalink / raw)
  To: Howard Chu
  Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, Adrian Hunter, Kan Liang,
	linux-kernel, linux-perf-users

On Wed, Jun 19, 2024 at 04:20:41PM +0800, Howard Chu wrote:
> 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

The above seems unrelated? (The definition of SCA_GETRANDOM_FLAGS) as it
is not used in any other place in this patch, right?

- Arnaldo

> +
> +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	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 5/5] perf trace: Add test for enum augmentation
  2024-06-19  8:20 ` [PATCH v2 5/5] perf trace: Add test for enum augmentation Howard Chu
@ 2024-06-19 13:51   ` Arnaldo Carvalho de Melo
  2024-06-19 14:36     ` Namhyung Kim
  2024-06-21 16:07   ` Namhyung Kim
  2024-06-28  6:37   ` kernel test robot
  2 siblings, 1 reply; 30+ messages in thread
From: Arnaldo Carvalho de Melo @ 2024-06-19 13:51 UTC (permalink / raw)
  To: Howard Chu
  Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, Adrian Hunter, Kan Liang,
	linux-kernel, linux-perf-users

On Wed, Jun 19, 2024 at 04:20:42PM +0800, Howard Chu wrote:
> Check for vmlinux's existence in sysfs as prerequisite.
> 
> Add landlock_add_rule.c workload. Trace landlock_add_rule syscall to see
> 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.
> 
> Suggested-by: Arnaldo Carvalho de Melo <acme@kernel.org>
> Signed-off-by: Howard Chu <howardchu95@gmail.com>
> ---
>  tools/perf/tests/builtin-test.c               |  1 +
>  tools/perf/tests/shell/trace_btf_enum.sh      | 57 +++++++++++++++++++
>  tools/perf/tests/tests.h                      |  1 +
>  tools/perf/tests/workloads/Build              |  1 +
>  .../perf/tests/workloads/landlock_add_rule.c  | 32 +++++++++++
>  5 files changed, 92 insertions(+)
>  create mode 100755 tools/perf/tests/shell/trace_btf_enum.sh
>  create mode 100644 tools/perf/tests/workloads/landlock_add_rule.c
> 
> diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
> index c3d84b67ca8e..e83200415ad1 100644
> --- a/tools/perf/tests/builtin-test.c
> +++ b/tools/perf/tests/builtin-test.c
> @@ -152,6 +152,7 @@ static struct test_workload *workloads[] = {
>  	&workload__sqrtloop,
>  	&workload__brstack,
>  	&workload__datasym,
> +	&workload__landlock_add_rule,
>  };
>  
>  static int num_subtests(const struct test_suite *t)
> 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..4861983553ab
> --- /dev/null
> +++ b/tools/perf/tests/shell/trace_btf_enum.sh
> @@ -0,0 +1,57 @@
> +#!/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"
> +
> +TESTPROG="perf test -w landlock_add_rule"
> +
> +. "$(dirname $0)"/lib/probe.sh
> +skip_if_no_perf_trace || exit 2
> +
> +check_vmlinux() {
> +  echo "Checking if vmlinux exists"
> +  if ! ls /sys/kernel/btf/vmlinux 1>/dev/null 2>&1
> +  then
> +    echo "trace+enum test [Skipped missing vmlinux BTF support]"
> +    err=2
> +  fi
> +}
> +
> +trace_landlock() {
> +  echo "Tracing syscall ${syscall}"
> +  if perf trace -e $syscall $TESTPROG 2>&1 | \
> +     grep -q -E ".*landlock_add_rule\(ruleset_fd: 11, rule_type: (LANDLOCK_RULE_PATH_BENEATH|LANDLOCK_RULE_NET_PORT), rule_attr: 0x[a-f0-9]+, flags: 45\) = -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
> +}
> +
> +check_vmlinux
> +
> +if [ $err = 0 ]; then
> +  trace_landlock
> +fi
> +
> +if [ $err = 0 ]; then
> +  trace_non_syscall
> +fi
> +
> +exit $err
> diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
> index 3aa7701ee0e9..69126299bb08 100644
> --- a/tools/perf/tests/tests.h
> +++ b/tools/perf/tests/tests.h
> @@ -205,6 +205,7 @@ DECLARE_WORKLOAD(leafloop);
>  DECLARE_WORKLOAD(sqrtloop);
>  DECLARE_WORKLOAD(brstack);
>  DECLARE_WORKLOAD(datasym);
> +DECLARE_WORKLOAD(landlock_add_rule);
>  
>  extern const char *dso_to_test;
>  extern const char *test_objdump_path;
> diff --git a/tools/perf/tests/workloads/Build b/tools/perf/tests/workloads/Build
> index a1f34d5861e3..5b12b93ecffa 100644
> --- a/tools/perf/tests/workloads/Build
> +++ b/tools/perf/tests/workloads/Build
> @@ -6,6 +6,7 @@ perf-y += leafloop.o
>  perf-y += sqrtloop.o
>  perf-y += brstack.o
>  perf-y += datasym.o
> +perf-y += landlock_add_rule.o
>  
>  CFLAGS_sqrtloop.o         = -g -O0 -fno-inline -U_FORTIFY_SOURCE
>  CFLAGS_leafloop.o         = -g -O0 -fno-inline -fno-omit-frame-pointer -U_FORTIFY_SOURCE
> diff --git a/tools/perf/tests/workloads/landlock_add_rule.c b/tools/perf/tests/workloads/landlock_add_rule.c
> new file mode 100644
> index 000000000000..529b5f1ea5a7
> --- /dev/null
> +++ b/tools/perf/tests/workloads/landlock_add_rule.c
> @@ -0,0 +1,32 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#include <linux/compiler.h>
> +#include <uapi/asm-generic/unistd.h> // for __NR_landlock_add_rule
> +#include <unistd.h>
> +#include <linux/landlock.h>

This file was introduced on linux in 2021, unsure if it will be present
in some of the older distros we test, I'll check with my container test
suite.

Maybe we'll have to just define those LANDLOCK_ACCESS_FS_READ_FILE,
LANDLOCK_ACCESS_NET_CONNECT_TCP, etc as plain #define to make sure it
builds ok with uCLibc, musl libc and older glibc.

- Arnaldo

> +#include "../tests.h"
> +
> +static int landlock_add_rule(int argc __maybe_unused, const char **argv __maybe_unused)
> +{
> +	int fd = 11;
> +	int flags = 45;
> +
> +	struct landlock_path_beneath_attr path_beneath_attr = {
> +	    .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE,
> +	    .parent_fd = 14,
> +	};
> +
> +	struct landlock_net_port_attr net_port_attr = {
> +	    .port = 19,
> +	    .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP,
> +	};
> +
> +	syscall(__NR_landlock_add_rule, fd, LANDLOCK_RULE_PATH_BENEATH,
> +		&path_beneath_attr, flags);
> +
> +	syscall(__NR_landlock_add_rule, fd, LANDLOCK_RULE_NET_PORT,
> +		&net_port_attr, flags);
> +
> +	return 0;
> +}
> +
> +DEFINE_WORKLOAD(landlock_add_rule);
> -- 
> 2.45.2
> 

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 0/5] perf trace: Augment enum arguments with BTF
  2024-06-19  8:20 [PATCH v2 0/5] perf trace: Augment enum arguments with BTF Howard Chu
                   ` (4 preceding siblings ...)
  2024-06-19  8:20 ` [PATCH v2 5/5] perf trace: Add test for enum augmentation Howard Chu
@ 2024-06-19 13:55 ` Arnaldo Carvalho de Melo
  2024-06-19 18:19   ` Namhyung Kim
  5 siblings, 1 reply; 30+ messages in thread
From: Arnaldo Carvalho de Melo @ 2024-06-19 13:55 UTC (permalink / raw)
  To: Howard Chu
  Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, Adrian Hunter, Kan Liang,
	linux-kernel, linux-perf-users

On Wed, Jun 19, 2024 at 04:20:37PM +0800, Howard Chu wrote:
> changes in v2:
> - Move inline landlock_add_rule c code to tests/workloads
> - Rename 'enum_aug_prereq' to 'check_vmlinux'

Usually the versions descriptions comes at the end, after your signature
line, just before the list of csets in the series.
 
> Augment enum arguments in perf trace, including syscall arguments and
> non-syscall tracepoint arguments. The augmentation is implemented using
> BTF.
> 
> This patch series also includes a bug fix by Arnaldo Carvalho de Melo 
> <acme@redhat.com>, which makes more syscalls to be traceable by perf trace.
> 
> Test is included.

Thanks, the patch submission is now very good, at some point you'll be
able to point to a git tree from where to do a pull, then have it with a
signed tag, etc, all this is not necessary at this point in our
collaboration, but as you evolve as a kernel developer, it eventually
will be asked from you.

And it comes with a test that introduces a 'perf test -w' workload,
super great!

- Arnaldo
 
> Howard Chu (5):
>   perf trace: Fix iteration of syscall ids in syscalltbl->entries
>   perf trace: Augment enum syscall arguments with BTF
>   perf trace: Augment enum tracepoint arguments with BTF
>   perf trace: Filter enum arguments with enum names
>   perf trace: Add test for enum augmentation
> 
>  tools/perf/builtin-trace.c                    | 214 ++++++++++++++++--
>  tools/perf/tests/builtin-test.c               |   1 +
>  tools/perf/tests/shell/trace_btf_enum.sh      |  57 +++++
>  tools/perf/tests/tests.h                      |   1 +
>  tools/perf/tests/workloads/Build              |   1 +
>  .../perf/tests/workloads/landlock_add_rule.c  |  32 +++
>  tools/perf/util/syscalltbl.c                  |   7 +
>  tools/perf/util/syscalltbl.h                  |   1 +
>  8 files changed, 289 insertions(+), 25 deletions(-)
>  create mode 100755 tools/perf/tests/shell/trace_btf_enum.sh
>  create mode 100644 tools/perf/tests/workloads/landlock_add_rule.c
> 
> -- 
> 2.45.2
> 

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 5/5] perf trace: Add test for enum augmentation
  2024-06-19 13:51   ` Arnaldo Carvalho de Melo
@ 2024-06-19 14:36     ` Namhyung Kim
  2024-06-19 18:26       ` Howard Chu
  0 siblings, 1 reply; 30+ messages in thread
From: Namhyung Kim @ 2024-06-19 14:36 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Howard Chu, Jiri Olsa, Ian Rogers, Adrian Hunter, Kan Liang,
	linux-kernel, linux-perf-users

Hi,

On Wed, Jun 19, 2024 at 6:51 AM Arnaldo Carvalho de Melo
<acme@kernel.org> wrote:
>
> On Wed, Jun 19, 2024 at 04:20:42PM +0800, Howard Chu wrote:
> > Check for vmlinux's existence in sysfs as prerequisite.
> >
> > Add landlock_add_rule.c workload. Trace landlock_add_rule syscall to see
> > 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.
> >
> > Suggested-by: Arnaldo Carvalho de Melo <acme@kernel.org>
> > Signed-off-by: Howard Chu <howardchu95@gmail.com>
> > ---
[SNIP]
> > diff --git a/tools/perf/tests/workloads/landlock_add_rule.c b/tools/perf/tests/workloads/landlock_add_rule.c
> > new file mode 100644
> > index 000000000000..529b5f1ea5a7
> > --- /dev/null
> > +++ b/tools/perf/tests/workloads/landlock_add_rule.c
> > @@ -0,0 +1,32 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +#include <linux/compiler.h>
> > +#include <uapi/asm-generic/unistd.h> // for __NR_landlock_add_rule
> > +#include <unistd.h>
> > +#include <linux/landlock.h>
>
> This file was introduced on linux in 2021, unsure if it will be present
> in some of the older distros we test, I'll check with my container test
> suite.
>
> Maybe we'll have to just define those LANDLOCK_ACCESS_FS_READ_FILE,
> LANDLOCK_ACCESS_NET_CONNECT_TCP, etc as plain #define to make sure it
> builds ok with uCLibc, musl libc and older glibc.

Maybe we can check if the syscall number is defined first and
include the landlock header.

#ifdef __NR_landlock_add_rule
#include <linux/landlock.h>
...

Then we need a way to skip the test if it's not defined.

Thanks,
Namhyung


>
> - Arnaldo
>
> > +#include "../tests.h"
> > +
> > +static int landlock_add_rule(int argc __maybe_unused, const char **argv __maybe_unused)
> > +{
> > +     int fd = 11;
> > +     int flags = 45;
> > +
> > +     struct landlock_path_beneath_attr path_beneath_attr = {
> > +         .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE,
> > +         .parent_fd = 14,
> > +     };
> > +
> > +     struct landlock_net_port_attr net_port_attr = {
> > +         .port = 19,
> > +         .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP,
> > +     };
> > +
> > +     syscall(__NR_landlock_add_rule, fd, LANDLOCK_RULE_PATH_BENEATH,
> > +             &path_beneath_attr, flags);
> > +
> > +     syscall(__NR_landlock_add_rule, fd, LANDLOCK_RULE_NET_PORT,
> > +             &net_port_attr, flags);
> > +
> > +     return 0;
> > +}
> > +
> > +DEFINE_WORKLOAD(landlock_add_rule);
> > --
> > 2.45.2
> >
>

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 2/5] perf trace: Augment enum syscall arguments with BTF
  2024-06-19 13:44   ` Arnaldo Carvalho de Melo
@ 2024-06-19 17:59     ` Howard Chu
  2024-06-20 16:34       ` Howard Chu
  2024-06-21 18:03       ` Arnaldo Carvalho de Melo
  0 siblings, 2 replies; 30+ messages in thread
From: Howard Chu @ 2024-06-19 17:59 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, Adrian Hunter, Kan Liang,
	linux-kernel, linux-perf-users

Hello,

Thanks for the in-depth review.

On Wed, Jun 19, 2024 at 9:44 PM Arnaldo Carvalho de Melo
<acme@kernel.org> wrote:
>
> On Wed, Jun 19, 2024 at 04:20:39PM +0800, Howard Chu wrote:
> > 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))
>
> No need for the strlen test? I.e. plain using strstarts() should be
> enough?

Agree. Thanks for pointing that out. Although if string 'type' is
'enum' and prefix is 'enum', strstarts() will be true, but to do 'type
+= strlen(ep) + 1', and then access 'type', might give us an
out-of-range access, but I don't think 'type' will ever be just 'enum'
or 'const enum', so I'll delete it then.

>
> > +                     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)
>
> a pr_debug() stating that something that tracefs says should be in BTF
> and isn't found there seems to be of value here.

Sure.

>
> > +             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;
>
>         struct btf_enum *be = (struct btf_enum *)arg_fmt->btf_entry.entries;
>
>  arg_fmt->btf_entry.entries is (void *), so we don't need the (struct
> btf_enum *) cast here, removing it makes the code more compact. And
> since we move the declaration to the same line, the info about its type
> is there as well.

Sure, thanks.

>
> > +
> > +     for (i = 0; i < arg_fmt->btf_entry.nr_entries; ++i, ++be) {
>
>
>         for (int i = 0; i < arg_fmt->btf_entry.nr_entries; ++i, ++be) {
>
> ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' tools/perf/ | wc -l
> 99
> ⬢[acme@toolbox perf-tools-next]$
>
> Doing it this way makes the code more compact and is allowed even in
> kernel code since some time ago:

Sure.

>
> ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' drivers/ | wc -l
> 294
> ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' kernel/ | wc -l
> 12
> ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' net/ | wc -l
> 3
> ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' mm/ | wc -l
> 21
> ⬢[acme@toolbox perf-tools-next]$
>
> > +             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;
>
> Here you have to check if use_btf is NULL, as you, in this patch, later
> call syscall_arg_fmt__init_array(arg, field, NULL) in
> evsel__init_tp_arg_scnprintf(), probably you tested it all at the end of
> the patch series, we have to make sure that it works after each patch,
> so that we keep the codebase bisectable.

I'm sorry, could you enlighten me on this? I thought:
```
else if (strstr(field->type, "enum") && use_btf != NULL) {
```
is doing the NULL checking. If you mean making sure the NULL checking
appears in all patches, I thought this is where we should introduce
the checking. Tiny reminder, the [1/5] patch is your syscalltbl
traversal bug fix.

>
> >               } 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
> >

Thanks,
Howard

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 3/5] perf trace: Augment enum tracepoint arguments with BTF
  2024-06-19 13:46   ` Arnaldo Carvalho de Melo
@ 2024-06-19 18:00     ` Howard Chu
  0 siblings, 0 replies; 30+ messages in thread
From: Howard Chu @ 2024-06-19 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

Sure, thanks.

On Wed, Jun 19, 2024 at 9:46 PM Arnaldo Carvalho de Melo
<acme@kernel.org> wrote:
>
> On Wed, Jun 19, 2024 at 04:20:40PM +0800, Howard Chu wrote:
> > 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);
>
> Can we defer loading btf to when one of those tracepoints is hit?
>
> >       }
> >
> >       if (trace.sort_events) {
> > --
> > 2.45.2

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 4/5] perf trace: Filter enum arguments with enum names
  2024-06-19 13:48   ` Arnaldo Carvalho de Melo
@ 2024-06-19 18:18     ` Howard Chu
  0 siblings, 0 replies; 30+ messages in thread
From: Howard Chu @ 2024-06-19 18:18 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, Adrian Hunter, Kan Liang,
	linux-kernel, linux-perf-users

You are right! What a silly mistake, I duplicated a line of code.
Sorry about that.

On Wed, Jun 19, 2024 at 9:48 PM Arnaldo Carvalho de Melo
<acme@kernel.org> wrote:
>
> On Wed, Jun 19, 2024 at 04:20:41PM +0800, Howard Chu wrote:
> > 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
>
> The above seems unrelated? (The definition of SCA_GETRANDOM_FLAGS) as it
> is not used in any other place in this patch, right?
>
> - Arnaldo
>
> > +
> > +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	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 0/5] perf trace: Augment enum arguments with BTF
  2024-06-19 13:55 ` [PATCH v2 0/5] perf trace: Augment enum arguments with BTF Arnaldo Carvalho de Melo
@ 2024-06-19 18:19   ` Namhyung Kim
  2024-06-19 18:25     ` Howard Chu
  0 siblings, 1 reply; 30+ messages in thread
From: Namhyung Kim @ 2024-06-19 18:19 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Howard Chu, Jiri Olsa, Ian Rogers, Adrian Hunter, Kan Liang,
	linux-kernel, linux-perf-users

Hello,

On Wed, Jun 19, 2024 at 9:55 AM Arnaldo Carvalho de Melo
<acme@kernel.org> wrote:
>
> On Wed, Jun 19, 2024 at 04:20:37PM +0800, Howard Chu wrote:
> > changes in v2:
> > - Move inline landlock_add_rule c code to tests/workloads
> > - Rename 'enum_aug_prereq' to 'check_vmlinux'
>
> Usually the versions descriptions comes at the end, after your signature
> line, just before the list of csets in the series.
>
> > Augment enum arguments in perf trace, including syscall arguments and
> > non-syscall tracepoint arguments. The augmentation is implemented using
> > BTF.
> >
> > This patch series also includes a bug fix by Arnaldo Carvalho de Melo
> > <acme@redhat.com>, which makes more syscalls to be traceable by perf trace.
> >
> > Test is included.
>
> Thanks, the patch submission is now very good, at some point you'll be
> able to point to a git tree from where to do a pull, then have it with a
> signed tag, etc, all this is not necessary at this point in our
> collaboration, but as you evolve as a kernel developer, it eventually
> will be asked from you.
>
> And it comes with a test that introduces a 'perf test -w' workload,
> super great!
>
> - Arnaldo
>
> > Howard Chu (5):
> >   perf trace: Fix iteration of syscall ids in syscalltbl->entries
> >   perf trace: Augment enum syscall arguments with BTF
> >   perf trace: Augment enum tracepoint arguments with BTF
> >   perf trace: Filter enum arguments with enum names
> >   perf trace: Add test for enum augmentation

Please make sure that your change doesn't break the build
in case libbpf is not available.  For example, a build without
libelf seems to be broken.

  $ make NO_LIBELF=1

Thanks,
Namhyung

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 0/5] perf trace: Augment enum arguments with BTF
  2024-06-19 18:19   ` Namhyung Kim
@ 2024-06-19 18:25     ` Howard Chu
  2024-06-20 19:12       ` Howard Chu
  0 siblings, 1 reply; 30+ messages in thread
From: Howard Chu @ 2024-06-19 18:25 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Arnaldo Carvalho de Melo, Jiri Olsa, Ian Rogers, Adrian Hunter,
	Kan Liang, linux-kernel, linux-perf-users

On Thu, Jun 20, 2024 at 2:19 AM Namhyung Kim <namhyung@kernel.org> wrote:
>
> Hello,
>
> On Wed, Jun 19, 2024 at 9:55 AM Arnaldo Carvalho de Melo
> <acme@kernel.org> wrote:
> >
> > On Wed, Jun 19, 2024 at 04:20:37PM +0800, Howard Chu wrote:
> > > changes in v2:
> > > - Move inline landlock_add_rule c code to tests/workloads
> > > - Rename 'enum_aug_prereq' to 'check_vmlinux'
> >
> > Usually the versions descriptions comes at the end, after your signature
> > line, just before the list of csets in the series.
> >
> > > Augment enum arguments in perf trace, including syscall arguments and
> > > non-syscall tracepoint arguments. The augmentation is implemented using
> > > BTF.
> > >
> > > This patch series also includes a bug fix by Arnaldo Carvalho de Melo
> > > <acme@redhat.com>, which makes more syscalls to be traceable by perf trace.
> > >
> > > Test is included.
> >
> > Thanks, the patch submission is now very good, at some point you'll be
> > able to point to a git tree from where to do a pull, then have it with a
> > signed tag, etc, all this is not necessary at this point in our
> > collaboration, but as you evolve as a kernel developer, it eventually
> > will be asked from you.
> >
> > And it comes with a test that introduces a 'perf test -w' workload,
> > super great!
> >
> > - Arnaldo
> >
> > > Howard Chu (5):
> > >   perf trace: Fix iteration of syscall ids in syscalltbl->entries
> > >   perf trace: Augment enum syscall arguments with BTF
> > >   perf trace: Augment enum tracepoint arguments with BTF
> > >   perf trace: Filter enum arguments with enum names
> > >   perf trace: Add test for enum augmentation
>
> Please make sure that your change doesn't break the build
> in case libbpf is not available.  For example, a build without
> libelf seems to be broken.
>
>   $ make NO_LIBELF=1
>
> Thanks,
> Namhyung

Thank you, I'll fix this.

Thanks,
Howard

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 5/5] perf trace: Add test for enum augmentation
  2024-06-19 14:36     ` Namhyung Kim
@ 2024-06-19 18:26       ` Howard Chu
  0 siblings, 0 replies; 30+ messages in thread
From: Howard Chu @ 2024-06-19 18:26 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Arnaldo Carvalho de Melo, Jiri Olsa, Ian Rogers, Adrian Hunter,
	Kan Liang, linux-kernel, linux-perf-users

On Wed, Jun 19, 2024 at 10:37 PM Namhyung Kim <namhyung@kernel.org> wrote:
>
> Hi,
>
> On Wed, Jun 19, 2024 at 6:51 AM Arnaldo Carvalho de Melo
> <acme@kernel.org> wrote:
> >
> > On Wed, Jun 19, 2024 at 04:20:42PM +0800, Howard Chu wrote:
> > > Check for vmlinux's existence in sysfs as prerequisite.
> > >
> > > Add landlock_add_rule.c workload. Trace landlock_add_rule syscall to see
> > > 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.
> > >
> > > Suggested-by: Arnaldo Carvalho de Melo <acme@kernel.org>
> > > Signed-off-by: Howard Chu <howardchu95@gmail.com>
> > > ---
> [SNIP]
> > > diff --git a/tools/perf/tests/workloads/landlock_add_rule.c b/tools/perf/tests/workloads/landlock_add_rule.c
> > > new file mode 100644
> > > index 000000000000..529b5f1ea5a7
> > > --- /dev/null
> > > +++ b/tools/perf/tests/workloads/landlock_add_rule.c
> > > @@ -0,0 +1,32 @@
> > > +/* SPDX-License-Identifier: GPL-2.0 */
> > > +#include <linux/compiler.h>
> > > +#include <uapi/asm-generic/unistd.h> // for __NR_landlock_add_rule
> > > +#include <unistd.h>
> > > +#include <linux/landlock.h>
> >
> > This file was introduced on linux in 2021, unsure if it will be present
> > in some of the older distros we test, I'll check with my container test
> > suite.
> >
> > Maybe we'll have to just define those LANDLOCK_ACCESS_FS_READ_FILE,
> > LANDLOCK_ACCESS_NET_CONNECT_TCP, etc as plain #define to make sure it
> > builds ok with uCLibc, musl libc and older glibc.
>
> Maybe we can check if the syscall number is defined first and
> include the landlock header.
>
> #ifdef __NR_landlock_add_rule
> #include <linux/landlock.h>
> ...
>
> Then we need a way to skip the test if it's not defined.

Thanks for the suggestion. I'll add this to the workload script.

Thanks,
Howard

>
> Thanks,
> Namhyung
>
>
> >
> > - Arnaldo
> >
> > > +#include "../tests.h"
> > > +
> > > +static int landlock_add_rule(int argc __maybe_unused, const char **argv __maybe_unused)
> > > +{
> > > +     int fd = 11;
> > > +     int flags = 45;
> > > +
> > > +     struct landlock_path_beneath_attr path_beneath_attr = {
> > > +         .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE,
> > > +         .parent_fd = 14,
> > > +     };
> > > +
> > > +     struct landlock_net_port_attr net_port_attr = {
> > > +         .port = 19,
> > > +         .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP,
> > > +     };
> > > +
> > > +     syscall(__NR_landlock_add_rule, fd, LANDLOCK_RULE_PATH_BENEATH,
> > > +             &path_beneath_attr, flags);
> > > +
> > > +     syscall(__NR_landlock_add_rule, fd, LANDLOCK_RULE_NET_PORT,
> > > +             &net_port_attr, flags);
> > > +
> > > +     return 0;
> > > +}
> > > +
> > > +DEFINE_WORKLOAD(landlock_add_rule);
> > > --
> > > 2.45.2
> > >
> >

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 2/5] perf trace: Augment enum syscall arguments with BTF
  2024-06-19 17:59     ` Howard Chu
@ 2024-06-20 16:34       ` Howard Chu
  2024-06-20 19:16         ` Howard Chu
  2024-06-21 18:03       ` Arnaldo Carvalho de Melo
  1 sibling, 1 reply; 30+ messages in thread
From: Howard Chu @ 2024-06-20 16:34 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, Adrian Hunter, Kan Liang,
	linux-kernel, linux-perf-users

Hello Arnaldo,

Thanks for the review, the code is refined based on your comments.
There is just a little thing:

On Thu, Jun 20, 2024 at 1:59 AM Howard Chu <howardchu95@gmail.com> wrote:
>
> Hello,
>
> Thanks for the in-depth review.
>
> On Wed, Jun 19, 2024 at 9:44 PM Arnaldo Carvalho de Melo
> <acme@kernel.org> wrote:
> >
> > On Wed, Jun 19, 2024 at 04:20:39PM +0800, Howard Chu wrote:
> > > 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))
> >
> > No need for the strlen test? I.e. plain using strstarts() should be
> > enough?
>
> Agree. Thanks for pointing that out. Although if string 'type' is
> 'enum' and prefix is 'enum', strstarts() will be true, but to do 'type
> += strlen(ep) + 1', and then access 'type', might give us an
> out-of-range access, but I don't think 'type' will ever be just 'enum'
> or 'const enum', so I'll delete it then.
>
> >
> > > +                     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)
> >
> > a pr_debug() stating that something that tracefs says should be in BTF
> > and isn't found there seems to be of value here.
>
> Sure.
>
> >
> > > +             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;
> >
> >         struct btf_enum *be = (struct btf_enum *)arg_fmt->btf_entry.entries;
> >
> >  arg_fmt->btf_entry.entries is (void *), so we don't need the (struct
> > btf_enum *) cast here, removing it makes the code more compact. And
> > since we move the declaration to the same line, the info about its type
> > is there as well.
>
> Sure, thanks.
>
> >
> > > +
> > > +     for (i = 0; i < arg_fmt->btf_entry.nr_entries; ++i, ++be) {
> >
> >
> >         for (int i = 0; i < arg_fmt->btf_entry.nr_entries; ++i, ++be) {

I did this, it's all good. But here:

for (i = 0, be = arg_fmt->btf_entry.entries; i <
arg_fmt->btf_entry.nr_entries; ++i, ++be) {

I can't do

for (int i = 0, struct btf_enum *be = arg_fmt->btf_entry.entries; i <
arg_fmt->btf_entry.nr_entries; ++i, ++be) {

The compiler will show errors:

builtin-trace.c: In function ‘btf_enum_scnprintf’:
builtin-trace.c:996:25: error: expected identifier or ‘(’ before ‘struct’
  996 |         for (int i = 0, struct btf_enum *be =
arg_fmt->btf_entry.entries; i < arg_fmt->btf_entry.nr_entries; ++i,
++be) {
      |                         ^~~~~~
builtin-trace.c:996:117: error: ‘be’ undeclared (first use in this
function); did you mean ‘bf’?
  996 |         for (int i = 0, struct btf_enum *be =
arg_fmt->btf_entry.entries; i < arg_fmt->btf_entry.nr_entries; ++i,
++be) {
      |
                                                     ^~
      |
                                                     bf
builtin-trace.c:996:117: note: each undeclared identifier is reported
only once for each function it appears in

This is not good either:

for (int i = 0, be = arg_fmt->btf_entry.entries; i <
arg_fmt->btf_entry.nr_entries; ++i, ++be) {

builtin-trace.c: In function ‘btf_enum_scnprintf’:
builtin-trace.c:998:25: error: declaration of ‘be’ shadows a previous
local [-Werror=shadow]
  998 |         for (int i = 0, be = arg_fmt->btf_entry.entries; i <
arg_fmt->btf_entry.nr_entries; ++i, ++be) {
      |                         ^~
builtin-trace.c:991:26: note: shadowed declaration is here
  991 |         struct btf_enum *be;
      |                          ^~
builtin-trace.c:998:30: error: initialization of ‘int’ from ‘void *’
makes integer from pointer without a cast [-Wint-conversion]
  998 |         for (int i = 0, be = arg_fmt->btf_entry.entries; i <
arg_fmt->btf_entry.nr_entries; ++i, ++be) {
      |                              ^~~~~~~

But I can do:

```
be = arg_fmt->btf_entry.entries;

for (int i = 0; i < arg_fmt->btf_entry.nr_entries; ++i, ++be) {
```

Thanks,
Howard


> >
> > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' tools/perf/ | wc -l
> > 99
> > ⬢[acme@toolbox perf-tools-next]$
> >
> > Doing it this way makes the code more compact and is allowed even in
> > kernel code since some time ago:
>
> Sure.
>
> >
> > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' drivers/ | wc -l
> > 294
> > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' kernel/ | wc -l
> > 12
> > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' net/ | wc -l
> > 3
> > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' mm/ | wc -l
> > 21
> > ⬢[acme@toolbox perf-tools-next]$
> >
> > > +             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;
> >
> > Here you have to check if use_btf is NULL, as you, in this patch, later
> > call syscall_arg_fmt__init_array(arg, field, NULL) in
> > evsel__init_tp_arg_scnprintf(), probably you tested it all at the end of
> > the patch series, we have to make sure that it works after each patch,
> > so that we keep the codebase bisectable.
>
> I'm sorry, could you enlighten me on this? I thought:
> ```
> else if (strstr(field->type, "enum") && use_btf != NULL) {
> ```
> is doing the NULL checking. If you mean making sure the NULL checking
> appears in all patches, I thought this is where we should introduce
> the checking. Tiny reminder, the [1/5] patch is your syscalltbl
> traversal bug fix.
>
> >
> > >               } 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
> > >
>
> Thanks,
> Howard

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 0/5] perf trace: Augment enum arguments with BTF
  2024-06-19 18:25     ` Howard Chu
@ 2024-06-20 19:12       ` Howard Chu
  0 siblings, 0 replies; 30+ messages in thread
From: Howard Chu @ 2024-06-20 19:12 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Arnaldo Carvalho de Melo, Jiri Olsa, Ian Rogers, Adrian Hunter,
	Kan Liang, linux-kernel, linux-perf-users

Hello,

Now $ make NO_LIBELF=1 can build successfully, and if
__NR_landlock_add_rule is not defined, we'll skip the test. The fixes
that Arnaldo suggested are also included.

I added too many things, changed some behaviors, figured it's probably
easier for you to merge from my source tree.

The fixes are on the perf-tools-next branch at:
https://github.com/Sberm/linux.git

please use
```
git pull https://github.com/Sberm/linux.git perf-tools-next
```

I'm no git expert so please tell me if something is wrong.

P.S. not my proudest patch, changed too many things, might be buggy.
If there're bugs please let me know.

On Thu, Jun 20, 2024 at 2:25 AM Howard Chu <howardchu95@gmail.com> wrote:
>
> On Thu, Jun 20, 2024 at 2:19 AM Namhyung Kim <namhyung@kernel.org> wrote:
> >
> > Hello,
> >
> > On Wed, Jun 19, 2024 at 9:55 AM Arnaldo Carvalho de Melo
> > <acme@kernel.org> wrote:
> > >
> > > On Wed, Jun 19, 2024 at 04:20:37PM +0800, Howard Chu wrote:
> > > > changes in v2:
> > > > - Move inline landlock_add_rule c code to tests/workloads
> > > > - Rename 'enum_aug_prereq' to 'check_vmlinux'
> > >
> > > Usually the versions descriptions comes at the end, after your signature
> > > line, just before the list of csets in the series.
> > >
> > > > Augment enum arguments in perf trace, including syscall arguments and
> > > > non-syscall tracepoint arguments. The augmentation is implemented using
> > > > BTF.
> > > >
> > > > This patch series also includes a bug fix by Arnaldo Carvalho de Melo
> > > > <acme@redhat.com>, which makes more syscalls to be traceable by perf trace.
> > > >
> > > > Test is included.
> > >
> > > Thanks, the patch submission is now very good, at some point you'll be
> > > able to point to a git tree from where to do a pull, then have it with a
> > > signed tag, etc, all this is not necessary at this point in our
> > > collaboration, but as you evolve as a kernel developer, it eventually
> > > will be asked from you.
> > >
> > > And it comes with a test that introduces a 'perf test -w' workload,
> > > super great!
> > >
> > > - Arnaldo
> > >
> > > > Howard Chu (5):
> > > >   perf trace: Fix iteration of syscall ids in syscalltbl->entries
> > > >   perf trace: Augment enum syscall arguments with BTF
> > > >   perf trace: Augment enum tracepoint arguments with BTF
> > > >   perf trace: Filter enum arguments with enum names
> > > >   perf trace: Add test for enum augmentation
> >
> > Please make sure that your change doesn't break the build
> > in case libbpf is not available.  For example, a build without
> > libelf seems to be broken.
> >
> >   $ make NO_LIBELF=1
> >
> > Thanks,
> > Namhyung
>
> Thank you, I'll fix this.
>
> Thanks,
> Howard

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 2/5] perf trace: Augment enum syscall arguments with BTF
  2024-06-20 16:34       ` Howard Chu
@ 2024-06-20 19:16         ` Howard Chu
  2024-06-21 13:40           ` Arnaldo Carvalho de Melo
  0 siblings, 1 reply; 30+ messages in thread
From: Howard Chu @ 2024-06-20 19:16 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, Adrian Hunter, Kan Liang,
	linux-kernel, linux-perf-users

Hello,

The fixes are on the perf-tools-next branch at:
https://github.com/Sberm/linux.git

please use
```
git pull https://github.com/Sberm/linux.git perf-tools-next
```

I'm no git expert so please tell me if something is wrong.

P.S. not my proudest patch, changed too many things, might be buggy.
If there're bugs please let me know.

On Fri, Jun 21, 2024 at 12:34 AM Howard Chu <howardchu95@gmail.com> wrote:
>
> Hello Arnaldo,
>
> Thanks for the review, the code is refined based on your comments.
> There is just a little thing:
>
> On Thu, Jun 20, 2024 at 1:59 AM Howard Chu <howardchu95@gmail.com> wrote:
> >
> > Hello,
> >
> > Thanks for the in-depth review.
> >
> > On Wed, Jun 19, 2024 at 9:44 PM Arnaldo Carvalho de Melo
> > <acme@kernel.org> wrote:
> > >
> > > On Wed, Jun 19, 2024 at 04:20:39PM +0800, Howard Chu wrote:
> > > > 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))
> > >
> > > No need for the strlen test? I.e. plain using strstarts() should be
> > > enough?
> >
> > Agree. Thanks for pointing that out. Although if string 'type' is
> > 'enum' and prefix is 'enum', strstarts() will be true, but to do 'type
> > += strlen(ep) + 1', and then access 'type', might give us an
> > out-of-range access, but I don't think 'type' will ever be just 'enum'
> > or 'const enum', so I'll delete it then.
> >
> > >
> > > > +                     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)
> > >
> > > a pr_debug() stating that something that tracefs says should be in BTF
> > > and isn't found there seems to be of value here.
> >
> > Sure.
> >
> > >
> > > > +             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;
> > >
> > >         struct btf_enum *be = (struct btf_enum *)arg_fmt->btf_entry.entries;
> > >
> > >  arg_fmt->btf_entry.entries is (void *), so we don't need the (struct
> > > btf_enum *) cast here, removing it makes the code more compact. And
> > > since we move the declaration to the same line, the info about its type
> > > is there as well.
> >
> > Sure, thanks.
> >
> > >
> > > > +
> > > > +     for (i = 0; i < arg_fmt->btf_entry.nr_entries; ++i, ++be) {
> > >
> > >
> > >         for (int i = 0; i < arg_fmt->btf_entry.nr_entries; ++i, ++be) {
>
> I did this, it's all good. But here:
>
> for (i = 0, be = arg_fmt->btf_entry.entries; i <
> arg_fmt->btf_entry.nr_entries; ++i, ++be) {
>
> I can't do
>
> for (int i = 0, struct btf_enum *be = arg_fmt->btf_entry.entries; i <
> arg_fmt->btf_entry.nr_entries; ++i, ++be) {
>
> The compiler will show errors:
>
> builtin-trace.c: In function ‘btf_enum_scnprintf’:
> builtin-trace.c:996:25: error: expected identifier or ‘(’ before ‘struct’
>   996 |         for (int i = 0, struct btf_enum *be =
> arg_fmt->btf_entry.entries; i < arg_fmt->btf_entry.nr_entries; ++i,
> ++be) {
>       |                         ^~~~~~
> builtin-trace.c:996:117: error: ‘be’ undeclared (first use in this
> function); did you mean ‘bf’?
>   996 |         for (int i = 0, struct btf_enum *be =
> arg_fmt->btf_entry.entries; i < arg_fmt->btf_entry.nr_entries; ++i,
> ++be) {
>       |
>                                                      ^~
>       |
>                                                      bf
> builtin-trace.c:996:117: note: each undeclared identifier is reported
> only once for each function it appears in
>
> This is not good either:
>
> for (int i = 0, be = arg_fmt->btf_entry.entries; i <
> arg_fmt->btf_entry.nr_entries; ++i, ++be) {
>
> builtin-trace.c: In function ‘btf_enum_scnprintf’:
> builtin-trace.c:998:25: error: declaration of ‘be’ shadows a previous
> local [-Werror=shadow]
>   998 |         for (int i = 0, be = arg_fmt->btf_entry.entries; i <
> arg_fmt->btf_entry.nr_entries; ++i, ++be) {
>       |                         ^~
> builtin-trace.c:991:26: note: shadowed declaration is here
>   991 |         struct btf_enum *be;
>       |                          ^~
> builtin-trace.c:998:30: error: initialization of ‘int’ from ‘void *’
> makes integer from pointer without a cast [-Wint-conversion]
>   998 |         for (int i = 0, be = arg_fmt->btf_entry.entries; i <
> arg_fmt->btf_entry.nr_entries; ++i, ++be) {
>       |                              ^~~~~~~
>
> But I can do:
>
> ```
> be = arg_fmt->btf_entry.entries;
>
> for (int i = 0; i < arg_fmt->btf_entry.nr_entries; ++i, ++be) {
> ```
>
> Thanks,
> Howard
>
>
> > >
> > > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' tools/perf/ | wc -l
> > > 99
> > > ⬢[acme@toolbox perf-tools-next]$
> > >
> > > Doing it this way makes the code more compact and is allowed even in
> > > kernel code since some time ago:
> >
> > Sure.
> >
> > >
> > > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' drivers/ | wc -l
> > > 294
> > > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' kernel/ | wc -l
> > > 12
> > > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' net/ | wc -l
> > > 3
> > > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' mm/ | wc -l
> > > 21
> > > ⬢[acme@toolbox perf-tools-next]$
> > >
> > > > +             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;
> > >
> > > Here you have to check if use_btf is NULL, as you, in this patch, later
> > > call syscall_arg_fmt__init_array(arg, field, NULL) in
> > > evsel__init_tp_arg_scnprintf(), probably you tested it all at the end of
> > > the patch series, we have to make sure that it works after each patch,
> > > so that we keep the codebase bisectable.
> >
> > I'm sorry, could you enlighten me on this? I thought:
> > ```
> > else if (strstr(field->type, "enum") && use_btf != NULL) {
> > ```
> > is doing the NULL checking. If you mean making sure the NULL checking
> > appears in all patches, I thought this is where we should introduce
> > the checking. Tiny reminder, the [1/5] patch is your syscalltbl
> > traversal bug fix.
> >
> > >
> > > >               } 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
> > > >
> >
> > Thanks,
> > Howard

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 2/5] perf trace: Augment enum syscall arguments with BTF
  2024-06-20 19:16         ` Howard Chu
@ 2024-06-21 13:40           ` Arnaldo Carvalho de Melo
  2024-06-21 16:18             ` Howard Chu
  0 siblings, 1 reply; 30+ messages in thread
From: Arnaldo Carvalho de Melo @ 2024-06-21 13:40 UTC (permalink / raw)
  To: Howard Chu
  Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, Adrian Hunter, Kan Liang,
	linux-kernel, linux-perf-users

On Fri, Jun 21, 2024 at 03:16:26AM +0800, Howard Chu wrote:
> Hello,
> 
> The fixes are on the perf-tools-next branch at:
> https://github.com/Sberm/linux.git
> 
> please use
> ```
> git pull https://github.com/Sberm/linux.git perf-tools-next
> ```
> 
> I'm no git expert so please tell me if something is wrong.

So, you combined everything into just one patch and there is no commit
log message, I'll see if I can redo it from the last broken out patch
series you submitted.

- Arnaldo
 
> P.S. not my proudest patch, changed too many things, might be buggy.
> If there're bugs please let me know.
> 
> On Fri, Jun 21, 2024 at 12:34 AM Howard Chu <howardchu95@gmail.com> wrote:
> >
> > Hello Arnaldo,
> >
> > Thanks for the review, the code is refined based on your comments.
> > There is just a little thing:
> >
> > On Thu, Jun 20, 2024 at 1:59 AM Howard Chu <howardchu95@gmail.com> wrote:
> > >
> > > Hello,
> > >
> > > Thanks for the in-depth review.
> > >
> > > On Wed, Jun 19, 2024 at 9:44 PM Arnaldo Carvalho de Melo
> > > <acme@kernel.org> wrote:
> > > >
> > > > On Wed, Jun 19, 2024 at 04:20:39PM +0800, Howard Chu wrote:
> > > > > 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))
> > > >
> > > > No need for the strlen test? I.e. plain using strstarts() should be
> > > > enough?
> > >
> > > Agree. Thanks for pointing that out. Although if string 'type' is
> > > 'enum' and prefix is 'enum', strstarts() will be true, but to do 'type
> > > += strlen(ep) + 1', and then access 'type', might give us an
> > > out-of-range access, but I don't think 'type' will ever be just 'enum'
> > > or 'const enum', so I'll delete it then.
> > >
> > > >
> > > > > +                     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)
> > > >
> > > > a pr_debug() stating that something that tracefs says should be in BTF
> > > > and isn't found there seems to be of value here.
> > >
> > > Sure.
> > >
> > > >
> > > > > +             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;
> > > >
> > > >         struct btf_enum *be = (struct btf_enum *)arg_fmt->btf_entry.entries;
> > > >
> > > >  arg_fmt->btf_entry.entries is (void *), so we don't need the (struct
> > > > btf_enum *) cast here, removing it makes the code more compact. And
> > > > since we move the declaration to the same line, the info about its type
> > > > is there as well.
> > >
> > > Sure, thanks.
> > >
> > > >
> > > > > +
> > > > > +     for (i = 0; i < arg_fmt->btf_entry.nr_entries; ++i, ++be) {
> > > >
> > > >
> > > >         for (int i = 0; i < arg_fmt->btf_entry.nr_entries; ++i, ++be) {
> >
> > I did this, it's all good. But here:
> >
> > for (i = 0, be = arg_fmt->btf_entry.entries; i <
> > arg_fmt->btf_entry.nr_entries; ++i, ++be) {
> >
> > I can't do
> >
> > for (int i = 0, struct btf_enum *be = arg_fmt->btf_entry.entries; i <
> > arg_fmt->btf_entry.nr_entries; ++i, ++be) {
> >
> > The compiler will show errors:
> >
> > builtin-trace.c: In function ‘btf_enum_scnprintf’:
> > builtin-trace.c:996:25: error: expected identifier or ‘(’ before ‘struct’
> >   996 |         for (int i = 0, struct btf_enum *be =
> > arg_fmt->btf_entry.entries; i < arg_fmt->btf_entry.nr_entries; ++i,
> > ++be) {
> >       |                         ^~~~~~
> > builtin-trace.c:996:117: error: ‘be’ undeclared (first use in this
> > function); did you mean ‘bf’?
> >   996 |         for (int i = 0, struct btf_enum *be =
> > arg_fmt->btf_entry.entries; i < arg_fmt->btf_entry.nr_entries; ++i,
> > ++be) {
> >       |
> >                                                      ^~
> >       |
> >                                                      bf
> > builtin-trace.c:996:117: note: each undeclared identifier is reported
> > only once for each function it appears in
> >
> > This is not good either:
> >
> > for (int i = 0, be = arg_fmt->btf_entry.entries; i <
> > arg_fmt->btf_entry.nr_entries; ++i, ++be) {
> >
> > builtin-trace.c: In function ‘btf_enum_scnprintf’:
> > builtin-trace.c:998:25: error: declaration of ‘be’ shadows a previous
> > local [-Werror=shadow]
> >   998 |         for (int i = 0, be = arg_fmt->btf_entry.entries; i <
> > arg_fmt->btf_entry.nr_entries; ++i, ++be) {
> >       |                         ^~
> > builtin-trace.c:991:26: note: shadowed declaration is here
> >   991 |         struct btf_enum *be;
> >       |                          ^~
> > builtin-trace.c:998:30: error: initialization of ‘int’ from ‘void *’
> > makes integer from pointer without a cast [-Wint-conversion]
> >   998 |         for (int i = 0, be = arg_fmt->btf_entry.entries; i <
> > arg_fmt->btf_entry.nr_entries; ++i, ++be) {
> >       |                              ^~~~~~~
> >
> > But I can do:
> >
> > ```
> > be = arg_fmt->btf_entry.entries;
> >
> > for (int i = 0; i < arg_fmt->btf_entry.nr_entries; ++i, ++be) {
> > ```
> >
> > Thanks,
> > Howard
> >
> >
> > > >
> > > > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' tools/perf/ | wc -l
> > > > 99
> > > > ⬢[acme@toolbox perf-tools-next]$
> > > >
> > > > Doing it this way makes the code more compact and is allowed even in
> > > > kernel code since some time ago:
> > >
> > > Sure.
> > >
> > > >
> > > > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' drivers/ | wc -l
> > > > 294
> > > > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' kernel/ | wc -l
> > > > 12
> > > > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' net/ | wc -l
> > > > 3
> > > > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' mm/ | wc -l
> > > > 21
> > > > ⬢[acme@toolbox perf-tools-next]$
> > > >
> > > > > +             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;
> > > >
> > > > Here you have to check if use_btf is NULL, as you, in this patch, later
> > > > call syscall_arg_fmt__init_array(arg, field, NULL) in
> > > > evsel__init_tp_arg_scnprintf(), probably you tested it all at the end of
> > > > the patch series, we have to make sure that it works after each patch,
> > > > so that we keep the codebase bisectable.
> > >
> > > I'm sorry, could you enlighten me on this? I thought:
> > > ```
> > > else if (strstr(field->type, "enum") && use_btf != NULL) {
> > > ```
> > > is doing the NULL checking. If you mean making sure the NULL checking
> > > appears in all patches, I thought this is where we should introduce
> > > the checking. Tiny reminder, the [1/5] patch is your syscalltbl
> > > traversal bug fix.
> > >
> > > >
> > > > >               } 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
> > > > >
> > >
> > > Thanks,
> > > Howard

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 5/5] perf trace: Add test for enum augmentation
  2024-06-19  8:20 ` [PATCH v2 5/5] perf trace: Add test for enum augmentation Howard Chu
  2024-06-19 13:51   ` Arnaldo Carvalho de Melo
@ 2024-06-21 16:07   ` Namhyung Kim
  2024-06-21 16:43     ` Howard Chu
  2024-06-28  6:37   ` kernel test robot
  2 siblings, 1 reply; 30+ messages in thread
From: Namhyung Kim @ 2024-06-21 16:07 UTC (permalink / raw)
  To: Howard Chu
  Cc: Arnaldo Carvalho de Melo, Jiri Olsa, Ian Rogers, Adrian Hunter,
	Kan Liang, linux-kernel, linux-perf-users

Hi Howard,

On Wed, Jun 19, 2024 at 04:20:42PM +0800, Howard Chu wrote:
> Check for vmlinux's existence in sysfs as prerequisite.
> 
> Add landlock_add_rule.c workload. Trace landlock_add_rule syscall to see
> if the output is desirable.

Do you expect to add more things to the landlock workload?  I think we
could simply call it landlock.c and probably do other things according
to the argument, if needed (e.g. landlock add).

Thanks,
Namhyung

> 
> 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.
> 
> Suggested-by: Arnaldo Carvalho de Melo <acme@kernel.org>
> Signed-off-by: Howard Chu <howardchu95@gmail.com>
> ---
>  tools/perf/tests/builtin-test.c               |  1 +
>  tools/perf/tests/shell/trace_btf_enum.sh      | 57 +++++++++++++++++++
>  tools/perf/tests/tests.h                      |  1 +
>  tools/perf/tests/workloads/Build              |  1 +
>  .../perf/tests/workloads/landlock_add_rule.c  | 32 +++++++++++
>  5 files changed, 92 insertions(+)
>  create mode 100755 tools/perf/tests/shell/trace_btf_enum.sh
>  create mode 100644 tools/perf/tests/workloads/landlock_add_rule.c
> 
> diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
> index c3d84b67ca8e..e83200415ad1 100644
> --- a/tools/perf/tests/builtin-test.c
> +++ b/tools/perf/tests/builtin-test.c
> @@ -152,6 +152,7 @@ static struct test_workload *workloads[] = {
>  	&workload__sqrtloop,
>  	&workload__brstack,
>  	&workload__datasym,
> +	&workload__landlock_add_rule,
>  };
>  
>  static int num_subtests(const struct test_suite *t)
> 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..4861983553ab
> --- /dev/null
> +++ b/tools/perf/tests/shell/trace_btf_enum.sh
> @@ -0,0 +1,57 @@
> +#!/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"
> +
> +TESTPROG="perf test -w landlock_add_rule"
> +
> +. "$(dirname $0)"/lib/probe.sh
> +skip_if_no_perf_trace || exit 2
> +
> +check_vmlinux() {
> +  echo "Checking if vmlinux exists"
> +  if ! ls /sys/kernel/btf/vmlinux 1>/dev/null 2>&1
> +  then
> +    echo "trace+enum test [Skipped missing vmlinux BTF support]"
> +    err=2
> +  fi
> +}
> +
> +trace_landlock() {
> +  echo "Tracing syscall ${syscall}"
> +  if perf trace -e $syscall $TESTPROG 2>&1 | \
> +     grep -q -E ".*landlock_add_rule\(ruleset_fd: 11, rule_type: (LANDLOCK_RULE_PATH_BENEATH|LANDLOCK_RULE_NET_PORT), rule_attr: 0x[a-f0-9]+, flags: 45\) = -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
> +}
> +
> +check_vmlinux
> +
> +if [ $err = 0 ]; then
> +  trace_landlock
> +fi
> +
> +if [ $err = 0 ]; then
> +  trace_non_syscall
> +fi
> +
> +exit $err
> diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
> index 3aa7701ee0e9..69126299bb08 100644
> --- a/tools/perf/tests/tests.h
> +++ b/tools/perf/tests/tests.h
> @@ -205,6 +205,7 @@ DECLARE_WORKLOAD(leafloop);
>  DECLARE_WORKLOAD(sqrtloop);
>  DECLARE_WORKLOAD(brstack);
>  DECLARE_WORKLOAD(datasym);
> +DECLARE_WORKLOAD(landlock_add_rule);
>  
>  extern const char *dso_to_test;
>  extern const char *test_objdump_path;
> diff --git a/tools/perf/tests/workloads/Build b/tools/perf/tests/workloads/Build
> index a1f34d5861e3..5b12b93ecffa 100644
> --- a/tools/perf/tests/workloads/Build
> +++ b/tools/perf/tests/workloads/Build
> @@ -6,6 +6,7 @@ perf-y += leafloop.o
>  perf-y += sqrtloop.o
>  perf-y += brstack.o
>  perf-y += datasym.o
> +perf-y += landlock_add_rule.o
>  
>  CFLAGS_sqrtloop.o         = -g -O0 -fno-inline -U_FORTIFY_SOURCE
>  CFLAGS_leafloop.o         = -g -O0 -fno-inline -fno-omit-frame-pointer -U_FORTIFY_SOURCE
> diff --git a/tools/perf/tests/workloads/landlock_add_rule.c b/tools/perf/tests/workloads/landlock_add_rule.c
> new file mode 100644
> index 000000000000..529b5f1ea5a7
> --- /dev/null
> +++ b/tools/perf/tests/workloads/landlock_add_rule.c
> @@ -0,0 +1,32 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#include <linux/compiler.h>
> +#include <uapi/asm-generic/unistd.h> // for __NR_landlock_add_rule
> +#include <unistd.h>
> +#include <linux/landlock.h>
> +#include "../tests.h"
> +
> +static int landlock_add_rule(int argc __maybe_unused, const char **argv __maybe_unused)
> +{
> +	int fd = 11;
> +	int flags = 45;
> +
> +	struct landlock_path_beneath_attr path_beneath_attr = {
> +	    .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE,
> +	    .parent_fd = 14,
> +	};
> +
> +	struct landlock_net_port_attr net_port_attr = {
> +	    .port = 19,
> +	    .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP,
> +	};
> +
> +	syscall(__NR_landlock_add_rule, fd, LANDLOCK_RULE_PATH_BENEATH,
> +		&path_beneath_attr, flags);
> +
> +	syscall(__NR_landlock_add_rule, fd, LANDLOCK_RULE_NET_PORT,
> +		&net_port_attr, flags);
> +
> +	return 0;
> +}
> +
> +DEFINE_WORKLOAD(landlock_add_rule);
> -- 
> 2.45.2
> 

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 2/5] perf trace: Augment enum syscall arguments with BTF
  2024-06-21 13:40           ` Arnaldo Carvalho de Melo
@ 2024-06-21 16:18             ` Howard Chu
  2024-06-22 18:28               ` Arnaldo Carvalho de Melo
  0 siblings, 1 reply; 30+ messages in thread
From: Howard Chu @ 2024-06-21 16:18 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, Adrian Hunter, Kan Liang,
	linux-kernel, linux-perf-users

Hello Arnaldo,

I think I need to add some explanations here, for the all-in-one patch
that I have on the tree.

Firstly, no use_btf is passed when constructing syscall argument
format. We just write is_enum when the type is enum. This is because
we don't load btf when we are constructing the formats, we load
vmlinux when a tracepoint hits.

before:

} else if (strstr(field->type, "enum") && use_btf != NULL) {
*use_btf = arg->is_enum = true;

after:

} else if (strstr(field->type, "enum")) {
arg->is_enum = true;

Another confusing place is when we do the filtering. When a user
passes an enum name, say --filter="mode==HRTIMER_MODE_ABS", we do not
know what 'HRTIMER_MODE_ABS' means. For this case, we have to load the
vmlinux BTF, and match the strings to get a value, not delaying till a
tracepoint hits.

Next thing is, for this section in trace__expand_filter():

```
struct btf_parm bparm;

if (fmt->is_enum) {
    if (trace->btf == NULL)
        trace__load_vmlinux_btf(trace);

    /* btf could be NULL */
    bparm.btf  = trace->btf;
    bparm.type = type;
    syscall_arg.parm = &bparm;
}
```

I don't handle the return value of trace__load_vmlinux_btf(), because
I want to make the latter " if (fmt->strtoul(right, right_size,
&syscall_arg, &val)) {
" evaluated to be false, so that we can fall into the else block,
therefore prints the pr_err of no resolver. This could be confusing.

pr_err("No resolver (strtoul) for \"%s\" in \"%s\", can't set filter \"%s\"\n",
       arg, evsel->name, evsel->filter);
return -1;

And another confusing part is, originally we discard 0 value for enum
arguments, now with enum aug, printed a 0-value enum makes sense,
therefore these lines:

if (val == 0 && !trace->show_zeros &&
    !(sc->arg_fmt && sc->arg_fmt[arg.idx].show_zero))
#ifdef HAVE_LIBBPF_SUPPORT
        if (!(sc->arg_fmt && sc->arg_fmt[arg.idx].is_enum))
#endif
        continue;

It suits as an &&, I don't know if it's the best way to do it.

That's it for all, thank you for finding time in your busy schedule to
review my code. I'll develop my skills to do self-checks in the
future,

Thanks,
Howard

On Fri, Jun 21, 2024 at 9:40 PM Arnaldo Carvalho de Melo
<acme@kernel.org> wrote:
>
> On Fri, Jun 21, 2024 at 03:16:26AM +0800, Howard Chu wrote:
> > Hello,
> >
> > The fixes are on the perf-tools-next branch at:
> > https://github.com/Sberm/linux.git
> >
> > please use
> > ```
> > git pull https://github.com/Sberm/linux.git perf-tools-next
> > ```
> >
> > I'm no git expert so please tell me if something is wrong.
>
> So, you combined everything into just one patch and there is no commit
> log message, I'll see if I can redo it from the last broken out patch
> series you submitted.
>
> - Arnaldo
>
> > P.S. not my proudest patch, changed too many things, might be buggy.
> > If there're bugs please let me know.
> >
> > On Fri, Jun 21, 2024 at 12:34 AM Howard Chu <howardchu95@gmail.com> wrote:
> > >
> > > Hello Arnaldo,
> > >
> > > Thanks for the review, the code is refined based on your comments.
> > > There is just a little thing:
> > >
> > > On Thu, Jun 20, 2024 at 1:59 AM Howard Chu <howardchu95@gmail.com> wrote:
> > > >
> > > > Hello,
> > > >
> > > > Thanks for the in-depth review.
> > > >
> > > > On Wed, Jun 19, 2024 at 9:44 PM Arnaldo Carvalho de Melo
> > > > <acme@kernel.org> wrote:
> > > > >
> > > > > On Wed, Jun 19, 2024 at 04:20:39PM +0800, Howard Chu wrote:
> > > > > > 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))
> > > > >
> > > > > No need for the strlen test? I.e. plain using strstarts() should be
> > > > > enough?
> > > >
> > > > Agree. Thanks for pointing that out. Although if string 'type' is
> > > > 'enum' and prefix is 'enum', strstarts() will be true, but to do 'type
> > > > += strlen(ep) + 1', and then access 'type', might give us an
> > > > out-of-range access, but I don't think 'type' will ever be just 'enum'
> > > > or 'const enum', so I'll delete it then.
> > > >
> > > > >
> > > > > > +                     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)
> > > > >
> > > > > a pr_debug() stating that something that tracefs says should be in BTF
> > > > > and isn't found there seems to be of value here.
> > > >
> > > > Sure.
> > > >
> > > > >
> > > > > > +             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;
> > > > >
> > > > >         struct btf_enum *be = (struct btf_enum *)arg_fmt->btf_entry.entries;
> > > > >
> > > > >  arg_fmt->btf_entry.entries is (void *), so we don't need the (struct
> > > > > btf_enum *) cast here, removing it makes the code more compact. And
> > > > > since we move the declaration to the same line, the info about its type
> > > > > is there as well.
> > > >
> > > > Sure, thanks.
> > > >
> > > > >
> > > > > > +
> > > > > > +     for (i = 0; i < arg_fmt->btf_entry.nr_entries; ++i, ++be) {
> > > > >
> > > > >
> > > > >         for (int i = 0; i < arg_fmt->btf_entry.nr_entries; ++i, ++be) {
> > >
> > > I did this, it's all good. But here:
> > >
> > > for (i = 0, be = arg_fmt->btf_entry.entries; i <
> > > arg_fmt->btf_entry.nr_entries; ++i, ++be) {
> > >
> > > I can't do
> > >
> > > for (int i = 0, struct btf_enum *be = arg_fmt->btf_entry.entries; i <
> > > arg_fmt->btf_entry.nr_entries; ++i, ++be) {
> > >
> > > The compiler will show errors:
> > >
> > > builtin-trace.c: In function ‘btf_enum_scnprintf’:
> > > builtin-trace.c:996:25: error: expected identifier or ‘(’ before ‘struct’
> > >   996 |         for (int i = 0, struct btf_enum *be =
> > > arg_fmt->btf_entry.entries; i < arg_fmt->btf_entry.nr_entries; ++i,
> > > ++be) {
> > >       |                         ^~~~~~
> > > builtin-trace.c:996:117: error: ‘be’ undeclared (first use in this
> > > function); did you mean ‘bf’?
> > >   996 |         for (int i = 0, struct btf_enum *be =
> > > arg_fmt->btf_entry.entries; i < arg_fmt->btf_entry.nr_entries; ++i,
> > > ++be) {
> > >       |
> > >                                                      ^~
> > >       |
> > >                                                      bf
> > > builtin-trace.c:996:117: note: each undeclared identifier is reported
> > > only once for each function it appears in
> > >
> > > This is not good either:
> > >
> > > for (int i = 0, be = arg_fmt->btf_entry.entries; i <
> > > arg_fmt->btf_entry.nr_entries; ++i, ++be) {
> > >
> > > builtin-trace.c: In function ‘btf_enum_scnprintf’:
> > > builtin-trace.c:998:25: error: declaration of ‘be’ shadows a previous
> > > local [-Werror=shadow]
> > >   998 |         for (int i = 0, be = arg_fmt->btf_entry.entries; i <
> > > arg_fmt->btf_entry.nr_entries; ++i, ++be) {
> > >       |                         ^~
> > > builtin-trace.c:991:26: note: shadowed declaration is here
> > >   991 |         struct btf_enum *be;
> > >       |                          ^~
> > > builtin-trace.c:998:30: error: initialization of ‘int’ from ‘void *’
> > > makes integer from pointer without a cast [-Wint-conversion]
> > >   998 |         for (int i = 0, be = arg_fmt->btf_entry.entries; i <
> > > arg_fmt->btf_entry.nr_entries; ++i, ++be) {
> > >       |                              ^~~~~~~
> > >
> > > But I can do:
> > >
> > > ```
> > > be = arg_fmt->btf_entry.entries;
> > >
> > > for (int i = 0; i < arg_fmt->btf_entry.nr_entries; ++i, ++be) {
> > > ```
> > >
> > > Thanks,
> > > Howard
> > >
> > >
> > > > >
> > > > > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' tools/perf/ | wc -l
> > > > > 99
> > > > > ⬢[acme@toolbox perf-tools-next]$
> > > > >
> > > > > Doing it this way makes the code more compact and is allowed even in
> > > > > kernel code since some time ago:
> > > >
> > > > Sure.
> > > >
> > > > >
> > > > > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' drivers/ | wc -l
> > > > > 294
> > > > > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' kernel/ | wc -l
> > > > > 12
> > > > > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' net/ | wc -l
> > > > > 3
> > > > > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' mm/ | wc -l
> > > > > 21
> > > > > ⬢[acme@toolbox perf-tools-next]$
> > > > >
> > > > > > +             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;
> > > > >
> > > > > Here you have to check if use_btf is NULL, as you, in this patch, later
> > > > > call syscall_arg_fmt__init_array(arg, field, NULL) in
> > > > > evsel__init_tp_arg_scnprintf(), probably you tested it all at the end of
> > > > > the patch series, we have to make sure that it works after each patch,
> > > > > so that we keep the codebase bisectable.
> > > >
> > > > I'm sorry, could you enlighten me on this? I thought:
> > > > ```
> > > > else if (strstr(field->type, "enum") && use_btf != NULL) {
> > > > ```
> > > > is doing the NULL checking. If you mean making sure the NULL checking
> > > > appears in all patches, I thought this is where we should introduce
> > > > the checking. Tiny reminder, the [1/5] patch is your syscalltbl
> > > > traversal bug fix.
> > > >
> > > > >
> > > > > >               } 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
> > > > > >
> > > >
> > > > Thanks,
> > > > Howard

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 5/5] perf trace: Add test for enum augmentation
  2024-06-21 16:07   ` Namhyung Kim
@ 2024-06-21 16:43     ` Howard Chu
  2024-06-21 17:15       ` Namhyung Kim
  0 siblings, 1 reply; 30+ messages in thread
From: Howard Chu @ 2024-06-21 16:43 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Arnaldo Carvalho de Melo, Jiri Olsa, Ian Rogers, Adrian Hunter,
	Kan Liang, linux-kernel, linux-perf-users

Hello Namhyung,

On Sat, Jun 22, 2024 at 12:07 AM Namhyung Kim <namhyung@kernel.org> wrote:
>
> Hi Howard,
>
> On Wed, Jun 19, 2024 at 04:20:42PM +0800, Howard Chu wrote:
> > Check for vmlinux's existence in sysfs as prerequisite.
> >
> > Add landlock_add_rule.c workload. Trace landlock_add_rule syscall to see
> > if the output is desirable.
>
> Do you expect to add more things to the landlock workload?  I think we
> could simply call it landlock.c and probably do other things according
> to the argument, if needed (e.g. landlock add).

Shortening the name is good, I'll change it, thanks. I think
landlock_add_rule is the only syscall that we need currently, for it
contains the only enum argument of all the syscalls. I'll look into
how we can use these arguments, thank you.

Thanks,
Howard

>
> Thanks,
> Namhyung
>
> >
> > 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.
> >
> > Suggested-by: Arnaldo Carvalho de Melo <acme@kernel.org>
> > Signed-off-by: Howard Chu <howardchu95@gmail.com>
> > ---
> >  tools/perf/tests/builtin-test.c               |  1 +
> >  tools/perf/tests/shell/trace_btf_enum.sh      | 57 +++++++++++++++++++
> >  tools/perf/tests/tests.h                      |  1 +
> >  tools/perf/tests/workloads/Build              |  1 +
> >  .../perf/tests/workloads/landlock_add_rule.c  | 32 +++++++++++
> >  5 files changed, 92 insertions(+)
> >  create mode 100755 tools/perf/tests/shell/trace_btf_enum.sh
> >  create mode 100644 tools/perf/tests/workloads/landlock_add_rule.c
> >
> > diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
> > index c3d84b67ca8e..e83200415ad1 100644
> > --- a/tools/perf/tests/builtin-test.c
> > +++ b/tools/perf/tests/builtin-test.c
> > @@ -152,6 +152,7 @@ static struct test_workload *workloads[] = {
> >       &workload__sqrtloop,
> >       &workload__brstack,
> >       &workload__datasym,
> > +     &workload__landlock_add_rule,
> >  };
> >
> >  static int num_subtests(const struct test_suite *t)
> > 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..4861983553ab
> > --- /dev/null
> > +++ b/tools/perf/tests/shell/trace_btf_enum.sh
> > @@ -0,0 +1,57 @@
> > +#!/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"
> > +
> > +TESTPROG="perf test -w landlock_add_rule"
> > +
> > +. "$(dirname $0)"/lib/probe.sh
> > +skip_if_no_perf_trace || exit 2
> > +
> > +check_vmlinux() {
> > +  echo "Checking if vmlinux exists"
> > +  if ! ls /sys/kernel/btf/vmlinux 1>/dev/null 2>&1
> > +  then
> > +    echo "trace+enum test [Skipped missing vmlinux BTF support]"
> > +    err=2
> > +  fi
> > +}
> > +
> > +trace_landlock() {
> > +  echo "Tracing syscall ${syscall}"
> > +  if perf trace -e $syscall $TESTPROG 2>&1 | \
> > +     grep -q -E ".*landlock_add_rule\(ruleset_fd: 11, rule_type: (LANDLOCK_RULE_PATH_BENEATH|LANDLOCK_RULE_NET_PORT), rule_attr: 0x[a-f0-9]+, flags: 45\) = -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
> > +}
> > +
> > +check_vmlinux
> > +
> > +if [ $err = 0 ]; then
> > +  trace_landlock
> > +fi
> > +
> > +if [ $err = 0 ]; then
> > +  trace_non_syscall
> > +fi
> > +
> > +exit $err
> > diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
> > index 3aa7701ee0e9..69126299bb08 100644
> > --- a/tools/perf/tests/tests.h
> > +++ b/tools/perf/tests/tests.h
> > @@ -205,6 +205,7 @@ DECLARE_WORKLOAD(leafloop);
> >  DECLARE_WORKLOAD(sqrtloop);
> >  DECLARE_WORKLOAD(brstack);
> >  DECLARE_WORKLOAD(datasym);
> > +DECLARE_WORKLOAD(landlock_add_rule);
> >
> >  extern const char *dso_to_test;
> >  extern const char *test_objdump_path;
> > diff --git a/tools/perf/tests/workloads/Build b/tools/perf/tests/workloads/Build
> > index a1f34d5861e3..5b12b93ecffa 100644
> > --- a/tools/perf/tests/workloads/Build
> > +++ b/tools/perf/tests/workloads/Build
> > @@ -6,6 +6,7 @@ perf-y += leafloop.o
> >  perf-y += sqrtloop.o
> >  perf-y += brstack.o
> >  perf-y += datasym.o
> > +perf-y += landlock_add_rule.o
> >
> >  CFLAGS_sqrtloop.o         = -g -O0 -fno-inline -U_FORTIFY_SOURCE
> >  CFLAGS_leafloop.o         = -g -O0 -fno-inline -fno-omit-frame-pointer -U_FORTIFY_SOURCE
> > diff --git a/tools/perf/tests/workloads/landlock_add_rule.c b/tools/perf/tests/workloads/landlock_add_rule.c
> > new file mode 100644
> > index 000000000000..529b5f1ea5a7
> > --- /dev/null
> > +++ b/tools/perf/tests/workloads/landlock_add_rule.c
> > @@ -0,0 +1,32 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +#include <linux/compiler.h>
> > +#include <uapi/asm-generic/unistd.h> // for __NR_landlock_add_rule
> > +#include <unistd.h>
> > +#include <linux/landlock.h>
> > +#include "../tests.h"
> > +
> > +static int landlock_add_rule(int argc __maybe_unused, const char **argv __maybe_unused)
> > +{
> > +     int fd = 11;
> > +     int flags = 45;
> > +
> > +     struct landlock_path_beneath_attr path_beneath_attr = {
> > +         .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE,
> > +         .parent_fd = 14,
> > +     };
> > +
> > +     struct landlock_net_port_attr net_port_attr = {
> > +         .port = 19,
> > +         .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP,
> > +     };
> > +
> > +     syscall(__NR_landlock_add_rule, fd, LANDLOCK_RULE_PATH_BENEATH,
> > +             &path_beneath_attr, flags);
> > +
> > +     syscall(__NR_landlock_add_rule, fd, LANDLOCK_RULE_NET_PORT,
> > +             &net_port_attr, flags);
> > +
> > +     return 0;
> > +}
> > +
> > +DEFINE_WORKLOAD(landlock_add_rule);
> > --
> > 2.45.2
> >

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 5/5] perf trace: Add test for enum augmentation
  2024-06-21 16:43     ` Howard Chu
@ 2024-06-21 17:15       ` Namhyung Kim
  0 siblings, 0 replies; 30+ messages in thread
From: Namhyung Kim @ 2024-06-21 17:15 UTC (permalink / raw)
  To: Howard Chu
  Cc: Arnaldo Carvalho de Melo, Jiri Olsa, Ian Rogers, Adrian Hunter,
	Kan Liang, linux-kernel, linux-perf-users

On Fri, Jun 21, 2024 at 9:43 AM Howard Chu <howardchu95@gmail.com> wrote:
>
> Hello Namhyung,
>
> On Sat, Jun 22, 2024 at 12:07 AM Namhyung Kim <namhyung@kernel.org> wrote:
> >
> > Hi Howard,
> >
> > On Wed, Jun 19, 2024 at 04:20:42PM +0800, Howard Chu wrote:
> > > Check for vmlinux's existence in sysfs as prerequisite.
> > >
> > > Add landlock_add_rule.c workload. Trace landlock_add_rule syscall to see
> > > if the output is desirable.
> >
> > Do you expect to add more things to the landlock workload?  I think we
> > could simply call it landlock.c and probably do other things according
> > to the argument, if needed (e.g. landlock add).
>
> Shortening the name is good, I'll change it, thanks. I think
> landlock_add_rule is the only syscall that we need currently, for it
> contains the only enum argument of all the syscalls. I'll look into
> how we can use these arguments, thank you.

If you don't plan to add something, let's call it landlock.c and forget
about the argument handling for now. :)

But please add a comment in the file that it's just to test BPF
handling of enum arguments.

Thanks,
Namhyung

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 2/5] perf trace: Augment enum syscall arguments with BTF
  2024-06-19 17:59     ` Howard Chu
  2024-06-20 16:34       ` Howard Chu
@ 2024-06-21 18:03       ` Arnaldo Carvalho de Melo
  1 sibling, 0 replies; 30+ messages in thread
From: Arnaldo Carvalho de Melo @ 2024-06-21 18:03 UTC (permalink / raw)
  To: Howard Chu
  Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, Adrian Hunter, Kan Liang,
	linux-kernel, linux-perf-users

[-- Attachment #1: Type: text/plain, Size: 14800 bytes --]

On Thu, Jun 20, 2024 at 01:59:37AM +0800, Howard Chu wrote:
> Hello,
> 
> Thanks for the in-depth review.
> 
> On Wed, Jun 19, 2024 at 9:44 PM Arnaldo Carvalho de Melo
> <acme@kernel.org> wrote:
> >
> > On Wed, Jun 19, 2024 at 04:20:39PM +0800, Howard Chu wrote:
> > > 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))
> >
> > No need for the strlen test? I.e. plain using strstarts() should be
> > enough?
> 
> Agree. Thanks for pointing that out. Although if string 'type' is
> 'enum' and prefix is 'enum', strstarts() will be true, but to do 'type
> += strlen(ep) + 1', and then access 'type', might give us an
> out-of-range access, but I don't think 'type' will ever be just 'enum'
> or 'const enum', so I'll delete it then.

So, I did some changes to this patch to fix some issues:

1) We need to make this conditional on libbpf being available, i.e. we
need to build with 'make NO_LIBBPF=1' so I had to add some ifdefs for
HAVE_LIBBPF_SUPPORT, and moved some checks for trace->btf to the
functions where it is used, so that we can pass just 'trace'.

2) syscall_arg_fmt->btf_entry became just syscall_arg_fmt->type, a
'struct btf_type' pointer, this way we can have:

#ifdef HAVE_LIBBPF_SUPPORT
static int syscall_arg_fmt__cache_btf_enum(struct syscall_arg_fmt *arg_fmt, struct btf *btf, char *type)
{
	int id;

	type = strstr(type, "enum ");
	if (type == NULL)
		return -1;

	type += 5; // skip "enum " to get the enumeration name

	id = btf__find_by_name(btf, type);
	if (id < 0)
		return -1;

	arg_fmt->type = btf__type_by_id(btf, id);
	return arg_fmt->type == NULL ? -1 : 0;
}


static size_t btf_enum_scnprintf(const struct btf_type *type, struct btf *btf, char *bf, size_t size, int val)
{
	struct btf_enum *be = btf_enum(type);
	const int nr_entries = btf_vlen(type);

	for (int i = 0; i < nr_entries; ++i, ++be) {
		if (be->val == val) {
			return scnprintf(bf, size, "%s",
					 btf__name_by_offset(btf, be->name_off));
		}
	}

	return 0;
}

static size_t trace__btf_enum_scnprintf(struct trace *trace, struct syscall_arg_fmt *arg_fmt, char *bf,
					size_t size, int val, char *type)
{
	if (trace->btf == NULL)
		return 0;
	/* if btf_entry is NULL, find and save it to arg_fmt */
	if (arg_fmt->type == NULL &&
	    syscall_arg_fmt__cache_btf_enum(arg_fmt, trace->btf, type) < 0)
		return 0;

	return btf_enum_scnprintf(arg_fmt->type, trace->btf, bf, size, val);
}
#else // HAVE_LIBBPF_SUPPORT
static size_t trace__btf_enum_scnprintf(struct trace *trace __maybe_unused, struct syscall_arg_fmt *arg_fmt __maybe_unused,
					char *bf __maybe_unused, size_t size __maybe_unused, int val __maybe_unused,
					char *type __maybe_unused)
{
	return 0;
}
#endif // HAVE_LIBBPF_SUPPORT

The patch on top of yours is attached. I'll fixup the following ones and
have them in a branch for you to check and see if you agree with the
changes.

- Arnaldo

> >
> > > +                     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)
> >
> > a pr_debug() stating that something that tracefs says should be in BTF
> > and isn't found there seems to be of value here.
> 
> Sure.
> 
> >
> > > +             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;
> >
> >         struct btf_enum *be = (struct btf_enum *)arg_fmt->btf_entry.entries;
> >
> >  arg_fmt->btf_entry.entries is (void *), so we don't need the (struct
> > btf_enum *) cast here, removing it makes the code more compact. And
> > since we move the declaration to the same line, the info about its type
> > is there as well.
> 
> Sure, thanks.
> 
> >
> > > +
> > > +     for (i = 0; i < arg_fmt->btf_entry.nr_entries; ++i, ++be) {
> >
> >
> >         for (int i = 0; i < arg_fmt->btf_entry.nr_entries; ++i, ++be) {
> >
> > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' tools/perf/ | wc -l
> > 99
> > ⬢[acme@toolbox perf-tools-next]$
> >
> > Doing it this way makes the code more compact and is allowed even in
> > kernel code since some time ago:
> 
> Sure.
> 
> >
> > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' drivers/ | wc -l
> > 294
> > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' kernel/ | wc -l
> > 12
> > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' net/ | wc -l
> > 3
> > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' mm/ | wc -l
> > 21
> > ⬢[acme@toolbox perf-tools-next]$
> >
> > > +             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;
> >
> > Here you have to check if use_btf is NULL, as you, in this patch, later
> > call syscall_arg_fmt__init_array(arg, field, NULL) in
> > evsel__init_tp_arg_scnprintf(), probably you tested it all at the end of
> > the patch series, we have to make sure that it works after each patch,
> > so that we keep the codebase bisectable.
> 
> I'm sorry, could you enlighten me on this? I thought:
> ```
> else if (strstr(field->type, "enum") && use_btf != NULL) {
> ```
> is doing the NULL checking. If you mean making sure the NULL checking
> appears in all patches, I thought this is where we should introduce
> the checking. Tiny reminder, the [1/5] patch is your syscalltbl
> traversal bug fix.

scrap my comment, was a brain fart ;-/ of course you are alreadyu
checking if use_btf is NULL, doh.
 
> >
> > >               } 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
> > >
> 
> Thanks,
> Howard

[-- Attachment #2: btf_enum_fprintf.patch --]
[-- Type: text/plain, Size: 5138 bytes --]

diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 3a1c031aff9ad049..49d99c20b7f711ef 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -112,10 +112,9 @@ struct syscall_arg_fmt {
 	u16	   nr_entries; // for arrays
 	bool	   show_zero;
 	bool	   is_enum;
-	struct {
-		void	*entry;
-		u16	nr_entries;
-	}	   btf_entry;
+#ifdef HAVE_LIBBPF_SUPPORT
+	const struct btf_type *type;
+#endif
 };
 
 struct syscall_fmt {
@@ -146,7 +145,9 @@ struct trace {
 #ifdef HAVE_BPF_SKEL
 	struct augmented_raw_syscalls_bpf *skel;
 #endif
+#ifdef HAVE_LIBBPF_SUPPORT
 	struct btf		*btf;
+#endif
 	struct record_opts	opts;
 	struct evlist	*evlist;
 	struct machine		*host;
@@ -894,47 +895,32 @@ static size_t syscall_arg__scnprintf_getrandom_flags(char *bf, size_t size,
 
 #define SCA_GETRANDOM_FLAGS syscall_arg__scnprintf_getrandom_flags
 
-static int btf_enum_find_entry(struct btf *btf, char *type, struct syscall_arg_fmt *arg_fmt)
+#ifdef HAVE_LIBBPF_SUPPORT
+static int syscall_arg_fmt__cache_btf_enum(struct syscall_arg_fmt *arg_fmt, struct btf *btf, char *type)
 {
-	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;
-	}
+	type = strstr(type, "enum ");
+	if (type == NULL)
+		return -1;
+
+	type += 5; // skip "enum " to get the enumeration name
 
 	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.entry      = btf_enum(bt);
-	arg_fmt->btf_entry.nr_entries = btf_vlen(bt);
-
-	return 0;
+	arg_fmt->type = btf__type_by_id(btf, id);
+	return arg_fmt->type == NULL ? -1 : 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.entry == NULL)
-		if (btf_enum_find_entry(btf, type, arg_fmt))
-			return 0;
 
-	be = (struct btf_enum *)arg_fmt->btf_entry.entry;
+static size_t btf_enum_scnprintf(const struct btf_type *type, struct btf *btf, char *bf, size_t size, int val)
+{
+	struct btf_enum *be = btf_enum(type);
+	const int nr_entries = btf_vlen(type);
 
-	for (i = 0; i < arg_fmt->btf_entry.nr_entries; ++i, ++be) {
+	for (int i = 0; i < nr_entries; ++i, ++be) {
 		if (be->val == val) {
 			return scnprintf(bf, size, "%s",
 					 btf__name_by_offset(btf, be->name_off));
@@ -944,6 +930,27 @@ static size_t btf_enum_scnprintf(char *bf, size_t size, int val, struct btf *btf
 	return 0;
 }
 
+static size_t trace__btf_enum_scnprintf(struct trace *trace, struct syscall_arg_fmt *arg_fmt, char *bf,
+					size_t size, int val, char *type)
+{
+	if (trace->btf == NULL)
+		return 0;
+	/* if btf_entry is NULL, find and save it to arg_fmt */
+	if (arg_fmt->type == NULL &&
+	    syscall_arg_fmt__cache_btf_enum(arg_fmt, trace->btf, type) < 0)
+		return 0;
+
+	return btf_enum_scnprintf(arg_fmt->type, trace->btf, bf, size, val);
+}
+#else // HAVE_LIBBPF_SUPPORT
+static size_t trace__btf_enum_scnprintf(struct trace *trace __maybe_unused, struct syscall_arg_fmt *arg_fmt __maybe_unused,
+					char *bf __maybe_unused, size_t size __maybe_unused, int val __maybe_unused,
+					char *type __maybe_unused)
+{
+	return 0;
+}
+#endif // HAVE_LIBBPF_SUPPORT
+
 #define STRARRAY(name, array) \
 	  { .scnprintf	= SCA_STRARRAY, \
 	    .strtoul	= STUL_STRARRAY, \
@@ -1757,13 +1764,18 @@ static void trace__symbols__exit(struct trace *trace)
 	symbol__exit();
 }
 
-static void trace__load_vmlinux_btf(struct trace *trace)
+static void trace__load_vmlinux_btf(struct trace *trace __maybe_unused)
 {
+#ifdef HAVE_LIBBPF_SUPPORT
+	if (trace->btf != NULL)
+		return;
+
 	trace->btf = btf__load_vmlinux_btf();
 	if (verbose > 0) {
 		fprintf(trace->output, trace->btf ? "vmlinux BTF loaded\n" :
 						    "Failed to load vmlinux BTF\n");
 	}
+#endif
 }
 
 static int syscall__alloc_arg_fmts(struct syscall *sc, int nr_args)
@@ -1959,7 +1971,7 @@ static int trace__read_syscall_info(struct trace *trace, int id)
 	err = syscall__set_arg_fmts(sc);
 
 	/* after calling syscall__set_arg_fmts() we'll know whether use_btf is true */
-	if (sc->use_btf && trace->btf == NULL)
+	if (sc->use_btf)
 		trace__load_vmlinux_btf(trace);
 
 	return err;
@@ -2182,10 +2194,9 @@ 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 (sc->arg_fmt[arg.idx].is_enum) {
+				size_t p = trace__btf_enum_scnprintf(trace, &sc->arg_fmt[arg.idx], bf + printed,
+								     size - printed, val, field->type);
 				if (p) {
 					printed += p;
 					continue;

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 2/5] perf trace: Augment enum syscall arguments with BTF
  2024-06-21 16:18             ` Howard Chu
@ 2024-06-22 18:28               ` Arnaldo Carvalho de Melo
  2024-06-23 11:34                 ` Howard Chu
  0 siblings, 1 reply; 30+ messages in thread
From: Arnaldo Carvalho de Melo @ 2024-06-22 18:28 UTC (permalink / raw)
  To: Howard Chu
  Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, Adrian Hunter, Kan Liang,
	linux-kernel, linux-perf-users

On Sat, Jun 22, 2024 at 12:18:10AM +0800, Howard Chu wrote:
> Hello Arnaldo,
> 
> I think I need to add some explanations here, for the all-in-one patch
> that I have on the tree.
> 
> Firstly, no use_btf is passed when constructing syscall argument
> format. We just write is_enum when the type is enum. This is because
> we don't load btf when we are constructing the formats, we load
> vmlinux when a tracepoint hits.
> 
> before:
> 
> } else if (strstr(field->type, "enum") && use_btf != NULL) {
> *use_btf = arg->is_enum = true;
> 
> after:
> 
> } else if (strstr(field->type, "enum")) {
> arg->is_enum = true;
> 
> Another confusing place is when we do the filtering. When a user
> passes an enum name, say --filter="mode==HRTIMER_MODE_ABS", we do not
> know what 'HRTIMER_MODE_ABS' means. For this case, we have to load the
> vmlinux BTF, and match the strings to get a value, not delaying till a
> tracepoint hits.

Right, I worked on your latest series before the combined patch, and to
solve that at filter expansion time, I did:

@@ -3861,9 +3903,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,
+                                       .trace = trace,
+                                       .fmt   = fmt,
                                };
 
+                               if (fmt->is_enum) {
+                                       syscall_arg.parm = type;
+                               } else {
+                                       syscall_arg.parm = fmt->parm;
+                               }
+
                                if (fmt->strtoul(right, right_size, &syscall_arg, &val)) {


And then in the enum btf strtoul we can load the btf:

+static bool syscall_arg__strtoul_btf_enum(char *bf, size_t size, struct syscall_arg *arg, u64 *val)
+{
+       const struct btf_type *bt;
+       char *type = arg->parm;
+       struct btf_enum *be;
+       struct btf *btf;
+
+       trace__load_vmlinux_btf(arg->trace);
+
+       btf = arg->trace->btf;
+       if (btf == NULL)
+               return false;
+
+       if (syscall_arg_fmt__cache_btf_enum(arg->fmt, btf, type) < 0)
+               return false;
+
+       bt = arg->fmt->type;
+       be = btf_enum(bt);
+       for (int 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;
+}

And:

+static int syscall_arg_fmt__cache_btf_enum(struct syscall_arg_fmt *arg_fmt, struct btf *btf, char *type)
+{
+       int id;
+
+       // Already cached?
+       if (arg_fmt->type != NULL)
+               return 0;
+
+       type = strstr(type, "enum ");
+       if (type == NULL)
+               return -1;
+
+       type += 5; // skip "enum " to get the enumeration name
+
+       id = btf__find_by_name(btf, type);
+       if (id < 0)
+               return -1;
+
+       arg_fmt->type = btf__type_by_id(btf, id);
+       return arg_fmt->type == NULL ? -1 : 0;
+}

So that in all cases we do the lookup for the BTF enum type just once
and cache it in the syscall_arg_fmt->type.
 
> Next thing is, for this section in trace__expand_filter():
> 
> ```
> struct btf_parm bparm;
> 
> if (fmt->is_enum) {
>     if (trace->btf == NULL)
>         trace__load_vmlinux_btf(trace);
> 
>     /* btf could be NULL */
>     bparm.btf  = trace->btf;
>     bparm.type = type;
>     syscall_arg.parm = &bparm;
> }
> ```
> 
> I don't handle the return value of trace__load_vmlinux_btf(), because
> I want to make the latter " if (fmt->strtoul(right, right_size,
> &syscall_arg, &val)) {
> " evaluated to be false, so that we can fall into the else block,
> therefore prints the pr_err of no resolver. This could be confusing.
> 
> pr_err("No resolver (strtoul) for \"%s\" in \"%s\", can't set filter \"%s\"\n",
>        arg, evsel->name, evsel->filter);
> return -1;
> 
> And another confusing part is, originally we discard 0 value for enum
> arguments, now with enum aug, printed a 0-value enum makes sense,
> therefore these lines:
> 
> if (val == 0 && !trace->show_zeros &&
>     !(sc->arg_fmt && sc->arg_fmt[arg.idx].show_zero))
> #ifdef HAVE_LIBBPF_SUPPORT
>         if (!(sc->arg_fmt && sc->arg_fmt[arg.idx].is_enum))
> #endif
>         continue;
> 
> It suits as an &&, I don't know if it's the best way to do it.

Excellent observation, that show_zero needs to be conditional on non-BTF
enum augmentation, i.e. if we're going to just print a zero and 'perf
trace' is configured not to show it but we are printing the enum entry,
then we should not consider that .show_zero setting. But this can be
done as a follow up patch to reduce the size of this one.
 
> That's it for all, thank you for finding time in your busy schedule to
> review my code. I'll develop my skills to do self-checks in the
> future,
> 
> Thanks,
> Howard
> 
> On Fri, Jun 21, 2024 at 9:40 PM Arnaldo Carvalho de Melo
> <acme@kernel.org> wrote:
> >
> > On Fri, Jun 21, 2024 at 03:16:26AM +0800, Howard Chu wrote:
> > > Hello,
> > >
> > > The fixes are on the perf-tools-next branch at:
> > > https://github.com/Sberm/linux.git
> > >
> > > please use
> > > ```
> > > git pull https://github.com/Sberm/linux.git perf-tools-next
> > > ```
> > >
> > > I'm no git expert so please tell me if something is wrong.
> >
> > So, you combined everything into just one patch and there is no commit
> > log message, I'll see if I can redo it from the last broken out patch
> > series you submitted.
> >
> > - Arnaldo
> >
> > > P.S. not my proudest patch, changed too many things, might be buggy.
> > > If there're bugs please let me know.
> > >
> > > On Fri, Jun 21, 2024 at 12:34 AM Howard Chu <howardchu95@gmail.com> wrote:
> > > >
> > > > Hello Arnaldo,
> > > >
> > > > Thanks for the review, the code is refined based on your comments.
> > > > There is just a little thing:
> > > >
> > > > On Thu, Jun 20, 2024 at 1:59 AM Howard Chu <howardchu95@gmail.com> wrote:
> > > > >
> > > > > Hello,
> > > > >
> > > > > Thanks for the in-depth review.
> > > > >
> > > > > On Wed, Jun 19, 2024 at 9:44 PM Arnaldo Carvalho de Melo
> > > > > <acme@kernel.org> wrote:
> > > > > >
> > > > > > On Wed, Jun 19, 2024 at 04:20:39PM +0800, Howard Chu wrote:
> > > > > > > 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))
> > > > > >
> > > > > > No need for the strlen test? I.e. plain using strstarts() should be
> > > > > > enough?
> > > > >
> > > > > Agree. Thanks for pointing that out. Although if string 'type' is
> > > > > 'enum' and prefix is 'enum', strstarts() will be true, but to do 'type
> > > > > += strlen(ep) + 1', and then access 'type', might give us an
> > > > > out-of-range access, but I don't think 'type' will ever be just 'enum'
> > > > > or 'const enum', so I'll delete it then.
> > > > >
> > > > > >
> > > > > > > +                     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)
> > > > > >
> > > > > > a pr_debug() stating that something that tracefs says should be in BTF
> > > > > > and isn't found there seems to be of value here.
> > > > >
> > > > > Sure.
> > > > >
> > > > > >
> > > > > > > +             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;
> > > > > >
> > > > > >         struct btf_enum *be = (struct btf_enum *)arg_fmt->btf_entry.entries;
> > > > > >
> > > > > >  arg_fmt->btf_entry.entries is (void *), so we don't need the (struct
> > > > > > btf_enum *) cast here, removing it makes the code more compact. And
> > > > > > since we move the declaration to the same line, the info about its type
> > > > > > is there as well.
> > > > >
> > > > > Sure, thanks.
> > > > >
> > > > > >
> > > > > > > +
> > > > > > > +     for (i = 0; i < arg_fmt->btf_entry.nr_entries; ++i, ++be) {
> > > > > >
> > > > > >
> > > > > >         for (int i = 0; i < arg_fmt->btf_entry.nr_entries; ++i, ++be) {
> > > >
> > > > I did this, it's all good. But here:
> > > >
> > > > for (i = 0, be = arg_fmt->btf_entry.entries; i <
> > > > arg_fmt->btf_entry.nr_entries; ++i, ++be) {
> > > >
> > > > I can't do
> > > >
> > > > for (int i = 0, struct btf_enum *be = arg_fmt->btf_entry.entries; i <
> > > > arg_fmt->btf_entry.nr_entries; ++i, ++be) {
> > > >
> > > > The compiler will show errors:
> > > >
> > > > builtin-trace.c: In function ‘btf_enum_scnprintf’:
> > > > builtin-trace.c:996:25: error: expected identifier or ‘(’ before ‘struct’
> > > >   996 |         for (int i = 0, struct btf_enum *be =
> > > > arg_fmt->btf_entry.entries; i < arg_fmt->btf_entry.nr_entries; ++i,
> > > > ++be) {
> > > >       |                         ^~~~~~
> > > > builtin-trace.c:996:117: error: ‘be’ undeclared (first use in this
> > > > function); did you mean ‘bf’?
> > > >   996 |         for (int i = 0, struct btf_enum *be =
> > > > arg_fmt->btf_entry.entries; i < arg_fmt->btf_entry.nr_entries; ++i,
> > > > ++be) {
> > > >       |
> > > >                                                      ^~
> > > >       |
> > > >                                                      bf
> > > > builtin-trace.c:996:117: note: each undeclared identifier is reported
> > > > only once for each function it appears in
> > > >
> > > > This is not good either:
> > > >
> > > > for (int i = 0, be = arg_fmt->btf_entry.entries; i <
> > > > arg_fmt->btf_entry.nr_entries; ++i, ++be) {
> > > >
> > > > builtin-trace.c: In function ‘btf_enum_scnprintf’:
> > > > builtin-trace.c:998:25: error: declaration of ‘be’ shadows a previous
> > > > local [-Werror=shadow]
> > > >   998 |         for (int i = 0, be = arg_fmt->btf_entry.entries; i <
> > > > arg_fmt->btf_entry.nr_entries; ++i, ++be) {
> > > >       |                         ^~
> > > > builtin-trace.c:991:26: note: shadowed declaration is here
> > > >   991 |         struct btf_enum *be;
> > > >       |                          ^~
> > > > builtin-trace.c:998:30: error: initialization of ‘int’ from ‘void *’
> > > > makes integer from pointer without a cast [-Wint-conversion]
> > > >   998 |         for (int i = 0, be = arg_fmt->btf_entry.entries; i <
> > > > arg_fmt->btf_entry.nr_entries; ++i, ++be) {
> > > >       |                              ^~~~~~~
> > > >
> > > > But I can do:
> > > >
> > > > ```
> > > > be = arg_fmt->btf_entry.entries;
> > > >
> > > > for (int i = 0; i < arg_fmt->btf_entry.nr_entries; ++i, ++be) {
> > > > ```
> > > >
> > > > Thanks,
> > > > Howard
> > > >
> > > >
> > > > > >
> > > > > > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' tools/perf/ | wc -l
> > > > > > 99
> > > > > > ⬢[acme@toolbox perf-tools-next]$
> > > > > >
> > > > > > Doing it this way makes the code more compact and is allowed even in
> > > > > > kernel code since some time ago:
> > > > >
> > > > > Sure.
> > > > >
> > > > > >
> > > > > > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' drivers/ | wc -l
> > > > > > 294
> > > > > > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' kernel/ | wc -l
> > > > > > 12
> > > > > > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' net/ | wc -l
> > > > > > 3
> > > > > > ⬢[acme@toolbox perf-tools-next]$ git grep 'for (int ' mm/ | wc -l
> > > > > > 21
> > > > > > ⬢[acme@toolbox perf-tools-next]$
> > > > > >
> > > > > > > +             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;
> > > > > >
> > > > > > Here you have to check if use_btf is NULL, as you, in this patch, later
> > > > > > call syscall_arg_fmt__init_array(arg, field, NULL) in
> > > > > > evsel__init_tp_arg_scnprintf(), probably you tested it all at the end of
> > > > > > the patch series, we have to make sure that it works after each patch,
> > > > > > so that we keep the codebase bisectable.
> > > > >
> > > > > I'm sorry, could you enlighten me on this? I thought:
> > > > > ```
> > > > > else if (strstr(field->type, "enum") && use_btf != NULL) {
> > > > > ```
> > > > > is doing the NULL checking. If you mean making sure the NULL checking
> > > > > appears in all patches, I thought this is where we should introduce
> > > > > the checking. Tiny reminder, the [1/5] patch is your syscalltbl
> > > > > traversal bug fix.
> > > > >
> > > > > >
> > > > > > >               } 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
> > > > > > >
> > > > >
> > > > > Thanks,
> > > > > Howard

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 2/5] perf trace: Augment enum syscall arguments with BTF
  2024-06-22 18:28               ` Arnaldo Carvalho de Melo
@ 2024-06-23 11:34                 ` Howard Chu
  0 siblings, 0 replies; 30+ messages in thread
From: Howard Chu @ 2024-06-23 11:34 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, Adrian Hunter, Kan Liang,
	linux-kernel, linux-perf-users

Hello Arnaldo,

Your changes are very very good, thank you so much.

On Sun, Jun 23, 2024 at 2:28 AM Arnaldo Carvalho de Melo
<acme@kernel.org> wrote:
>
> On Sat, Jun 22, 2024 at 12:18:10AM +0800, Howard Chu wrote:
> > Hello Arnaldo,
> >
> > I think I need to add some explanations here, for the all-in-one patch
> > that I have on the tree.
> >
> > Firstly, no use_btf is passed when constructing syscall argument
> > format. We just write is_enum when the type is enum. This is because
> > we don't load btf when we are constructing the formats, we load
> > vmlinux when a tracepoint hits.
> >
> > before:
> >
> > } else if (strstr(field->type, "enum") && use_btf != NULL) {
> > *use_btf = arg->is_enum = true;
> >
> > after:
> >
> > } else if (strstr(field->type, "enum")) {
> > arg->is_enum = true;
> >
> > Another confusing place is when we do the filtering. When a user
> > passes an enum name, say --filter="mode==HRTIMER_MODE_ABS", we do not
> > know what 'HRTIMER_MODE_ABS' means. For this case, we have to load the
> > vmlinux BTF, and match the strings to get a value, not delaying till a
> > tracepoint hits.
>
> Right, I worked on your latest series before the combined patch, and to
> solve that at filter expansion time, I did:
>
> @@ -3861,9 +3903,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,
> +                                       .trace = trace,
> +                                       .fmt   = fmt,
>                                 };
>
> +                               if (fmt->is_enum) {
> +                                       syscall_arg.parm = type;
> +                               } else {
> +                                       syscall_arg.parm = fmt->parm;
> +                               }
> +
>                                 if (fmt->strtoul(right, right_size, &syscall_arg, &val)) {
>
>
> And then in the enum btf strtoul we can load the btf:
>
> +static bool syscall_arg__strtoul_btf_enum(char *bf, size_t size, struct syscall_arg *arg, u64 *val)
> +{
> +       const struct btf_type *bt;
> +       char *type = arg->parm;
> +       struct btf_enum *be;
> +       struct btf *btf;
> +
> +       trace__load_vmlinux_btf(arg->trace);
> +
> +       btf = arg->trace->btf;
> +       if (btf == NULL)
> +               return false;
> +
> +       if (syscall_arg_fmt__cache_btf_enum(arg->fmt, btf, type) < 0)
> +               return false;
> +
> +       bt = arg->fmt->type;
> +       be = btf_enum(bt);
> +       for (int 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;
> +}
>
> And:
>
> +static int syscall_arg_fmt__cache_btf_enum(struct syscall_arg_fmt *arg_fmt, struct btf *btf, char *type)
> +{
> +       int id;
> +
> +       // Already cached?
> +       if (arg_fmt->type != NULL)
> +               return 0;
> +
> +       type = strstr(type, "enum ");
> +       if (type == NULL)
> +               return -1;
> +
> +       type += 5; // skip "enum " to get the enumeration name
> +

The shifting of enum name before doing btf__type_by_id() is handled
perfectly. And the extra wrapper function trace__btf_scnprintf() makes
a lot of sense.

Just two tiny little things:

1) the `make NO_LIBBPF=1` and `make NO_LIBELF=1` won't build

builtin-trace.c: In function ‘syscall__scnprintf_args’:
builtin-trace.c:2258:28: error: expected expression before ‘)’ token
 2258 |                            )
      |                            ^

if (val == 0 && !trace->show_zeros &&
    !(sc->arg_fmt && sc->arg_fmt[arg.idx].show_zero) &&
#ifdef HAVE_LIBBPF_SUPPORT
    !(sc->arg_fmt && sc->arg_fmt[arg.idx].strtoul == STUL_BTF_TYPE)
#endif
   )
        continue;

The fix is simple, just change the above to:

if (val == 0 && !trace->show_zeros &&
    !(sc->arg_fmt && sc->arg_fmt[arg.idx].show_zero)
#ifdef HAVE_LIBBPF_SUPPORT
    && !(sc->arg_fmt && sc->arg_fmt[arg.idx].strtoul == STUL_BTF_TYPE)
#endif
   )
        continue;

2) In this version, we won't defer the loading of vmlinux BTF till an
enum-augmentable tracepoint hits. I'm sure you made this conscious
change, not that there's anything wrong with it, just want to point it
out for clarity.

Overall, it is a great revision. Man, what can I say, this patch is
better, simpler and it handles details very well. Thank you so much
for doing this.

Thanks,
Howard

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2 5/5] perf trace: Add test for enum augmentation
  2024-06-19  8:20 ` [PATCH v2 5/5] perf trace: Add test for enum augmentation Howard Chu
  2024-06-19 13:51   ` Arnaldo Carvalho de Melo
  2024-06-21 16:07   ` Namhyung Kim
@ 2024-06-28  6:37   ` kernel test robot
  2 siblings, 0 replies; 30+ messages in thread
From: kernel test robot @ 2024-06-28  6:37 UTC (permalink / raw)
  To: Howard Chu, Arnaldo Carvalho de Melo
  Cc: oe-kbuild-all, Jiri Olsa, Namhyung Kim, Ian Rogers, Adrian Hunter,
	Kan Liang, linux-kernel, linux-perf-users

Hi Howard,

kernel test robot noticed the following build errors:

[auto build test ERROR on perf-tools-next/perf-tools-next]
[also build test ERROR on next-20240621]
[cannot apply to tip/perf/core perf-tools/perf-tools acme/perf/core linus/master v6.10-rc5]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Howard-Chu/perf-trace-Fix-iteration-of-syscall-ids-in-syscalltbl-entries/20240619-162417
base:   https://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git perf-tools-next
patch link:    https://lore.kernel.org/r/20240619082042.4173621-6-howardchu95%40gmail.com
patch subject: [PATCH v2 5/5] perf trace: Add test for enum augmentation
:::::: branch date: 5 days ago
:::::: commit date: 5 days ago
compiler: clang version 18.1.5 (https://github.com/llvm/llvm-project 617a15a9eac96088ae5e9134248d8236e34b91b1)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240625/202406250302.E4WaX9Ud-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/r/202406250302.E4WaX9Ud-lkp@intel.com/

All errors (new ones prefixed by >>):

   Makefile.config:663: No libunwind found. Please install libunwind-dev[el] >= 1.1 and/or set LIBUNWIND_DIR
     PERF_VERSION = 6.10.rc1.gf6ac54426465
>> tests/workloads/landlock_add_rule.c:20:24: error: use of undeclared identifier 'LANDLOCK_ACCESS_NET_CONNECT_TCP'
      20 |             .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP,
         |                               ^
>> tests/workloads/landlock_add_rule.c:18:32: error: variable has incomplete type 'struct landlock_net_port_attr'
      18 |         struct landlock_net_port_attr net_port_attr = {
         |                                       ^
   tests/workloads/landlock_add_rule.c:18:9: note: forward declaration of 'struct landlock_net_port_attr'
      18 |         struct landlock_net_port_attr net_port_attr = {
         |                ^
>> tests/workloads/landlock_add_rule.c:26:38: error: use of undeclared identifier 'LANDLOCK_RULE_NET_PORT'
      26 |         syscall(__NR_landlock_add_rule, fd, LANDLOCK_RULE_NET_PORT,
         |                                             ^
   3 errors generated.
   make[8]: *** [tools/build/Makefile.build:105: tools/perf/tests/workloads/landlock_add_rule.o] Error 1
   make[8]: *** Waiting for unfinished jobs....
   make[7]: *** [tools/build/Makefile.build:158: workloads] Error 2
   make[7]: *** Waiting for unfinished jobs....
   make[6]: *** [tools/build/Makefile.build:158: tests] Error 2
   make[6]: *** Waiting for unfinished jobs....
   make[5]: *** [Makefile.perf:727: tools/perf/perf-in.o] Error 2
   make[5]: *** Waiting for unfinished jobs....
   make[4]: *** [Makefile.perf:264: sub-make] Error 2
   make[3]: *** [Makefile:70: all] Error 2


vim +/LANDLOCK_ACCESS_NET_CONNECT_TCP +20 tools/perf/tests/workloads/landlock_add_rule.c

f6ac54426465a5 Howard Chu 2024-06-19   7  
f6ac54426465a5 Howard Chu 2024-06-19   8  static int landlock_add_rule(int argc __maybe_unused, const char **argv __maybe_unused)
f6ac54426465a5 Howard Chu 2024-06-19   9  {
f6ac54426465a5 Howard Chu 2024-06-19  10  	int fd = 11;
f6ac54426465a5 Howard Chu 2024-06-19  11  	int flags = 45;
f6ac54426465a5 Howard Chu 2024-06-19  12  
f6ac54426465a5 Howard Chu 2024-06-19  13  	struct landlock_path_beneath_attr path_beneath_attr = {
f6ac54426465a5 Howard Chu 2024-06-19  14  	    .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE,
f6ac54426465a5 Howard Chu 2024-06-19  15  	    .parent_fd = 14,
f6ac54426465a5 Howard Chu 2024-06-19  16  	};
f6ac54426465a5 Howard Chu 2024-06-19  17  
f6ac54426465a5 Howard Chu 2024-06-19  18  	struct landlock_net_port_attr net_port_attr = {
f6ac54426465a5 Howard Chu 2024-06-19  19  	    .port = 19,
f6ac54426465a5 Howard Chu 2024-06-19 @20  	    .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP,
f6ac54426465a5 Howard Chu 2024-06-19  21  	};
f6ac54426465a5 Howard Chu 2024-06-19  22  
f6ac54426465a5 Howard Chu 2024-06-19  23  	syscall(__NR_landlock_add_rule, fd, LANDLOCK_RULE_PATH_BENEATH,
f6ac54426465a5 Howard Chu 2024-06-19  24  		&path_beneath_attr, flags);
f6ac54426465a5 Howard Chu 2024-06-19  25  
f6ac54426465a5 Howard Chu 2024-06-19  26  	syscall(__NR_landlock_add_rule, fd, LANDLOCK_RULE_NET_PORT,
f6ac54426465a5 Howard Chu 2024-06-19  27  		&net_port_attr, flags);
f6ac54426465a5 Howard Chu 2024-06-19  28  
f6ac54426465a5 Howard Chu 2024-06-19  29  	return 0;
f6ac54426465a5 Howard Chu 2024-06-19  30  }
f6ac54426465a5 Howard Chu 2024-06-19  31  

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki


^ permalink raw reply	[flat|nested] 30+ messages in thread

end of thread, other threads:[~2024-06-28  6:37 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-06-19  8:20 [PATCH v2 0/5] perf trace: Augment enum arguments with BTF Howard Chu
2024-06-19  8:20 ` [PATCH v2 1/5] perf trace: Fix iteration of syscall ids in syscalltbl->entries Howard Chu
2024-06-19  8:20 ` [PATCH v2 2/5] perf trace: Augment enum syscall arguments with BTF Howard Chu
2024-06-19 13:44   ` Arnaldo Carvalho de Melo
2024-06-19 17:59     ` Howard Chu
2024-06-20 16:34       ` Howard Chu
2024-06-20 19:16         ` Howard Chu
2024-06-21 13:40           ` Arnaldo Carvalho de Melo
2024-06-21 16:18             ` Howard Chu
2024-06-22 18:28               ` Arnaldo Carvalho de Melo
2024-06-23 11:34                 ` Howard Chu
2024-06-21 18:03       ` Arnaldo Carvalho de Melo
2024-06-19  8:20 ` [PATCH v2 3/5] perf trace: Augment enum tracepoint " Howard Chu
2024-06-19 13:46   ` Arnaldo Carvalho de Melo
2024-06-19 18:00     ` Howard Chu
2024-06-19  8:20 ` [PATCH v2 4/5] perf trace: Filter enum arguments with enum names Howard Chu
2024-06-19 13:48   ` Arnaldo Carvalho de Melo
2024-06-19 18:18     ` Howard Chu
2024-06-19  8:20 ` [PATCH v2 5/5] perf trace: Add test for enum augmentation Howard Chu
2024-06-19 13:51   ` Arnaldo Carvalho de Melo
2024-06-19 14:36     ` Namhyung Kim
2024-06-19 18:26       ` Howard Chu
2024-06-21 16:07   ` Namhyung Kim
2024-06-21 16:43     ` Howard Chu
2024-06-21 17:15       ` Namhyung Kim
2024-06-28  6:37   ` kernel test robot
2024-06-19 13:55 ` [PATCH v2 0/5] perf trace: Augment enum arguments with BTF Arnaldo Carvalho de Melo
2024-06-19 18:19   ` Namhyung Kim
2024-06-19 18:25     ` Howard Chu
2024-06-20 19:12       ` Howard Chu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).