[PATCH v4 02/18] perf trace: Free the whole evsel_trace in evsel__put_and_free_priv
From: Ian Rogers
Date: Fri Sep 18 2026 - 17:20:44 EST
Every evsel->priv in builtin-trace.c is a struct evsel_trace, allocated
by evsel_trace__new(). It holds a syscall_arg_fmt array in its fmt
member, which evsel__syscall_arg_fmt() allocates on demand for the
syscalls:sys_{enter,exit}_SYSCALL tracepoints and for every other
tracepoint that gets its arguments pretty printed.
evsel__put_and_free_priv() only did zfree(&evsel->priv), releasing the
evsel_trace itself and leaking that array. Use evsel_trace__delete(),
which frees fmt first, exactly as the out_delete path of
evsel__syscall_arg_fmt() already does.
The current callers are all error paths that run before fmt can have
been allocated, so nothing leaks in practice today, but the helper is
the obvious thing to reach for whenever an evsel is discarded and it
should be safe for that.
Assisted-by: Antigravity:gemini-3.1-pro
Signed-off-by: Ian Rogers <irogers@xxxxxxxxxx>
---
tools/perf/builtin-trace.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 20fffc24507b..f67557e7a254 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -464,7 +464,13 @@ static int evsel__init_tp_ptr_field(struct evsel *evsel, struct tp_field *field,
static void evsel__put_and_free_priv(struct evsel *evsel)
{
- zfree(&evsel->priv);
+ /*
+ * evsel->priv is always a struct evsel_trace here, so it has to go
+ * through evsel_trace__delete(): zfree() on its own would release the
+ * struct while leaking the syscall_arg_fmt array hanging off it.
+ */
+ evsel_trace__delete(evsel->priv);
+ evsel->priv = NULL;
evsel__put(evsel);
}
--
2.55.0.1082.g2b9226bbc0-goog