From 5128492b2b6bb3a2881e135da54fd8e224a5f610 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 4 Jun 2025 10:45:44 -0700 Subject: perf thread_map: Remove uid options Now the target doesn't have a uid, it is handled through BPF filters, remove the uid options to thread_map creation. Tidy up the functions used in tests to avoid passing unused arguments. Signed-off-by: Ian Rogers Link: https://lore.kernel.org/r/20250604174545.2853620-11-irogers@google.com Signed-off-by: Namhyung Kim --- tools/perf/util/python.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'tools/perf/util/python.c') diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 321c333877fa..82666bcd2eda 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -566,14 +566,14 @@ struct pyrf_thread_map { static int pyrf_thread_map__init(struct pyrf_thread_map *pthreads, PyObject *args, PyObject *kwargs) { - static char *kwlist[] = { "pid", "tid", "uid", NULL }; - int pid = -1, tid = -1, uid = UINT_MAX; + static char *kwlist[] = { "pid", "tid", NULL }; + int pid = -1, tid = -1; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|iii", - kwlist, &pid, &tid, &uid)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", + kwlist, &pid, &tid)) return -1; - pthreads->threads = thread_map__new(pid, tid, uid); + pthreads->threads = thread_map__new(pid, tid); if (pthreads->threads == NULL) return -1; return 0; -- cgit From faebee18d720d9e209946ece3e468c06cf13f5ec Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 10 Jul 2025 16:51:19 -0700 Subject: perf stat: Move metric list from config to evlist The rblist of metric_event that then have a list of associated metric_expr is moved out of the stat_config and into the evlist. This is done as part of refactoring things for python, having the state split in two places complicates that implementation. The evlist is doing the harder work of enabling and disabling events, the metrics are needed to compute a value and it doesn't seem unreasonable to hang them from the evlist. Signed-off-by: Ian Rogers Link: https://lore.kernel.org/r/20250710235126.1086011-7-irogers@google.com Signed-off-by: Namhyung Kim --- tools/perf/util/python.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'tools/perf/util/python.c') diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 82666bcd2eda..b5ee9f7a4662 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -18,6 +18,7 @@ #include "strbuf.h" #include "thread_map.h" #include "trace-event.h" +#include "metricgroup.h" #include "mmap.h" #include "util/sample.h" #include @@ -1544,6 +1545,9 @@ static PyObject *pyrf_evlist__from_evlist(struct evlist *evlist) evlist__add(&pevlist->evlist, &pevsel->evsel); } + metricgroup__copy_metric_events(&pevlist->evlist, /*cgrp=*/NULL, + &pevlist->evlist.metric_events, + &evlist->metric_events); return (PyObject *)pevlist; } -- cgit From 7d5b635d9f4314c93bc1f9828f5d757decb860bc Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 10 Jul 2025 16:51:22 -0700 Subject: perf python: In str(evsel) use the evsel__pmu_name helper The evsel__pmu_name helper will internally use evsel__find_pmu that handles legacy events, extended types, etc. in determining a PMU and will provide a better value than just trying to access the PMU's name directly as the PMU may not have been computed. Signed-off-by: Ian Rogers Link: https://lore.kernel.org/r/20250710235126.1086011-10-irogers@google.com Signed-off-by: Namhyung Kim --- tools/perf/util/python.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'tools/perf/util/python.c') diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index b5ee9f7a4662..0821205b1aaa 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -925,10 +925,7 @@ static PyObject *pyrf_evsel__str(PyObject *self) struct pyrf_evsel *pevsel = (void *)self; struct evsel *evsel = &pevsel->evsel; - if (!evsel->pmu) - return PyUnicode_FromFormat("evsel(%s)", evsel__name(evsel)); - - return PyUnicode_FromFormat("evsel(%s/%s/)", evsel->pmu->name, evsel__name(evsel)); + return PyUnicode_FromFormat("evsel(%s/%s/)", evsel__pmu_name(evsel), evsel__name(evsel)); } static PyMethodDef pyrf_evsel__methods[] = { -- cgit From 64ec9b997f3a9462901a404ad60f452f76dd2d6e Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 10 Jul 2025 16:51:23 -0700 Subject: perf python: Fix thread check in pyrf_evsel__read The CPU index is incorrectly checked rather than the thread index. Fixes: 739621f65702 ("perf python: Add evsel read method") Signed-off-by: Ian Rogers Link: https://lore.kernel.org/r/20250710235126.1086011-11-irogers@google.com Signed-off-by: Namhyung Kim --- tools/perf/util/python.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf/util/python.c') diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 0821205b1aaa..4a3c2b4dd79f 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -910,7 +910,7 @@ static PyObject *pyrf_evsel__read(struct pyrf_evsel *pevsel, return NULL; } thread_idx = perf_thread_map__idx(evsel->core.threads, thread); - if (cpu_idx < 0) { + if (thread_idx < 0) { PyErr_Format(PyExc_TypeError, "Thread %d is not part of evsel's threads", thread); return NULL; -- cgit From 6183afcba9c1c810656ddb36170106aaf3cf778c Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 10 Jul 2025 16:51:24 -0700 Subject: 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 Link: https://lore.kernel.org/r/20250710235126.1086011-12-irogers@google.com Signed-off-by: Namhyung Kim --- tools/perf/util/python.c | 47 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) (limited to 'tools/perf/util/python.c') diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 4a3c2b4dd79f..f689560192f4 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -10,6 +10,7 @@ #endif #include #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; } -- cgit From 421c5f39adcdf292ca5c7162f40ed6d120d136a8 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 10 Jul 2025 16:51:25 -0700 Subject: perf python: Improve leader copying from evlist The struct pyrf_evlist embeds the evlist requiring the copying from things like parsed events. The copying logic handles the leader being the event itself, but if the leader group event is a different in the list it will cause an evsel to point to the evsel in the list that was copied from which is bad. Fix this by adding another pass over the evlist rewriting leaders, simplified by the introductin of two evlist helpers. Signed-off-by: Ian Rogers Link: https://lore.kernel.org/r/20250710235126.1086011-13-irogers@google.com Signed-off-by: Namhyung Kim --- tools/perf/util/python.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) (limited to 'tools/perf/util/python.c') diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index f689560192f4..1d9fa33d377a 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -1568,10 +1568,37 @@ static PyObject *pyrf_evsel__from_evsel(struct evsel *evsel) return (PyObject *)pevsel; } +static int evlist__pos(struct evlist *evlist, struct evsel *evsel) +{ + struct evsel *pos; + int idx = 0; + + evlist__for_each_entry(evlist, pos) { + if (evsel == pos) + return idx; + idx++; + } + return -1; +} + +static struct evsel *evlist__at(struct evlist *evlist, int idx) +{ + struct evsel *pos; + int idx2 = 0; + + evlist__for_each_entry(evlist, pos) { + if (idx == idx2) + return pos; + idx2++; + } + return NULL; +} + static PyObject *pyrf_evlist__from_evlist(struct evlist *evlist) { struct pyrf_evlist *pevlist = PyObject_New(struct pyrf_evlist, &pyrf_evlist__type); struct evsel *pos; + struct rb_node *node; if (!pevlist) return NULL; @@ -1583,9 +1610,39 @@ static PyObject *pyrf_evlist__from_evlist(struct evlist *evlist) evlist__add(&pevlist->evlist, &pevsel->evsel); } + evlist__for_each_entry(&pevlist->evlist, pos) { + struct evsel *leader = evsel__leader(pos); + + if (pos != leader) { + int idx = evlist__pos(evlist, leader); + + if (idx >= 0) + evsel__set_leader(pos, evlist__at(&pevlist->evlist, idx)); + else if (leader == NULL) + evsel__set_leader(pos, pos); + } + } metricgroup__copy_metric_events(&pevlist->evlist, /*cgrp=*/NULL, &pevlist->evlist.metric_events, &evlist->metric_events); + for (node = rb_first_cached(&pevlist->evlist.metric_events.entries); node; + node = rb_next(node)) { + struct metric_event *me = container_of(node, struct metric_event, nd); + struct list_head *mpos; + int idx = evlist__pos(evlist, me->evsel); + + if (idx >= 0) + me->evsel = evlist__at(&pevlist->evlist, idx); + list_for_each(mpos, &me->head) { + struct metric_expr *e = container_of(mpos, struct metric_expr, nd); + + for (int j = 0; e->metric_events[j]; j++) { + idx = evlist__pos(evlist, e->metric_events[j]); + if (idx >= 0) + e->metric_events[j] = evlist__at(&pevlist->evlist, idx); + } + } + } return (PyObject *)pevlist; } -- cgit From b4aff7ed7a4c1360e8b29d545c7bc9e05af1a995 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 10 Jul 2025 16:51:26 -0700 Subject: perf python: Set index error for invalid thread/cpu map items Returning NULL for out of bound CPU or thread map items causes internal errors. Fix by correctly setting the error to be an index error. Signed-off-by: Ian Rogers Link: https://lore.kernel.org/r/20250710235126.1086011-14-irogers@google.com Signed-off-by: Namhyung Kim --- tools/perf/util/python.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'tools/perf/util/python.c') diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 1d9fa33d377a..2f28f71325a8 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -529,8 +529,10 @@ static PyObject *pyrf_cpu_map__item(PyObject *obj, Py_ssize_t i) { struct pyrf_cpu_map *pcpus = (void *)obj; - if (i >= perf_cpu_map__nr(pcpus->cpus)) + if (i >= perf_cpu_map__nr(pcpus->cpus)) { + PyErr_SetString(PyExc_IndexError, "Index out of range"); return NULL; + } return Py_BuildValue("i", perf_cpu_map__cpu(pcpus->cpus, i).cpu); } @@ -598,8 +600,10 @@ static PyObject *pyrf_thread_map__item(PyObject *obj, Py_ssize_t i) { struct pyrf_thread_map *pthreads = (void *)obj; - if (i >= perf_thread_map__nr(pthreads->threads)) + if (i >= perf_thread_map__nr(pthreads->threads)) { + PyErr_SetString(PyExc_IndexError, "Index out of range"); return NULL; + } return Py_BuildValue("i", perf_thread_map__pid(pthreads->threads, i)); } -- cgit From 59edbec7a5c70af6c0058e32eb3750bfb8928d7b Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 30 Jul 2025 10:34:20 -0300 Subject: perf python: Stop using deprecated PyUnicode_AsString() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As noticed while building for Fedora 43: GEN /tmp/build/perf/python/perf.cpython-314-x86_64-linux-gnu.so /git/perf-6.16.0-rc3/tools/perf/util/python.c: In function ‘get_tracepoint_field’: /git/perf-6.16.0-rc3/tools/perf/util/python.c:340:9: error: ‘_PyUnicode_AsString’ is deprecated [-Werror=deprecated-declarations] 340 | const char *str = _PyUnicode_AsString(PyObject_Str(attr_name)); | ^~~~~ In file included from /usr/include/python3.14/unicodeobject.h:1022, from /usr/include/python3.14/Python.h:89, from /git/perf-6.16.0-rc3/tools/perf/util/python.c:2: /usr/include/python3.14/cpython/unicodeobject.h:648:1: note: declared here 648 | _PyUnicode_AsString(PyObject *unicode) | ^~~~~~~~~~~~~~~~~~~ cc1: all warnings being treated as errors error: command '/usr/bin/gcc' failed with exit code 1 Use PyUnicode_AsUTF8() instead and also check if PyObject_Str() fails before doing so. Signed-off-by: Arnaldo Carvalho de Melo Link: https://lore.kernel.org/r/aIofXNK8QLtLIaI3@x1 Signed-off-by: Namhyung Kim --- tools/perf/util/python.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'tools/perf/util/python.c') diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 2f28f71325a8..ea77bea0306f 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -337,7 +337,6 @@ tracepoint_field(const struct pyrf_event *pe, struct tep_format_field *field) static PyObject* get_tracepoint_field(struct pyrf_event *pevent, PyObject *attr_name) { - const char *str = _PyUnicode_AsString(PyObject_Str(attr_name)); struct evsel *evsel = pevent->evsel; struct tep_event *tp_format = evsel__tp_format(evsel); struct tep_format_field *field; @@ -345,7 +344,18 @@ get_tracepoint_field(struct pyrf_event *pevent, PyObject *attr_name) if (IS_ERR_OR_NULL(tp_format)) return NULL; + PyObject *obj = PyObject_Str(attr_name); + if (obj == NULL) + return NULL; + + const char *str = PyUnicode_AsUTF8(obj); + if (str == NULL) { + Py_DECREF(obj); + return NULL; + } + field = tep_find_any_field(tp_format, str); + Py_DECREF(obj); return field ? tracepoint_field(pevent, field) : NULL; } #endif /* HAVE_LIBTRACEEVENT */ -- cgit