Re: [PATCH v6 13/59] perf python: Use evsel in sample in pyrf_event

sashiko-bot
2026-04-25T19:06:33Z

Sashiko AI review found 2 potential issue(s):
- [High] Python bindings leak the `evsel` reference for all non-SAMPLE event types upon garbage collection.
- [High] Reassigning `sample.evsel` without dropping the existing reference causes an evsel reference leak for deferred callchains.
--

commit 7853ce0ab6089ce39f4b73ee696251d881df5b58
Author: Ian Rogers <irogers@google.com>

perf python: Use evsel in sample in pyrf_event

Avoid a duplicated evsel by using the one in sample. Add evsel__get/put to
the evsel in perf_sample.

> diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
> index 3015b9b4b4da..9b16d832810f 100644
> --- a/tools/perf/util/evsel.c
> +++ b/tools/perf/util/evsel.c
> @@ -3235,7 +3235,7 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event,
> union u64_swap u;
>
> perf_sample__init(data, /*all=*/true);
> - data->evsel = evsel;
> + data->evsel = evsel__get(evsel);

Will this leak the evsel reference for non-SAMPLE event types in the Python
bindings?

In tools/perf/util/python.c, pyrf_evlist__read_on_cpu() calls
evsel__parse_sample() for all event types read from the ring buffer. This now
unconditionally acquires an evsel reference.

However, pyrf_event__setup_types() only registers the custom destructor
pyrf_sample_event__delete() (which calls perf_sample__exit() to drop the
reference) for pyrf_sample_event__type. Other event types fall back to the
default deallocator, bypassing perf_sample__exit(). When these objects are
garbage collected, the evsel reference appears to be permanently leaked.

I see this regression was fixed later in the series in commit 1daefbea296a47c
("perf python: Refactor and add accessors to sample event"), but wanted
to confirm the behavior introduced here.

Could this also cause an evsel reference leak for deferred callchains?

In tools/perf/util/session.c, both evlist__deliver_deferred_callchain() and
session__flush_deferred_samples() process deferred samples by calling
evlist__parse_sample(), which internally calls evsel__parse_sample(),
acquiring the reference here.

Immediately after, both functions execute:

sample.evsel = evlist__id2evsel(evlist, sample.id);
if (sample.evsel)
sample.evsel = evsel__get(sample.evsel);

Does this overwrite the non-NULL sample.evsel pointer without dropping the
original reference, and then increment the reference count a second time?

If so, perf_sample__exit() will only drop one reference, permanently leaking
the first one for every deferred sample.

> data->cpu = data->pid = data->tid = -1;
> data->stream_id = data->id = data->time = -1ULL;
> data->period = evsel->core.attr.sample_period;

--
Sashiko AI review · https://sashiko.dev/#/patchset/20260425174858.3922152-1-irogers@google.com?part=13