From: Ian Rogers <irogers@google.com>
To: Peter Zijlstra <peterz@infradead.org>,
Ingo Molnar <mingo@redhat.com>,
Arnaldo Carvalho de Melo <acme@kernel.org>,
Namhyung Kim <namhyung@kernel.org>, Jiri Olsa <jolsa@kernel.org>,
Ian Rogers <irogers@google.com>,
Adrian Hunter <adrian.hunter@intel.com>,
James Clark <james.clark@linaro.org>,
Thomas Falcon <thomas.falcon@intel.com>,
Alice Rogers <alice.mei.rogers@gmail.com>,
Changbin Du <changbin.du@huawei.com>,
Tengda Wu <wutengda@huaweicloud.com>, tanze <tanze@kylinos.cn>,
Athira Rajeev <atrajeev@linux.ibm.com>,
Dapeng Mi <dapeng1.mi@linux.intel.com>,
linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org
Subject: [PATCH v1 03/13] perf python: Lazily copy events and samples from process_events
Date: Fri, 2 Oct 2026 11:26:12 -0700 [thread overview]
Message-ID: <20261002182624.3259797-4-irogers@google.com> (raw)
In-Reply-To: <20261002182624.3259797-1-irogers@google.com>
When perf.session.process_events() delivers an event to a Python
callback, pyrf_event__new() previously copied the raw perf_event into a
4,160-byte union perf_event embedded in struct pyrf_event and called
evsel__parse_sample() a second time to re-parse the sample against the
copied buffer.
In the common case, the Python callback inspects the event and returns
without storing a reference to it. Avoid the event memcpy, the duplicate
evsel__parse_sample() call, and the large PyObject allocation by:
1. Storing pointers to the underlying union perf_event and
struct perf_sample in struct pyrf_event (using PyGetSetDef descriptors
backed by PyMember_GetOne() to read fields through those pointers),
shrinking struct pyrf_event so it fits in CPython's small-object
allocator pool.
2. Borrowing the event and pre-parsed sample pointers from
process_events() for the duration of the Python callback.
3. Checking Py_REFCNT(pyevent) > 1 after the callback returns and only
allocating event_copy and re-parsing into sample_storage when the
Python callback retained a reference to the event object (or immediately
in pyrf_evlist__read_on_cpu() where no pre-parsed sample is passed).
Assisted-by: Antigravity:gemini-3.1-pro
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/python.c | 664 ++++++++++++++++++++++-----------------
1 file changed, 384 insertions(+), 280 deletions(-)
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index 5b4963d5784d..a40e89c1b329 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -70,16 +70,6 @@ PyMODINIT_FUNC PyInit_perf(void);
static PyObject *pyrf_evsel__from_evsel(struct evsel *evsel);
-#define member_def(type, member, ptype, help) \
- { #member, ptype, \
- offsetof(struct pyrf_event, event) + offsetof(struct type, member), \
- 0, help }
-
-#define sample_member_def(name, member, ptype, help) \
- { #name, ptype, \
- offsetof(struct pyrf_event, sample) + offsetof(struct perf_sample, member), \
- 0, help }
-
#define CHECK_INITIALIZED(ptr, msg) \
do { \
if (!(ptr)) { \
@@ -98,8 +88,12 @@ static PyObject *pyrf_evsel__from_evsel(struct evsel *evsel);
struct pyrf_event {
PyObject_HEAD
- /** @sample: The parsed sample from the event. */
- struct perf_sample sample;
+ /** @sample: The parsed sample, pointing to @sample_storage or a borrowed sample. */
+ struct perf_sample *sample;
+ /** @sample_storage: Storage for @sample when owned by this object. */
+ struct perf_sample sample_storage;
+ /** @evsel: The evsel associated with the event. */
+ struct evsel *evsel;
/** @machine: The machine associated with the sample/event. */
struct machine *machine;
/** @al: The address location from machine__resolve, lazily computed. */
@@ -110,11 +104,85 @@ struct pyrf_event {
PyObject *callchain;
/** @brstack: Resolved branch stack, eagerly computed if requested. */
PyObject *brstack;
- /** @event: The underlying perf_event that may be in a file or ring buffer. */
- union perf_event event;
+ /** @event: The underlying perf_event, pointing to @event_copy or a borrowed event. */
+ const union perf_event *event;
+ /** @event_copy: Copy of @event when owned by this object. */
+ union perf_event *event_copy;
+};
+
+static const union perf_event zero_event;
+
+static PyObject *pyrf_event__get_member(PyObject *self, void *closure)
+{
+ struct pyrf_event *pevent = (void *)self;
+ const union perf_event *event = pevent->event ?: &zero_event;
+
+ return PyMember_GetOne((const char *)event, closure);
+}
+
+static PyObject *pyrf_event__get_sample_member(PyObject *self, void *closure)
+{
+ struct pyrf_event *pevent = (void *)self;
+ const struct perf_sample *sample = pevent->sample ?: &pevent->sample_storage;
+
+ return PyMember_GetOne((const char *)sample, closure);
+}
+
+#define named_member_def(field, type, member, ptype, help) \
+ { .name = #field, .get = pyrf_event__get_member, .doc = help, \
+ .closure = &(struct PyMemberDef){ #field, ptype, \
+ offsetof(struct type, member), READONLY, help } }
+
+#define member_def(type, member, ptype, help) \
+ named_member_def(member, type, member, ptype, help)
+
+struct pyrf_string_member {
+ size_t offset;
+ size_t maxlen;
};
+static size_t pyrf_event__str_len(const struct pyrf_event *pevent, size_t offset, size_t maxlen)
+{
+ const char *base = (const char *)(pevent->event ?: &zero_event);
+ size_t event_size = pevent->event ? pevent->event->header.size : 0;
+
+ if (event_size <= offset)
+ return 0;
+ return strnlen(base + offset, min(event_size - offset, maxlen));
+}
+
+static PyObject *pyrf_event__get_string_member(PyObject *self, void *closure)
+{
+ struct pyrf_event *pevent = (void *)self;
+ const struct pyrf_string_member *sm = closure;
+ const char *base = (const char *)(pevent->event ?: &zero_event);
+
+ return PyUnicode_FromStringAndSize(base + sm->offset,
+ pyrf_event__str_len(pevent, sm->offset, sm->maxlen));
+}
+
+#define string_member_def(type, member, help) \
+ { .name = #member, .get = pyrf_event__get_string_member, .doc = help, \
+ .closure = &(struct pyrf_string_member){ offsetof(struct type, member), \
+ sizeof(((struct type *)0)->member) } }
+
+#define sample_member_def(field, member, ptype, help) \
+ { .name = #field, .get = pyrf_event__get_sample_member, .doc = help, \
+ .closure = &(struct PyMemberDef){ #field, ptype, \
+ offsetof(struct perf_sample, member), READONLY, help } }
+
+static PyObject *pyrf_event__get_evsel(PyObject *self, void *closure __maybe_unused)
+{
+ struct pyrf_event *pevent = (void *)self;
+
+ if (!pevent->sample || !pevent->sample->evsel)
+ Py_RETURN_NONE;
+
+ return pyrf_evsel__from_evsel(pevent->sample->evsel);
+}
+
#define sample_members \
+ { .name = "evsel", .get = pyrf_event__get_evsel, .doc = "tracking event." }, \
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"), \
@@ -125,39 +193,21 @@ struct pyrf_event {
sample_member_def(sample_period, period, T_ULONGLONG, "event period"), \
sample_member_def(sample_cpu, cpu, T_UINT, "event cpu"),
-static PyObject *pyrf_event__get_evsel(PyObject *self, void *closure __maybe_unused)
-{
- struct pyrf_event *pevent = (void *)self;
-
- if (!pevent->sample.evsel)
- Py_RETURN_NONE;
-
- return pyrf_evsel__from_evsel(pevent->sample.evsel);
-}
-
-static PyGetSetDef pyrf_event__getset[] = {
- {
- .name = "evsel",
- .get = pyrf_event__get_evsel,
- .set = NULL,
- .doc = "tracking event.",
- },
- { .name = NULL, },
-};
-
static void pyrf_event__delete(struct pyrf_event *pevent)
{
if (pevent->al_resolved)
addr_location__exit(&pevent->al);
Py_XDECREF(pevent->callchain);
Py_XDECREF(pevent->brstack);
- perf_sample__exit(&pevent->sample);
+ perf_sample__exit(&pevent->sample_storage);
+ evsel__put(pevent->evsel);
+ free(pevent->event_copy);
Py_TYPE(pevent)->tp_free((PyObject *)pevent);
}
static const char pyrf_mmap_event__doc[] = PyDoc_STR("perf mmap event object.");
-static PyMemberDef pyrf_mmap_event__members[] = {
+static PyGetSetDef pyrf_mmap_event__getset[] = {
sample_members
member_def(perf_event_header, type, T_UINT, "event type"),
member_def(perf_event_header, misc, T_USHORT, "event misc"),
@@ -166,7 +216,7 @@ static PyMemberDef pyrf_mmap_event__members[] = {
member_def(perf_record_mmap, start, T_ULONGLONG, "start of the map"),
member_def(perf_record_mmap, len, T_ULONGLONG, "map length"),
member_def(perf_record_mmap, pgoff, T_ULONGLONG, "page offset"),
- member_def(perf_record_mmap, filename, T_STRING_INPLACE, "backing store"),
+ string_member_def(perf_record_mmap, filename, "backing store"),
{ .name = NULL, },
};
@@ -174,13 +224,15 @@ static PyObject *pyrf_mmap_event__repr(const struct pyrf_event *pevent)
{
PyObject *ret;
char *s;
+ int fn_len = pyrf_event__str_len(pevent, offsetof(struct perf_record_mmap, filename),
+ sizeof(pevent->event->mmap.filename));
if (asprintf(&s, "{ type: mmap, pid: %u, tid: %u, start: %#" PRI_lx64 ", "
"length: %#" PRI_lx64 ", offset: %#" PRI_lx64 ", "
- "filename: %s }",
- pevent->event.mmap.pid, pevent->event.mmap.tid,
- pevent->event.mmap.start, pevent->event.mmap.len,
- pevent->event.mmap.pgoff, pevent->event.mmap.filename) < 0) {
+ "filename: %.*s }",
+ pevent->event->mmap.pid, pevent->event->mmap.tid,
+ pevent->event->mmap.start, pevent->event->mmap.len,
+ pevent->event->mmap.pgoff, fn_len, pevent->event->mmap.filename) < 0) {
ret = PyErr_NoMemory();
} else {
ret = PyUnicode_FromString(s);
@@ -196,8 +248,7 @@ static PyTypeObject pyrf_mmap_event__type = {
.tp_dealloc = (destructor)pyrf_event__delete,
.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
.tp_doc = pyrf_mmap_event__doc,
- .tp_members = pyrf_mmap_event__members,
- .tp_getset = pyrf_event__getset,
+ .tp_getset = pyrf_mmap_event__getset,
.tp_repr = (reprfunc)pyrf_mmap_event__repr,
};
@@ -207,64 +258,69 @@ static PyObject *pyrf_mmap2_event__get_maj(PyObject *self, void *closure __maybe
{
struct pyrf_event *pevent = (void *)self;
- if (pevent->event.header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID)
+ if (pevent->event->header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID)
Py_RETURN_NONE;
- return PyLong_FromUnsignedLong(pevent->event.mmap2.maj);
+ return PyLong_FromUnsignedLong(pevent->event->mmap2.maj);
}
static PyObject *pyrf_mmap2_event__get_min(PyObject *self, void *closure __maybe_unused)
{
struct pyrf_event *pevent = (void *)self;
- if (pevent->event.header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID)
+ if (pevent->event->header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID)
Py_RETURN_NONE;
- return PyLong_FromUnsignedLong(pevent->event.mmap2.min);
+ return PyLong_FromUnsignedLong(pevent->event->mmap2.min);
}
static PyObject *pyrf_mmap2_event__get_ino(PyObject *self, void *closure __maybe_unused)
{
struct pyrf_event *pevent = (void *)self;
- if (pevent->event.header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID)
+ if (pevent->event->header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID)
Py_RETURN_NONE;
- return PyLong_FromUnsignedLongLong(pevent->event.mmap2.ino);
+ return PyLong_FromUnsignedLongLong(pevent->event->mmap2.ino);
}
static PyObject *pyrf_mmap2_event__get_ino_generation(PyObject *self, void *closure __maybe_unused)
{
struct pyrf_event *pevent = (void *)self;
- if (pevent->event.header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID)
+ if (pevent->event->header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID)
Py_RETURN_NONE;
- return PyLong_FromUnsignedLongLong(pevent->event.mmap2.ino_generation);
+ return PyLong_FromUnsignedLongLong(pevent->event->mmap2.ino_generation);
}
static PyObject *pyrf_mmap2_event__get_build_id(PyObject *self, void *closure __maybe_unused)
{
struct pyrf_event *pevent = (void *)self;
- if (!(pevent->event.header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID))
+ if (!(pevent->event->header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID))
Py_RETURN_NONE;
- int size = pevent->event.mmap2.build_id_size;
+ int size = pevent->event->mmap2.build_id_size;
if (size > 20)
size = 20;
- return PyBytes_FromStringAndSize((const char *)pevent->event.mmap2.build_id, size);
+ return PyBytes_FromStringAndSize((const char *)pevent->event->mmap2.build_id, size);
}
static PyGetSetDef pyrf_mmap2_event__getset[] = {
- {
- .name = "evsel",
- .get = pyrf_event__get_evsel,
- .set = NULL,
- .doc = "tracking event.",
- },
+ sample_members
+ member_def(perf_event_header, type, T_UINT, "event type"),
+ member_def(perf_event_header, misc, T_USHORT, "event misc"),
+ member_def(perf_record_mmap2, pid, T_UINT, "event pid"),
+ member_def(perf_record_mmap2, tid, T_UINT, "event tid"),
+ member_def(perf_record_mmap2, start, T_ULONGLONG, "start of the map"),
+ member_def(perf_record_mmap2, len, T_ULONGLONG, "map length"),
+ member_def(perf_record_mmap2, pgoff, T_ULONGLONG, "page offset"),
+ member_def(perf_record_mmap2, prot, T_UINT, "protection"),
+ member_def(perf_record_mmap2, flags, T_UINT, "flags"),
+ string_member_def(perf_record_mmap2, filename, "backing store"),
{
.name = "maj",
.get = pyrf_mmap2_event__get_maj,
@@ -298,31 +354,18 @@ static PyGetSetDef pyrf_mmap2_event__getset[] = {
{ .name = NULL, },
};
-static PyMemberDef pyrf_mmap2_event__members[] = {
- sample_members
- member_def(perf_event_header, type, T_UINT, "event type"),
- member_def(perf_event_header, misc, T_USHORT, "event misc"),
- member_def(perf_record_mmap2, pid, T_UINT, "event pid"),
- member_def(perf_record_mmap2, tid, T_UINT, "event tid"),
- member_def(perf_record_mmap2, start, T_ULONGLONG, "start of the map"),
- member_def(perf_record_mmap2, len, T_ULONGLONG, "map length"),
- member_def(perf_record_mmap2, pgoff, T_ULONGLONG, "page offset"),
- member_def(perf_record_mmap2, prot, T_UINT, "protection"),
- member_def(perf_record_mmap2, flags, T_UINT, "flags"),
- member_def(perf_record_mmap2, filename, T_STRING_INPLACE, "backing store"),
- { .name = NULL, },
-};
-
static PyObject *pyrf_mmap2_event__repr(const struct pyrf_event *pevent)
{
PyObject *ret;
char *s;
-
- if (asprintf(&s, "{ type: mmap2, pid: %u, tid: %u, start: %#" PRI_lx64 ", length: %#" PRI_lx64 ", offset: %#" PRI_lx64 ", flags: %#x, prot: %#x, filename: %s }",
- pevent->event.mmap2.pid, pevent->event.mmap2.tid,
- pevent->event.mmap2.start, pevent->event.mmap2.len,
- pevent->event.mmap2.pgoff, pevent->event.mmap2.flags,
- pevent->event.mmap2.prot, pevent->event.mmap2.filename) < 0)
+ int fn_len = pyrf_event__str_len(pevent, offsetof(struct perf_record_mmap2, filename),
+ sizeof(pevent->event->mmap2.filename));
+
+ if (asprintf(&s, "{ type: mmap2, pid: %u, tid: %u, start: %#" PRI_lx64 ", length: %#" PRI_lx64 ", offset: %#" PRI_lx64 ", flags: %#x, prot: %#x, filename: %.*s }",
+ pevent->event->mmap2.pid, pevent->event->mmap2.tid,
+ pevent->event->mmap2.start, pevent->event->mmap2.len,
+ pevent->event->mmap2.pgoff, pevent->event->mmap2.flags,
+ pevent->event->mmap2.prot, fn_len, pevent->event->mmap2.filename) < 0)
return PyErr_NoMemory();
ret = PyUnicode_FromString(s);
@@ -337,14 +380,13 @@ static PyTypeObject pyrf_mmap2_event__type = {
.tp_dealloc = (destructor)pyrf_event__delete,
.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
.tp_doc = pyrf_mmap2_event__doc,
- .tp_members = pyrf_mmap2_event__members,
.tp_getset = pyrf_mmap2_event__getset,
.tp_repr = (reprfunc)pyrf_mmap2_event__repr,
};
static const char pyrf_task_event__doc[] = PyDoc_STR("perf task (fork/exit) event object.");
-static PyMemberDef pyrf_task_event__members[] = {
+static PyGetSetDef pyrf_task_event__getset[] = {
sample_members
member_def(perf_event_header, type, T_UINT, "event type"),
member_def(perf_record_fork, pid, T_UINT, "event pid"),
@@ -359,12 +401,12 @@ static PyObject *pyrf_task_event__repr(const struct pyrf_event *pevent)
{
return PyUnicode_FromFormat("{ type: %s, pid: %u, ppid: %u, tid: %u, "
"ptid: %u, time: %" PRI_lu64 "}",
- pevent->event.header.type == PERF_RECORD_FORK ? "fork" : "exit",
- pevent->event.fork.pid,
- pevent->event.fork.ppid,
- pevent->event.fork.tid,
- pevent->event.fork.ptid,
- pevent->event.fork.time);
+ pevent->event->header.type == PERF_RECORD_FORK ? "fork" : "exit",
+ pevent->event->fork.pid,
+ pevent->event->fork.ppid,
+ pevent->event->fork.tid,
+ pevent->event->fork.ptid,
+ pevent->event->fork.time);
}
static PyTypeObject pyrf_task_event__type = {
@@ -374,28 +416,36 @@ static PyTypeObject pyrf_task_event__type = {
.tp_dealloc = (destructor)pyrf_event__delete,
.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
.tp_doc = pyrf_task_event__doc,
- .tp_members = pyrf_task_event__members,
- .tp_getset = pyrf_event__getset,
+ .tp_getset = pyrf_task_event__getset,
.tp_repr = (reprfunc)pyrf_task_event__repr,
};
static const char pyrf_comm_event__doc[] = PyDoc_STR("perf comm event object.");
-static PyMemberDef pyrf_comm_event__members[] = {
+static PyGetSetDef pyrf_comm_event__getset[] = {
sample_members
member_def(perf_event_header, type, T_UINT, "event type"),
member_def(perf_record_comm, pid, T_UINT, "event pid"),
member_def(perf_record_comm, tid, T_UINT, "event tid"),
- member_def(perf_record_comm, comm, T_STRING_INPLACE, "process name"),
+ string_member_def(perf_record_comm, comm, "process name"),
{ .name = NULL, },
};
static PyObject *pyrf_comm_event__repr(const struct pyrf_event *pevent)
{
- return PyUnicode_FromFormat("{ type: comm, pid: %u, tid: %u, comm: %s }",
- pevent->event.comm.pid,
- pevent->event.comm.tid,
- pevent->event.comm.comm);
+ PyObject *ret;
+ char *s;
+ int comm_len = pyrf_event__str_len(pevent, offsetof(struct perf_record_comm, comm),
+ sizeof(pevent->event->comm.comm));
+
+ if (asprintf(&s, "{ type: comm, pid: %u, tid: %u, comm: %.*s }",
+ pevent->event->comm.pid, pevent->event->comm.tid,
+ comm_len, pevent->event->comm.comm) < 0)
+ return PyErr_NoMemory();
+
+ ret = PyUnicode_FromString(s);
+ free(s);
+ return ret;
}
static PyTypeObject pyrf_comm_event__type = {
@@ -405,14 +455,13 @@ static PyTypeObject pyrf_comm_event__type = {
.tp_dealloc = (destructor)pyrf_event__delete,
.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
.tp_doc = pyrf_comm_event__doc,
- .tp_members = pyrf_comm_event__members,
- .tp_getset = pyrf_event__getset,
+ .tp_getset = pyrf_comm_event__getset,
.tp_repr = (reprfunc)pyrf_comm_event__repr,
};
static const char pyrf_throttle_event__doc[] = PyDoc_STR("perf throttle event object.");
-static PyMemberDef pyrf_throttle_event__members[] = {
+static PyGetSetDef pyrf_throttle_event__getset[] = {
sample_members
member_def(perf_event_header, type, T_UINT, "event type"),
member_def(perf_record_throttle, time, T_ULONGLONG, "timestamp"),
@@ -424,11 +473,11 @@ static PyMemberDef pyrf_throttle_event__members[] = {
static PyObject *pyrf_throttle_event__repr(const struct pyrf_event *pevent)
{
const struct perf_record_throttle *te = (const struct perf_record_throttle *)
- (&pevent->event.header + 1);
+ (&pevent->event->header + 1);
return PyUnicode_FromFormat("{ type: %sthrottle, time: %" PRI_lu64 ", id: %" PRI_lu64
", stream_id: %" PRI_lu64 " }",
- pevent->event.header.type == PERF_RECORD_THROTTLE ? "" : "un",
+ pevent->event->header.type == PERF_RECORD_THROTTLE ? "" : "un",
te->time, te->id, te->stream_id);
}
@@ -439,14 +488,13 @@ static PyTypeObject pyrf_throttle_event__type = {
.tp_dealloc = (destructor)pyrf_event__delete,
.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
.tp_doc = pyrf_throttle_event__doc,
- .tp_members = pyrf_throttle_event__members,
- .tp_getset = pyrf_event__getset,
+ .tp_getset = pyrf_throttle_event__getset,
.tp_repr = (reprfunc)pyrf_throttle_event__repr,
};
static const char pyrf_lost_event__doc[] = PyDoc_STR("perf lost event object.");
-static PyMemberDef pyrf_lost_event__members[] = {
+static PyGetSetDef pyrf_lost_event__getset[] = {
sample_members
member_def(perf_event_header, type, T_UINT, "event type"),
member_def(perf_record_lost, id, T_ULONGLONG, "event id"),
@@ -461,7 +509,7 @@ static PyObject *pyrf_lost_event__repr(const struct pyrf_event *pevent)
if (asprintf(&s, "{ type: lost, id: %#" PRI_lx64 ", "
"lost: %#" PRI_lx64 " }",
- pevent->event.lost.id, pevent->event.lost.lost) < 0) {
+ pevent->event->lost.id, pevent->event->lost.lost) < 0) {
ret = PyErr_NoMemory();
} else {
ret = PyUnicode_FromString(s);
@@ -477,14 +525,13 @@ static PyTypeObject pyrf_lost_event__type = {
.tp_dealloc = (destructor)pyrf_event__delete,
.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
.tp_doc = pyrf_lost_event__doc,
- .tp_members = pyrf_lost_event__members,
- .tp_getset = pyrf_event__getset,
+ .tp_getset = pyrf_lost_event__getset,
.tp_repr = (reprfunc)pyrf_lost_event__repr,
};
static const char pyrf_stat_event__doc[] = PyDoc_STR("perf stat event object.");
-static PyMemberDef pyrf_stat_event__members[] = {
+static PyGetSetDef pyrf_stat_event__getset[] = {
sample_members
member_def(perf_event_header, type, T_UINT, "event type"),
member_def(perf_record_stat, id, T_ULONGLONG, "event id"),
@@ -498,14 +545,16 @@ static PyMemberDef pyrf_stat_event__members[] = {
static PyObject *pyrf_stat_event__repr(const struct pyrf_event *pevent)
{
+ const union perf_event *event = pevent->event ?: &zero_event;
+
return PyUnicode_FromFormat(
"{ type: stat, id: %llu, cpu: %u, thread: %u, val: %llu, ena: %llu, run: %llu }",
- pevent->event.stat.id,
- pevent->event.stat.cpu,
- pevent->event.stat.thread,
- pevent->event.stat.val,
- pevent->event.stat.ena,
- pevent->event.stat.run);
+ event->stat.id,
+ event->stat.cpu,
+ event->stat.thread,
+ event->stat.val,
+ event->stat.ena,
+ event->stat.run);
}
static PyTypeObject pyrf_stat_event__type = {
@@ -516,28 +565,27 @@ static PyTypeObject pyrf_stat_event__type = {
.tp_dealloc = (destructor)pyrf_event__delete,
.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
.tp_doc = pyrf_stat_event__doc,
- .tp_members = pyrf_stat_event__members,
- .tp_getset = pyrf_event__getset,
+ .tp_getset = pyrf_stat_event__getset,
.tp_repr = (reprfunc)pyrf_stat_event__repr,
};
static const char pyrf_stat_round_event__doc[] = PyDoc_STR("perf stat round event object.");
-static PyMemberDef pyrf_stat_round_event__members[] = {
+static PyGetSetDef pyrf_stat_round_event__getset[] = {
sample_members
member_def(perf_event_header, type, T_UINT, "event type"),
- { .name = "stat_round_type", .type = T_ULONGLONG,
- .offset = offsetof(struct pyrf_event, event) + offsetof(struct perf_record_stat_round, type),
- .doc = "round type" },
+ named_member_def(stat_round_type, perf_record_stat_round, type, T_ULONGLONG, "round type"),
member_def(perf_record_stat_round, time, T_ULONGLONG, "round time"),
{ .name = NULL, },
};
static PyObject *pyrf_stat_round_event__repr(const struct pyrf_event *pevent)
{
+ const union perf_event *event = pevent->event ?: &zero_event;
+
return PyUnicode_FromFormat("{ type: stat_round, type: %llu, time: %llu }",
- pevent->event.stat_round.type,
- pevent->event.stat_round.time);
+ event->stat_round.type,
+ event->stat_round.time);
}
static PyTypeObject pyrf_stat_round_event__type = {
@@ -548,14 +596,13 @@ static PyTypeObject pyrf_stat_round_event__type = {
.tp_dealloc = (destructor)pyrf_event__delete,
.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
.tp_doc = pyrf_stat_round_event__doc,
- .tp_members = pyrf_stat_round_event__members,
- .tp_getset = pyrf_event__getset,
+ .tp_getset = pyrf_stat_round_event__getset,
.tp_repr = (reprfunc)pyrf_stat_round_event__repr,
};
static const char pyrf_read_event__doc[] = PyDoc_STR("perf read event object.");
-static PyMemberDef pyrf_read_event__members[] = {
+static PyGetSetDef pyrf_read_event__getset[] = {
sample_members
member_def(perf_event_header, type, T_UINT, "event type"),
member_def(perf_record_read, pid, T_UINT, "event pid"),
@@ -566,8 +613,8 @@ static PyMemberDef pyrf_read_event__members[] = {
static PyObject *pyrf_read_event__repr(const struct pyrf_event *pevent)
{
return PyUnicode_FromFormat("{ type: read, pid: %u, tid: %u }",
- pevent->event.read.pid,
- pevent->event.read.tid);
+ pevent->event->read.pid,
+ pevent->event->read.tid);
/*
* FIXME: return the array of read values,
* making this method useful ;-)
@@ -581,27 +628,12 @@ static PyTypeObject pyrf_read_event__type = {
.tp_dealloc = (destructor)pyrf_event__delete,
.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
.tp_doc = pyrf_read_event__doc,
- .tp_members = pyrf_read_event__members,
- .tp_getset = pyrf_event__getset,
+ .tp_getset = pyrf_read_event__getset,
.tp_repr = (reprfunc)pyrf_read_event__repr,
};
static const char pyrf_sample_event__doc[] = PyDoc_STR("perf sample event object.");
-static PyMemberDef pyrf_sample_event__members[] = {
- sample_members
- sample_member_def(sample_ip, ip, T_ULONGLONG, "event ip"),
- sample_member_def(sample_addr, addr, T_ULONGLONG, "event addr"),
- sample_member_def(sample_phys_addr, phys_addr, T_ULONGLONG, "event physical addr"),
- sample_member_def(sample_weight, weight, T_ULONGLONG, "event weight"),
- sample_member_def(sample_data_src, data_src, T_ULONGLONG, "event data source"),
- sample_member_def(sample_insn_count, insn_cnt, T_ULONGLONG, "event instruction count"),
- sample_member_def(sample_cyc_count, cyc_cnt, T_ULONGLONG, "event cycle count"),
- sample_member_def(flags, flags, T_UINT, "event flags"),
- member_def(perf_event_header, type, T_UINT, "event type"),
- { .name = NULL, },
-};
-
static PyObject *pyrf_sample_event__repr(const struct pyrf_event *pevent)
{
PyObject *ret;
@@ -619,16 +651,18 @@ static PyObject *pyrf_sample_event__repr(const struct pyrf_event *pevent)
#ifdef HAVE_LIBTRACEEVENT
static bool is_tracepoint(const struct pyrf_event *pevent)
{
- if (!pevent->sample.evsel)
+ struct evsel *evsel = pevent->sample->evsel ?: pevent->evsel;
+
+ if (!evsel)
return false;
- return pevent->sample.evsel->core.attr.type == PERF_TYPE_TRACEPOINT;
+ return evsel->core.attr.type == PERF_TYPE_TRACEPOINT;
}
static PyObject*
tracepoint_field(const struct pyrf_event *pe, struct tep_format_field *field)
{
struct tep_handle *pevent = field->event->tep;
- void *data = pe->sample.raw_data;
+ void *data = pe->sample->raw_data;
PyObject *ret = NULL;
unsigned long long val;
unsigned int offset, len;
@@ -668,7 +702,7 @@ tracepoint_field(const struct pyrf_event *pe, struct tep_format_field *field)
static PyObject*
get_tracepoint_field(struct pyrf_event *pevent, PyObject *attr_name)
{
- struct evsel *evsel = pevent->sample.evsel;
+ struct evsel *evsel = pevent->sample->evsel ?: pevent->evsel;
struct tep_event *tp_format = evsel__tp_format(evsel);
struct tep_format_field *field;
@@ -693,7 +727,7 @@ get_tracepoint_field(struct pyrf_event *pevent, PyObject *attr_name)
static int pyrf_sample_event__resolve_al(struct pyrf_event *pevent)
{
- struct evsel *evsel = pevent->sample.evsel;
+ struct evsel *evsel = pevent->sample->evsel ?: pevent->evsel;
struct evlist *evlist = evsel ? evsel->evlist : NULL;
struct perf_session *session = evlist ? evlist__session(evlist) : NULL;
struct machine *machine;
@@ -714,7 +748,7 @@ static int pyrf_sample_event__resolve_al(struct pyrf_event *pevent)
machine = pevent->machine ? pevent->machine : &session->machines.host;
addr_location__init(&pevent->al);
- if (machine__resolve(machine, &pevent->al, &pevent->sample) < 0) {
+ if (machine__resolve(machine, &pevent->al, pevent->sample) < 0) {
addr_location__exit(&pevent->al);
return -1;
}
@@ -819,17 +853,17 @@ static PyObject *pyrf_sample_event__get_sym_end(struct pyrf_event *pevent,
static PyObject *pyrf_sample_event__get_raw_buf(struct pyrf_event *pevent,
void *closure __maybe_unused)
{
- if (pevent->event.header.type != PERF_RECORD_SAMPLE)
+ if (pevent->event->header.type != PERF_RECORD_SAMPLE)
Py_RETURN_NONE;
- return PyBytes_FromStringAndSize((const char *)pevent->sample.raw_data,
- pevent->sample.raw_size);
+ return PyBytes_FromStringAndSize((const char *)pevent->sample->raw_data,
+ pevent->sample->raw_size);
}
static PyObject *pyrf_sample_event__srccode(PyObject *self, PyObject *args)
{
struct pyrf_event *pevent = (void *)self;
- u64 addr = pevent->sample.ip;
+ u64 addr = pevent->sample->ip;
char *srcfile = NULL;
char *srccode = NULL;
unsigned int line = 0;
@@ -838,8 +872,8 @@ static PyObject *pyrf_sample_event__srccode(PyObject *self, PyObject *args)
struct addr_location al;
/*
- * addr defaults to pevent->sample.ip when omitted, and when
- * addr != pevent->sample.ip a local addr_location is resolved so
+ * addr defaults to pevent->sample->ip when omitted, and when
+ * addr != pevent->sample->ip a local addr_location is resolved so
* callers can inspect callchain/branch addresses without mutating
* the sample's cached pevent->al.
*/
@@ -849,9 +883,9 @@ static PyObject *pyrf_sample_event__srccode(PyObject *self, PyObject *args)
if (pyrf_sample_event__resolve_al(pevent) < 0)
Py_RETURN_NONE;
- if (addr != pevent->sample.ip) {
+ if (addr != pevent->sample->ip) {
addr_location__init(&al);
- thread__find_symbol_fb(pevent->al.thread, pevent->sample.cpumode, addr, &al);
+ thread__find_symbol_fb(pevent->al.thread, pevent->sample->cpumode, addr, &al);
} else {
addr_location__init(&al);
al.thread = thread__get(pevent->al.thread);
@@ -905,14 +939,14 @@ static PyObject *pyrf_sample_event__insn(PyObject *self, PyObject *args __maybe_
if (!machine)
Py_RETURN_NONE;
- if (pevent->sample.ip && !pevent->sample.insn_len)
- perf_sample__fetch_insn(&pevent->sample, thread, machine);
+ if (pevent->sample->ip && !pevent->sample->insn_len)
+ perf_sample__fetch_insn(pevent->sample, thread, machine);
- if (!pevent->sample.insn_len)
+ if (!pevent->sample->insn_len)
Py_RETURN_NONE;
- return PyBytes_FromStringAndSize((const char *)pevent->sample.insn,
- pevent->sample.insn_len);
+ return PyBytes_FromStringAndSize((const char *)pevent->sample->insn,
+ pevent->sample->insn_len);
}
struct pyrf_callchain_node {
@@ -1242,8 +1276,8 @@ static int pyrf_sample_event__resolve_addr_al(struct pyrf_event *pevent,
if (pyrf_sample_event__resolve_al(pevent) < 0 || !pevent->al.thread)
return -1;
- thread__find_symbol_fb(pevent->al.thread, pevent->sample.cpumode,
- pevent->sample.addr, addr_al);
+ thread__find_symbol_fb(pevent->al.thread, pevent->sample->cpumode,
+ pevent->sample->addr, addr_al);
return 0;
}
@@ -1295,23 +1329,32 @@ static PyObject *pyrf_sample_event__get_addr_sym_offset(struct pyrf_event *peven
static PyObject *pyrf_sample_event__get_branch_type(struct pyrf_event *pevent,
void *closure __maybe_unused)
{
- return PyLong_FromUnsignedLong(pevent->sample.flags & PERF_BRANCH_MASK);
+ return PyLong_FromUnsignedLong(pevent->sample->flags & PERF_BRANCH_MASK);
}
static PyObject *pyrf_sample_event__get_in_tx(struct pyrf_event *pevent,
void *closure __maybe_unused)
{
- return PyLong_FromUnsignedLong(!!(pevent->sample.flags & PERF_IP_FLAG_IN_TX));
+ return PyLong_FromUnsignedLong(!!(pevent->sample->flags & PERF_IP_FLAG_IN_TX));
}
static PyObject *pyrf_sample_event__get_transaction(struct pyrf_event *pevent,
void *closure __maybe_unused)
{
- return PyLong_FromUnsignedLongLong(pevent->sample.transaction);
+ return PyLong_FromUnsignedLongLong(pevent->sample->transaction);
}
static PyGetSetDef pyrf_sample_event__getset[] = {
-
+ sample_members
+ sample_member_def(sample_ip, ip, T_ULONGLONG, "event ip"),
+ sample_member_def(sample_addr, addr, T_ULONGLONG, "event addr"),
+ sample_member_def(sample_phys_addr, phys_addr, T_ULONGLONG, "event physical addr"),
+ sample_member_def(sample_weight, weight, T_ULONGLONG, "event weight"),
+ sample_member_def(sample_data_src, data_src, T_ULONGLONG, "event data source"),
+ sample_member_def(sample_insn_count, insn_cnt, T_ULONGLONG, "event instruction count"),
+ sample_member_def(sample_cyc_count, cyc_cnt, T_ULONGLONG, "event cycle count"),
+ sample_member_def(flags, flags, T_UINT, "event flags"),
+ member_def(perf_event_header, type, T_UINT, "event type"),
{
.name = "addr_dso",
.get = (getter)pyrf_sample_event__get_addr_dso,
@@ -1361,12 +1404,6 @@ static PyGetSetDef pyrf_sample_event__getset[] = {
.set = NULL,
.doc = "event raw buffer.",
},
- {
- .name = "evsel",
- .get = pyrf_event__get_evsel,
- .set = NULL,
- .doc = "tracking event.",
- },
{
.name = "dso",
.get = (getter)pyrf_sample_event__get_dso,
@@ -1453,7 +1490,6 @@ static PyTypeObject pyrf_sample_event__type = {
.tp_dealloc = (destructor)pyrf_event__delete,
.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
.tp_doc = pyrf_sample_event__doc,
- .tp_members = pyrf_sample_event__members,
.tp_getset = pyrf_sample_event__getset,
.tp_methods = pyrf_sample_event__methods,
.tp_repr = (reprfunc)pyrf_sample_event__repr,
@@ -1462,36 +1498,26 @@ static PyTypeObject pyrf_sample_event__type = {
static const char pyrf_context_switch_event__doc[] = PyDoc_STR("perf context_switch event object.");
-static PyMemberDef pyrf_context_switch_event__members[] = {
- sample_members
- member_def(perf_event_header, type, T_UINT, "event type"),
- member_def(perf_event_header, misc, T_USHORT, "event misc"),
- { .name = NULL, },
-};
-
static PyObject *pyrf_context_switch_event__get_next_prev_pid(const struct pyrf_event *pevent,
void *closure __maybe_unused)
{
- if (pevent->event.header.type == PERF_RECORD_SWITCH_CPU_WIDE)
- return PyLong_FromUnsignedLong(pevent->event.context_switch.next_prev_pid);
+ if (pevent->event->header.type == PERF_RECORD_SWITCH_CPU_WIDE)
+ return PyLong_FromUnsignedLong(pevent->event->context_switch.next_prev_pid);
Py_RETURN_NONE;
}
static PyObject *pyrf_context_switch_event__get_next_prev_tid(const struct pyrf_event *pevent,
void *closure __maybe_unused)
{
- if (pevent->event.header.type == PERF_RECORD_SWITCH_CPU_WIDE)
- return PyLong_FromUnsignedLong(pevent->event.context_switch.next_prev_tid);
+ if (pevent->event->header.type == PERF_RECORD_SWITCH_CPU_WIDE)
+ return PyLong_FromUnsignedLong(pevent->event->context_switch.next_prev_tid);
Py_RETURN_NONE;
}
static PyGetSetDef pyrf_context_switch_event__getset[] = {
- {
- .name = "evsel",
- .get = pyrf_event__get_evsel,
- .set = NULL,
- .doc = "tracking event.",
- },
+ sample_members
+ member_def(perf_event_header, type, T_UINT, "event type"),
+ member_def(perf_event_header, misc, T_USHORT, "event misc"),
{
.name = "next_prev_pid",
.get = (getter)pyrf_context_switch_event__get_next_prev_pid,
@@ -1513,15 +1539,15 @@ static PyObject *pyrf_context_switch_event__repr(const struct pyrf_event *pevent
char *s;
int res;
- if (pevent->event.header.type == PERF_RECORD_SWITCH_CPU_WIDE) {
+ if (pevent->event->header.type == PERF_RECORD_SWITCH_CPU_WIDE) {
res = asprintf(&s,
"{ type: context_switch, next_prev_pid: %u, next_prev_tid: %u, switch_out: %u }",
- pevent->event.context_switch.next_prev_pid,
- pevent->event.context_switch.next_prev_tid,
- !!(pevent->event.header.misc & PERF_RECORD_MISC_SWITCH_OUT));
+ pevent->event->context_switch.next_prev_pid,
+ pevent->event->context_switch.next_prev_tid,
+ !!(pevent->event->header.misc & PERF_RECORD_MISC_SWITCH_OUT));
} else {
res = asprintf(&s, "{ type: context_switch, switch_out: %u }",
- !!(pevent->event.header.misc & PERF_RECORD_MISC_SWITCH_OUT));
+ !!(pevent->event->header.misc & PERF_RECORD_MISC_SWITCH_OUT));
}
if (res < 0) {
ret = PyErr_NoMemory();
@@ -1539,7 +1565,6 @@ static PyTypeObject pyrf_context_switch_event__type = {
.tp_dealloc = (destructor)pyrf_event__delete,
.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
.tp_doc = pyrf_context_switch_event__doc,
- .tp_members = pyrf_context_switch_event__members,
.tp_getset = pyrf_context_switch_event__getset,
.tp_repr = (reprfunc)pyrf_context_switch_event__repr,
};
@@ -1614,15 +1639,102 @@ static PyTypeObject *pyrf_event__type[] = {
[PERF_RECORD_STAT_ROUND] = &pyrf_stat_round_event__type,
};
+static int pyrf_event__copy(struct pyrf_event *pevent)
+{
+ const union perf_event *orig_event = pevent->event;
+ const struct perf_sample *orig_sample = pevent->sample;
+ size_t copy_size = orig_event->header.size;
+ int err = -EINVAL;
+
+ if (pevent->event_copy)
+ return 0;
+
+ /*
+ * Clear borrowed pointers immediately so that even if copying fails,
+ * pevent does not retain dangling pointers to the caller's stack or
+ * ring buffer.
+ */
+ pevent->event = &zero_event;
+ pevent->sample = &pevent->sample_storage;
+
+ /*
+ * Allocate an extra zero byte so string fields such as mmap/mmap2
+ * filenames or comm strings are always NUL-terminated.
+ */
+ pevent->event_copy = calloc(1, copy_size + 1);
+ if (!pevent->event_copy) {
+ PyErr_NoMemory();
+ return -ENOMEM;
+ }
+
+ memcpy(pevent->event_copy, orig_event, copy_size);
+ pevent->event = pevent->event_copy;
+
+ if (pevent->evsel) {
+ bool needs_swap = pevent->evsel->needs_swap;
+
+ pevent->evsel->needs_swap = false;
+ err = evsel__parse_sample(pevent->evsel, pevent->event_copy,
+ &pevent->sample_storage);
+ pevent->evsel->needs_swap = needs_swap;
+ }
+
+ if (err == 0) {
+ if (orig_sample != &pevent->sample_storage) {
+ if (orig_sample->id)
+ pevent->sample_storage.id = orig_sample->id;
+ if (orig_sample->period)
+ pevent->sample_storage.period = orig_sample->period;
+ if (orig_sample->machine_pid)
+ pevent->sample_storage.machine_pid =
+ orig_sample->machine_pid;
+ if (orig_sample->vcpu != (u32)-1)
+ pevent->sample_storage.vcpu = orig_sample->vcpu;
+ if (orig_sample->insn_len && !pevent->sample_storage.insn_len) {
+ memcpy(pevent->sample_storage.insn, orig_sample->insn,
+ sizeof(pevent->sample_storage.insn));
+ pevent->sample_storage.insn_len = orig_sample->insn_len;
+ }
+ }
+ pevent->sample = &pevent->sample_storage;
+ } else if (orig_sample != &pevent->sample_storage) {
+ /*
+ * Synthesized events (e.g. Intel PT itrace) or events without
+ * an evsel may not have raw sample buffers for
+ * evsel__parse_sample(); copy the scalar fields from the
+ * borrowed sample.
+ */
+ perf_sample__exit(&pevent->sample_storage);
+ pevent->sample_storage = *orig_sample;
+ if (pevent->sample_storage.evsel)
+ pevent->sample_storage.evsel =
+ evsel__get(pevent->sample_storage.evsel);
+ pevent->sample_storage.callchain = NULL;
+ pevent->sample_storage.merged_callchain = false;
+ pevent->sample_storage.raw_data = NULL;
+ pevent->sample_storage.raw_size = 0;
+ pevent->sample_storage.branch_stack = NULL;
+ pevent->sample_storage.user_regs = NULL;
+ pevent->sample_storage.intr_regs = NULL;
+ pevent->sample_storage.user_stack.size = 0;
+ pevent->sample_storage.user_stack.data = NULL;
+ pevent->sample_storage.aux_sample.size = 0;
+ pevent->sample_storage.aux_sample.data = NULL;
+ pevent->sample = &pevent->sample_storage;
+ } else if (pevent->evsel) {
+ PyErr_Format(PyExc_OSError, "perf: can't parse sample, err=%d", err);
+ return err;
+ }
+ return 0;
+}
+
static PyObject *pyrf_event__new(const union perf_event *event, struct evsel *evsel,
struct perf_session *session, struct machine *machine,
struct perf_sample *sample_arg)
{
struct pyrf_event *pevent;
struct perf_sample *sample;
- int err;
u32 min_size;
- bool needs_swap;
if (event->header.type >= ARRAY_SIZE(pyrf_event__type) ||
pyrf_event__type[event->header.type] == NULL) {
@@ -1635,31 +1747,35 @@ static PyObject *pyrf_event__new(const union perf_event *event, struct evsel *ev
event->header.size, event->header.type);
}
- size_t copy_size = event->header.size;
-
- if (copy_size > sizeof(pevent->event)) {
- return PyErr_Format(PyExc_TypeError, "Unexpected event size: %zd < %zu",
- sizeof(pevent->event), copy_size);
+ if (event->header.size > sizeof(union perf_event)) {
+ return PyErr_Format(PyExc_TypeError, "Unexpected event size: %zu < %u",
+ sizeof(union perf_event), event->header.size);
}
pevent = PyObject_New(struct pyrf_event, pyrf_event__type[event->header.type]);
if (pevent == NULL)
return PyErr_NoMemory();
- /* Copy the event for memory safety and initialize variables. */
- memcpy(&pevent->event, event, copy_size);
- if (copy_size < sizeof(pevent->event))
- memset((char *)&pevent->event + copy_size, 0, sizeof(pevent->event) - copy_size);
-
- if (event->header.type == PERF_RECORD_MMAP2)
- pevent->event.mmap2.filename[sizeof(pevent->event.mmap2.filename) - 1] = '\0';
-
- perf_sample__init(&pevent->sample, /*all=*/true);
+ perf_sample__init(&pevent->sample_storage, /*all=*/true);
+ pevent->sample = sample_arg ?: &pevent->sample_storage;
+ pevent->evsel = evsel ? evsel__get(evsel) : NULL;
pevent->machine = machine;
pevent->callchain = NULL;
pevent->brstack = NULL;
pevent->al_resolved = false;
addr_location__init(&pevent->al);
+ pevent->event = event;
+ pevent->event_copy = NULL;
+
+ /*
+ * When called outside a session callback (e.g. pyrf_evlist__read_on_cpu)
+ * or without a pre-parsed sample for an evsel, copy the event and parse
+ * its sample into sample_storage immediately.
+ */
+ if ((!session || (evsel && !sample_arg)) && pyrf_event__copy(pevent) < 0) {
+ Py_DECREF(pevent);
+ return NULL;
+ }
if (!evsel) {
if (!pevent->machine && session)
@@ -1667,53 +1783,25 @@ static PyObject *pyrf_event__new(const union perf_event *event, struct evsel *ev
return (PyObject *)pevent;
}
- /* Parse the sample again so that pointers are within the copied event. */
- needs_swap = evsel->needs_swap;
-
- evsel->needs_swap = false;
- err = evsel__parse_sample(evsel, &pevent->event, &pevent->sample);
- evsel->needs_swap = needs_swap;
- if (err < 0) {
- /*
- * Synthesized events (e.g. Intel PT itrace) may not have raw sample
- * buffers for evsel__parse_sample(); use the pre-parsed sample_arg.
- */
- if (sample_arg) {
- perf_sample__exit(&pevent->sample);
- pevent->sample = *sample_arg;
- if (pevent->sample.evsel)
- pevent->sample.evsel = evsel__get(pevent->sample.evsel);
- pevent->sample.merged_callchain = false;
-
- pevent->sample.user_regs = NULL;
- pevent->sample.intr_regs = NULL;
- pevent->sample.raw_data = NULL;
- pevent->sample.raw_size = 0;
- } else {
- Py_DECREF(pevent);
- return PyErr_Format(PyExc_OSError,
- "perf: can't parse sample, err=%d", err);
- }
- }
- if (session && session->evlist && perf_guest && pevent->sample.id) {
- struct perf_sample_id *sid = evlist__id2sid(session->evlist, pevent->sample.id);
+ sample = pevent->sample;
+ if (session && session->evlist && perf_guest && sample->id) {
+ struct perf_sample_id *sid = evlist__id2sid(session->evlist, sample->id);
if (sid) {
- pevent->sample.machine_pid = sid->machine_pid;
- pevent->sample.vcpu = sid->vcpu.cpu;
+ sample->machine_pid = sid->machine_pid;
+ sample->vcpu = sid->vcpu.cpu;
}
}
if (!machine && session) {
- machine = pevent->sample.machine_pid ?
- machines__find(&session->machines, pevent->sample.machine_pid) :
+ machine = sample->machine_pid ?
+ machines__find(&session->machines, sample->machine_pid) :
&session->machines.host;
if (!machine)
machine = &session->machines.host;
}
pevent->machine = machine;
- if (machine && machine->pid > 0 && !pevent->sample.machine_pid)
- pevent->sample.machine_pid = machine->pid;
- sample = &pevent->sample;
+ if (machine && machine->pid > 0 && !sample->machine_pid)
+ sample->machine_pid = machine->pid;
if (machine && sample->callchain) {
struct addr_location al;
struct callchain_cursor *cursor;
@@ -4524,6 +4612,41 @@ static int pyrf_session__call_return_process(struct call_return *cr,
return 0;
}
+static int pyrf_session__call_event(struct pyrf_session *psession, PyObject *callback,
+ PyObject *pyevent, const char *extra_arg, bool has_extra_arg)
+{
+ PyObject *ret;
+ int err = 0;
+
+ if (has_extra_arg)
+ ret = PyObject_CallFunction(callback, "Oz", pyevent, extra_arg);
+ else
+ ret = PyObject_CallFunctionObjArgs(callback, pyevent, NULL);
+
+ Py_XDECREF(ret);
+ if (Py_REFCNT(pyevent) > 1) {
+ PyObject *exc_type = NULL, *exc_value = NULL, *exc_tb = NULL;
+
+ /*
+ * The Python callback retained a reference to the event
+ * beyond the callback's return (or in an exception frame);
+ * copy the underlying event and sample before process_events
+ * reuses or unmaps its buffer.
+ */
+ if (!ret)
+ PyErr_Fetch(&exc_type, &exc_value, &exc_tb);
+ err = pyrf_event__copy((struct pyrf_event *)pyevent);
+ if (!ret) {
+ PyErr_Clear();
+ PyErr_Restore(exc_type, exc_value, exc_tb);
+ }
+ }
+ Py_DECREF(pyevent);
+ if (!ret || err < 0)
+ return pyrf_session__callback_raised(psession);
+ return 0;
+}
+
static int pyrf_session_tool__sample(const struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
@@ -4575,16 +4698,12 @@ static int pyrf_session_tool__sample(const struct perf_tool *tool,
PyObject *pyevent = pyrf_event__new(event, sample->evsel,
psession->session,
machine, sample);
- PyObject *ret;
if (pyevent == NULL)
return -ENOMEM;
- ret = PyObject_CallFunction(psession->sample, "O", pyevent);
- Py_DECREF(pyevent);
- if (!ret)
- return pyrf_session__callback_raised(psession);
- Py_DECREF(ret);
+ return pyrf_session__call_event(psession, psession->sample, pyevent,
+ /*extra_arg=*/NULL, /*has_extra_arg=*/false);
}
return 0;
}
@@ -4596,7 +4715,6 @@ static int pyrf_session_tool__context_switch(const struct perf_tool *tool,
{
struct pyrf_session *psession = container_of(tool, struct pyrf_session, tool);
PyObject *pyevent;
- PyObject *ret;
if (pyrf_session__exception_pending()) {
/*
@@ -4607,7 +4725,7 @@ static int pyrf_session_tool__context_switch(const struct perf_tool *tool,
return perf_event__process_switch(tool, event, sample, machine);
}
- pyevent = pyrf_event__new(event, sample->evsel, psession->session, machine, NULL);
+ pyevent = pyrf_event__new(event, sample->evsel, psession->session, machine, sample);
if (perf_event__process_switch(tool, event, sample, machine) < 0) {
Py_XDECREF(pyevent);
return -1;
@@ -4616,12 +4734,8 @@ static int pyrf_session_tool__context_switch(const struct perf_tool *tool,
if (pyevent == NULL)
return -ENOMEM;
- ret = PyObject_CallFunction(psession->context_switch, "O", pyevent);
- Py_DECREF(pyevent);
- if (!ret)
- return pyrf_session__callback_raised(psession);
- Py_DECREF(ret);
- return 0;
+ return pyrf_session__call_event(psession, psession->context_switch, pyevent,
+ /*extra_arg=*/NULL, /*has_extra_arg=*/false);
}
static int pyrf_session_tool__stat(const struct perf_tool *tool,
@@ -4632,7 +4746,6 @@ static int pyrf_session_tool__stat(const struct perf_tool *tool,
struct evsel *evsel = evlist__id2evsel(session->evlist, event->stat.id);
const char *name = evsel ? evsel__name(evsel) : "unknown";
PyObject *pyevent;
- PyObject *ret;
if (pyrf_session__exception_pending())
return 0;
@@ -4642,12 +4755,8 @@ static int pyrf_session_tool__stat(const struct perf_tool *tool,
if (pyevent == NULL)
return -ENOMEM;
- ret = PyObject_CallFunction(psession->stat, "Oz", pyevent, name);
- Py_DECREF(pyevent);
- if (!ret)
- return pyrf_session__callback_raised(psession);
- Py_DECREF(ret);
- return 0;
+ return pyrf_session__call_event(psession, psession->stat, pyevent,
+ name, /*has_extra_arg=*/true);
}
static int pyrf_session_tool__stat_round(const struct perf_tool *tool,
@@ -4656,7 +4765,6 @@ static int pyrf_session_tool__stat_round(const struct perf_tool *tool,
{
struct pyrf_session *psession = container_of(tool, struct pyrf_session, tool);
PyObject *pyevent;
- PyObject *ret;
if (pyrf_session__exception_pending())
return 0;
@@ -4666,12 +4774,8 @@ static int pyrf_session_tool__stat_round(const struct perf_tool *tool,
if (pyevent == NULL)
return -ENOMEM;
- ret = PyObject_CallFunction(psession->stat, "Oz", pyevent, NULL);
- Py_DECREF(pyevent);
- if (!ret)
- return pyrf_session__callback_raised(psession);
- Py_DECREF(ret);
- return 0;
+ return pyrf_session__call_event(psession, psession->stat, pyevent,
+ /*extra_arg=*/NULL, /*has_extra_arg=*/true);
}
static PyObject *pyrf_session__find_thread(struct pyrf_session *psession, PyObject *args)
--
2.56.0.rc1.315.gc6ed9934b7-goog
next prev parent reply other threads:[~2026-10-02 18:26 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-02 18:26 [PATCH v1 00/13] perf timechart/list/treport: Interactive Textual TUIs and perf python session improvements Ian Rogers
2026-10-02 18:26 ` [PATCH v1 01/13] perf session: Don't flush remaining events once processing is done Ian Rogers
2026-10-02 18:26 ` [PATCH v1 02/13] perf python: Quietly stop processing events when a callback raises Ian Rogers
2026-10-02 18:26 ` Ian Rogers [this message]
2026-10-02 18:26 ` [PATCH v1 04/13] perf python: Lazily resolve sample callchains Ian Rogers
2026-10-02 18:26 ` [PATCH v1 05/13] perf python: Release the GIL while processing session events Ian Rogers
2026-10-02 18:26 ` [PATCH v1 06/13] perf list: Add a --tui option to launch ilist Ian Rogers
2026-10-02 18:26 ` [PATCH v1 07/13] perf test: Add a test for the ilist script Ian Rogers
2026-10-02 18:26 ` [PATCH v1 08/13] perf treport: Show the profile while it loads Ian Rogers
2026-10-02 18:26 ` [PATCH v1 09/13] perf test: Add a test for the treport script Ian Rogers
2026-10-02 18:26 ` [PATCH v1 10/13] perf timechart: Add an interactive --tui mode Ian Rogers
2026-10-02 18:26 ` [PATCH v1 11/13] perf test: Add a test for perf timechart --tui Ian Rogers
2026-10-02 18:26 ` [PATCH v1 12/13] perf timechart: Add a --live mode to the TUI Ian Rogers
2026-10-02 18:26 ` [PATCH v1 13/13] perf test: Test perf timechart --live Ian Rogers
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261002182624.3259797-4-irogers@google.com \
--to=irogers@google.com \
--cc=acme@kernel.org \
--cc=adrian.hunter@intel.com \
--cc=alice.mei.rogers@gmail.com \
--cc=atrajeev@linux.ibm.com \
--cc=changbin.du@huawei.com \
--cc=dapeng1.mi@linux.intel.com \
--cc=james.clark@linaro.org \
--cc=jolsa@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-perf-users@vger.kernel.org \
--cc=mingo@redhat.com \
--cc=namhyung@kernel.org \
--cc=peterz@infradead.org \
--cc=tanze@kylinos.cn \
--cc=thomas.falcon@intel.com \
--cc=wutengda@huaweicloud.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®