From 5a3d7afdfb6c430e1377d779df4e16ec56e22e32 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 17 Sep 2026 23:32:47 -0700 Subject: [PATCH] perf trace-event: Free the global trace_event when a command ends The tep handle behind trace_event__tp_format() is a process wide cache of the running kernel's tracepoint formats and nothing ever released it, as the comment above it had noted since the code was added. Add trace_event__exit() and call it from run_builtin() next to the existing perf_config__exit(). By then the builtin has returned, so the tep_event pointers it handed out, such as the ones cached in evsel->tp_format, are no longer reachable. Note that this does not silence every libtraceevent report under leak sanitizer. Parsing a print fmt drops a token inside the library itself, and as those allocations are not linked into the handle, freeing the handle cannot reclaim them. Assisted-by: Antigravity:gemini-3.1-pro Signed-off-by: Ian Rogers Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/perf.c | 6 ++++++ tools/perf/util/trace-event.c | 22 +++++++++++++++++----- tools/perf/util/trace-event.h | 1 + 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 6c5baa285b13..d60bc929c257 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -32,6 +32,9 @@ #include "util/build-id.h" #include "util/config.h" #include "util/debug.h" +#ifdef HAVE_LIBTRACEEVENT +#include "util/trace-event.h" +#endif const char perf_usage_string[] = "perf [--version] [--help] [OPTIONS] COMMAND [ARGS]"; @@ -379,6 +382,9 @@ static int run_builtin(const struct cmd_struct *p, int argc, const char **argv) status = p->fn(argc, argv); perf_config__exit(); +#ifdef HAVE_LIBTRACEEVENT + trace_event__exit(); +#endif exit_browser(status); if (status) diff --git a/tools/perf/util/trace-event.c b/tools/perf/util/trace-event.c index 826f75464171..819542a73f55 100644 --- a/tools/perf/util/trace-event.c +++ b/tools/perf/util/trace-event.c @@ -14,11 +14,9 @@ #include "machine.h" /* - * global trace_event object used by trace_event__tp_format - * - * TODO There's no cleanup call for this. Add some sort of - * __exit function support and call trace_event__cleanup - * there. + * Global trace_event object used by trace_event__tp_format. It caches the + * tracepoint formats of the running kernel for the lifetime of the command + * and is released by trace_event__exit. */ static struct trace_event tevent; static bool tevent_initialized; @@ -75,6 +73,20 @@ void trace_event__cleanup(struct trace_event *t) t->plugin_list = NULL; } +/* + * Release the global trace_event. Called once the command is done, when the + * tep_event pointers handed out by trace_event__tp_format are no longer in + * use. + */ +void trace_event__exit(void) +{ + if (!tevent_initialized) + return; + + trace_event__cleanup(&tevent); + tevent_initialized = false; +} + /* * Returns NULL and sets errno on failure. */ diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index 720121c74f1d..1c342fce36bb 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -32,6 +32,7 @@ bool have_tracepoints(struct list_head *evlist); int trace_event__init(struct trace_event *t); void trace_event__cleanup(struct trace_event *t); +void trace_event__exit(void); int trace_event__register_resolver(struct machine *machine, tep_func_resolver_t *func); struct tep_event*