mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 15:22:21 -04:00
perf python: Extend API for stat events in python.c
Add stat information to the session. Add call backs for stat events. 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:
@@ -458,6 +458,77 @@ static PyTypeObject pyrf_lost_event__type = {
|
||||
.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[] = {
|
||||
sample_members
|
||||
member_def(perf_event_header, type, T_UINT, "event type"),
|
||||
member_def(perf_record_stat, id, T_ULONGLONG, "event id"),
|
||||
member_def(perf_record_stat, cpu, T_UINT, "event cpu"),
|
||||
member_def(perf_record_stat, thread, T_UINT, "event thread"),
|
||||
member_def(perf_record_stat, val, T_ULONGLONG, "counter value"),
|
||||
member_def(perf_record_stat, ena, T_ULONGLONG, "enabled time"),
|
||||
member_def(perf_record_stat, run, T_ULONGLONG, "running time"),
|
||||
{ .name = NULL, },
|
||||
};
|
||||
|
||||
static PyObject *pyrf_stat_event__repr(const struct pyrf_event *pevent)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
static PyTypeObject pyrf_stat_event__type = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
.tp_name = "perf.stat_event",
|
||||
.tp_basicsize = sizeof(struct pyrf_event),
|
||||
.tp_new = PyType_GenericNew,
|
||||
.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_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[] = {
|
||||
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" },
|
||||
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)
|
||||
{
|
||||
return PyUnicode_FromFormat("{ type: stat_round, type: %llu, time: %llu }",
|
||||
pevent->event.stat_round.type,
|
||||
pevent->event.stat_round.time);
|
||||
}
|
||||
|
||||
static PyTypeObject pyrf_stat_round_event__type = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
.tp_name = "perf.stat_round_event",
|
||||
.tp_basicsize = sizeof(struct pyrf_event),
|
||||
.tp_new = PyType_GenericNew,
|
||||
.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_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[] = {
|
||||
@@ -1113,6 +1184,12 @@ static int pyrf_event__setup_types(void)
|
||||
if (err < 0)
|
||||
goto out;
|
||||
err = PyType_Ready(&pyrf_context_switch_event__type);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
err = PyType_Ready(&pyrf_stat_event__type);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
err = PyType_Ready(&pyrf_stat_round_event__type);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
err = PyType_Ready(&pyrf_callchain_node__type);
|
||||
@@ -1138,6 +1215,8 @@ static PyTypeObject *pyrf_event__type[] = {
|
||||
[PERF_RECORD_SAMPLE] = &pyrf_sample_event__type,
|
||||
[PERF_RECORD_SWITCH] = &pyrf_context_switch_event__type,
|
||||
[PERF_RECORD_SWITCH_CPU_WIDE] = &pyrf_context_switch_event__type,
|
||||
[PERF_RECORD_STAT] = &pyrf_stat_event__type,
|
||||
[PERF_RECORD_STAT_ROUND] = &pyrf_stat_round_event__type,
|
||||
};
|
||||
|
||||
static PyObject *pyrf_event__new(const union perf_event *event, struct evsel *evsel,
|
||||
@@ -2132,7 +2211,45 @@ static PyObject *pyrf_evsel__get_attr_wakeup_events(PyObject *self, void *closur
|
||||
return PyLong_FromUnsignedLong(pevsel->evsel->core.attr.wakeup_events);
|
||||
}
|
||||
|
||||
static PyObject *pyrf_evsel__get_ids(struct pyrf_evsel *pevsel, void *closure __maybe_unused)
|
||||
{
|
||||
struct evsel *evsel;
|
||||
PyObject *list;
|
||||
|
||||
CHECK_INITIALIZED(pevsel->evsel, "evsel");
|
||||
|
||||
evsel = pevsel->evsel;
|
||||
list = PyList_New(0);
|
||||
|
||||
if (!list)
|
||||
return NULL;
|
||||
|
||||
for (u32 i = 0; i < evsel->core.ids; i++) {
|
||||
PyObject *id = PyLong_FromUnsignedLongLong(evsel->core.id[i]);
|
||||
int ret;
|
||||
|
||||
if (!id) {
|
||||
Py_DECREF(list);
|
||||
return NULL;
|
||||
}
|
||||
ret = PyList_Append(list, id);
|
||||
Py_DECREF(id);
|
||||
if (ret < 0) {
|
||||
Py_DECREF(list);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static PyGetSetDef pyrf_evsel__getset[] = {
|
||||
{
|
||||
.name = "ids",
|
||||
.get = (getter)pyrf_evsel__get_ids,
|
||||
.set = NULL,
|
||||
.doc = "event IDs.",
|
||||
},
|
||||
{
|
||||
.name = "tracking",
|
||||
.get = pyrf_evsel__get_tracking,
|
||||
@@ -2985,6 +3102,8 @@ static const struct perf_constant perf__constants[] = {
|
||||
PERF_CONST(RECORD_LOST_SAMPLES),
|
||||
PERF_CONST(RECORD_SWITCH),
|
||||
PERF_CONST(RECORD_SWITCH_CPU_WIDE),
|
||||
PERF_CONST(RECORD_STAT),
|
||||
PERF_CONST(RECORD_STAT_ROUND),
|
||||
|
||||
PERF_CONST(RECORD_MISC_SWITCH_OUT),
|
||||
{ .name = NULL, },
|
||||
@@ -3384,6 +3503,7 @@ struct pyrf_session {
|
||||
struct perf_tool tool;
|
||||
struct pyrf_data *pdata;
|
||||
PyObject *sample;
|
||||
PyObject *stat;
|
||||
};
|
||||
|
||||
static int pyrf_session_tool__sample(const struct perf_tool *tool,
|
||||
@@ -3408,6 +3528,52 @@ static int pyrf_session_tool__sample(const struct perf_tool *tool,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pyrf_session_tool__stat(const struct perf_tool *tool,
|
||||
struct perf_session *session,
|
||||
union perf_event *event)
|
||||
{
|
||||
struct pyrf_session *psession = container_of(tool, struct pyrf_session, tool);
|
||||
struct evsel *evsel = evlist__id2evsel(session->evlist, event->stat.id);
|
||||
PyObject *pyevent = pyrf_event__new(event, /*evsel=*/NULL, psession->session,
|
||||
/*machine=*/NULL);
|
||||
const char *name = evsel ? evsel__name(evsel) : "unknown";
|
||||
PyObject *ret;
|
||||
|
||||
if (pyevent == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = PyObject_CallFunction(psession->stat, "Oz", pyevent, name);
|
||||
if (!ret) {
|
||||
Py_DECREF(pyevent);
|
||||
return -1;
|
||||
}
|
||||
Py_DECREF(ret);
|
||||
Py_DECREF(pyevent);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pyrf_session_tool__stat_round(const struct perf_tool *tool,
|
||||
struct perf_session *session __maybe_unused,
|
||||
union perf_event *event)
|
||||
{
|
||||
struct pyrf_session *psession = container_of(tool, struct pyrf_session, tool);
|
||||
PyObject *pyevent = pyrf_event__new(event, /*evsel=*/NULL, psession->session,
|
||||
/*machine=*/NULL);
|
||||
PyObject *ret;
|
||||
|
||||
if (pyevent == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = PyObject_CallFunction(psession->stat, "Oz", pyevent, NULL);
|
||||
if (!ret) {
|
||||
Py_DECREF(pyevent);
|
||||
return -1;
|
||||
}
|
||||
Py_DECREF(ret);
|
||||
Py_DECREF(pyevent);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PyObject *pyrf_session__find_thread(struct pyrf_session *psession, PyObject *args)
|
||||
{
|
||||
struct machine *machine;
|
||||
@@ -3441,13 +3607,13 @@ static PyObject *pyrf_session__find_thread(struct pyrf_session *psession, PyObje
|
||||
static PyObject *pyrf_session__new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
|
||||
{
|
||||
struct pyrf_data *pdata;
|
||||
PyObject *sample = NULL;
|
||||
static char *kwlist[] = { "data", "sample", NULL };
|
||||
PyObject *sample = NULL, *stat = NULL;
|
||||
static char *kwlist[] = { "data", "sample", "stat", NULL };
|
||||
struct pyrf_session *psession;
|
||||
struct perf_session *session;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|O", kwlist, &pyrf_data__type, &pdata,
|
||||
&sample))
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|OO", kwlist, &pyrf_data__type, &pdata,
|
||||
&sample, &stat))
|
||||
return NULL;
|
||||
|
||||
psession = PyObject_New(struct pyrf_session, type);
|
||||
@@ -3456,6 +3622,7 @@ static PyObject *pyrf_session__new(PyTypeObject *type, PyObject *args, PyObject
|
||||
|
||||
psession->session = NULL;
|
||||
psession->sample = NULL;
|
||||
psession->stat = NULL;
|
||||
psession->pdata = NULL;
|
||||
|
||||
Py_INCREF(pdata);
|
||||
@@ -3478,8 +3645,13 @@ static PyObject *pyrf_session__new(PyTypeObject *type, PyObject *args, PyObject
|
||||
} while (0)
|
||||
|
||||
ADD_TOOL(sample);
|
||||
ADD_TOOL(stat);
|
||||
#undef ADD_TOOL
|
||||
|
||||
if (stat)
|
||||
psession->tool.stat_round = pyrf_session_tool__stat_round;
|
||||
|
||||
|
||||
psession->tool.comm = perf_event__process_comm;
|
||||
psession->tool.mmap = perf_event__process_mmap;
|
||||
psession->tool.mmap2 = perf_event__process_mmap2;
|
||||
@@ -3492,7 +3664,7 @@ static PyObject *pyrf_session__new(PyTypeObject *type, PyObject *args, PyObject
|
||||
psession->tool.build_id = perf_event__process_build_id;
|
||||
psession->tool.attr = perf_event__process_attr;
|
||||
psession->tool.feature = perf_event__process_feature;
|
||||
psession->tool.stat = perf_event__process_stat_event;
|
||||
|
||||
session = perf_session__new(&pdata->data, &psession->tool);
|
||||
if (IS_ERR(session)) {
|
||||
PyErr_Format(PyExc_IOError, "failed to create session: %ld", PTR_ERR(session));
|
||||
@@ -3521,6 +3693,7 @@ static void pyrf_session__delete(struct pyrf_session *psession)
|
||||
perf_session__delete(psession->session);
|
||||
Py_XDECREF(psession->pdata);
|
||||
Py_XDECREF(psession->sample);
|
||||
Py_XDECREF(psession->stat);
|
||||
Py_TYPE(psession)->tp_free((PyObject *)psession);
|
||||
}
|
||||
|
||||
@@ -3718,6 +3891,12 @@ PyMODINIT_FUNC PyInit_perf(void)
|
||||
Py_INCREF(&pyrf_context_switch_event__type);
|
||||
PyModule_AddObject(module, "switch_event", (PyObject *)&pyrf_context_switch_event__type);
|
||||
|
||||
Py_INCREF(&pyrf_stat_event__type);
|
||||
PyModule_AddObject(module, "stat_event", (PyObject *)&pyrf_stat_event__type);
|
||||
|
||||
Py_INCREF(&pyrf_stat_round_event__type);
|
||||
PyModule_AddObject(module, "stat_round_event", (PyObject *)&pyrf_stat_round_event__type);
|
||||
|
||||
Py_INCREF(&pyrf_thread_map__type);
|
||||
PyModule_AddObject(module, "thread_map", (PyObject*)&pyrf_thread_map__type);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user