mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 12:44:03 +02:00
perf python: Expose brstack in sample event
Implement pyrf_branch_entry and pyrf_branch_stack for lazy iteration over branch stack entries. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers <irogers@google.com> Cc: Adrian Hunter <adrian.hunter@intel.com> Cc: Alice Rogers <alice.mei.rogers@gmail.com> Cc: Dapeng Mi <dapeng1.mi@linux.intel.com> Cc: Ingo Molnar <mingo@redhat.com> Cc: James Clark <james.clark@linaro.org> Cc: Leo Yan <leo.yan@linux.dev> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Richter <tmricht@linux.ibm.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Namhyung Kim <namhyung@kernel.org>
This commit is contained in:
parent
231007698a
commit
cc47714fd7
|
|
@ -22,6 +22,7 @@
|
|||
#include "debug.h"
|
||||
#include "dso.h"
|
||||
#include "event.h"
|
||||
#include "branch.h"
|
||||
#include "evlist.h"
|
||||
#include "evsel.h"
|
||||
#include "expr.h"
|
||||
|
|
@ -89,6 +90,8 @@ struct pyrf_event {
|
|||
bool al_resolved;
|
||||
/** @callchain: Resolved callchain, eagerly computed if requested. */
|
||||
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;
|
||||
};
|
||||
|
|
@ -127,6 +130,7 @@ 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);
|
||||
Py_TYPE(pevent)->tp_free((PyObject *)pevent);
|
||||
}
|
||||
|
|
@ -997,6 +1001,154 @@ static PyObject *pyrf_sample_event__get_callchain(PyObject *self, void *closure
|
|||
return pevent->callchain;
|
||||
}
|
||||
|
||||
struct pyrf_branch_entry {
|
||||
PyObject_HEAD
|
||||
u64 from;
|
||||
u64 to;
|
||||
struct branch_flags flags;
|
||||
};
|
||||
|
||||
static void pyrf_branch_entry__delete(struct pyrf_branch_entry *pentry)
|
||||
{
|
||||
Py_TYPE(pentry)->tp_free((PyObject *)pentry);
|
||||
}
|
||||
|
||||
static PyObject *pyrf_branch_entry__get_from(struct pyrf_branch_entry *pentry,
|
||||
void *closure __maybe_unused)
|
||||
{
|
||||
return PyLong_FromUnsignedLongLong(pentry->from);
|
||||
}
|
||||
|
||||
static PyObject *pyrf_branch_entry__get_to(struct pyrf_branch_entry *pentry,
|
||||
void *closure __maybe_unused)
|
||||
{
|
||||
return PyLong_FromUnsignedLongLong(pentry->to);
|
||||
}
|
||||
|
||||
static PyObject *pyrf_branch_entry__get_mispred(struct pyrf_branch_entry *pentry,
|
||||
void *closure __maybe_unused)
|
||||
{
|
||||
return PyBool_FromLong(pentry->flags.mispred);
|
||||
}
|
||||
|
||||
static PyObject *pyrf_branch_entry__get_predicted(struct pyrf_branch_entry *pentry,
|
||||
void *closure __maybe_unused)
|
||||
{
|
||||
return PyBool_FromLong(pentry->flags.predicted);
|
||||
}
|
||||
|
||||
static PyObject *pyrf_branch_entry__get_in_tx(struct pyrf_branch_entry *pentry,
|
||||
void *closure __maybe_unused)
|
||||
{
|
||||
return PyBool_FromLong(pentry->flags.in_tx);
|
||||
}
|
||||
|
||||
static PyObject *pyrf_branch_entry__get_abort(struct pyrf_branch_entry *pentry,
|
||||
void *closure __maybe_unused)
|
||||
{
|
||||
return PyBool_FromLong(pentry->flags.abort);
|
||||
}
|
||||
|
||||
static PyObject *pyrf_branch_entry__get_cycles(struct pyrf_branch_entry *pentry,
|
||||
void *closure __maybe_unused)
|
||||
{
|
||||
return PyLong_FromUnsignedLongLong(pentry->flags.cycles);
|
||||
}
|
||||
|
||||
static PyObject *pyrf_branch_entry__get_type(struct pyrf_branch_entry *pentry,
|
||||
void *closure __maybe_unused)
|
||||
{
|
||||
return PyLong_FromUnsignedLongLong((unsigned long long)pentry->flags.type);
|
||||
}
|
||||
|
||||
static PyGetSetDef pyrf_branch_entry__getset[] = {
|
||||
{ .name = "from_ip", .get = (getter)pyrf_branch_entry__get_from, },
|
||||
{ .name = "to_ip", .get = (getter)pyrf_branch_entry__get_to, },
|
||||
{ .name = "mispred", .get = (getter)pyrf_branch_entry__get_mispred, },
|
||||
{ .name = "predicted", .get = (getter)pyrf_branch_entry__get_predicted, },
|
||||
{ .name = "in_tx", .get = (getter)pyrf_branch_entry__get_in_tx, },
|
||||
{ .name = "abort", .get = (getter)pyrf_branch_entry__get_abort, },
|
||||
{ .name = "cycles", .get = (getter)pyrf_branch_entry__get_cycles, },
|
||||
{ .name = "type", .get = (getter)pyrf_branch_entry__get_type, },
|
||||
{ .name = NULL, },
|
||||
};
|
||||
|
||||
static PyTypeObject pyrf_branch_entry__type = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
.tp_name = "perf.branch_entry",
|
||||
.tp_basicsize = sizeof(struct pyrf_branch_entry),
|
||||
.tp_dealloc = (destructor)pyrf_branch_entry__delete,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
|
||||
.tp_doc = "perf branch entry object.",
|
||||
.tp_getset = pyrf_branch_entry__getset,
|
||||
};
|
||||
|
||||
struct pyrf_branch_stack {
|
||||
PyObject_HEAD
|
||||
struct branch_entry *entries;
|
||||
u64 nr;
|
||||
};
|
||||
|
||||
static void pyrf_branch_stack__delete(struct pyrf_branch_stack *pstack)
|
||||
{
|
||||
free(pstack->entries);
|
||||
Py_TYPE(pstack)->tp_free((PyObject *)pstack);
|
||||
}
|
||||
|
||||
static Py_ssize_t pyrf_branch_stack__length(PyObject *obj)
|
||||
{
|
||||
struct pyrf_branch_stack *pstack = (void *)obj;
|
||||
|
||||
return pstack->nr;
|
||||
}
|
||||
|
||||
static PyObject *pyrf_branch_stack__item(PyObject *obj, Py_ssize_t i)
|
||||
{
|
||||
struct pyrf_branch_stack *pstack = (void *)obj;
|
||||
struct pyrf_branch_entry *pentry;
|
||||
|
||||
if (i < 0 || i >= (Py_ssize_t)pstack->nr) {
|
||||
PyErr_SetString(PyExc_IndexError, "Index out of range");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pentry = PyObject_New(struct pyrf_branch_entry, &pyrf_branch_entry__type);
|
||||
if (!pentry)
|
||||
return NULL;
|
||||
|
||||
pentry->from = pstack->entries[i].from;
|
||||
pentry->to = pstack->entries[i].to;
|
||||
pentry->flags = pstack->entries[i].flags;
|
||||
|
||||
return (PyObject *)pentry;
|
||||
}
|
||||
|
||||
static PySequenceMethods pyrf_branch_stack__sequence_methods = {
|
||||
.sq_length = pyrf_branch_stack__length,
|
||||
.sq_item = pyrf_branch_stack__item,
|
||||
};
|
||||
|
||||
static PyTypeObject pyrf_branch_stack__type = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
.tp_name = "perf.branch_stack",
|
||||
.tp_basicsize = sizeof(struct pyrf_branch_stack),
|
||||
.tp_dealloc = (destructor)pyrf_branch_stack__delete,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
|
||||
.tp_doc = "perf branch stack object.",
|
||||
.tp_as_sequence = &pyrf_branch_stack__sequence_methods,
|
||||
};
|
||||
|
||||
static PyObject *pyrf_sample_event__get_brstack(PyObject *self, void *closure __maybe_unused)
|
||||
{
|
||||
struct pyrf_event *pevent = (void *)self;
|
||||
|
||||
if (!pevent->brstack)
|
||||
Py_RETURN_NONE;
|
||||
|
||||
Py_INCREF(pevent->brstack);
|
||||
return pevent->brstack;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
pyrf_sample_event__getattro(struct pyrf_event *pevent, PyObject *attr_name)
|
||||
{
|
||||
|
|
@ -1017,6 +1169,12 @@ static PyGetSetDef pyrf_sample_event__getset[] = {
|
|||
.set = NULL,
|
||||
.doc = "event callchain.",
|
||||
},
|
||||
{
|
||||
.name = "brstack",
|
||||
.get = pyrf_sample_event__get_brstack,
|
||||
.set = NULL,
|
||||
.doc = "event branch stack.",
|
||||
},
|
||||
{
|
||||
.name = "raw_buf",
|
||||
.get = (getter)pyrf_sample_event__get_raw_buf,
|
||||
|
|
@ -1198,6 +1356,12 @@ static int pyrf_event__setup_types(void)
|
|||
err = PyType_Ready(&pyrf_callchain__type);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
err = PyType_Ready(&pyrf_branch_entry__type);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
err = PyType_Ready(&pyrf_branch_stack__type);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
|
@ -1264,6 +1428,7 @@ static PyObject *pyrf_event__new(const union perf_event *event, struct evsel *ev
|
|||
|
||||
perf_sample__init(&pevent->sample, /*all=*/true);
|
||||
pevent->callchain = NULL;
|
||||
pevent->brstack = NULL;
|
||||
pevent->al_resolved = false;
|
||||
addr_location__init(&pevent->al);
|
||||
|
||||
|
|
@ -1325,6 +1490,27 @@ static PyObject *pyrf_event__new(const union perf_event *event, struct evsel *ev
|
|||
addr_location__exit(&al);
|
||||
}
|
||||
}
|
||||
if (sample->branch_stack) {
|
||||
struct branch_stack *bs = sample->branch_stack;
|
||||
struct branch_entry *entries = perf_sample__branch_entries(sample);
|
||||
struct pyrf_branch_stack *pstack;
|
||||
|
||||
pstack = PyObject_New(struct pyrf_branch_stack, &pyrf_branch_stack__type);
|
||||
if (!pstack) {
|
||||
Py_DECREF(pevent);
|
||||
return NULL;
|
||||
}
|
||||
pstack->nr = bs->nr;
|
||||
pstack->entries = calloc(bs->nr, sizeof(struct branch_entry));
|
||||
if (!pstack->entries) {
|
||||
Py_DECREF(pstack);
|
||||
Py_DECREF(pevent);
|
||||
return PyErr_NoMemory();
|
||||
}
|
||||
memcpy(pstack->entries, entries,
|
||||
bs->nr * sizeof(struct branch_entry));
|
||||
pevent->brstack = (PyObject *)pstack;
|
||||
}
|
||||
return (PyObject *)pevent;
|
||||
}
|
||||
|
||||
|
|
@ -3912,6 +4098,18 @@ PyMODINIT_FUNC PyInit_perf(void)
|
|||
Py_INCREF(&pyrf_session__type);
|
||||
PyModule_AddObject(module, "session", (PyObject *)&pyrf_session__type);
|
||||
|
||||
Py_INCREF(&pyrf_branch_entry__type);
|
||||
if (PyModule_AddObject(module, "branch_entry", (PyObject *)&pyrf_branch_entry__type) < 0) {
|
||||
Py_DECREF(&pyrf_branch_entry__type);
|
||||
goto error;
|
||||
}
|
||||
|
||||
Py_INCREF(&pyrf_branch_stack__type);
|
||||
if (PyModule_AddObject(module, "branch_stack", (PyObject *)&pyrf_branch_stack__type) < 0) {
|
||||
Py_DECREF(&pyrf_branch_stack__type);
|
||||
goto error;
|
||||
}
|
||||
|
||||
dict = PyModule_GetDict(module);
|
||||
if (dict == NULL)
|
||||
goto error;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user