* [PATCH] perf trace: Support --bitmask-list with --libtraceevent
@ 2026-10-02 21:31 Aaron Tomlin
2026-10-03 1:34 ` sashiko-bot
0 siblings, 1 reply; 3+ messages in thread
From: Aaron Tomlin @ 2026-10-02 21:31 UTC (permalink / raw)
To: peterz, mingo, acme, namhyung
Cc: mark.rutland, alexander.shishkin, jolsa, irogers, adrian.hunter,
atomlin, linux-perf-users, linux-kernel
Commit ab74d1fbe061 ("perf trace: Add --bitmask-list command-line option")
introduced the --bitmask-list option to display cpumasks and bitmasks as
condensed, human-readable range lists (e.g. "0,2-5,7") instead of the
default hexadecimal format.
However, that implementation only augmented trace__fprintf_tp_fields().
When --libtraceevent is specified, event formatting is delegated to
event_format__fprintf(), which bypasses trace__fprintf_tp_fields() and
relies directly on libtraceevent.
In libtraceevent, tracepoint bitmask fields defined using __get_bitmask()
are parsed into TEP_PRINT_BITMASK AST nodes and unconditionally formatted
as raw 32-bit hex chunks (e.g. "00000000,00000005"), ignoring the
--bitmask-list command-line option.
While a dedicated configuration API in libtraceevent would be the ideal
long-term solution, that requires a new upstream libtraceevent release
and conditional version checks in perf. To provide immediate support for
existing installations, traverse the event format's print argument AST
in perf trace and rewrite any TEP_PRINT_BITMASK nodes to TEP_PRINT_CPUMASK
when --bitmask-list is enabled. Both types share the identical union
payload (arg->bitmask.field), but TEP_PRINT_CPUMASK instructs
libtraceevent's print engine (print_cpumask_to_seq()) to render the mask
as a condensed range list.
To avoid any per-sample overhead in the event handling fast path, perform
this transformation once during tracepoint setup via
trace__init_tp_bitmask_list() for both live tracing (cmd_trace()) and
recording replay (trace__replay()), leaving trace__event_handler()
untouched. All enum values of enum tep_print_arg_type are explicitly
covered in the switch statement to satisfy -Wswitch-enum under -Werror.
Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
---
tools/perf/builtin-trace.c | 75 ++++++++++++++++++++++++++++++++++++++
1 file changed, 75 insertions(+)
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index c3c7f1f85c53..57249df65fe3 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -4857,6 +4857,77 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
goto out_put_evlist;
}
+#ifdef HAVE_LIBTRACEEVENT
+static void tep_print_arg__set_bitmask_list(struct tep_print_arg *arg)
+{
+ for (; arg; arg = arg->next) {
+ switch (arg->type) {
+ case TEP_PRINT_BITMASK:
+ arg->type = TEP_PRINT_CPUMASK;
+ break;
+ case TEP_PRINT_OP:
+ tep_print_arg__set_bitmask_list(arg->op.left);
+ tep_print_arg__set_bitmask_list(arg->op.right);
+ break;
+ case TEP_PRINT_FUNC:
+ tep_print_arg__set_bitmask_list(arg->func.args);
+ break;
+ case TEP_PRINT_FLAGS:
+ tep_print_arg__set_bitmask_list(arg->flags.field);
+ break;
+ case TEP_PRINT_SYMBOL:
+ tep_print_arg__set_bitmask_list(arg->symbol.field);
+ break;
+ case TEP_PRINT_HEX:
+ tep_print_arg__set_bitmask_list(arg->hex.field);
+ tep_print_arg__set_bitmask_list(arg->hex.size);
+ break;
+ case TEP_PRINT_INT_ARRAY:
+ tep_print_arg__set_bitmask_list(arg->int_array.field);
+ tep_print_arg__set_bitmask_list(arg->int_array.count);
+ tep_print_arg__set_bitmask_list(arg->int_array.el_size);
+ break;
+ case TEP_PRINT_TYPE:
+ tep_print_arg__set_bitmask_list(arg->typecast.item);
+ break;
+ case TEP_PRINT_DYNAMIC_ARRAY:
+ tep_print_arg__set_bitmask_list(arg->dynarray.index);
+ break;
+ case TEP_PRINT_NULL:
+ case TEP_PRINT_ATOM:
+ case TEP_PRINT_FIELD:
+ case TEP_PRINT_STRING:
+ case TEP_PRINT_BSTRING:
+ case TEP_PRINT_DYNAMIC_ARRAY_LEN:
+ case TEP_PRINT_HEX_STR:
+ case TEP_PRINT_CPUMASK:
+ default:
+ break;
+ }
+ }
+}
+
+static void trace__init_tp_bitmask_list(struct trace *trace, struct evlist *evlist)
+{
+ struct evsel *evsel;
+
+ if (!trace->libtraceevent_print || !trace->bitmask_list)
+ return;
+
+ evlist__for_each_entry(evlist, evsel) {
+ struct tep_event *tp_format = evsel__tp_format(evsel);
+
+ if (tp_format)
+ tep_print_arg__set_bitmask_list(tp_format->print_fmt.args);
+ }
+}
+#else
+static void trace__init_tp_bitmask_list(struct trace *trace __maybe_unused,
+ struct evlist *evlist __maybe_unused)
+{
+}
+#endif
+
static int trace__replay(struct trace *trace)
{
const struct evsel_str_handler handlers[] = {
@@ -4908,6 +4979,8 @@ static int trace__replay(struct trace *trace)
if (err)
goto out;
+ trace__init_tp_bitmask_list(trace, session->evlist);
+
evsel = evlist__find_tracepoint_by_name(session->evlist, "raw_syscalls:sys_enter");
trace->syscalls.events.sys_enter = evsel;
/* older kernels have syscalls tp versus raw_syscalls */
@@ -5814,6 +5887,8 @@ int cmd_trace(int argc, const char **argv)
if (use_btf)
trace__load_vmlinux_btf(&trace);
+
+ trace__init_tp_bitmask_list(&trace, trace.evlist);
}
/*
--
2.55.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH] perf trace: Support --bitmask-list with --libtraceevent
2026-10-02 21:31 [PATCH] perf trace: Support --bitmask-list with --libtraceevent Aaron Tomlin
@ 2026-10-03 1:34 ` sashiko-bot
2026-10-04 12:27 ` Aaron Tomlin
0 siblings, 1 reply; 3+ messages in thread
From: sashiko-bot @ 2026-10-03 1:34 UTC (permalink / raw)
To: Aaron Tomlin; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Medium] perf trace: incomplete AST traversal for TEP_PRINT_HEX_STR nodes
- [Medium] perf trace: --bitmask-list broken for pipe mode streams
- [Low] perf trace: build failure on older libtraceevent due to missing TEP_PRINT_CPUMASK
- [Low] perf trace: future build failures due to -Wswitch-enum on external libtraceevent enum
--
commit f1dd0cf882822a7f067e6b6c6825d55f9ca3811c
Author: Aaron Tomlin <atomlin@atomlin.com>
perf trace: Support --bitmask-list with --libtraceevent
Commit ab74d1fbe061 ("perf trace: Add --bitmask-list command-line option")
introduced the --bitmask-list option to display cpumasks and bitmasks as
condensed, human-readable range lists (e.g. "0,2-5,7") instead of the
default hexadecimal format.
However, that implementation only augmented trace__fprintf_tp_fields().
When --libtraceevent is specified, event formatting is delegated to
event_format__fprintf(), which bypasses trace__fprintf_tp_fields() and
relies directly on libtraceevent.
> diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
> index c3c7f1f85c530..57249df65fe31 100644
> --- a/tools/perf/builtin-trace.c
> +++ b/tools/perf/builtin-trace.c
> @@ -4857,6 +4857,77 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
> goto out_put_evlist;
> }
>
> +#ifdef HAVE_LIBTRACEEVENT
> +static void tep_print_arg__set_bitmask_list(struct tep_print_arg *arg)
> +{
> + for (; arg; arg = arg->next) {
> + switch (arg->type) {
[Severity: Low]
Will this cause build failures on future libtraceevent updates?
The commit message mentions adding all enum values of tep_print_arg_type to
satisfy -Wswitch-enum. However, because tep_print_arg_type is an external
enum from libtraceevent, any future updates to libtraceevent that add new
values to this enum will break the perf build due to -Werror.
> + case TEP_PRINT_BITMASK:
> + arg->type = TEP_PRINT_CPUMASK;
[Severity: Low]
Can this cause a build failure on older libtraceevent versions?
If TEP_PRINT_CPUMASK is missing from the installed system libtraceevent
headers, this unconditional assignment will fail to compile.
> + break;
> + case TEP_PRINT_OP:
> + tep_print_arg__set_bitmask_list(arg->op.left);
> + tep_print_arg__set_bitmask_list(arg->op.right);
> + break;
> + case TEP_PRINT_FUNC:
> + tep_print_arg__set_bitmask_list(arg->func.args);
> + break;
> + case TEP_PRINT_FLAGS:
> + tep_print_arg__set_bitmask_list(arg->flags.field);
> + break;
> + case TEP_PRINT_SYMBOL:
> + tep_print_arg__set_bitmask_list(arg->symbol.field);
> + break;
> + case TEP_PRINT_HEX:
> + tep_print_arg__set_bitmask_list(arg->hex.field);
> + tep_print_arg__set_bitmask_list(arg->hex.size);
> + break;
> + case TEP_PRINT_INT_ARRAY:
> + tep_print_arg__set_bitmask_list(arg->int_array.field);
> + tep_print_arg__set_bitmask_list(arg->int_array.count);
> + tep_print_arg__set_bitmask_list(arg->int_array.el_size);
> + break;
> + case TEP_PRINT_TYPE:
> + tep_print_arg__set_bitmask_list(arg->typecast.item);
> + break;
> + case TEP_PRINT_DYNAMIC_ARRAY:
> + tep_print_arg__set_bitmask_list(arg->dynarray.index);
> + break;
> + case TEP_PRINT_NULL:
> + case TEP_PRINT_ATOM:
> + case TEP_PRINT_FIELD:
> + case TEP_PRINT_STRING:
> + case TEP_PRINT_BSTRING:
> + case TEP_PRINT_DYNAMIC_ARRAY_LEN:
> + case TEP_PRINT_HEX_STR:
[Severity: Medium]
Does this skip the AST traversal for TEP_PRINT_HEX_STR nodes?
The fallthrough here prevents the recursive traversal of arg->hex.field and
arg->hex.size for TEP_PRINT_HEX_STR nodes, which share the same structural
payload as TEP_PRINT_HEX. Nested bitmask evaluations inside a hex string
length or field argument could be silently ignored.
> + case TEP_PRINT_CPUMASK:
> + default:
> + break;
> + }
> + }
> +}
> +
[ ... ]
> @@ -4908,6 +4979,8 @@ static int trace__replay(struct trace *trace)
> if (err)
> goto out;
>
> + trace__init_tp_bitmask_list(trace, session->evlist);
[Severity: Medium]
Is --bitmask-list broken for pipe mode streams?
In pipe mode, the event list is populated dynamically from synthesized
events during perf_session__process_events(), which happens later in this
function. By calling trace__init_tp_bitmask_list() here, the initialization
operates on an empty evlist, skipping the AST transformation.
> +
> evsel = evlist__find_tracepoint_by_name(session->evlist, "raw_syscalls:sys_enter");
> trace->syscalls.events.sys_enter = evsel;
> /* older kernels have syscalls tp versus raw_syscalls */
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261002213145.333100-1-atomlin@atomlin.com?part=1
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] perf trace: Support --bitmask-list with --libtraceevent
2026-10-03 1:34 ` sashiko-bot
@ 2026-10-04 12:27 ` Aaron Tomlin
0 siblings, 0 replies; 3+ messages in thread
From: Aaron Tomlin @ 2026-10-04 12:27 UTC (permalink / raw)
To: sashiko-reviews, acme, namhyung, irogers; +Cc: linux-perf-users
On Sat, Oct 03, 2026 at 01:34:01AM +0000, sashiko-bot@kernel.org wrote:
> [Severity: Low]
> Will this cause build failures on future libtraceevent updates?
>
> The commit message mentions adding all enum values of tep_print_arg_type to
> satisfy -Wswitch-enum. However, because tep_print_arg_type is an external
> enum from libtraceevent, any future updates to libtraceevent that add new
> values to this enum will break the perf build due to -Werror.
Good point. Explicitly enumerating all external enum variants is fragile
against future upstream additions. In the next iteration, this is resolved
by casting arg->type explicitly to (int) in the switch evaluation
>
> > + case TEP_PRINT_BITMASK:
> > + arg->type = TEP_PRINT_CPUMASK;
>
> [Severity: Low]
> Can this cause a build failure on older libtraceevent versions?
>
> If TEP_PRINT_CPUMASK is missing from the installed system libtraceevent
> headers, this unconditional assignment will fail to compile.
TEP_PRINT_CPUMASK was introduced in libtraceevent 1.7.0, so building
against older versions without a version check breaks compilation.
In the next iteration, the transformation and helper are guarded with:
#if defined(HAVE_LIBTRACEEVENT) && \
defined(LIBTRACEEVENT_VERSION) && \
(MAKE_LIBTRACEEVENT_VERSION(1, 7, 0) <= LIBTRACEEVENT_VERSION)
>
> > + break;
> > + case TEP_PRINT_OP:
> > + tep_print_arg__set_bitmask_list(arg->op.left);
> > + tep_print_arg__set_bitmask_list(arg->op.right);
> > + break;
> > + case TEP_PRINT_FUNC:
> > + tep_print_arg__set_bitmask_list(arg->func.args);
> > + break;
> > + case TEP_PRINT_FLAGS:
> > + tep_print_arg__set_bitmask_list(arg->flags.field);
> > + break;
> > + case TEP_PRINT_SYMBOL:
> > + tep_print_arg__set_bitmask_list(arg->symbol.field);
> > + break;
> > + case TEP_PRINT_HEX:
> > + tep_print_arg__set_bitmask_list(arg->hex.field);
> > + tep_print_arg__set_bitmask_list(arg->hex.size);
> > + break;
> > + case TEP_PRINT_INT_ARRAY:
> > + tep_print_arg__set_bitmask_list(arg->int_array.field);
> > + tep_print_arg__set_bitmask_list(arg->int_array.count);
> > + tep_print_arg__set_bitmask_list(arg->int_array.el_size);
> > + break;
> > + case TEP_PRINT_TYPE:
> > + tep_print_arg__set_bitmask_list(arg->typecast.item);
> > + break;
> > + case TEP_PRINT_DYNAMIC_ARRAY:
> > + tep_print_arg__set_bitmask_list(arg->dynarray.index);
> > + break;
> > + case TEP_PRINT_NULL:
> > + case TEP_PRINT_ATOM:
> > + case TEP_PRINT_FIELD:
> > + case TEP_PRINT_STRING:
> > + case TEP_PRINT_BSTRING:
> > + case TEP_PRINT_DYNAMIC_ARRAY_LEN:
> > + case TEP_PRINT_HEX_STR:
>
> [Severity: Medium]
> Does this skip the AST traversal for TEP_PRINT_HEX_STR nodes?
>
> The fallthrough here prevents the recursive traversal of arg->hex.field and
> arg->hex.size for TEP_PRINT_HEX_STR nodes, which share the same structural
> payload as TEP_PRINT_HEX. Nested bitmask evaluations inside a hex string
> length or field argument could be silently ignored.
TEP_PRINT_HEX_STR shares the identical struct tep_print_arg_hex payload
(arg->hex.field and arg->hex.size) with TEP_PRINT_HEX. The next iteration,
TEP_PRINT_HEX_STR is grouped together with TEP_PRINT_HEX:
case TEP_PRINT_HEX:
case TEP_PRINT_HEX_STR:
tep_print_arg__set_bitmask_list(arg->hex.field);
tep_print_arg__set_bitmask_list(arg->hex.size);
break;
>
> > + case TEP_PRINT_CPUMASK:
> > + default:
> > + break;
> > + }
> > + }
> > +}
> > +
>
> [ ... ]
>
> > @@ -4908,6 +4979,8 @@ static int trace__replay(struct trace *trace)
> > if (err)
> > goto out;
> >
> > + trace__init_tp_bitmask_list(trace, session->evlist);
>
> [Severity: Medium]
> Is --bitmask-list broken for pipe mode streams?
>
> In pipe mode, the event list is populated dynamically from synthesized
> events during perf_session__process_events(), which happens later in this
> function. By calling trace__init_tp_bitmask_list() here, the initialization
> operates on an empty evlist, skipping the AST transformation.
In perf trace, file replay (trace__replay()) expects to read from a
recorded perf.data file where the session header (including event
descriptions and tracepoint formats) is read upfront by
perf_session__new(). Existing tracepoint setup in trace__replay() already
relies on session->evlist being populated prior to
perf_session__process_events() e.g. evlist__find_tracepoint_by_name() for
raw_syscalls and syscalls.
Doing this transformation once during tracepoint setup avoids adding
per-event checks or lookups to trace__event_handler() in the sample
delivery hot path. The commit message has been updated to explicitly state
that, as with existing tracepoint replay in perf trace, this expects the
evlist to be populated from the session header.
Kind regards,
--
Aaron Tomlin
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-10-04 12:27 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-02 21:31 [PATCH] perf trace: Support --bitmask-list with --libtraceevent Aaron Tomlin
2026-10-03 1:34 ` sashiko-bot
2026-10-04 12:27 ` Aaron Tomlin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox