[PATCH v3 4/6] trace-cmd record / report: Record BTF file if function arguments are recorded
Steven Rostedt <[email protected]> Mon, 2 Feb 2026 17:36:59 -0500
| Newsgroups | org.kernel.vger.linux-trace-devel |
|---|---|
| Message-ID | <[email protected]> |
From: "Steven Rostedt (Google)" <[email protected]> If function tracer is recorded with func-args option set, or the function_graph tracer is recorded with the funcgraph-args option set, then record the BTF file in a new BTF_FILE section in the trace.dat file. This is compressed on disk, and when the trace.dat file is read, it is loaded into memory and sent to the libtraceevent tep_load_btf() function. This will be used to show arguments of the function tracer: cat-33501 [005] ..... 136155.767937: function: mutex_unlock(lock=0xffffffff831dbbe0) cat-33501 [005] ..... 136155.767939: function: __mutex_unlock_slowpath(lock=0xffffffff831dbbe0, ip=0xffffffff814a7154) cat-33501 [005] ..... 136155.767940: function: __f_unlock_pos(f=0xffff8881538de000) cat-33501 [005] ..... 136155.767940: function: mutex_unlock(lock=0xffff8881538de090) cat-33501 [005] ..... 136155.767940: function: __mutex_unlock_slowpath(lock=0xffff8881538de090, ip=0xffffffff816e8ed1) cat-33501 [005] ..... 136155.767941: function: mem_cgroup_handle_over_high(gfp_mask=0xcc0) cat-33501 [005] ..... 136155.767941: function: blkcg_maybe_throttle_current() cat-33501 [005] ..... 136155.767942: function: __rseq_handle_notify_resume(ksig=0x0, regs=0xffffc9000e3eff58) cat-33501 [005] d.... 136155.767943: function: fpregs_assert_state_consistent() cat-33501 [005] d.... 136155.767943: function: switch_fpu_return() cat-33501 [005] ..... 136155.767950: function: __x64_sys_execve(regs=0xffffc9000e3eff58) cat-33501 [005] ..... 136155.767951: function: getname_flags(filename=0x7ffe7d33f3d0, flags=0) cat-33501 [005] ..... 136155.767951: function: getname_flags.part.0(7ffe7d33f3d0, 0, 0, 0, 0, 0) cat-33501 [005] ..... 136155.767951: function: kmem_cache_alloc_noprof(s=0xffff8881001d3800, gfpflags=0xcc0) Note, the parsing of BTF from the trace.dat file requires libtraceevent version 1.9 or higher. Signed-off-by: Steven Rostedt (Google) <[email protected]> --- Changes since v2: https://lore.kernel.org/[email protected] - Fixed tracecmd_append_btf_file() exiting out early when btf file doesn't exist. That would cause the offset to be incorrect and the next write to the trace.dat file would corrupt it. This happened with trace-cmd extract. Makefile | 9 +++ .../include/private/trace-cmd-private.h | 3 + lib/trace-cmd/trace-compress.c | 26 ++++++++ lib/trace-cmd/trace-input.c | 26 ++++++++ lib/trace-cmd/trace-output.c | 64 +++++++++++++++++++ meson.build | 3 + tracecmd/trace-record.c | 42 +++++++++++- 7 files changed, 172 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c594633b210b..aea6a1aae111 100644 --- a/Makefile +++ b/Makefile @@ -230,6 +230,8 @@ LIBTRACEFS=libtracefs TEST_LIBTRACEEVENT := $(shell sh -c "$(PKG_CONFIG) --atleast-version $(LIBTRACEEVENT_MIN_VERSION) $(LIBTRACEEVENT) > /dev/null 2>&1 && echo y") TEST_LIBTRACEFS := $(shell sh -c "$(PKG_CONFIG) --atleast-version $(LIBTRACEFS_MIN_VERSION) $(LIBTRACEFS) > /dev/null 2>&1 && echo y") +TEST_LIBTRACEEVENT_BTF := $(shell sh -c "$(PKG_CONFIG) --atleast-version 1.9 $(LIBTRACEEVENT) > /dev/null 2>&1 && echo y") + ifeq ("$(TEST_LIBTRACEEVENT)", "y") LIBTRACEEVENT_CFLAGS := $(shell sh -c "$(PKG_CONFIG) --cflags $(LIBTRACEEVENT)") LIBTRACEEVENT_LDLAGS := $(shell sh -c "$(PKG_CONFIG) --libs $(LIBTRACEEVENT)") @@ -247,6 +249,13 @@ warning: @echo "********************************************" endif +ifeq ("$(TEST_LIBTRACEEVENT_BTF)", "y") +CFLAGS += -DHAVE_KERNEL_BTF +else +.PHONY: warning +$(info Min version libtraceevent 1.9 not found. Will not use BTF for function args) +endif + export LIBTRACEEVENT_CFLAGS LIBTRACEEVENT_LDLAGS ifeq ("$(TEST_LIBTRACEFS)", "y") diff --git a/lib/trace-cmd/include/private/trace-cmd-private.h b/lib/trace-cmd/include/private/trace-cmd-private.h index 809e244b2d1c..4743bbc62048 100644 --- a/lib/trace-cmd/include/private/trace-cmd-private.h +++ b/lib/trace-cmd/include/private/trace-cmd-private.h @@ -159,6 +159,7 @@ enum { TRACECMD_OPTION_PRINTK, TRACECMD_OPTION_CMDLINES, TRACECMD_OPTION_BUFFER_TEXT, + TRACECMD_OPTION_BTF_FILE, TRACECMD_OPTION_MAX, }; @@ -340,6 +341,7 @@ int tracecmd_append_buffer_cpu_data(struct tracecmd_output *handle, struct tracecmd_output *tracecmd_get_output_handle_fd(int fd); unsigned long tracecmd_get_out_file_version(struct tracecmd_output *handle); size_t tracecmd_get_out_file_offset(struct tracecmd_output *handle); +int tracecmd_append_btf_file(struct tracecmd_output *handle); /* --- Reading the Fly Recorder Trace --- */ @@ -568,6 +570,7 @@ int tracecmd_uncompress_chunk(struct tracecmd_compression *handle, struct tracecmd_compress_chunk *chunk, char *data); int tracecmd_load_chunks_info(struct tracecmd_compression *handle, struct tracecmd_compress_chunk **chunks_info); +void *tracecmd_uncompress_buffer(struct tracecmd_compression *handle, size_t *size); /* --- Plugin handling --- */ extern struct tep_plugin_option trace_ftrace_options[]; diff --git a/lib/trace-cmd/trace-compress.c b/lib/trace-cmd/trace-compress.c index 3e10cf5bc385..fe4ab84a7e27 100644 --- a/lib/trace-cmd/trace-compress.c +++ b/lib/trace-cmd/trace-compress.c @@ -366,6 +366,32 @@ out: free(buf); return ret; } +/** + * tracecmd_uncompress_buffer - Returned the current uncompressed buffer + * @handle: compression handle + * @size: A pointer to place the size of the uncompressed buffer in + * + * Returns the current uncompressed buffer and resets the compression + * @handle. + * + * Returns the buffer (must be freed with free()) or NULL. + * @size (if not NULL) will contain the size of the uncompressed buffer. + */ +void *tracecmd_uncompress_buffer(struct tracecmd_compression *handle, size_t *size) +{ + void *buffer; + + if (!handle) + return NULL; + + if (size) + *size = handle->capacity; + + buffer = handle->buffer; + handle->buffer = NULL; + tracecmd_compress_reset(handle); + return buffer; +} /** * tracecmd_compress_buffer_write - write() to compression buffer diff --git a/lib/trace-cmd/trace-input.c b/lib/trace-cmd/trace-input.c index fba9c5b6a7fc..7e03c0da5485 100644 --- a/lib/trace-cmd/trace-input.c +++ b/lib/trace-cmd/trace-input.c @@ -1026,6 +1026,28 @@ static int read_ftrace_printk(struct tracecmd_input *handle) return 0; } +#ifdef HAVE_KERNEL_BTF +static int read_btf(struct tracecmd_input *handle) +{ + void *raw_data; + size_t size; + + raw_data = tracecmd_uncompress_buffer(handle->compress, &size); + if (!raw_data) + return -1; + + tep_load_btf(handle->pevent, raw_data, size); + + free(raw_data); + return 0; +} +#else +static inline int read_btf(struct tracecmd_input *handle) +{ + return 0; +} +#endif + static int read_and_parse_cmdlines(struct tracecmd_input *handle); /** @@ -1203,6 +1225,9 @@ static int handle_section(struct tracecmd_input *handle, struct file_section *se case TRACECMD_OPTION_CMDLINES: ret = read_and_parse_cmdlines(handle); break; + case TRACECMD_OPTION_BTF_FILE: + ret = read_btf(handle); + break; default: ret = 0; break; @@ -4208,6 +4233,7 @@ static int handle_options(struct tracecmd_input *handle) case TRACECMD_OPTION_KALLSYMS: case TRACECMD_OPTION_PRINTK: case TRACECMD_OPTION_CMDLINES: + case TRACECMD_OPTION_BTF_FILE: if (size < 8) break; section_add_or_update(handle, option, -1, diff --git a/lib/trace-cmd/trace-output.c b/lib/trace-cmd/trace-output.c index ec72011dc2ad..b849686ecfdf 100644 --- a/lib/trace-cmd/trace-output.c +++ b/lib/trace-cmd/trace-output.c @@ -2324,6 +2324,70 @@ int tracecmd_write_cmdlines(struct tracecmd_output *handle) return 0; } +#define BTF_FILE "/sys/kernel/btf/vmlinux" + +int tracecmd_append_btf_file(struct tracecmd_output *handle) +{ + tsize_t offset, size, check_size; + struct tracecmd_option *option; + struct iovec *vect; + struct stat st; + int ret; + + if (!HAS_SECTIONS(handle)) + return -1; + + ret = stat(BTF_FILE, &st); + if (ret < 0) + return -1; + + offset = write_compress_section_header(handle, TRACECMD_OPTION_BTF_FILE, NULL, true); + if (offset == (off_t)-1) + return -1; + + tcmd_out_compression_start(handle); + + check_size = copy_file(handle, BTF_FILE); + if (st.st_size != check_size) { + errno = EINVAL; + tracecmd_warning("error in size of file '%s'", BTF_FILE); + return -1; + } + + if (tcmd_out_compression_end(handle)) + return -1; + + if (tcmd_out_update_section_header(handle, offset)) + return -1; + + return 0; + + /* + * BTF file + * - data offset in the file + * - data size + */ + + vect = calloc(2, sizeof(struct iovec)); + if (!vect) + return -1; + vect[0].iov_base = &offset; + vect[0].iov_len = 8; + vect[1].iov_base = &size; + vect[1].iov_len = 8; + + option = tracecmd_add_option_v(handle, TRACECMD_OPTION_BTF_FILE, vect, 2); + free(vect); + + if (!option) + return -1; + + if (do_lseek(handle, 0, SEEK_END) == (off_t)-1) + return -1; + + return 0; +} + static char *get_clock(struct tracecmd_output *handle) { struct tracefs_instance *inst; diff --git a/meson.build b/meson.build index e7a0398761f3..c86d1a6b7f0b 100644 --- a/meson.build +++ b/meson.build @@ -25,8 +25,11 @@ htmldir = join_paths(prefixdir, get_option('htmldir')) conf = configuration_data() libtraceevent_dep = dependency('libtraceevent', version: '>= 1.5.0', required: true) +libtraceevent_btf_dep = dependency('libtraceevent', version: '>= 1.9', required: false) libtracefs_dep = dependency('libtracefs', version: '>= 1.8.0', required: true) +conf.set('HAVE_KERNEL_BTF', libtraceevent_btf_dep.found(), description: 'Is BTF parsing available?') + threads_dep = dependency('threads', required: true) dl_dep = cc.find_library('dl', required : false) diff --git a/tracecmd/trace-record.c b/tracecmd/trace-record.c index 4e1157c99294..4872d0d77c8e 100644 --- a/tracecmd/trace-record.c +++ b/tracecmd/trace-record.c @@ -4633,12 +4633,46 @@ error: return NULL; } +static void check_need_btf(bool *need_btf, struct tracefs_instance *instance) +{ + bool func_graph_trace; + bool func_trace; + char *current; + + /* Nothing to do if it's already set */ + if (*need_btf) + return; + + current = tracefs_instance_file_read(instance, "current_tracer", NULL); + if (!current) + return; + + func_trace = strcmp(current, "function\n") == 0; + func_graph_trace = strcmp(current, "function_graph\n") == 0; + + free(current); + if (!func_trace && !func_graph_trace) + return; + + if (func_trace) + current = tracefs_instance_file_read(instance, "options/func-args", NULL); + else + current = tracefs_instance_file_read(instance, "options/funcgraph-args", NULL); + if (!current) + return; + + *need_btf = strncmp(current, "1", 1) == 0; + + free(current); +} + static void record_data(struct common_record_context *ctx) { struct tracecmd_output *handle; struct buffer_instance *instance; bool have_proxy = false; bool local = false; + bool need_btf = false; int max_cpu_count = local_cpu_count; char **temp_files; int i; @@ -4734,11 +4768,17 @@ static void record_data(struct common_record_context *ctx) tracefs_instance_get_name(instance->tracefs), cpus); add_buffer_stat(handle, instance); + check_need_btf(&need_btf, instance->tracefs); } } - if (!no_top_instance() && !top_instance.msg_handle) + if (!no_top_instance() && !top_instance.msg_handle) { print_stat(&top_instance); + check_need_btf(&need_btf, top_instance.tracefs); + } + + if (need_btf) + tracecmd_append_btf_file(handle); for_all_instances(instance) { add_pid_maps(handle, instance); -- 2.51.0