perf python: Correct pyrf_evsel__read for tool PMUs

Tool PMUs assume that stat's process_counter_values is being used to
read the counters. Specifically they hold onto old values in
evsel->prev_raw_counts and give the cumulative count based off of this
value. Update pyrf_evsel__read to allocate counts and prev_raw_counts,
use evsel__read_counter rather than perf_evsel__read so tool PMUs are
read from not just perf_event_open events, make the returned
pyrf_counts_values contain the delta value rather than the cumulative
value.

Fixes: 739621f657 ("perf python: Add evsel read method")
Signed-off-by: Ian Rogers <irogers@google.com>
Link: https://lore.kernel.org/r/20250710235126.1086011-12-irogers@google.com
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
This commit is contained in:
Ian Rogers
2025-07-10 16:51:24 -07:00
committed by Namhyung Kim
parent 64ec9b997f
commit 6183afcba9

View File

@@ -10,6 +10,7 @@
#endif
#include <perf/mmap.h>
#include "callchain.h"
#include "counts.h"
#include "evlist.h"
#include "evsel.h"
#include "event.h"
@@ -889,12 +890,38 @@ static PyObject *pyrf_evsel__threads(struct pyrf_evsel *pevsel)
return (PyObject *)pthread_map;
}
/*
* Ensure evsel's counts and prev_raw_counts are allocated, the latter
* used by tool PMUs to compute the cumulative count as expected by
* stat's process_counter_values.
*/
static int evsel__ensure_counts(struct evsel *evsel)
{
int nthreads, ncpus;
if (evsel->counts != NULL)
return 0;
nthreads = perf_thread_map__nr(evsel->core.threads);
ncpus = perf_cpu_map__nr(evsel->core.cpus);
evsel->counts = perf_counts__new(ncpus, nthreads);
if (evsel->counts == NULL)
return -ENOMEM;
evsel->prev_raw_counts = perf_counts__new(ncpus, nthreads);
if (evsel->prev_raw_counts == NULL)
return -ENOMEM;
return 0;
}
static PyObject *pyrf_evsel__read(struct pyrf_evsel *pevsel,
PyObject *args, PyObject *kwargs)
{
struct evsel *evsel = &pevsel->evsel;
int cpu = 0, cpu_idx, thread = 0, thread_idx;
struct perf_counts_values counts;
struct perf_counts_values *old_count, *new_count;
struct pyrf_counts_values *count_values = PyObject_New(struct pyrf_counts_values,
&pyrf_counts_values__type);
@@ -915,8 +942,22 @@ static PyObject *pyrf_evsel__read(struct pyrf_evsel *pevsel,
thread);
return NULL;
}
perf_evsel__read(&(evsel->core), cpu_idx, thread_idx, &counts);
count_values->values = counts;
if (evsel__ensure_counts(evsel))
return PyErr_NoMemory();
/* Set up pointers to the old and newly read counter values. */
old_count = perf_counts(evsel->prev_raw_counts, cpu_idx, thread_idx);
new_count = perf_counts(evsel->counts, cpu_idx, thread_idx);
/* Update the value in evsel->counts. */
evsel__read_counter(evsel, cpu_idx, thread_idx);
/* Copy the value and turn it into the delta from old_count. */
count_values->values = *new_count;
count_values->values.val -= old_count->val;
count_values->values.ena -= old_count->ena;
count_values->values.run -= old_count->run;
/* Save the new count over the old_count for the next read. */
*old_count = *new_count;
return (PyObject *)count_values;
}