diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-19 14:06:14 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-19 14:06:14 -0700 |
| commit | 081e5bf2a9d941da60cd70c97c5a704b29e47f7f (patch) | |
| tree | b3c0df90abf9b3e64ec54358a778d46b20a95568 /tools | |
| parent | 00d66b29a66ce18e417a8436076c629d03186a27 (diff) | |
| parent | ae70b04ab9c7f6162a8c0fdd18a62a945c133142 (diff) | |
Merge tag 'trace-v7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace
Pull tracing updates from Steven Rostedt:
- Expose btf_ids to trace events
In order to allow BPF programs to attach to system call trace events
(which are actually pseudo trace events built on top of raw_syscall
events), expose the BTF ID of the events. This will allow BPF
programs better precision in attaching to events.
- Use "u64" to assign to hist_field->type
Instead of using kstrdup("u64", GFP_KERNEL) to assign the
hist_field->type, just point it to "u64" instead. The
hist_field->type is freed via kfree_const().
- Replace kmalloc()/strcpy() with kstrdup() for trace_printk
Instead of having two calls to copy the module format string, just
use kstrdup().
- Use __free() in trace event histograms and triggres where possible
- Use seq_buf in trace event code instead of strcat()
Instead of calculating the size of the buffer to use and filling it
with strcat(), use the seq_buf infrastructure that takes care of
making sure not to overflow the string size.
- Reject invalid preemptirq_delay_test CPU affinity
The preempt_delay_test module can take an invalid CPU affinity mask
and create confusing output. Simply have the module reject invalid
affinity masks.
- Prevent division by zero in ftrace_ops sample module code
If the ftrace_ops sample module code receives the module parameter
nr_function_calls set to zero, it can cause a division by zero error.
- Warn when an event dereferences a parameter in TP_printk()
On boot up and module load, the trace event TP_printk() is scanned
for possible bugs. As the TP_printk() code is executed when the user
reads the "trace" file and processes the data written when the
trace_event executed, the data it reads can be literally days old.
The scan currently checks for dereferencing printk formats like
"%pI6". But it does not check if the parameters themselves have a
dereference like:
TP_printk("offset %08x: value %08x",
(u32)(__entry->addr - __entry->edma->membase), __entry->value)
__entry represents the pointer to the event on the ring buffer. The
__entry->edma->membase is dereferencing a pointer on the ring buffer
to find membase, but the __entry->edma may no longer be a valid
pointer.
Warn on this case too.
- Replace some strcpy() with strscpy()
- Clean up mmiotrace events to use assign_type() macro
The assign_type() macro makes sure the event type is indeed the type
that is being parsed. The mmiotrace trace was written before that
macro was created so it just simply typecasted the pointer.
Replace the typecasting with the macro.
- Have the ENUM processing to numbers only process what is added
The code that converts ENUMs to their numbers in the trace events
scanned all events to do the processing. This was true when a module
was loaded too. That is, instead of processing just the events for
the module, it processed *all* events. Even the builtin ones that
were processed at boot up.
Add a check for the event->module matching mod if it is a module
before processing it.
* tag 'trace-v7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: (21 commits)
tracing: Have trace_event_update_all() only handle module that is loading
tracing: Cleanup event_enable_trigger_parse() by using __free()
tracing: Report every TP_printk double dereference
tracing/mmiotrace: Use trace_assign_type() in mmio_print_mark()
tracing: Make per-template BTF id lists file-local
tracing: Use seq_buf for string concatenation
tracing: Use strscpy() instead of strcpy() in trace_sched_switch
tracing: Warn when an event dereferences a pointer in TP_printk()
samples/ftrace: Prevent division by zero when nr_function_calls is zero
tracing: Reject invalid preemptirq_delay_test CPU affinity
fgraph: Use trace_seq_putc() in print_graph_return()
tracing/user_events: Replace a seq_printf() call by seq_puts() in user_seq_show()
tracing/user_events: Use seq_putc() in two functions
tracing: Bound histogram expression strings with seq_buf
tracing: Return ERR_PTR() from expr_str()
tracing: Use __free() for expr_str() buffer
kernel/trace/trace_printk: Use kstrdup() instead of kmalloc() and strcpy()
tracing: Point constant hist field type to string literal
selftests/bpf: Add test for tracepoint btf_ids tracefs file
tracing: Expose tracepoint BTF ids via tracefs
...
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/testing/selftests/bpf/prog_tests/tp_btf_ids.c | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/tools/testing/selftests/bpf/prog_tests/tp_btf_ids.c b/tools/testing/selftests/bpf/prog_tests/tp_btf_ids.c new file mode 100644 index 000000000000..c0e7e11e71b8 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/tp_btf_ids.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <test_progs.h> +#include <bpf/btf.h> + +#define TRACEFS "/sys/kernel/tracing" +#define DEBUGFS_TRACING "/sys/kernel/debug/tracing" +#define EVENT_SUBPATH "events/bpf_testmod/bpf_testmod_test_read/btf_ids" + +struct btf_ids_info { + __u32 obj_id; + __u32 raw_id; + __u32 tp_id; +}; + +static const char *btf_ids_path(char *buf, size_t sz) +{ + if (access(TRACEFS "/trace", F_OK) == 0) + snprintf(buf, sz, "%s/%s", TRACEFS, EVENT_SUBPATH); + else + snprintf(buf, sz, "%s/%s", DEBUGFS_TRACING, EVENT_SUBPATH); + return buf; +} + +static int read_btf_ids(struct btf_ids_info *info) +{ + char path[256], buf[256]; + int fd, n; + + fd = open(btf_ids_path(path, sizeof(path)), O_RDONLY); + if (fd < 0) + return -errno; + + n = read(fd, buf, sizeof(buf) - 1); + close(fd); + if (n <= 0) + return -EIO; + buf[n] = '\0'; + + if (sscanf(buf, + "btf_obj_id: %u\nraw_btf_id: %u\ntp_btf_id: %u\n", + &info->obj_id, &info->raw_id, &info->tp_id) != 3) + return -EINVAL; + return 0; +} + +static const char *param_name(struct btf *btf, const struct btf_param *p) +{ + return btf__name_by_offset(btf, p->name_off); +} + +static const char *member_name(struct btf *btf, const struct btf_member *m) +{ + return btf__name_by_offset(btf, m->name_off); +} + +void test_tp_btf_ids(void) +{ + const struct btf_type *proto_t, *rec_t; + const struct btf_param *params; + const struct btf_member *members; + struct btf_ids_info info; + struct btf *vmlinux_btf, *btf; + const char *name; + int err; + + if (!env.has_testmod) { + test__skip(); + return; + } + + err = read_btf_ids(&info); + if (!ASSERT_OK(err, "read btf_ids")) + return; + + ASSERT_GT(info.obj_id, 0, "obj_id non-zero"); + ASSERT_GT(info.raw_id, 0, "raw_id non-zero"); + ASSERT_GT(info.tp_id, 0, "tp_id non-zero"); + + vmlinux_btf = btf__load_vmlinux_btf(); + if (!ASSERT_OK_PTR(vmlinux_btf, "load vmlinux BTF")) + return; + + /* Module BTF is split BTF; load with vmlinux as base. */ + btf = btf__load_from_kernel_by_id_split(info.obj_id, vmlinux_btf); + if (!ASSERT_OK_PTR(btf, "load module BTF")) { + btf__free(vmlinux_btf); + return; + } + + /* + * raw_btf_id should be the FUNC_PROTO of __bpf_trace_<call>: + * void *__data, struct task_struct *task, + * struct bpf_testmod_test_read_ctx *ctx + */ + proto_t = btf__type_by_id(btf, info.raw_id); + if (!ASSERT_OK_PTR(proto_t, "raw type_by_id")) + goto out; + if (!ASSERT_TRUE(btf_is_func_proto(proto_t), "raw is FUNC_PROTO")) + goto out; + if (!ASSERT_EQ(btf_vlen(proto_t), 3, "func_proto arg count")) + goto out; + + params = btf_params(proto_t); + ASSERT_STREQ(param_name(btf, ¶ms[0]), "__data", "arg0 name"); + ASSERT_STREQ(param_name(btf, ¶ms[1]), "task", "arg1 name"); + ASSERT_STREQ(param_name(btf, ¶ms[2]), "ctx", "arg2 name"); + + /* + * tp_btf_id should be STRUCT trace_event_raw_<call> with the + * fields declared by TP_STRUCT__entry plus the common header. + */ + rec_t = btf__type_by_id(btf, info.tp_id); + if (!ASSERT_OK_PTR(rec_t, "tp type_by_id")) + goto out; + if (!ASSERT_TRUE(btf_is_struct(rec_t), "tp is STRUCT")) + goto out; + name = btf__name_by_offset(btf, rec_t->name_off); + ASSERT_STREQ(name, "trace_event_raw_bpf_testmod_test_read", + "tp struct name"); + if (!ASSERT_GE(btf_vlen(rec_t), 5, "tp struct field count")) + goto out; + + members = btf_members(rec_t); + ASSERT_STREQ(member_name(btf, &members[0]), "ent", "field0 name"); + ASSERT_STREQ(member_name(btf, &members[1]), "pid", "field1 name"); + ASSERT_STREQ(member_name(btf, &members[2]), "comm", "field2 name"); + ASSERT_STREQ(member_name(btf, &members[3]), "off", "field3 name"); + ASSERT_STREQ(member_name(btf, &members[4]), "len", "field4 name"); +out: + btf__free(btf); + btf__free(vmlinux_btf); +} |
