Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: 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>

authored by

Ian Rogers and committed by
Namhyung Kim
6183afcb 64ec9b99

+44 -3
+44 -3
tools/perf/util/python.c
··· 10 10 #endif 11 11 #include <perf/mmap.h> 12 12 #include "callchain.h" 13 + #include "counts.h" 13 14 #include "evlist.h" 14 15 #include "evsel.h" 15 16 #include "event.h" ··· 890 889 return (PyObject *)pthread_map; 891 890 } 892 891 892 + /* 893 + * Ensure evsel's counts and prev_raw_counts are allocated, the latter 894 + * used by tool PMUs to compute the cumulative count as expected by 895 + * stat's process_counter_values. 896 + */ 897 + static int evsel__ensure_counts(struct evsel *evsel) 898 + { 899 + int nthreads, ncpus; 900 + 901 + if (evsel->counts != NULL) 902 + return 0; 903 + 904 + nthreads = perf_thread_map__nr(evsel->core.threads); 905 + ncpus = perf_cpu_map__nr(evsel->core.cpus); 906 + 907 + evsel->counts = perf_counts__new(ncpus, nthreads); 908 + if (evsel->counts == NULL) 909 + return -ENOMEM; 910 + 911 + evsel->prev_raw_counts = perf_counts__new(ncpus, nthreads); 912 + if (evsel->prev_raw_counts == NULL) 913 + return -ENOMEM; 914 + 915 + return 0; 916 + } 917 + 893 918 static PyObject *pyrf_evsel__read(struct pyrf_evsel *pevsel, 894 919 PyObject *args, PyObject *kwargs) 895 920 { 896 921 struct evsel *evsel = &pevsel->evsel; 897 922 int cpu = 0, cpu_idx, thread = 0, thread_idx; 898 - struct perf_counts_values counts; 923 + struct perf_counts_values *old_count, *new_count; 899 924 struct pyrf_counts_values *count_values = PyObject_New(struct pyrf_counts_values, 900 925 &pyrf_counts_values__type); 901 926 ··· 942 915 thread); 943 916 return NULL; 944 917 } 945 - perf_evsel__read(&(evsel->core), cpu_idx, thread_idx, &counts); 946 - count_values->values = counts; 918 + 919 + if (evsel__ensure_counts(evsel)) 920 + return PyErr_NoMemory(); 921 + 922 + /* Set up pointers to the old and newly read counter values. */ 923 + old_count = perf_counts(evsel->prev_raw_counts, cpu_idx, thread_idx); 924 + new_count = perf_counts(evsel->counts, cpu_idx, thread_idx); 925 + /* Update the value in evsel->counts. */ 926 + evsel__read_counter(evsel, cpu_idx, thread_idx); 927 + /* Copy the value and turn it into the delta from old_count. */ 928 + count_values->values = *new_count; 929 + count_values->values.val -= old_count->val; 930 + count_values->values.ena -= old_count->ena; 931 + count_values->values.run -= old_count->run; 932 + /* Save the new count over the old_count for the next read. */ 933 + *old_count = *new_count; 947 934 return (PyObject *)count_values; 948 935 } 949 936