]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
perf python: Correct pyrf_evsel__read for tool PMUs
authorIan Rogers <irogers@google.com>
Thu, 10 Jul 2025 23:51:24 +0000 (16:51 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 15 Aug 2025 14:39:07 +0000 (16:39 +0200)
[ Upstream commit 6183afcba9c1c810656ddb36170106aaf3cf778c ]

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: 739621f65702 ("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>
Signed-off-by: Sasha Levin <sashal@kernel.org>
tools/perf/util/python.c

index eb560e3f9e3510b79a0795306bf6ccd74896ef69..b9fe7f2c14af6802e86a7c1ef25af79da3aff1b0 100644 (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"
@@ -888,12 +889,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);
 
@@ -914,8 +941,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;
 }