* [PATCH] libtraceevent: parse %pB and %pa address specifiers
@ 2026-04-05 22:10 CaoRuichuang
2026-04-06 6:20 ` [PATCH v2] " CaoRuichuang
0 siblings, 1 reply; 2+ messages in thread
From: CaoRuichuang @ 2026-04-05 22:10 UTC (permalink / raw)
To: linux-trace-devel; +Cc: rostedt, tz.stoyanov, create0818
tep_parse_format() currently rejects events whose print fmt uses
%pB, %pap or %pad. The pointer format parser does not recognise
those suffixes, so libtraceevent fails before trace-cmd can report
the event at all.
Teach the pointer-format parser to accept %pB and %pa[p]/%pad,
print %pB with backtrace-style symbol lookup, and format %pa[p]/%pad
values from the referenced event fields.
Add unit tests that cover %pB, %pap and %pad.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=217337
Signed-off-by: CaoRuichuang <create0818@163.com>
---
src/event-parse.c | 90 ++++++++++++++++++++++++++++++++
utest/traceevent-utest.c | 108 +++++++++++++++++++++++++++++++++++++++
2 files changed, 198 insertions(+)
diff --git a/src/event-parse.c b/src/event-parse.c
index fee0f91..4bd5c1b 100644
--- a/src/event-parse.c
+++ b/src/event-parse.c
@@ -6631,6 +6631,78 @@ static int print_function(struct trace_seq *s, const char *format,
return 0;
}
+static int print_function_backtrace(struct trace_seq *s,
+ void *data, int size,
+ struct tep_event *event,
+ struct tep_print_arg *arg, bool raw)
+{
+ struct func_map *func = NULL;
+ unsigned long long lookup;
+ unsigned long long val;
+
+ val = eval_num_arg(data, size, event, arg);
+ lookup = val ? val - 1 : 0;
+ func = val ? find_func(event->tep, lookup) : NULL;
+
+ if (func) {
+ trace_seq_puts(s, func->func);
+ trace_seq_printf(s, "+0x%llx", lookup - func->addr);
+ }
+
+ if (!func || raw) {
+ if (raw)
+ trace_seq_puts(s, " (");
+ if (event->tep->long_size == 4)
+ trace_seq_printf(s, "0x%lx", (long)val);
+ else
+ trace_seq_printf(s, "0x%llx", (long long)val);
+ if (raw)
+ trace_seq_puts(s, ")");
+ }
+
+ return 0;
+}
+
+static int print_addr_ref_arg(struct trace_seq *s, const char *format,
+ void *data, int size,
+ struct tep_event *event,
+ struct tep_print_arg *arg)
+{
+ struct tep_format_field *field;
+ struct tep_handle *tep = event->tep;
+ unsigned long long val;
+ int width;
+ int ret = 0;
+
+ while (arg->type == TEP_PRINT_TYPE)
+ arg = arg->typecast.item;
+
+ if (arg->type != TEP_PRINT_FIELD) {
+ trace_seq_printf(s, "ARG TYPE NOT FIELD BUT %d", arg->type);
+ return ret;
+ }
+
+ field = arg->field.field;
+ if (!field) {
+ field = tep_find_any_field(event, arg->field.name);
+ if (!field) {
+ do_warning_event(event, "%s: field %s not found",
+ __func__, arg->field.name);
+ return ret;
+ }
+ arg->field.field = field;
+ }
+
+ val = tep_read_number(tep, data + field->offset, field->size);
+ width = field->size * 2;
+ trace_seq_printf(s, "0x%0*llx", width, val);
+
+ if (format[1] == 'p' || format[1] == 'd')
+ ret++;
+
+ return ret;
+}
+
static int print_arg_pointer(struct trace_seq *s, const char *format, int plen,
void *data, int size,
struct tep_event *event, struct tep_print_arg *arg,
@@ -6652,12 +6724,18 @@ static int print_arg_pointer(struct trace_seq *s, const char *format, int plen,
}
switch (*format) {
+ case 'B':
+ ret += print_function_backtrace(s, data, size, event, arg, raw);
+ break;
case 'F':
case 'f':
case 'S':
case 's':
ret += print_function(s, format, data, size, event, arg, raw);
break;
+ case 'a':
+ ret += print_addr_ref_arg(s, format, data, size, event, arg);
+ break;
case 'M':
case 'm':
ret += print_mac_arg(s, format, data, size, event, arg);
@@ -6752,12 +6830,24 @@ static int parse_arg_format_pointer(const char *format)
int loop;
switch (*format) {
+ case 'B':
+ ret++;
+ break;
case 'F':
case 'S':
case 'f':
case 's':
ret++;
break;
+ case 'a':
+ switch (format[1]) {
+ case 'p':
+ case 'd':
+ ret++;
+ break;
+ }
+ ret++;
+ break;
case 'M':
case 'm':
/* [mM]R , [mM]F */
diff --git a/utest/traceevent-utest.c b/utest/traceevent-utest.c
index b62411c..e64c923 100644
--- a/utest/traceevent-utest.c
+++ b/utest/traceevent-utest.c
@@ -16,6 +16,7 @@
#include <ftw.h>
#include <sys/mman.h>
+#include <stdint.h>
#include <CUnit/CUnit.h>
#include <CUnit/Basic.h>
@@ -182,6 +183,45 @@ static char sizeof_data[] = {
};
static void *sizeof_event_data = (void *)sizeof_data;
+static const char pointer_backtrace_event[] =
+ "name: pointer_backtrace_event\n"
+ "ID: 40\n"
+ "format:\n"
+ "\tfield:unsigned short common_type;\toffset:0;\tsize:2;\tsigned:0;\n"
+ "\tfield:unsigned char common_flags;\toffset:2;\tsize:1;\tsigned:0;\n"
+ "\tfield:unsigned char common_preempt_count;\toffset:3;\tsize:1;\tsigned:0;\n"
+ "\tfield:int common_pid;\toffset:4;\tsize:4;\tsigned:1;\n"
+ "\n"
+ "\tfield:unsigned long long ptr;\toffset:8;\tsize:8;\tsigned:0;\n"
+ "\n"
+ "print fmt: \"ptr=%pB\", REC->ptr\n";
+
+static const char phys_addr_event[] =
+ "name: phys_addr_event\n"
+ "ID: 41\n"
+ "format:\n"
+ "\tfield:unsigned short common_type;\toffset:0;\tsize:2;\tsigned:0;\n"
+ "\tfield:unsigned char common_flags;\toffset:2;\tsize:1;\tsigned:0;\n"
+ "\tfield:unsigned char common_preempt_count;\toffset:3;\tsize:1;\tsigned:0;\n"
+ "\tfield:int common_pid;\toffset:4;\tsize:4;\tsigned:1;\n"
+ "\n"
+ "\tfield:unsigned long long phys;\toffset:8;\tsize:8;\tsigned:0;\n"
+ "\n"
+ "print fmt: \"phys=%pap\", REC->phys\n";
+
+static const char dma_addr_event[] =
+ "name: dma_addr_event\n"
+ "ID: 42\n"
+ "format:\n"
+ "\tfield:unsigned short common_type;\toffset:0;\tsize:2;\tsigned:0;\n"
+ "\tfield:unsigned char common_flags;\toffset:2;\tsize:1;\tsigned:0;\n"
+ "\tfield:unsigned char common_preempt_count;\toffset:3;\tsize:1;\tsigned:0;\n"
+ "\tfield:int common_pid;\toffset:4;\tsize:4;\tsigned:1;\n"
+ "\n"
+ "\tfield:unsigned int dma;\toffset:8;\tsize:4;\tsigned:0;\n"
+ "\n"
+ "print fmt: \"dma=%pad\", REC->dma\n";
+
DECL_CPUMASK_EVENT_DATA(full, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff);
#define CPUMASK_FULL "ARRAY[ff, ff, ff, ff, ff, ff, ff, ff]"
#define CPUMASK_FULL_FMT "cpumask=0-63"
@@ -380,6 +420,64 @@ static void test_parse_sizeof_undef(void)
test_parse_sizeof(0, 5, "sizeof_undef", SIZEOF_LONG0_FMT);
}
+static void parse_pointer_event(const char *format, const char *system,
+ void *data, int size, const char *expected)
+{
+ struct tep_event *event;
+ struct tep_record record;
+
+ record.data = data;
+ record.size = size;
+
+ CU_TEST(tep_parse_format(test_tep, &event, format, strlen(format),
+ system) == TEP_ERRNO__SUCCESS);
+
+ trace_seq_reset(test_seq);
+ tep_print_event(test_tep, test_seq, &record, "%s", TEP_PRINT_INFO);
+ trace_seq_do_printf(test_seq);
+ CU_TEST(strcmp(test_seq->buffer, expected) == 0);
+}
+
+static void test_parse_pointer_backtrace(void)
+{
+ unsigned char data[16] = { 0 };
+ unsigned short type = 40;
+ uint64_t ptr = 0x1001ULL;
+
+ memcpy(data, &type, sizeof(type));
+ memcpy(data + 8, &ptr, sizeof(ptr));
+
+ parse_pointer_event(pointer_backtrace_event, "ptr_backtrace",
+ data, sizeof(data), "ptr=test_func+0x0");
+}
+
+static void test_parse_phys_addr(void)
+{
+ unsigned char data[16] = { 0 };
+ unsigned short type = 41;
+ uint64_t phys = 0x123456789abcdef0ULL;
+
+ memcpy(data, &type, sizeof(type));
+ memcpy(data + 8, &phys, sizeof(phys));
+
+ parse_pointer_event(phys_addr_event, "ptr_phys",
+ data, sizeof(data),
+ "phys=0x123456789abcdef0");
+}
+
+static void test_parse_dma_addr(void)
+{
+ unsigned char data[12] = { 0 };
+ unsigned short type = 42;
+ uint32_t dma = 0x1234abcdU;
+
+ memcpy(data, &type, sizeof(type));
+ memcpy(data + 8, &dma, sizeof(dma));
+
+ parse_pointer_event(dma_addr_event, "ptr_dma",
+ data, sizeof(data), "dma=0x1234abcd");
+}
+
static void test_btf_read(void)
{
unsigned long args[] = {0x7ffe7d33f3d0, 0, 0, 0, 0, 0};
@@ -429,6 +527,10 @@ static int test_suite_init(void)
test_tep = tep_alloc();
if (!test_tep)
return 1;
+ if (tep_register_function(test_tep, "test_func", 0x1000, NULL))
+ return 1;
+ if (tep_register_function(test_tep, "test_func_end", 0x2000, NULL))
+ return 1;
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
tep_set_file_bigendian(test_tep, TEP_BIG_ENDIAN);
#endif
@@ -466,6 +568,12 @@ 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, "parse %pB pointer format",
+ test_parse_pointer_backtrace);
+ CU_add_test(suite, "parse %pap pointer format",
+ test_parse_phys_addr);
+ CU_add_test(suite, "parse %pad pointer format",
+ test_parse_dma_addr);
CU_add_test(suite, "read BTF",
test_btf_read);
}
--
2.39.5 (Apple Git-154)
^ permalink raw reply related [flat|nested] 2+ messages in thread
* [PATCH v2] libtraceevent: parse %pB and %pa address specifiers
2026-04-05 22:10 [PATCH] libtraceevent: parse %pB and %pa address specifiers CaoRuichuang
@ 2026-04-06 6:20 ` CaoRuichuang
0 siblings, 0 replies; 2+ messages in thread
From: CaoRuichuang @ 2026-04-06 6:20 UTC (permalink / raw)
To: linux-trace-devel; +Cc: rostedt, tz.stoyanov, create0818
tep_parse_format() currently rejects events whose print fmt uses
%pB, %pa, %pap or %pad. The pointer format parser does not recognise
those suffixes, so libtraceevent fails before trace-cmd can report
the event at all.
Teach the pointer-format parser to accept %pB and %pa[p]/%pad,
print %pB with backtrace-style symbol lookup, and format %pa[p]/%pad
values from the referenced event fields.
Add unit tests that cover %pB, %pa, %pap and %pad.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=217337
Signed-off-by: CaoRuichuang <create0818@163.com>
---
src/event-parse.c | 90 +++++++++++++++++++++++++
utest/traceevent-utest.c | 137 +++++++++++++++++++++++++++++++++++++++
2 files changed, 227 insertions(+)
diff --git a/src/event-parse.c b/src/event-parse.c
index fee0f91..4bd5c1b 100644
--- a/src/event-parse.c
+++ b/src/event-parse.c
@@ -6631,6 +6631,78 @@ static int print_function(struct trace_seq *s, const char *format,
return 0;
}
+static int print_function_backtrace(struct trace_seq *s,
+ void *data, int size,
+ struct tep_event *event,
+ struct tep_print_arg *arg, bool raw)
+{
+ struct func_map *func = NULL;
+ unsigned long long lookup;
+ unsigned long long val;
+
+ val = eval_num_arg(data, size, event, arg);
+ lookup = val ? val - 1 : 0;
+ func = val ? find_func(event->tep, lookup) : NULL;
+
+ if (func) {
+ trace_seq_puts(s, func->func);
+ trace_seq_printf(s, "+0x%llx", lookup - func->addr);
+ }
+
+ if (!func || raw) {
+ if (raw)
+ trace_seq_puts(s, " (");
+ if (event->tep->long_size == 4)
+ trace_seq_printf(s, "0x%lx", (long)val);
+ else
+ trace_seq_printf(s, "0x%llx", (long long)val);
+ if (raw)
+ trace_seq_puts(s, ")");
+ }
+
+ return 0;
+}
+
+static int print_addr_ref_arg(struct trace_seq *s, const char *format,
+ void *data, int size,
+ struct tep_event *event,
+ struct tep_print_arg *arg)
+{
+ struct tep_format_field *field;
+ struct tep_handle *tep = event->tep;
+ unsigned long long val;
+ int width;
+ int ret = 0;
+
+ while (arg->type == TEP_PRINT_TYPE)
+ arg = arg->typecast.item;
+
+ if (arg->type != TEP_PRINT_FIELD) {
+ trace_seq_printf(s, "ARG TYPE NOT FIELD BUT %d", arg->type);
+ return ret;
+ }
+
+ field = arg->field.field;
+ if (!field) {
+ field = tep_find_any_field(event, arg->field.name);
+ if (!field) {
+ do_warning_event(event, "%s: field %s not found",
+ __func__, arg->field.name);
+ return ret;
+ }
+ arg->field.field = field;
+ }
+
+ val = tep_read_number(tep, data + field->offset, field->size);
+ width = field->size * 2;
+ trace_seq_printf(s, "0x%0*llx", width, val);
+
+ if (format[1] == 'p' || format[1] == 'd')
+ ret++;
+
+ return ret;
+}
+
static int print_arg_pointer(struct trace_seq *s, const char *format, int plen,
void *data, int size,
struct tep_event *event, struct tep_print_arg *arg,
@@ -6652,12 +6724,18 @@ static int print_arg_pointer(struct trace_seq *s, const char *format, int plen,
}
switch (*format) {
+ case 'B':
+ ret += print_function_backtrace(s, data, size, event, arg, raw);
+ break;
case 'F':
case 'f':
case 'S':
case 's':
ret += print_function(s, format, data, size, event, arg, raw);
break;
+ case 'a':
+ ret += print_addr_ref_arg(s, format, data, size, event, arg);
+ break;
case 'M':
case 'm':
ret += print_mac_arg(s, format, data, size, event, arg);
@@ -6752,12 +6830,24 @@ static int parse_arg_format_pointer(const char *format)
int loop;
switch (*format) {
+ case 'B':
+ ret++;
+ break;
case 'F':
case 'S':
case 'f':
case 's':
ret++;
break;
+ case 'a':
+ switch (format[1]) {
+ case 'p':
+ case 'd':
+ ret++;
+ break;
+ }
+ ret++;
+ break;
case 'M':
case 'm':
/* [mM]R , [mM]F */
diff --git a/utest/traceevent-utest.c b/utest/traceevent-utest.c
index b62411c..e0b9686 100644
--- a/utest/traceevent-utest.c
+++ b/utest/traceevent-utest.c
@@ -16,6 +16,7 @@
#include <ftw.h>
#include <sys/mman.h>
+#include <stdint.h>
#include <CUnit/CUnit.h>
#include <CUnit/Basic.h>
@@ -182,6 +183,58 @@ static char sizeof_data[] = {
};
static void *sizeof_event_data = (void *)sizeof_data;
+static const char pointer_backtrace_event[] =
+ "name: pointer_backtrace_event\n"
+ "ID: 40\n"
+ "format:\n"
+ "\tfield:unsigned short common_type;\toffset:0;\tsize:2;\tsigned:0;\n"
+ "\tfield:unsigned char common_flags;\toffset:2;\tsize:1;\tsigned:0;\n"
+ "\tfield:unsigned char common_preempt_count;\toffset:3;\tsize:1;\tsigned:0;\n"
+ "\tfield:int common_pid;\toffset:4;\tsize:4;\tsigned:1;\n"
+ "\n"
+ "\tfield:unsigned long long ptr;\toffset:8;\tsize:8;\tsigned:0;\n"
+ "\n"
+ "print fmt: \"ptr=%pB\", REC->ptr\n";
+
+static const char phys_addr_event[] =
+ "name: phys_addr_event\n"
+ "ID: 41\n"
+ "format:\n"
+ "\tfield:unsigned short common_type;\toffset:0;\tsize:2;\tsigned:0;\n"
+ "\tfield:unsigned char common_flags;\toffset:2;\tsize:1;\tsigned:0;\n"
+ "\tfield:unsigned char common_preempt_count;\toffset:3;\tsize:1;\tsigned:0;\n"
+ "\tfield:int common_pid;\toffset:4;\tsize:4;\tsigned:1;\n"
+ "\n"
+ "\tfield:unsigned long long phys;\toffset:8;\tsize:8;\tsigned:0;\n"
+ "\n"
+ "print fmt: \"phys=%pap\", REC->phys\n";
+
+static const char phys_addr_plain_event[] =
+ "name: phys_addr_plain_event\n"
+ "ID: 43\n"
+ "format:\n"
+ "\tfield:unsigned short common_type;\toffset:0;\tsize:2;\tsigned:0;\n"
+ "\tfield:unsigned char common_flags;\toffset:2;\tsize:1;\tsigned:0;\n"
+ "\tfield:unsigned char common_preempt_count;\toffset:3;\tsize:1;\tsigned:0;\n"
+ "\tfield:int common_pid;\toffset:4;\tsize:4;\tsigned:1;\n"
+ "\n"
+ "\tfield:unsigned long long phys;\toffset:8;\tsize:8;\tsigned:0;\n"
+ "\n"
+ "print fmt: \"phys=%pa\", REC->phys\n";
+
+static const char dma_addr_event[] =
+ "name: dma_addr_event\n"
+ "ID: 42\n"
+ "format:\n"
+ "\tfield:unsigned short common_type;\toffset:0;\tsize:2;\tsigned:0;\n"
+ "\tfield:unsigned char common_flags;\toffset:2;\tsize:1;\tsigned:0;\n"
+ "\tfield:unsigned char common_preempt_count;\toffset:3;\tsize:1;\tsigned:0;\n"
+ "\tfield:int common_pid;\toffset:4;\tsize:4;\tsigned:1;\n"
+ "\n"
+ "\tfield:unsigned int dma;\toffset:8;\tsize:4;\tsigned:0;\n"
+ "\n"
+ "print fmt: \"dma=%pad\", REC->dma\n";
+
DECL_CPUMASK_EVENT_DATA(full, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff);
#define CPUMASK_FULL "ARRAY[ff, ff, ff, ff, ff, ff, ff, ff]"
#define CPUMASK_FULL_FMT "cpumask=0-63"
@@ -380,6 +433,78 @@ static void test_parse_sizeof_undef(void)
test_parse_sizeof(0, 5, "sizeof_undef", SIZEOF_LONG0_FMT);
}
+static void parse_pointer_event(const char *format, const char *system,
+ void *data, int size, const char *expected)
+{
+ struct tep_event *event;
+ struct tep_record record;
+
+ record.data = data;
+ record.size = size;
+
+ CU_TEST(tep_parse_format(test_tep, &event, format, strlen(format),
+ system) == TEP_ERRNO__SUCCESS);
+
+ trace_seq_reset(test_seq);
+ tep_print_event(test_tep, test_seq, &record, "%s", TEP_PRINT_INFO);
+ trace_seq_do_printf(test_seq);
+ CU_TEST(strcmp(test_seq->buffer, expected) == 0);
+}
+
+static void test_parse_pointer_backtrace(void)
+{
+ unsigned char data[16] = { 0 };
+ unsigned short type = 40;
+ uint64_t ptr = 0x1001ULL;
+
+ memcpy(data, &type, sizeof(type));
+ memcpy(data + 8, &ptr, sizeof(ptr));
+
+ parse_pointer_event(pointer_backtrace_event, "ptr_backtrace",
+ data, sizeof(data), "ptr=test_func+0x0");
+}
+
+static void test_parse_phys_addr(void)
+{
+ unsigned char data[16] = { 0 };
+ unsigned short type = 41;
+ uint64_t phys = 0x123456789abcdef0ULL;
+
+ memcpy(data, &type, sizeof(type));
+ memcpy(data + 8, &phys, sizeof(phys));
+
+ parse_pointer_event(phys_addr_event, "ptr_phys",
+ data, sizeof(data),
+ "phys=0x123456789abcdef0");
+}
+
+static void test_parse_phys_addr_plain(void)
+{
+ unsigned char data[16] = { 0 };
+ unsigned short type = 43;
+ uint64_t phys = 0x123456789abcdef0ULL;
+
+ memcpy(data, &type, sizeof(type));
+ memcpy(data + 8, &phys, sizeof(phys));
+
+ parse_pointer_event(phys_addr_plain_event, "ptr_phys_plain",
+ data, sizeof(data),
+ "phys=0x123456789abcdef0");
+}
+
+static void test_parse_dma_addr(void)
+{
+ unsigned char data[12] = { 0 };
+ unsigned short type = 42;
+ uint32_t dma = 0x1234abcdU;
+
+ memcpy(data, &type, sizeof(type));
+ memcpy(data + 8, &dma, sizeof(dma));
+
+ parse_pointer_event(dma_addr_event, "ptr_dma",
+ data, sizeof(data), "dma=0x1234abcd");
+}
+
static void test_btf_read(void)
{
unsigned long args[] = {0x7ffe7d33f3d0, 0, 0, 0, 0, 0};
@@ -429,6 +554,10 @@ static int test_suite_init(void)
test_tep = tep_alloc();
if (!test_tep)
return 1;
+ if (tep_register_function(test_tep, "test_func", 0x1000, NULL))
+ return 1;
+ if (tep_register_function(test_tep, "test_func_end", 0x2000, NULL))
+ return 1;
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
tep_set_file_bigendian(test_tep, TEP_BIG_ENDIAN);
#endif
@@ -466,6 +595,14 @@ 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, "parse %pB pointer format",
+ test_parse_pointer_backtrace);
+ CU_add_test(suite, "parse %pap pointer format",
+ test_parse_phys_addr);
+ CU_add_test(suite, "parse %pa pointer format",
+ test_parse_phys_addr_plain);
+ CU_add_test(suite, "parse %pad pointer format",
+ test_parse_dma_addr);
CU_add_test(suite, "read BTF",
test_btf_read);
}
--
2.39.5 (Apple Git-154)
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-04-06 6:20 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-05 22:10 [PATCH] libtraceevent: parse %pB and %pa address specifiers CaoRuichuang
2026-04-06 6:20 ` [PATCH v2] " CaoRuichuang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox