linux-trace-devel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 0/4] libtraceevent: Add BTF parsing for function arguments
@ 2025-11-12 21:42 Steven Rostedt
  2025-11-12 21:42 ` [PATCH v3 1/4] libtraceevent: Add loading of BTF to the tep handle Steven Rostedt
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Steven Rostedt @ 2025-11-12 21:42 UTC (permalink / raw)
  To: linux-trace-devel
  Cc: Douglas Raillard, Masami Hiramatsu, Namhyung Kim, Takaya Saeki,
	Ian Rogers, aahringo, Steven Rostedt (Google)

From: "Steven Rostedt (Google)" <rostedt@goodmis.org>

Now that functions can save the first 6 registers of their args during a
trace, and BTF can be used to decipher those arguments, have libtraceevent
handle this as well.

Add a tep_load_btf() function that loads the content of /sys/kernel/btf/vmlinux
and then uses that to parse it to show the arguments of functions in the trace.

When trace-cmd supports this, it would look like:

 # trace-cmd record -p function -O func-args cat /etc/passwd
 # trace-cmd report
             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)
             cat-33501 [005] ..... 136155.767951: function:                      fs_reclaim_acquire(gfp_mask=0xcc0)
             cat-33501 [005] ..... 136155.767952: function:                      fs_reclaim_release(gfp_mask=0xcc0)
             cat-33501 [005] ..... 136155.767953: function:                      kmemleak_alloc(ptr=0xffff8881114a0000, size=0x1000, min_count=1, gfp=0xcc0)
             cat-33501 [005] ..... 136155.767953: function:                      __create_object(ptr=0xffff8881114a0000, size=0x1000, min_count=1, gfp=0xcc0, objflags=0x0)
             cat-33501 [005] ..... 136155.767954: function:                         __alloc_object(gfp=0xcc0)

Changes since v2: https://lore.kernel.org/all/20250802024035.1893851-1-rostedt@goodmis.org/

- Removed debug print statement

- Replaced some printf() with tep_warning()

- Added trace-btf.c to meson build

- Added reference to BTF document: https://docs.kernel.org/bpf/btf.html

- Do not free btf->strings as that wasn't allocated.

- Fail to load if BTF endianess is different than the running machine.
  We can add support for that later.

- Make sure the raw data of str_hdr is valid (Douglas Raillard)

- Rename tep_btf_init() and tep_btf_free() to btf_init() and btf_free()
  as they no longer need to be unique as they are not exported.

- Have btf->raw_data allocated and not expect the caller to give up
  ownership of the raw_data. This allows the raw data passed in to simply
  be memory mapped.

- Updated the man page to not state the raw_data will become part of the
  tep handler, as the tep handler will now allocate its own.

- Use mmap() to load the btf kernel file.

- Make the example program a full featured program.

- If function isn't found and has extra characters (like ".isra.0")
  strip the extra characters and try again.

- Add simple unit test.


Steven Rostedt (Google) (4):
  libtraceevent: Add loading of BTF to the tep handle
  libtraceevent: Add man page for the new BTF functions
  libtraceevent: Have BTF find functions with extra characters
  libtraceevent utest: Add simple test to test BTF parsing

 Documentation/libtraceevent-btf.txt | 156 +++++++++
 Documentation/libtraceevent.txt     |   5 +
 include/traceevent/event-parse.h    |   5 +
 plugins/plugin_function.c           |  15 +-
 src/Makefile                        |   1 +
 src/event-parse-local.h             |   6 +
 src/event-parse.c                   |   2 +
 src/meson.build                     |   1 +
 src/trace-btf.c                     | 469 ++++++++++++++++++++++++++++
 utest/traceevent-utest.c            |  39 +++
 10 files changed, 689 insertions(+), 10 deletions(-)
 create mode 100644 Documentation/libtraceevent-btf.txt
 create mode 100644 src/trace-btf.c

-- 
2.51.0


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

* [PATCH v3 1/4] libtraceevent: Add loading of BTF to the tep handle
  2025-11-12 21:42 [PATCH v3 0/4] libtraceevent: Add BTF parsing for function arguments Steven Rostedt
@ 2025-11-12 21:42 ` Steven Rostedt
  2025-11-12 21:42 ` [PATCH v3 2/4] libtraceevent: Add man page for the new BTF functions Steven Rostedt
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Steven Rostedt @ 2025-11-12 21:42 UTC (permalink / raw)
  To: linux-trace-devel
  Cc: Douglas Raillard, Masami Hiramatsu, Namhyung Kim, Takaya Saeki,
	Ian Rogers, aahringo, Steven Rostedt (Google)

From: "Steven Rostedt (Google)" <rostedt@goodmis.org>

Add loading of the BTF file /sys/kernel/btf/vmlinux into the tep_handler and
then use it to format the function tracer if it has arguments:

    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)
    cat-33501 [005] ..... 136155.767951: function:                      fs_reclaim_acquire(gfp_mask=0xcc0)
    cat-33501 [005] ..... 136155.767952: function:                      fs_reclaim_release(gfp_mask=0xcc0)
    cat-33501 [005] ..... 136155.767953: function:                      kmemleak_alloc(ptr=0xffff8881114a0000, size=0x1000, min_count=1, gfp=0xcc0)
    cat-33501 [005] ..... 136155.767953: function:                      __create_object(ptr=0xffff8881114a0000, size=0x1000, min_count=1, gfp=0xcc0, objflags=0x0)
    cat-33501 [005] ..... 136155.767954: function:                         __alloc_object(gfp=0xcc0)
    cat-33501 [005] ..... 136155.767954: function:                            kmem_cache_alloc_noprof(s=0xffff888100045700, gfpflags=0x92cc0)
    cat-33501 [005] ..... 136155.767954: function:                      fs_reclaim_acquire(gfp_mask=0x92cc0)
    cat-33501 [005] ..... 136155.767955: function:                      fs_reclaim_release(gfp_mask=0x92cc0)

Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
---
Changes since v2: https://lore.kernel.org/20250802024035.1893851-2-rostedt@goodmis.org

- Removed debug print statement

- Replaced some printf() with tep_warning()

- Added trace-btf.c to meson build

- Added reference to BTF document: https://docs.kernel.org/bpf/btf.html

- Do not free btf->strings as that wasn't allocated.

- Fail to load if BTF endianess is different than the running machine.
  We can add support for that later.

- Make sure the raw data of str_hdr is valid (Douglas Raillard)

- Rename tep_btf_init() and tep_btf_free() to btf_init() and btf_free()
  as they no longer need to be unique as they are not exported.

- Have btf->raw_data allocated and not expect the caller to give up
  ownership of the raw_data. This allows the raw data passed in to simply
  be memory mapped.

 include/traceevent/event-parse.h |   5 +
 plugins/plugin_function.c        |  15 +-
 src/Makefile                     |   1 +
 src/event-parse-local.h          |   6 +
 src/event-parse.c                |   2 +
 src/meson.build                  |   1 +
 src/trace-btf.c                  | 457 +++++++++++++++++++++++++++++++
 7 files changed, 477 insertions(+), 10 deletions(-)
 create mode 100644 src/trace-btf.c

diff --git a/include/traceevent/event-parse.h b/include/traceevent/event-parse.h
index d3ce0a43a8fc..ebfc2c7abac2 100644
--- a/include/traceevent/event-parse.h
+++ b/include/traceevent/event-parse.h
@@ -580,6 +580,11 @@ int tep_get_ref(struct tep_handle *tep);
 
 struct kbuffer *tep_kbuffer(struct tep_handle *tep);
 
+/* BTF */
+int tep_load_btf(struct tep_handle *tep, void *raw_data, size_t data_size);
+int tep_btf_print_args(struct tep_handle *tep, struct trace_seq *s, void *args,
+		       int nmem, int size, const char *func);
+
 /* for debugging */
 void tep_print_funcs(struct tep_handle *tep);
 void tep_print_printk(struct tep_handle *tep);
diff --git a/plugins/plugin_function.c b/plugins/plugin_function.c
index a337446e3462..379fc3ad0ffd 100644
--- a/plugins/plugin_function.c
+++ b/plugins/plugin_function.c
@@ -135,10 +135,9 @@ static void show_function(struct trace_seq *s, struct tep_handle *tep,
 
 /* Returns true if it printed args, otherwise it returns false */
 static bool print_args(struct trace_seq *s, struct tep_event *event,
-		       struct tep_record *record)
+		       struct tep_record *record, const char *func)
 {
 	struct tep_format_field *field;
-	unsigned long arg;
 	void *args;
 	int len;
 
@@ -156,12 +155,8 @@ static bool print_args(struct trace_seq *s, struct tep_event *event,
 
 	trace_seq_putc(s, '(');
 
-	for (int i = 0; i < len; i += sizeof(long), args += sizeof(long)) {
-		memcpy(&arg, args, sizeof(long));
-		trace_seq_printf(s, "%lx", arg);
-		if (i + sizeof(long) < len)
-			trace_seq_puts(s, ", ");
-	}
+	tep_btf_print_args(event->tep, s, args, len / sizeof(long), sizeof(long), func);
+
 	trace_seq_putc(s, ')');
 	return true;
 }
@@ -196,7 +191,7 @@ static int function_handler(struct trace_seq *s, struct tep_record *record,
 	else
 		trace_seq_printf(s, "0x%llx", function);
 
-	print_args(s, event, record);
+	print_args(s, event, record, func);
 
 	if (ftrace_parent->set) {
 		trace_seq_printf(s, " <-- ");
@@ -281,7 +276,7 @@ trace_stack_handler(struct trace_seq *s, struct tep_record *record,
 
 static int
 trace_raw_data_handler(struct trace_seq *s, struct tep_record *record,
-		    struct tep_event *event, void *context)
+		       struct tep_event *event, void *context)
 {
 	struct tep_format_field *field;
 	unsigned long long id;
diff --git a/src/Makefile b/src/Makefile
index 169bc6af1cbc..4f16b81bb989 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -11,6 +11,7 @@ OBJS += parse-filter.o
 OBJS += parse-utils.o
 OBJS += tep_strerror.o
 OBJS += trace-seq.o
+OBJS += trace-btf.o
 
 OBJS := $(OBJS:%.o=$(bdir)/%.o)
 DEPS := $(OBJS:$(bdir)/%.o=$(bdir)/.%.d)
diff --git a/src/event-parse-local.h b/src/event-parse-local.h
index c6bfc611bfbd..8fdb2141d48e 100644
--- a/src/event-parse-local.h
+++ b/src/event-parse-local.h
@@ -14,6 +14,7 @@ struct func_list;
 struct event_handler;
 struct func_resolver;
 struct tep_plugins_dir;
+struct tep_btf;
 
 #define __hidden __attribute__((visibility ("hidden")))
 
@@ -89,6 +90,8 @@ struct tep_handle {
 	const char *input_buf;
 	unsigned long long input_buf_ptr;
 	unsigned long long input_buf_siz;
+
+	struct tep_btf *btf;
 };
 
 enum tep_print_parse_type {
@@ -124,4 +127,7 @@ const char *get_input_buf(struct tep_handle *tep);
 enum tep_event_type read_token(struct tep_handle *tep, char **tok);
 void free_token(char *tok);
 
+/* BTF routines */
+void btf_free(struct tep_btf *btf);
+
 #endif /* _PARSE_EVENTS_INT_H */
diff --git a/src/event-parse.c b/src/event-parse.c
index 8daf91d8be83..939b0a8fbdf2 100644
--- a/src/event-parse.c
+++ b/src/event-parse.c
@@ -8726,6 +8726,8 @@ void tep_free(struct tep_handle *tep)
 	free(tep->func_resolver);
 	free_tep_plugin_paths(tep);
 
+	btf_free(tep->btf);
+
 	free(tep);
 }
 
diff --git a/src/meson.build b/src/meson.build
index cd48de719266..3a7a08206a5a 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -11,6 +11,7 @@ sources= [
    'parse-utils.c',
    'tep_strerror.c',
    'trace-seq.c',
+   'trace-btf.c',
 ]
 
 cc = meson.get_compiler('c')
diff --git a/src/trace-btf.c b/src/trace-btf.c
new file mode 100644
index 000000000000..12dc49447027
--- /dev/null
+++ b/src/trace-btf.c
@@ -0,0 +1,457 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2025 Google, Steven Rostedt <rostedt@goodmis.org>
+ *
+ * Reference: https://docs.kernel.org/bpf/btf.html
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include <linux/btf.h>
+
+#include "event-parse.h"
+#include "event-utils.h"
+#include "event-parse-local.h"
+
+struct btf_header;
+struct btf_type;
+
+struct tep_btf {
+	struct btf_header	*hdr;
+	const char		*strings;
+	struct btf_type		**types;
+	unsigned long		nr_types;
+	struct btf_type		**funcs;
+	unsigned long		nr_funcs;
+	void			*data;
+	size_t			raw_size;
+	void			*raw_data;
+};
+
+#define REALLOC_SIZE (1 << 10)
+#define REALLOC_MASK (REALLOC_SIZE - 1)
+
+static const char *btf_name(struct tep_btf *btf, int off)
+{
+	if (off < btf->hdr->str_len)
+		return btf->strings + off;
+	return "";
+}
+
+/* List taken from the Linux kernel */
+static const char * const btf_kind_str[NR_BTF_KINDS] = {
+	[BTF_KIND_UNKN]		= "UNKNOWN",
+	[BTF_KIND_INT]		= "INT",
+	[BTF_KIND_PTR]		= "PTR",
+	[BTF_KIND_ARRAY]	= "ARRAY",
+	[BTF_KIND_STRUCT]	= "STRUCT",
+	[BTF_KIND_UNION]	= "UNION",
+	[BTF_KIND_ENUM]		= "ENUM",
+	[BTF_KIND_FWD]		= "FWD",
+	[BTF_KIND_TYPEDEF]	= "TYPEDEF",
+	[BTF_KIND_VOLATILE]	= "VOLATILE",
+	[BTF_KIND_CONST]	= "CONST",
+	[BTF_KIND_RESTRICT]	= "RESTRICT",
+	[BTF_KIND_FUNC]		= "FUNC",
+	[BTF_KIND_FUNC_PROTO]	= "FUNC_PROTO",
+	[BTF_KIND_VAR]		= "VAR",
+	[BTF_KIND_DATASEC]	= "DATASEC",
+	[BTF_KIND_FLOAT]	= "FLOAT",
+	[BTF_KIND_DECL_TAG]	= "DECL_TAG",
+	[BTF_KIND_TYPE_TAG]	= "TYPE_TAG",
+	[BTF_KIND_ENUM64]	= "ENUM64",
+};
+
+static const char *btf_type_str(const struct btf_type *t)
+{
+	return btf_kind_str[BTF_INFO_KIND(t->info)];
+}
+
+static int insert_type(struct btf_type ***types, unsigned long *cnt, struct btf_type *type)
+{
+	unsigned long nr_types = *cnt;
+        struct btf_type **array = *types;
+
+        if (!(nr_types & REALLOC_MASK)) {
+                int size = nr_types + REALLOC_SIZE;
+
+                array = realloc(array, sizeof(struct btf_type *) * size);
+                if (!array) {
+			tep_warning("Failed to alloct memory for new type");
+			return -1;
+		}
+                *types = array;
+        }
+
+        array[nr_types++] = type;
+	*cnt = nr_types;
+	return 0;
+}
+
+static int add_type(struct tep_btf *btf, struct btf_type *type)
+{
+	return insert_type(&btf->types, &btf->nr_types, type);
+}
+
+static int add_func(struct tep_btf *btf, struct btf_type *type)
+{
+	return insert_type(&btf->funcs, &btf->nr_funcs, type);
+}
+
+static int btf_type_size(struct btf_type *type)
+{
+	int kind = BTF_INFO_KIND(type->info);
+	int size = sizeof(*type);
+
+	switch (kind) {
+	case BTF_KIND_INT:
+		return size + sizeof(int);
+	case BTF_KIND_VAR:
+		return size + sizeof(struct btf_var);
+	case BTF_KIND_ARRAY:
+		return size + sizeof(struct btf_array);
+	case BTF_KIND_DECL_TAG:
+		return size + sizeof(struct btf_decl_tag);
+	case BTF_KIND_ENUM:
+		return size + sizeof(struct btf_enum) * BTF_INFO_VLEN(type->info);
+	case BTF_KIND_ENUM64:
+		return size + sizeof(struct btf_enum64) * BTF_INFO_VLEN(type->info);
+	case BTF_KIND_STRUCT:
+	case BTF_KIND_UNION:
+		return size + sizeof(struct btf_member) * BTF_INFO_VLEN(type->info);
+	case BTF_KIND_FUNC_PROTO:
+		return size + sizeof(struct btf_param) * BTF_INFO_VLEN(type->info);
+	case BTF_KIND_DATASEC:
+		return size + sizeof(struct btf_var_secinfo) * BTF_INFO_VLEN(type->info);
+	case BTF_KIND_PTR:
+	case BTF_KIND_FWD:
+	case BTF_KIND_TYPEDEF:
+	case BTF_KIND_VOLATILE:
+	case BTF_KIND_CONST:
+	case BTF_KIND_RESTRICT:
+	case BTF_KIND_TYPE_TAG:
+	case BTF_KIND_FUNC:
+	case BTF_KIND_FLOAT:
+		return size;
+	}
+	return -1;
+}
+
+static int cmp_funcs(const void *A, const void *B, void *data)
+{
+	struct tep_btf *btf = data;
+	const struct btf_type *a = *(const struct btf_type **)A;
+	const struct btf_type *b = *(const struct btf_type **)B;
+	const char *name_a = btf_name(btf, a->name_off);
+	const char *name_b = btf_name(btf, b->name_off);
+
+	return strcmp(name_a, name_b);
+}
+
+struct tcmd_search {
+	struct tep_btf		*btf;
+	const char		*name;
+};
+
+static int cmp_key_func(const void *A, const void *B)
+{
+	const struct tcmd_search *key = A;
+	const struct btf_type *b = *(const struct btf_type **)B;
+	const char *name_b = btf_name(key->btf, b->name_off);
+
+	return strcmp(key->name, name_b);
+}
+
+static struct btf_type *tep_btf_find_func(struct tep_btf *btf, const char *name)
+{
+	struct tcmd_search tsearch;
+	struct btf_type **t;
+
+	if (!btf || !name)
+		return NULL;
+
+	tsearch.btf = btf;
+	tsearch.name = name;
+
+	t = bsearch(&tsearch, btf->funcs, btf->nr_funcs, sizeof(btf->funcs[0]),
+		    cmp_key_func);
+
+	return t ? *t : NULL;
+}
+
+static int load_types(struct tep_btf *btf)
+{
+	struct btf_type *type;
+	void *start, *end;
+	int size;
+
+	start = btf->data + btf->hdr->type_off;
+	end = start + btf->hdr->type_len;
+
+	if (end > btf->raw_data + btf->raw_size)
+		return -1;
+
+	for (type = start; (void *)type < end;) {
+		if (add_type(btf, type))
+			return -1;
+		if (BTF_INFO_KIND(type->info) == BTF_KIND_FUNC) {
+			if (add_func(btf, type))
+				return -1;
+		}
+		size = btf_type_size(type);
+		if (size < 0) {
+			tep_warning("Invalid type %d\n", BTF_INFO_KIND(type->info));
+			return -1;
+		}
+		type = (void *)type + size;
+	}
+
+	qsort_r(btf->funcs, btf->nr_funcs, sizeof(btf->funcs[0]), cmp_funcs, btf);
+	return 0;
+}
+
+__hidden void btf_free(struct tep_btf *btf)
+{
+	if (!btf)
+		return;
+
+	free(btf->types);
+	free(btf->funcs);
+	free(btf->raw_data);
+}
+
+static struct tep_btf *btf_init(void *raw_data, size_t data_size)
+{
+	struct tep_btf *btf;
+
+	btf = calloc(1, sizeof(*btf));
+	if (!btf)
+		return NULL;
+
+	btf->raw_data = malloc(data_size);
+	if (!btf->raw_data)
+		goto fail;
+
+	memcpy(btf->raw_data, raw_data, data_size);
+
+	btf->raw_size = data_size;
+	btf->hdr = btf->raw_data;
+
+	/* Currently only same endianess is supported */
+	if (btf->hdr->magic != 0xeb9f) {
+		if (btf->hdr->magic != 0x9feb)
+			tep_warning("BTF does not match endianess of this machine");
+		else
+			tep_warning("Invalid BTF header");
+		goto fail;
+	}
+
+	if (btf->hdr->hdr_len < sizeof(*btf->hdr)) {
+		tep_warning("Header (%d) smaller than expected header %zd",
+			    btf->hdr->hdr_len, sizeof(*btf->hdr));
+		goto fail;
+	}
+
+	if (btf->hdr->str_off > data_size) {
+		tep_warning("String header (%d) greater than data size %zd",
+			    btf->hdr->str_len, data_size);
+		goto fail;
+	}
+
+	btf->data = btf->raw_data + btf->hdr->hdr_len;
+
+	btf->strings = btf->data + btf->hdr->str_off;
+
+	if (load_types(btf) < 0)
+		goto fail;
+
+	return btf;
+ fail:
+	btf_free(btf);
+	return NULL;
+}
+
+/**
+ * tep_load_btf - Load BTF information into a tep handle
+ * @tep: The tep handle to load the BTF info into
+ * @raw_data: The raw data containing the BTF file
+ * @data_size: The amount of data in @raw_data
+ *
+ * Initializes BTF into the @tep handle. If it had already had BTF
+ * loaded, it will free the previous BTF and recreate it from @raw_data.
+ *
+ * Returns: 0 on success and -1 on failure.
+ */
+int tep_load_btf(struct tep_handle *tep, void *raw_data, size_t data_size)
+{
+	/* If btf was already loaded, free it */
+	btf_free(tep->btf);
+
+	tep->btf = btf_init(raw_data, data_size);
+	if (!tep->btf)
+		return -1;
+	return 0;
+}
+
+static struct btf_type *btf_get_type(struct tep_btf *btf, int id)
+{
+	if (!id || id > btf->nr_types)
+		return NULL;
+
+	return btf->types[id - 1];
+}
+
+static struct btf_type *btf_skip_modifiers(struct tep_btf *btf, int id)
+{
+	struct btf_type *t = btf_get_type(btf, id);
+
+	for (;;) {
+		switch (BTF_INFO_KIND(t->info)) {
+		case BTF_KIND_TYPEDEF:
+		case BTF_KIND_VOLATILE:
+		case BTF_KIND_CONST:
+		case BTF_KIND_RESTRICT:
+		case BTF_KIND_TYPE_TAG:
+			id = t->type;
+			t = btf_get_type(btf, t->type);
+			continue;
+		}
+		break;
+	}
+
+	return t;
+}
+
+static void assign_arg(unsigned long long *arg, void *args, int size, int a)
+{
+	*arg = size == 4 ?
+		*(unsigned int *)(args + a * sizeof(int)) :
+		*(unsigned long long *)(args + a * sizeof(long long));
+}
+
+/**
+ * tep_btf_print_args - Print function arguments from BTF info
+ * @tep: The tep descriptor to use
+ * @s: The trace_seq to write the arguments into
+ * @args: The array that holds the arguments
+ * @nmem: The number of arguments
+ * @size: The size of each item in args (4 or 8).
+ * @func: The name of the function.
+ *
+ * Loads up the @s with a list of arguments for the function based on
+ * the @args.
+ *
+ * If there's no BTF loaded or @func is not found, then it just writes
+ * the @args as raw numbers. Otherwise it will pretty print the
+ * arguments based on the function info in BTF.
+ *
+ * Returns: 0 on success and -1 on failure.
+ */
+int tep_btf_print_args(struct tep_handle *tep, struct trace_seq *s, void *args,
+		       int nmem, int size, const char *func)
+{
+	struct tep_btf *btf = tep->btf;
+	struct btf_type *type = tep_btf_find_func(btf, func);
+	struct btf_param *param;
+	unsigned long long arg;
+	unsigned int encode;
+	const char *param_name;
+	int a, p, x, nr;
+
+	if (size != 4 && size != 8)
+		return -1;
+
+	if (!type) {
+		for (int i = 0; i < nmem; i++) {
+			assign_arg(&arg, args, size, i);
+			trace_seq_printf(s, "%llx", arg);
+			if (i + 1 < nmem)
+				trace_seq_puts(s, ", ");
+		}
+		return 0;
+	}
+
+	if (BTF_INFO_KIND(type->info) != BTF_KIND_FUNC) {
+		tep_warning("Invalid func type %d %s for function %s\n",
+			    BTF_INFO_KIND(type->info),
+			    btf_type_str(type), func);
+		return -1;
+	}
+
+	/* Get the function proto */
+	type = btf_get_type(btf, type->type);
+
+	/* No proto means "()" ? */
+	if (!type)
+		return 0;
+
+	if (BTF_INFO_KIND(type->info) != BTF_KIND_FUNC_PROTO) {
+		tep_warning("Invalid func proto type %d %s for function %s\n",
+			    BTF_INFO_KIND(type->info),
+			    btf_type_str(type), func);
+		return -1;
+	}
+
+	/* Get the number of parameters */
+	nr = BTF_INFO_VLEN(type->info);
+
+	/* The parameters are right after the FUNC_PROTO type */
+	param = ((void *)type) + sizeof(*type);
+
+	for (a = 0, p = 0; p < nr; a++, p++) {
+		struct btf_type *t;
+
+		if (p)
+			trace_seq_puts(s, ", ");
+
+		if (a == nmem) {
+			trace_seq_puts(s, "...");
+			break;
+		}
+
+		assign_arg(&arg, args, size, a);
+
+		param_name = btf_name(btf, param[p].name_off);
+		if (param_name)
+			trace_seq_printf(s, "%s=", param_name);
+
+		t = btf_skip_modifiers(btf, param[p].type);
+
+		switch (t ? BTF_INFO_KIND(t->info) : BTF_KIND_UNKN) {
+		case BTF_KIND_UNKN:
+			trace_seq_putc(s, '?');
+			/* Still print unknown type values */
+			/* fallthough */
+		case BTF_KIND_PTR:
+			trace_seq_printf(s, "0x%llx", arg);
+			break;
+		case BTF_KIND_INT:
+			encode = *(int *)((void *)t + sizeof(*t));
+			/* Print unsigned ints as hex */
+			if (BTF_INT_ENCODING(encode) & BTF_INT_SIGNED)
+				trace_seq_printf(s, "%lld", arg);
+			else
+				trace_seq_printf(s, "0x%llx", arg);
+			break;
+		case BTF_KIND_ENUM:
+			trace_seq_printf(s, "%lld", arg);
+			break;
+		default:
+			/* This does not handle complex arguments */
+			trace_seq_printf(s, "(%s)[0x%llx", btf_type_str(t), arg);
+			for (x = sizeof(long); x < t->size; x += sizeof(long)) {
+				trace_seq_putc(s, ':');
+				if (++a == nmem) {
+					trace_seq_puts(s, "...]");
+					return 0;
+				}
+				assign_arg(&arg, args, size, a);
+				trace_seq_printf(s, "0x%llx", arg);
+			}
+			trace_seq_putc(s, ']');
+			break;
+		}
+	}
+	return 0;
+}
-- 
2.51.0


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

* [PATCH v3 2/4] libtraceevent: Add man page for the new BTF functions
  2025-11-12 21:42 [PATCH v3 0/4] libtraceevent: Add BTF parsing for function arguments Steven Rostedt
  2025-11-12 21:42 ` [PATCH v3 1/4] libtraceevent: Add loading of BTF to the tep handle Steven Rostedt
@ 2025-11-12 21:42 ` Steven Rostedt
  2025-11-12 21:42 ` [PATCH v3 3/4] libtraceevent: Have BTF find functions with extra characters Steven Rostedt
  2025-11-12 21:42 ` [PATCH v3 4/4] libtraceevent utest: Add simple test to test BTF parsing Steven Rostedt
  3 siblings, 0 replies; 5+ messages in thread
From: Steven Rostedt @ 2025-11-12 21:42 UTC (permalink / raw)
  To: linux-trace-devel
  Cc: Douglas Raillard, Masami Hiramatsu, Namhyung Kim, Takaya Saeki,
	Ian Rogers, aahringo, Steven Rostedt (Google)

From: "Steven Rostedt (Google)" <rostedt@goodmis.org>

Add a man page for tep_load_btf() and tep_btf_print_args()

Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
---
Changes since v2: https://lore.kernel.org/all/20250802024035.1893851-3-rostedt@goodmis.org/

- Updated the man page to not state the raw_data will become part of the
  tep handler, as the tep handler will now allocate its own.

- Use mmap() to load the btf kernel file.

- Make the example program a full featured program.

 Documentation/libtraceevent-btf.txt | 156 ++++++++++++++++++++++++++++
 Documentation/libtraceevent.txt     |   5 +
 2 files changed, 161 insertions(+)
 create mode 100644 Documentation/libtraceevent-btf.txt

diff --git a/Documentation/libtraceevent-btf.txt b/Documentation/libtraceevent-btf.txt
new file mode 100644
index 000000000000..d922f80ee27d
--- /dev/null
+++ b/Documentation/libtraceevent-btf.txt
@@ -0,0 +1,156 @@
+libtraceevent(3)
+================
+
+NAME
+----
+tep_load_btf, tep_btf_print_args -
+Load BTF file and use it to pretty print function arguments.
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <event-parse.h>*
+
+int *tep_load_btf*(struct tep_handle pass:[*]_tep_, void pass:[*]_raw_data_, size_t _data_size_);
+int *tep_btf_print_args*(struct tep_handle pass:[*]_tep_, struct trace_seq pass:[*]_s_, void pass:[*]_args_,
+		       int _nmem_, int _size_, const char pass:[*]_func_);
+--
+
+DESCRIPTION
+-----------
+If the Linux kernel has BTF configured, then a binary file will exist
+in the path of */sys/kernel/btf/vmlinux*. If this file is read into memory
+and passed to *tep_load_btf()* function, then it will be used to parse
+the arguments of a given function, if that function data is found within
+the BTF file.
+
+The *tep_load_btf()* takes the _tep_ handle and will load the _raw_data_
+into it.  The _data_size_ should be set to the size of the content in _raw_data_.
+
+The *tep_btf_print_args()* takes a _tep_ handle, a trace_seq _s_ pointer
+(that was initialized by *trace_seq_init(3)*), an _args_ array that holds either
+4 byte or 8 byte values, the _nmem_ that is the number of values in the _args_
+parameter, a _size_ that is either 4 or 8 to denote the size of each value in _args_,
+and a _func_ string that is the name of the function to find the BTF information
+to use for parsing. If BTF is not loaded or the _func_ name is not found it
+will just print a hex value of all the _args_ into the _s_ descriptor.
+
+RETURN VALUE
+------------
+*tep_load_btf()* function returns 0 on success.  It returns -1 on failure and
+the _raw_data_ will need to be freed by the caller.
+
+*tep_btf_print_args()* returns 0 on success and -1 on failure, which happens
+if the _size_ is not valid or the BTF file that was loaded is corrupted.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <event-parse.h>
+
+int print_args(const char *func, unsigned long *args, int nr)
+{
+	struct tep_handle *tep = tep_alloc();
+	struct trace_seq s;
+	struct stat st;
+	void *buf;
+	int fd;
+	int ret;
+
+	if (!tep)
+		return -1;
+
+	fd = open("/sys/kernel/btf/vmlinux", O_RDONLY);
+	if (fd < 0)
+		return -1;
+
+	if (fstat(fd, &st) < 0) {
+		close(fd);
+		return -1;
+	}
+
+	buf = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+	if (buf == MAP_FAILED) {
+		close(fd);
+		return -1;
+	}
+
+	ret = tep_load_btf(tep, buf, st.st_size);
+	munmap(buf, st.st_size);
+	close(fd);
+
+	if (ret < 0)
+		return -1;
+
+	trace_seq_init(&s);
+	trace_seq_printf(&s, "%s(", func);
+
+	tep_btf_print_args(tep, &s, args, nr, sizeof(long), func);
+
+	trace_seq_puts(&s, ")\n");
+	trace_seq_do_printf(&s);
+
+	tep_free(tep);
+	return 0;
+}
+
+int main(int argc, char **argv)
+{
+	unsigned long args[] = {0x7ffe7d33f3d0, 0, 0, 0, 0, 0};
+
+	print_args("getname_flags", args, 6);
+	return 0;
+}
+...
+The above may output:
+
+  getname_flags(filename=0x7ffe7d33f3d0, flags=0)
+
+If BTF is loaded and the function was found, or it may show:
+
+  getname_flags(7ffe7d33f3d0, 0, 0, 0, 0, 0)
+
+If it was not.
+...
+--
+FILES
+-----
+[verse]
+--
+*event-parse.h*
+	Header file to include in order to have access to the library APIs.
+*-ltraceevent*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtraceevent*(3), *trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>, author of this man page.
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtraceevent is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtraceevent.git/
diff --git a/Documentation/libtraceevent.txt b/Documentation/libtraceevent.txt
index 9e7777283c52..5fb5fc19ffc1 100644
--- a/Documentation/libtraceevent.txt
+++ b/Documentation/libtraceevent.txt
@@ -166,6 +166,11 @@ KVM plugin calllbacks: (Defined by the application and complied with -rdynamic)
 				    unsigned long long pass:[*]paddr);
 	void *tep_plugin_kvm_put_func*(const char pass:[*]func);
 
+BTF parsing:
+	int *tep_load_btf*(struct tep_handle pass:[*]_tep_, void pass:[*]_raw_data_, size_t _data_size_);
+	int *tep_btf_print_args*(struct tep_handle pass:[*]_tep_, struct trace_seq pass:[*]_s_, void pass:[*]_args_,
+		       int nmem, int size, const char pass:[*]_func_);
+
 Trace sequences:
 *#include <trace-seq.h>*
 	void *trace_seq_init*(struct trace_seq pass:[*]_s_);
-- 
2.51.0


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

* [PATCH v3 3/4] libtraceevent: Have BTF find functions with extra characters
  2025-11-12 21:42 [PATCH v3 0/4] libtraceevent: Add BTF parsing for function arguments Steven Rostedt
  2025-11-12 21:42 ` [PATCH v3 1/4] libtraceevent: Add loading of BTF to the tep handle Steven Rostedt
  2025-11-12 21:42 ` [PATCH v3 2/4] libtraceevent: Add man page for the new BTF functions Steven Rostedt
@ 2025-11-12 21:42 ` Steven Rostedt
  2025-11-12 21:42 ` [PATCH v3 4/4] libtraceevent utest: Add simple test to test BTF parsing Steven Rostedt
  3 siblings, 0 replies; 5+ messages in thread
From: Steven Rostedt @ 2025-11-12 21:42 UTC (permalink / raw)
  To: linux-trace-devel
  Cc: Douglas Raillard, Masami Hiramatsu, Namhyung Kim, Takaya Saeki,
	Ian Rogers, aahringo, Steven Rostedt (Google)

From: "Steven Rostedt (Google)" <rostedt@goodmis.org>

Sometimes the symbol of a function has extra characters followed by a '.'.
In this case, the BTF lookup may not find the function. If the function is
not found and the function name contains a '.', try stripping off the
extra characters and try again. That way, instead of:

  __mutex_unlock_slowpath.isra.0(ffffffffb5cd4e40, 8, 1, 1, ffffffffb43e0fe4, ffffed1024699f88)

It can produce:

  __mutex_unlock_slowpath.isra.0(lock=0xffffffffb5cd4e40, ip=0x8)

Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
---
 src/trace-btf.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/src/trace-btf.c b/src/trace-btf.c
index 12dc49447027..742cffd35edc 100644
--- a/src/trace-btf.c
+++ b/src/trace-btf.c
@@ -357,11 +357,23 @@ int tep_btf_print_args(struct tep_handle *tep, struct trace_seq *s, void *args,
 	unsigned long long arg;
 	unsigned int encode;
 	const char *param_name;
+	const char *fp;
 	int a, p, x, nr;
 
 	if (size != 4 && size != 8)
 		return -1;
 
+	if (!type && (fp = strchr(func, '.'))) {
+		char *f;
+		/* func name has extra characters */
+		f = strdup(func);
+		if (f) {
+			f[fp - func] = '\0';
+			type = tep_btf_find_func(btf, f);
+			free(f);
+		}
+	}
+
 	if (!type) {
 		for (int i = 0; i < nmem; i++) {
 			assign_arg(&arg, args, size, i);
-- 
2.51.0


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

* [PATCH v3 4/4] libtraceevent utest: Add simple test to test BTF parsing
  2025-11-12 21:42 [PATCH v3 0/4] libtraceevent: Add BTF parsing for function arguments Steven Rostedt
                   ` (2 preceding siblings ...)
  2025-11-12 21:42 ` [PATCH v3 3/4] libtraceevent: Have BTF find functions with extra characters Steven Rostedt
@ 2025-11-12 21:42 ` Steven Rostedt
  3 siblings, 0 replies; 5+ messages in thread
From: Steven Rostedt @ 2025-11-12 21:42 UTC (permalink / raw)
  To: linux-trace-devel
  Cc: Douglas Raillard, Masami Hiramatsu, Namhyung Kim, Takaya Saeki,
	Ian Rogers, aahringo, Steven Rostedt (Google)

From: "Steven Rostedt (Google)" <rostedt@goodmis.org>

Add a test that reads the kernel BTF and does a simple test.

Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
---
 utest/traceevent-utest.c | 39 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/utest/traceevent-utest.c b/utest/traceevent-utest.c
index b95e47869437..b62411c16a11 100644
--- a/utest/traceevent-utest.c
+++ b/utest/traceevent-utest.c
@@ -15,6 +15,8 @@
 #include <dirent.h>
 #include <ftw.h>
 
+#include <sys/mman.h>
+
 #include <CUnit/CUnit.h>
 #include <CUnit/Basic.h>
 
@@ -378,6 +380,41 @@ static void test_parse_sizeof_undef(void)
 	test_parse_sizeof(0, 5, "sizeof_undef", SIZEOF_LONG0_FMT);
 }
 
+static void test_btf_read(void)
+{
+	unsigned long args[] = {0x7ffe7d33f3d0, 0, 0, 0, 0, 0};
+	const char *func = "getname_flags";
+	struct trace_seq *s = test_seq;
+	struct stat st;
+	void *buf;
+	int fd, nr = 6;
+
+	fd = open("/sys/kernel/btf/vmlinux", O_RDONLY);
+	if (fd < 0) {
+		printf("[KERNEL DOES NOT HAVE BTF] ...");
+		return;
+	}
+	CU_TEST(fstat(fd, &st) == 0);
+
+	buf = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+	CU_TEST(buf != MAP_FAILED);
+
+	CU_TEST(tep_load_btf(test_tep, buf, st.st_size) == 0);
+
+	munmap(buf, st.st_size);
+	close(fd);
+
+	trace_seq_init(s);
+	trace_seq_printf(s, "%s(", func);
+
+	CU_TEST(tep_btf_print_args(test_tep, s, args, nr, sizeof(long), func) == 0);
+
+	trace_seq_puts(s, ")\n");
+	trace_seq_terminate(s);
+
+	CU_TEST(strcmp(s->buffer, "getname_flags(filename=0x7ffe7d33f3d0, flags=0)\n") == 0);
+}
+
 static int test_suite_destroy(void)
 {
 	tep_free(test_tep);
@@ -429,4 +466,6 @@ void test_traceevent_lib(void)
 		    test_parse_sizeof4);
 	CU_add_test(suite, "parse sizeof() no long size defined",
 		    test_parse_sizeof_undef);
+	CU_add_test(suite, "read BTF",
+		    test_btf_read);
 }
-- 
2.51.0


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

end of thread, other threads:[~2025-11-12 21:49 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-12 21:42 [PATCH v3 0/4] libtraceevent: Add BTF parsing for function arguments Steven Rostedt
2025-11-12 21:42 ` [PATCH v3 1/4] libtraceevent: Add loading of BTF to the tep handle Steven Rostedt
2025-11-12 21:42 ` [PATCH v3 2/4] libtraceevent: Add man page for the new BTF functions Steven Rostedt
2025-11-12 21:42 ` [PATCH v3 3/4] libtraceevent: Have BTF find functions with extra characters Steven Rostedt
2025-11-12 21:42 ` [PATCH v3 4/4] libtraceevent utest: Add simple test to test BTF parsing Steven Rostedt

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