[PATCH v2 2/2] libtraceevent: Add tep_btf_list_args()

Steven Rostedt <[email protected]> Tue, 3 Feb 2026 17:27:27 -0500
Newsgroups org.kernel.vger.linux-trace-devel
Message-ID <[email protected]>
From: "Steven Rostedt (Google)" <[email protected]>

Add tep_btf_list_args() to display a function's prototype:

 struct trace_seq s;

 trace_seq_init(&s);
 tep_btf_list_args(tep, &s, "proc_scheduler");
 trace_seq_do_printf(&s);

produces:

    const struct ctl_table *ctl, int write, void *buffer, size_t *lenp, loff_t *ppos

Where the prototype of proc_scheduler() is:

 static int proc_scheduler(const struct ctl_table *ctl, int write,
			  void *buffer, size_t *lenp, loff_t *ppos)

Signed-off-by: Steven Rostedt (Google) <[email protected]>
---
Changes since v1: https://lore.kernel.org/[email protected]

- Fixed whitespace issue.

 Documentation/libtraceevent-btf-proto.txt | 124 ++++++++++++++++++
 Documentation/libtraceevent.txt           |   1 +
 include/traceevent/event-parse.h          |   1 +
 src/trace-btf.c                           | 151 +++++++++++++++++++++-
 4 files changed, 275 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/libtraceevent-btf-proto.txt

diff --git a/Documentation/libtraceevent-btf-proto.txt b/Documentation/libtraceevent-btf-proto.txt
new file mode 100644
index 000000000000..9a4ce15af1c9
--- /dev/null
+++ b/Documentation/libtraceevent-btf-proto.txt
@@ -0,0 +1,124 @@
+libtraceevent(3)
+================
+
+NAME
+----
+tep_btf_list_args - Show the arguments of a function from BTF
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <event-parse.h>*
+
+int *tep_btf_list_args*(struct tep_handle pass:[*]_tep_, struct trace_seq pass:[*]_s_, 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 can be used to read the arguments
+of a given function, if that function data is found within the BTF file.
+
+The *tep_btf_print_args()* takes a _tep_ handle, a trace_seq _s_ pointer
+(that was initialized by *trace_seq_init(3)*), and a _func_ string that is
+the name of the function to find the BTF information to use to print the function's prototype.
+If BTF is not loaded or the _func_ name is not found it will return a negative.
+
+RETURN VALUE
+------------
+*tep_btf_list_args()* returns the number of arguments read on success and -1 on failure
+(for example, if the function is not found).
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <event-parse.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#define BTF_FILE "/sys/kernel/btf/vmlinux"
+
+int main(int argc, char **argv)
+{
+	struct tep_handle *tep;
+	struct trace_seq s;
+	struct stat st;
+	char *buf;
+	int fd, r, z;
+
+	if (argc < 2)
+		exit(-1);
+
+	if (stat(BTF_FILE, &st) < 0) {
+		perror(BTF_FILE);
+		exit(-1);
+	}
+
+	buf = malloc(st.st_size);
+	if (!buf)
+		exit(-1);
+	fd = open(BTF_FILE, O_RDONLY);
+	if (fd < 0) {
+		perror(BTF_FILE);
+		exit(-1);
+	}
+	for (z = 0; z < st.st_size; ) {
+		r = read(fd, buf + z, st.st_size - z);
+		if (r <= 0)
+			break;
+		z += r;
+	}
+	close(fd);
+	tep = tep_alloc();
+	if (!tep)
+		exit(-1);
+
+	tep_load_btf(tep, buf, z);
+	free(buf);
+
+	trace_seq_init(&s);
+	tep_btf_list_args(tep, &s, argv[1]);
+	printf("%s(", argv[1]);
+	trace_seq_do_printf(&s);
+	printf(")\n");
+	exit(0);
+}
+
+--
+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
+--------
+*tep_btf_load*(3), *tep_btf_print_args*(3), *libtraceevent*(3), *trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <[email protected]>, author of *libtraceevent*.
+--
+REPORTING BUGS
+--------------
+Report bugs to  <[email protected]>
+
+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 5fb5fc19ffc1..6e5fdbc6e886 100644
--- a/Documentation/libtraceevent.txt
+++ b/Documentation/libtraceevent.txt
@@ -170,6 +170,7 @@ 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_);
+	int *tep_btf_list_args*(struct tep_handle pass:[*]_tep_, struct trace_seq pass:[*]_s_, const char pass:[*]_func_);
 
 Trace sequences:
 *#include <trace-seq.h>*
diff --git a/include/traceevent/event-parse.h b/include/traceevent/event-parse.h
index 9c1abfae28d9..0070e21eb952 100644
--- a/include/traceevent/event-parse.h
+++ b/include/traceevent/event-parse.h
@@ -589,6 +589,7 @@ 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_list_args(struct tep_handle *tep, struct trace_seq *s, const char *func);
 int tep_btf_print_args(struct tep_handle *tep, struct trace_seq *s, void *args,
 		       int nmem, int size, const char *func);
 
diff --git a/src/trace-btf.c b/src/trace-btf.c
index 0b733bf9cb75..cf5a9780ab8e 100644
--- a/src/trace-btf.c
+++ b/src/trace-btf.c
@@ -323,6 +323,89 @@ static struct btf_type *btf_skip_modifiers(struct tep_btf *btf, int id)
 	return t;
 }
 
+static void add_name(struct tep_btf *btf, struct btf_type *t,
+		     struct trace_seq *s, const char *alt)
+{
+	const char *name;
+
+	name = btf_name(btf, t->name_off);
+	if (name)
+		trace_seq_printf(s, "%s ", name);
+	else if (alt)
+		trace_seq_printf(s, "%s ", alt);
+	else
+		trace_seq_puts(s, "?? ");
+}
+
+static void btf_add_type(struct tep_btf *btf, struct trace_seq *s, int id)
+{
+	struct btf_type *t = btf_get_type(btf, id);
+	unsigned int encode;
+	int bits;
+
+	while (t) {
+		switch (BTF_INFO_KIND(t->info)) {
+		case BTF_KIND_TYPEDEF:
+			add_name(btf, t, s, "typedef");
+			return;
+
+		case BTF_KIND_ENUM:
+			trace_seq_puts(s, "enum ");
+			add_name(btf, t, s, NULL);
+			return;
+
+		case BTF_KIND_STRUCT:
+			trace_seq_puts(s, "struct ");
+			add_name(btf, t, s, NULL);
+			return;
+
+		case BTF_KIND_UNION:
+			trace_seq_puts(s, "union ");
+			add_name(btf, t, s, NULL);
+			return;
+
+		case BTF_KIND_PTR:
+			if (t->type)
+				btf_add_type(btf, s, t->type);
+			else
+				trace_seq_puts(s, "void ");
+			trace_seq_puts(s, "*");
+			return;
+
+		case BTF_KIND_VOLATILE:	trace_seq_puts(s, "volatile ");
+			btf_add_type(btf, s, t->type);
+			return;
+
+		case BTF_KIND_CONST:	trace_seq_puts(s, "const ");
+			btf_add_type(btf, s, t->type);
+			return;
+
+		case BTF_KIND_INT:
+			encode = *(int *)((void *)t + sizeof(*t));
+			if (!(BTF_INT_ENCODING(encode) & BTF_INT_SIGNED))
+				trace_seq_puts(s, "unsigned ");
+
+			bits = BTF_INT_BITS(encode);
+			switch (bits) {
+			case 8:		trace_seq_puts(s, "char "); break;
+			case 16:	trace_seq_puts(s, "short "); break;
+			case 32:	trace_seq_puts(s, "int "); break;
+			case 64:	trace_seq_puts(s, "long long "); break;
+			default:	trace_seq_printf(s, "int%d ", bits);
+			}
+			return;
+
+
+		case BTF_KIND_RESTRICT:
+		case BTF_KIND_TYPE_TAG:
+			id = t->type;
+			t = btf_get_type(btf, t->type);
+			continue;
+		}
+		break;
+	}
+}
+
 static void assign_arg(unsigned long long *arg, void *args, int size, int a)
 {
 	*arg = size == 4 ?
@@ -338,7 +421,7 @@ static int init_btf_func(struct tep_btf *btf, struct trace_seq *s,
 	unsigned long long arg;
 	const char *fp;
 
-	if (size != 4 && size != 8)
+	if (args && (size != 4 && size != 8))
 		return -1;
 
 	if (!type && (fp = strchr(func, '.'))) {
@@ -353,7 +436,7 @@ static int init_btf_func(struct tep_btf *btf, struct trace_seq *s,
 	}
 
 	if (!type) {
-		for (int i = 0; i < nmem; i++) {
+		for (int i = 0; args && i < nmem; i++) {
 			assign_arg(&arg, args, size, i);
 			trace_seq_printf(s, "%llx", arg);
 			if (i + 1 < nmem)
@@ -375,6 +458,70 @@ static int init_btf_func(struct tep_btf *btf, struct trace_seq *s,
 	return 0;
 }
 
+/**
+ * tep_btf_list_args - List the arguments (type and name) for a function
+ * @tep: The tep descriptor to use
+ * @s: The trace_seq to write the arguments into
+ * @func: The name of the function.
+ *
+ * Loads up @s with the type and name of @func's arguments (basically
+ * its prototype).
+ *
+ * Returns: number of arguments found, or -1 on failure.
+ */
+int tep_btf_list_args(struct tep_handle *tep, struct trace_seq *s, const char *func)
+{
+	struct tep_btf *btf = tep->btf;
+	struct btf_type *type = tep_btf_find_func(btf, func);
+	struct btf_param *param;
+	const char *param_name;
+	int p, nr;
+
+	if (init_btf_func(btf, s, NULL, 0, 0, func, &type) < 0)
+		return -1;
+
+	/* Type is NULL if function wasn't found */
+	if (!type)
+		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 (p = 0; p < nr; p++) {
+
+		if (p)
+			trace_seq_puts(s, ", ");
+
+		param_name = btf_name(btf, param[p].name_off);
+		if (!param_name)
+			param_name = "??";
+
+		btf_add_type(btf, s, param[p].type);
+
+
+		if (param_name)
+			trace_seq_printf(s, "%s", param_name);
+	}
+	return p;
+}
+
 /**
  * tep_btf_print_args - Print function arguments from BTF info
  * @tep: The tep descriptor to use
-- 
2.51.0