From dc6d2bc2d893a878e7b58578ff01b4738708deb4 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 13 Jan 2025 11:43:45 -0800 Subject: perf sample: Make user_regs and intr_regs optional The struct dump_regs contains 512 bytes of cache_regs, meaning the two values in perf_sample contribute 1088 bytes of its total 1384 bytes size. Initializing this much memory has a cost reported by Tavian Barnes as about 2.5% when running `perf script --itrace=i0`: https://lore.kernel.org/lkml/d841b97b3ad2ca8bcab07e4293375fb7c32dfce7.1736618095.git.tavianator@tavianator.com/ Adrian Hunter replied that the zero initialization was necessary and couldn't simply be removed. This patch aims to strike a middle ground of still zeroing the perf_sample, but removing 79% of its size by make user_regs and intr_regs optional pointers to zalloc-ed memory. To support the allocation accessors are created for user_regs and intr_regs. To support correct cleanup perf_sample__init and perf_sample__exit functions are created and added throughout the code base. Signed-off-by: Ian Rogers Link: https://lore.kernel.org/r/20250113194345.1537821-1-irogers@google.com Signed-off-by: Namhyung Kim --- tools/perf/util/python.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'tools/perf/util/python.c') diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index b4bc57859f73..e2b9032c1311 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -270,6 +270,12 @@ static PyMemberDef pyrf_sample_event__members[] = { { .name = NULL, }, }; +static void pyrf_sample_event__delete(struct pyrf_event *pevent) +{ + perf_sample__exit(&pevent->sample); + Py_TYPE(pevent)->tp_free((PyObject*)pevent); +} + static PyObject *pyrf_sample_event__repr(const struct pyrf_event *pevent) { PyObject *ret; @@ -428,6 +434,9 @@ static int pyrf_event__setup_types(void) pyrf_sample_event__type.tp_new = pyrf_context_switch_event__type.tp_new = pyrf_throttle_event__type.tp_new = PyType_GenericNew; + + pyrf_sample_event__type.tp_dealloc = (destructor)pyrf_sample_event__delete, + err = PyType_Ready(&pyrf_mmap_event__type); if (err < 0) goto out; -- cgit From d8e17677799c7e30e37ac0ccb523516322ebad20 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 28 Feb 2025 14:23:01 -0800 Subject: perf python: Add evlist enable and disable methods By default the evsels from parse_events will be disabled. Add access to the evlist functions so they can be enabled/disabled. Reviewed-by: Howard Chu Signed-off-by: Ian Rogers Reviewed-by: Arnaldo Carvalho de Melo Link: https://lore.kernel.org/r/20250228222308.626803-5-irogers@google.com Signed-off-by: Namhyung Kim --- tools/perf/util/python.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'tools/perf/util/python.c') diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index e2b9032c1311..0cf81cfcfafb 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -1028,6 +1028,20 @@ static PyObject *pyrf_evlist__open(struct pyrf_evlist *pevlist, return Py_None; } +static PyObject *pyrf_evlist__disable(struct pyrf_evlist *pevlist) +{ + evlist__disable(&pevlist->evlist); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *pyrf_evlist__enable(struct pyrf_evlist *pevlist) +{ + evlist__enable(&pevlist->evlist); + Py_INCREF(Py_None); + return Py_None; +} + static PyMethodDef pyrf_evlist__methods[] = { { .ml_name = "mmap", @@ -1065,6 +1079,18 @@ static PyMethodDef pyrf_evlist__methods[] = { .ml_flags = METH_VARARGS | METH_KEYWORDS, .ml_doc = PyDoc_STR("reads an event.") }, + { + .ml_name = "disable", + .ml_meth = (PyCFunction)pyrf_evlist__disable, + .ml_flags = METH_NOARGS, + .ml_doc = PyDoc_STR("Disable the evsels in the evlist.") + }, + { + .ml_name = "enable", + .ml_meth = (PyCFunction)pyrf_evlist__enable, + .ml_flags = METH_NOARGS, + .ml_doc = PyDoc_STR("Enable the evsels in the evlist.") + }, { .ml_name = NULL, } }; -- cgit From cc8bf352dd1c1700b56cbe8b0151ed7b6364f68b Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 28 Feb 2025 14:23:02 -0800 Subject: perf python: Add member access to a number of evsel variables Most variables are part of the perf_event_attr, so that they may be queried and modified. Reviewed-by: Howard Chu Signed-off-by: Ian Rogers Acked-by: Arnaldo Carvalho de Melo Link: https://lore.kernel.org/r/20250228222308.626803-6-irogers@google.com Signed-off-by: Namhyung Kim --- tools/perf/util/python.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'tools/perf/util/python.c') diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 0cf81cfcfafb..b600b6379b4e 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -811,6 +811,28 @@ static PyMethodDef pyrf_evsel__methods[] = { { .ml_name = NULL, } }; +#define evsel_member_def(member, ptype, help) \ + { #member, ptype, \ + offsetof(struct pyrf_evsel, evsel.member), \ + 0, help } + +#define evsel_attr_member_def(member, ptype, help) \ + { #member, ptype, \ + offsetof(struct pyrf_evsel, evsel.core.attr.member), \ + 0, help } + +static PyMemberDef pyrf_evsel__members[] = { + evsel_member_def(tracking, T_BOOL, "tracking event."), + evsel_attr_member_def(type, T_UINT, "attribute type."), + evsel_attr_member_def(size, T_UINT, "attribute size."), + evsel_attr_member_def(config, T_ULONGLONG, "attribute config."), + evsel_attr_member_def(sample_period, T_ULONGLONG, "attribute sample_period."), + evsel_attr_member_def(sample_type, T_ULONGLONG, "attribute sample_type."), + evsel_attr_member_def(read_format, T_ULONGLONG, "attribute read_format."), + evsel_attr_member_def(wakeup_events, T_UINT, "attribute wakeup_events."), + { .name = NULL, }, +}; + static const char pyrf_evsel__doc[] = PyDoc_STR("perf event selector list object."); static PyTypeObject pyrf_evsel__type = { @@ -820,6 +842,7 @@ static PyTypeObject pyrf_evsel__type = { .tp_dealloc = (destructor)pyrf_evsel__delete, .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, .tp_doc = pyrf_evsel__doc, + .tp_members = pyrf_evsel__members, .tp_methods = pyrf_evsel__methods, .tp_init = (initproc)pyrf_evsel__init, .tp_str = pyrf_evsel__str, -- cgit From 6c62403b5ad4511090d2f3ffd00d4cc6fc608480 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 28 Feb 2025 14:23:03 -0800 Subject: perf python: Add optional cpus and threads arguments to parse_events Used for the evlist initialization. Reviewed-by: Howard Chu Signed-off-by: Ian Rogers Reviewed-by: Arnaldo Carvalho de Melo Link: https://lore.kernel.org/r/20250228222308.626803-7-irogers@google.com Signed-off-by: Namhyung Kim --- tools/perf/util/python.c | 10 ++++++++-- 1 file changed, 8 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 b600b6379b4e..4a3015e7dc83 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -1339,12 +1339,18 @@ static PyObject *pyrf__parse_events(PyObject *self, PyObject *args) struct evlist evlist = {}; struct parse_events_error err; PyObject *result; + PyObject *pcpus = NULL, *pthreads = NULL; + struct perf_cpu_map *cpus; + struct perf_thread_map *threads; - if (!PyArg_ParseTuple(args, "s", &input)) + if (!PyArg_ParseTuple(args, "s|OO", &input, &pcpus, &pthreads)) return NULL; + threads = pthreads ? ((struct pyrf_thread_map *)pthreads)->threads : NULL; + cpus = pcpus ? ((struct pyrf_cpu_map *)pcpus)->cpus : NULL; + parse_events_error__init(&err); - evlist__init(&evlist, NULL, NULL); + evlist__init(&evlist, cpus, threads); if (parse_events(&evlist, input, &err)) { parse_events_error__print(&err, input); PyErr_SetFromErrno(PyExc_OSError); -- cgit From 07fc231617378bd74b54661615a3e4772b514ed1 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 28 Feb 2025 14:23:04 -0800 Subject: perf python: Update ungrouped evsel leader in clone evsels are cloned in the python code as they form part of the Python object pyrf_evsel. The cloning doesn't update the evsel's leader, do this for the case of an evsel being ungrouped. Reviewed-by: Howard Chu Signed-off-by: Ian Rogers Reviewed-by: Arnaldo Carvalho de Melo Link: https://lore.kernel.org/r/20250228222308.626803-8-irogers@google.com Signed-off-by: Namhyung Kim --- tools/perf/util/python.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tools/perf/util/python.c') diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 4a3015e7dc83..e244cc74f16d 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -1312,6 +1312,8 @@ static PyObject *pyrf_evsel__from_evsel(struct evsel *evsel) evsel__init(&pevsel->evsel, &evsel->core.attr, evsel->core.idx); evsel__clone(&pevsel->evsel, evsel); + if (evsel__is_group_leader(evsel)) + evsel__set_leader(&pevsel->evsel, &pevsel->evsel); return (PyObject *)pevsel; } -- cgit From 9e9472c148b8c61ed5eb50eea3277e779bf601fe Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 28 Feb 2025 14:23:05 -0800 Subject: perf python: Avoid duplicated code in get_tracepoint_field The code replicates computations done in evsel__tp_format, reuse evsel__tp_format to simplify the python C code. Reviewed-by: Howard Chu Signed-off-by: Ian Rogers Reviewed-by: Arnaldo Carvalho de Melo Link: https://lore.kernel.org/r/20250228222308.626803-9-irogers@google.com Signed-off-by: Namhyung Kim --- tools/perf/util/python.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) (limited to 'tools/perf/util/python.c') diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index e244cc74f16d..7f2513ffe866 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -342,23 +342,14 @@ 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; - if (!evsel->tp_format) { - struct tep_event *tp_format; - - tp_format = trace_event__tp_format_id(evsel->core.attr.config); - if (IS_ERR_OR_NULL(tp_format)) - return NULL; - - evsel->tp_format = tp_format; - } - - field = tep_find_any_field(evsel->tp_format, str); - if (!field) + if (IS_ERR_OR_NULL(tp_format)) return NULL; - return tracepoint_field(pevent, field); + field = tep_find_any_field(tp_format, str); + return field ? tracepoint_field(pevent, field) : NULL; } #endif /* HAVE_LIBTRACEEVENT */ -- cgit From 1a8356fbf89beeea29ce39a233f932dc06a7109a Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 28 Feb 2025 14:23:06 -0800 Subject: perf python: Add evlist all_cpus accessor Add a means to get the reference counted all_cpus CPU map from an evlist in its python form. Reviewed-by: Howard Chu Signed-off-by: Ian Rogers Reviewed-by: Arnaldo Carvalho de Melo Link: https://lore.kernel.org/r/20250228222308.626803-10-irogers@google.com Signed-off-by: Namhyung Kim --- tools/perf/util/python.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'tools/perf/util/python.c') diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 7f2513ffe866..c55c8392bc07 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -874,6 +874,16 @@ static void pyrf_evlist__delete(struct pyrf_evlist *pevlist) Py_TYPE(pevlist)->tp_free((PyObject*)pevlist); } +static PyObject *pyrf_evlist__all_cpus(struct pyrf_evlist *pevlist) +{ + struct pyrf_cpu_map *pcpu_map = PyObject_New(struct pyrf_cpu_map, &pyrf_cpu_map__type); + + if (pcpu_map) + pcpu_map->cpus = perf_cpu_map__get(pevlist->evlist.core.all_cpus); + + return (PyObject *)pcpu_map; +} + static PyObject *pyrf_evlist__mmap(struct pyrf_evlist *pevlist, PyObject *args, PyObject *kwargs) { @@ -1057,6 +1067,12 @@ static PyObject *pyrf_evlist__enable(struct pyrf_evlist *pevlist) } static PyMethodDef pyrf_evlist__methods[] = { + { + .ml_name = "all_cpus", + .ml_meth = (PyCFunction)pyrf_evlist__all_cpus, + .ml_flags = METH_NOARGS, + .ml_doc = PyDoc_STR("CPU map union of all evsel CPU maps.") + }, { .ml_name = "mmap", .ml_meth = (PyCFunction)pyrf_evlist__mmap, -- cgit From 0dfcc7c86c91ffe9375e7146e19b77b1d6659b3d Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 28 Feb 2025 14:23:07 -0800 Subject: perf python: Add evlist.config to set up record options Add access to evlist__config that is used to configure an evlist with record options. Reviewed-by: Howard Chu Signed-off-by: Ian Rogers Acked-by: Arnaldo Carvalho de Melo Link: https://lore.kernel.org/r/20250228222308.626803-11-irogers@google.com Signed-off-by: Namhyung Kim --- tools/perf/util/python.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'tools/perf/util/python.c') diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index c55c8392bc07..69ec2ad60d98 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -9,10 +9,12 @@ #include #endif #include +#include "callchain.h" #include "evlist.h" #include "evsel.h" #include "event.h" #include "print_binary.h" +#include "record.h" #include "strbuf.h" #include "thread_map.h" #include "trace-event.h" @@ -1052,6 +1054,31 @@ static PyObject *pyrf_evlist__open(struct pyrf_evlist *pevlist, return Py_None; } +static PyObject *pyrf_evlist__config(struct pyrf_evlist *pevlist) +{ + struct record_opts opts = { + .sample_time = true, + .mmap_pages = UINT_MAX, + .user_freq = UINT_MAX, + .user_interval = ULLONG_MAX, + .freq = 4000, + .target = { + .uses_mmap = true, + .default_per_cpu = true, + }, + .nr_threads_synthesize = 1, + .ctl_fd = -1, + .ctl_fd_ack = -1, + .no_buffering = true, + .no_inherit = true, + }; + struct evlist *evlist = &pevlist->evlist; + + evlist__config(evlist, &opts, &callchain_param); + Py_INCREF(Py_None); + return Py_None; +} + static PyObject *pyrf_evlist__disable(struct pyrf_evlist *pevlist) { evlist__disable(&pevlist->evlist); @@ -1109,6 +1136,12 @@ static PyMethodDef pyrf_evlist__methods[] = { .ml_flags = METH_VARARGS | METH_KEYWORDS, .ml_doc = PyDoc_STR("reads an event.") }, + { + .ml_name = "config", + .ml_meth = (PyCFunction)pyrf_evlist__config, + .ml_flags = METH_NOARGS, + .ml_doc = PyDoc_STR("Apply default record options to the evlist.") + }, { .ml_name = "disable", .ml_meth = (PyCFunction)pyrf_evlist__disable, -- cgit From 1376c195e8ad327bb9f2d32e0acc5ac39e7cb30a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 12 Mar 2025 17:31:36 -0300 Subject: perf python: Fixup description of sample.id event member Some old cut'n'paste error, its "ip", so the description should be "event ip", not "event type". Fixes: 877108e42b1b9ba6 ("perf tools: Initial python binding") Signed-off-by: Arnaldo Carvalho de Melo Reviewed-by: Ian Rogers Link: https://lore.kernel.org/r/20250312203141.285263-2-acme@kernel.org 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 69ec2ad60d98..6c5bb5e88939 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -49,7 +49,7 @@ struct pyrf_event { }; #define sample_members \ - sample_member_def(sample_ip, ip, T_ULONGLONG, "event type"), \ + sample_member_def(sample_ip, ip, T_ULONGLONG, "event ip"), \ sample_member_def(sample_pid, pid, T_INT, "event pid"), \ sample_member_def(sample_tid, tid, T_INT, "event tid"), \ sample_member_def(sample_time, time, T_ULONGLONG, "event timestamp"), \ -- cgit From 1882625c9113a3cf775dd46695466c80b922991d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 12 Mar 2025 17:31:37 -0300 Subject: perf python: Remove some unused macros (_PyUnicode_FromString(arg), etc) When python2 support was removed in e7e9943c87d857da ("perf python: Remove python 2 scripting support"), all use of the _PyUnicode_FromString(arg), _PyUnicode_FromFormat(...), and _PyLong_FromLong(arg) macros was removed as well, so remove it. Signed-off-by: Arnaldo Carvalho de Melo Reviewed-by: Ian Rogers Link: https://lore.kernel.org/r/20250312203141.285263-3-acme@kernel.org Signed-off-by: Namhyung Kim --- tools/perf/util/python.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'tools/perf/util/python.c') diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 6c5bb5e88939..f9491b669976 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -22,13 +22,6 @@ #include "util/sample.h" #include -#define _PyUnicode_FromString(arg) \ - PyUnicode_FromString(arg) -#define _PyUnicode_FromFormat(...) \ - PyUnicode_FromFormat(__VA_ARGS__) -#define _PyLong_FromLong(arg) \ - PyLong_FromLong(arg) - PyMODINIT_FUNC PyInit_perf(void); #define member_def(type, member, ptype, help) \ -- cgit From 3de5a2bf5b4847f7a59a184568f969f8fe05d57f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 12 Mar 2025 17:31:39 -0300 Subject: perf python: Decrement the refcount of just created event on failure To avoid a leak if we have the python object but then something happens and we need to return the operation, decrement the offset of the newly created object. Fixes: 377f698db12150a1 ("perf python: Add struct evsel into struct pyrf_event") Signed-off-by: Arnaldo Carvalho de Melo Reviewed-by: Ian Rogers Link: https://lore.kernel.org/r/20250312203141.285263-5-acme@kernel.org Signed-off-by: Namhyung Kim --- tools/perf/util/python.c | 6 +++++- 1 file changed, 5 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 f9491b669976..31a877a8eb8f 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -1012,6 +1012,7 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, evsel = evlist__event2evsel(evlist, event); if (!evsel) { + Py_DECREF(pyevent); Py_INCREF(Py_None); return Py_None; } @@ -1023,9 +1024,12 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, /* Consume the even only after we parsed it out. */ perf_mmap__consume(&md->core); - if (err) + if (err) { + Py_DECREF(pyevent); return PyErr_Format(PyExc_OSError, "perf: can't parse sample, err=%d", err); + } + return pyevent; } end: -- cgit From f3fed3ae34d606819d87a63d970cc3092a5be7ab Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 12 Mar 2025 17:31:40 -0300 Subject: perf python: Don't keep a raw_data pointer to consumed ring buffer space MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When processing tracepoints the perf python binding was parsing the event before calling perf_mmap__consume(&md->core) in pyrf_evlist__read_on_cpu(). But part of this event parsing was to set the perf_sample->raw_data pointer to the payload of the event, which then could be overwritten by other event before tracepoint fields were asked for via event.prev_comm in a python program, for instance. This also happened with other fields, but strings were were problems were surfacing, as there is UTF-8 validation for the potentially garbled data. This ended up showing up as (with some added debugging messages): ( field 'prev_comm' ret=0x7f7c31f65110, raw_size=68 ) ( field 'prev_pid' ret=0x7f7c23b1bed0, raw_size=68 ) ( field 'prev_prio' ret=0x7f7c239c0030, raw_size=68 ) ( field 'prev_state' ret=0x7f7c239c0250, raw_size=68 ) time 14771421785867 prev_comm= prev_pid=1919907691 prev_prio=796026219 prev_state=0x303a32313175 ==> ( XXX '��' len=16, raw_size=68) ( field 'next_comm' ret=(nil), raw_size=68 ) Traceback (most recent call last): File "/home/acme/git/perf-tools-next/tools/perf/python/tracepoint.py", line 51, in main() File "/home/acme/git/perf-tools-next/tools/perf/python/tracepoint.py", line 46, in main event.next_comm, ^^^^^^^^^^^^^^^ AttributeError: 'perf.sample_event' object has no attribute 'next_comm' When event.next_comm was asked for, the PyUnicode_FromString() python API would fail and that tracepoint field wouldn't be available, stopping the tools/perf/python/tracepoint.py test tool. But, since we already do a copy of the whole event in pyrf_event__new, just use it and while at it remove what was done in in e8968e654191390a ("perf python: Fix pyrf_evlist__read_on_cpu event consuming") because we don't really need to wait for parsing the sample before declaring the event as consumed. This copy is questionable as is now, as it limits the maximum event + sample_type and tracepoint payload to sizeof(union perf_event), this all has been "working" because 'struct perf_event_mmap2', the largest entry in 'union perf_event' is: $ pahole -C perf_event ~/bin/perf | grep mmap2 struct perf_record_mmap2 mmap2; /* 0 4168 */ $ Fixes: bae57e3825a3dded ("perf python: Add support to resolve tracepoint fields") Signed-off-by: Arnaldo Carvalho de Melo Reviewed-by: Ian Rogers Link: https://lore.kernel.org/r/20250312203141.285263-6-acme@kernel.org Signed-off-by: Namhyung Kim --- tools/perf/util/python.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'tools/perf/util/python.c') diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 31a877a8eb8f..6a03341e1788 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -1019,11 +1019,9 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, pevent->evsel = evsel; - err = evsel__parse_sample(evsel, event, &pevent->sample); - - /* Consume the even only after we parsed it out. */ perf_mmap__consume(&md->core); + err = evsel__parse_sample(evsel, &pevent->event, &pevent->sample); if (err) { Py_DECREF(pyevent); return PyErr_Format(PyExc_OSError, -- cgit From 89aaeaf84231157288035b366cb6300c1c6cac64 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 12 Mar 2025 17:31:41 -0300 Subject: perf python: Check if there is space to copy all the event The pyrf_event__new() method copies the event obtained from the perf ring buffer to a structure that will then be turned into a python object for further consumption, so it copies perf_event.header.size bytes to its 'event' member: $ pahole -C pyrf_event /tmp/build/perf-tools-next/python/perf.cpython-312-x86_64-linux-gnu.so struct pyrf_event { PyObject ob_base; /* 0 16 */ struct evsel * evsel; /* 16 8 */ struct perf_sample sample; /* 24 312 */ /* XXX last struct has 7 bytes of padding, 2 holes */ /* --- cacheline 5 boundary (320 bytes) was 16 bytes ago --- */ union perf_event event; /* 336 4168 */ /* size: 4504, cachelines: 71, members: 4 */ /* member types with holes: 1, total: 2 */ /* paddings: 1, sum paddings: 7 */ /* last cacheline: 24 bytes */ }; $ It was doing so without checking if the event just obtained has more than that space, fix it. This isn't a proper, final solution, as we need to support larger events, but for the time being we at least bounds check and document it. Fixes: 877108e42b1b9ba6 ("perf tools: Initial python binding") Signed-off-by: Arnaldo Carvalho de Melo Reviewed-by: Ian Rogers Link: https://lore.kernel.org/r/20250312203141.285263-7-acme@kernel.org Signed-off-by: Namhyung Kim --- tools/perf/util/python.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'tools/perf/util/python.c') diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 6a03341e1788..f3c05da25b4a 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -476,6 +476,11 @@ static PyObject *pyrf_event__new(const union perf_event *event) event->header.type == PERF_RECORD_SWITCH_CPU_WIDE)) return NULL; + // FIXME this better be dynamic or we need to parse everything + // before calling perf_mmap__consume(), including tracepoint fields. + if (sizeof(pevent->event) < event->header.size) + return NULL; + ptype = pyrf_event__type[event->header.type]; pevent = PyObject_New(struct pyrf_event, ptype); if (pevent != NULL) -- cgit