diff options
Diffstat (limited to 'tools/perf/util/python.c')
| -rw-r--r-- | tools/perf/util/python.c | 1721 |
1 files changed, 1497 insertions, 224 deletions
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 925e0c3e6d91..cc1019d29a5d 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -1,26 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 #include <Python.h> #include <structmember.h> #include <inttypes.h> #include <poll.h> +#include <linux/err.h> +#include <perf/cpumap.h> +#ifdef HAVE_LIBTRACEEVENT +#include <event-parse.h> +#endif +#include <perf/mmap.h> +#include "callchain.h" +#include "counts.h" #include "evlist.h" #include "evsel.h" #include "event.h" -#include "cpumap.h" +#include "expr.h" +#include "print_binary.h" +#include "record.h" +#include "strbuf.h" #include "thread_map.h" +#include "tp_pmu.h" +#include "trace-event.h" +#include "metricgroup.h" +#include "mmap.h" +#include "util/sample.h" +#include <internal/lib.h> -/* Define PyVarObject_HEAD_INIT for python 2.5 */ -#ifndef PyVarObject_HEAD_INIT -# define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size, -#endif - -struct throttle_event { - struct perf_event_header header; - u64 time; - u64 id; - u64 stream_id; -}; - -PyMODINIT_FUNC initperf(void); +PyMODINIT_FUNC PyInit_perf(void); #define member_def(type, member, ptype, help) \ { #member, ptype, \ @@ -34,12 +40,13 @@ PyMODINIT_FUNC initperf(void); struct pyrf_event { PyObject_HEAD + struct evsel *evsel; struct perf_sample sample; union perf_event 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"), \ @@ -49,34 +56,35 @@ struct pyrf_event { sample_member_def(sample_period, period, T_ULONGLONG, "event period"), \ sample_member_def(sample_cpu, cpu, T_UINT, "event cpu"), -static char pyrf_mmap_event__doc[] = PyDoc_STR("perf mmap event object."); +static const char pyrf_mmap_event__doc[] = PyDoc_STR("perf mmap event object."); static PyMemberDef pyrf_mmap_event__members[] = { sample_members member_def(perf_event_header, type, T_UINT, "event type"), - member_def(mmap_event, pid, T_UINT, "event pid"), - member_def(mmap_event, tid, T_UINT, "event tid"), - member_def(mmap_event, start, T_ULONGLONG, "start of the map"), - member_def(mmap_event, len, T_ULONGLONG, "map length"), - member_def(mmap_event, pgoff, T_ULONGLONG, "page offset"), - member_def(mmap_event, filename, T_STRING_INPLACE, "backing store"), + member_def(perf_event_header, misc, T_UINT, "event misc"), + member_def(perf_record_mmap, pid, T_UINT, "event pid"), + member_def(perf_record_mmap, tid, T_UINT, "event tid"), + 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"), { .name = NULL, }, }; -static PyObject *pyrf_mmap_event__repr(struct pyrf_event *pevent) +static PyObject *pyrf_mmap_event__repr(const struct pyrf_event *pevent) { PyObject *ret; char *s; - if (asprintf(&s, "{ type: mmap, pid: %u, tid: %u, start: %#" PRIx64 ", " - "length: %#" PRIx64 ", offset: %#" PRIx64 ", " + 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) { ret = PyErr_NoMemory(); } else { - ret = PyString_FromString(s); + ret = PyUnicode_FromString(s); free(s); } return ret; @@ -92,23 +100,23 @@ static PyTypeObject pyrf_mmap_event__type = { .tp_repr = (reprfunc)pyrf_mmap_event__repr, }; -static char pyrf_task_event__doc[] = PyDoc_STR("perf task (fork/exit) event object."); +static const char pyrf_task_event__doc[] = PyDoc_STR("perf task (fork/exit) event object."); static PyMemberDef pyrf_task_event__members[] = { sample_members member_def(perf_event_header, type, T_UINT, "event type"), - member_def(fork_event, pid, T_UINT, "event pid"), - member_def(fork_event, ppid, T_UINT, "event ppid"), - member_def(fork_event, tid, T_UINT, "event tid"), - member_def(fork_event, ptid, T_UINT, "event ptid"), - member_def(fork_event, time, T_ULONGLONG, "timestamp"), + member_def(perf_record_fork, pid, T_UINT, "event pid"), + member_def(perf_record_fork, ppid, T_UINT, "event ppid"), + member_def(perf_record_fork, tid, T_UINT, "event tid"), + member_def(perf_record_fork, ptid, T_UINT, "event ptid"), + member_def(perf_record_fork, time, T_ULONGLONG, "timestamp"), { .name = NULL, }, }; -static PyObject *pyrf_task_event__repr(struct pyrf_event *pevent) +static PyObject *pyrf_task_event__repr(const struct pyrf_event *pevent) { - return PyString_FromFormat("{ type: %s, pid: %u, ppid: %u, tid: %u, " - "ptid: %u, time: %" PRIu64 "}", + 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, @@ -127,20 +135,20 @@ static PyTypeObject pyrf_task_event__type = { .tp_repr = (reprfunc)pyrf_task_event__repr, }; -static char pyrf_comm_event__doc[] = PyDoc_STR("perf comm event object."); +static const char pyrf_comm_event__doc[] = PyDoc_STR("perf comm event object."); static PyMemberDef pyrf_comm_event__members[] = { sample_members member_def(perf_event_header, type, T_UINT, "event type"), - member_def(comm_event, pid, T_UINT, "event pid"), - member_def(comm_event, tid, T_UINT, "event tid"), - member_def(comm_event, comm, T_STRING_INPLACE, "process name"), + 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"), { .name = NULL, }, }; -static PyObject *pyrf_comm_event__repr(struct pyrf_event *pevent) +static PyObject *pyrf_comm_event__repr(const struct pyrf_event *pevent) { - return PyString_FromFormat("{ type: comm, pid: %u, tid: %u, comm: %s }", + return PyUnicode_FromFormat("{ type: comm, pid: %u, tid: %u, comm: %s }", pevent->event.comm.pid, pevent->event.comm.tid, pevent->event.comm.comm); @@ -156,23 +164,24 @@ static PyTypeObject pyrf_comm_event__type = { .tp_repr = (reprfunc)pyrf_comm_event__repr, }; -static char pyrf_throttle_event__doc[] = PyDoc_STR("perf throttle event object."); +static const char pyrf_throttle_event__doc[] = PyDoc_STR("perf throttle event object."); static PyMemberDef pyrf_throttle_event__members[] = { sample_members member_def(perf_event_header, type, T_UINT, "event type"), - member_def(throttle_event, time, T_ULONGLONG, "timestamp"), - member_def(throttle_event, id, T_ULONGLONG, "event id"), - member_def(throttle_event, stream_id, T_ULONGLONG, "event stream id"), + member_def(perf_record_throttle, time, T_ULONGLONG, "timestamp"), + member_def(perf_record_throttle, id, T_ULONGLONG, "event id"), + member_def(perf_record_throttle, stream_id, T_ULONGLONG, "event stream id"), { .name = NULL, }, }; -static PyObject *pyrf_throttle_event__repr(struct pyrf_event *pevent) +static PyObject *pyrf_throttle_event__repr(const struct pyrf_event *pevent) { - struct throttle_event *te = (struct throttle_event *)(&pevent->event.header + 1); + const struct perf_record_throttle *te = (const struct perf_record_throttle *) + (&pevent->event.header + 1); - return PyString_FromFormat("{ type: %sthrottle, time: %" PRIu64 ", id: %" PRIu64 - ", stream_id: %" PRIu64 " }", + return PyUnicode_FromFormat("{ type: %sthrottle, time: %" PRI_lu64 ", id: %" PRI_lu64 + ", stream_id: %" PRI_lu64 " }", pevent->event.header.type == PERF_RECORD_THROTTLE ? "" : "un", te->time, te->id, te->stream_id); } @@ -187,26 +196,26 @@ static PyTypeObject pyrf_throttle_event__type = { .tp_repr = (reprfunc)pyrf_throttle_event__repr, }; -static char pyrf_lost_event__doc[] = PyDoc_STR("perf lost event object."); +static const char pyrf_lost_event__doc[] = PyDoc_STR("perf lost event object."); static PyMemberDef pyrf_lost_event__members[] = { sample_members - member_def(lost_event, id, T_ULONGLONG, "event id"), - member_def(lost_event, lost, T_ULONGLONG, "number of lost events"), + member_def(perf_record_lost, id, T_ULONGLONG, "event id"), + member_def(perf_record_lost, lost, T_ULONGLONG, "number of lost events"), { .name = NULL, }, }; -static PyObject *pyrf_lost_event__repr(struct pyrf_event *pevent) +static PyObject *pyrf_lost_event__repr(const struct pyrf_event *pevent) { PyObject *ret; char *s; - if (asprintf(&s, "{ type: lost, id: %#" PRIx64 ", " - "lost: %#" PRIx64 " }", + if (asprintf(&s, "{ type: lost, id: %#" PRI_lx64 ", " + "lost: %#" PRI_lx64 " }", pevent->event.lost.id, pevent->event.lost.lost) < 0) { ret = PyErr_NoMemory(); } else { - ret = PyString_FromString(s); + ret = PyUnicode_FromString(s); free(s); } return ret; @@ -222,18 +231,18 @@ static PyTypeObject pyrf_lost_event__type = { .tp_repr = (reprfunc)pyrf_lost_event__repr, }; -static char pyrf_read_event__doc[] = PyDoc_STR("perf read event object."); +static const char pyrf_read_event__doc[] = PyDoc_STR("perf read event object."); static PyMemberDef pyrf_read_event__members[] = { sample_members - member_def(read_event, pid, T_UINT, "event pid"), - member_def(read_event, tid, T_UINT, "event tid"), + member_def(perf_record_read, pid, T_UINT, "event pid"), + member_def(perf_record_read, tid, T_UINT, "event tid"), { .name = NULL, }, }; -static PyObject *pyrf_read_event__repr(struct pyrf_event *pevent) +static PyObject *pyrf_read_event__repr(const struct pyrf_event *pevent) { - return PyString_FromFormat("{ type: read, pid: %u, tid: %u }", + return PyUnicode_FromFormat("{ type: read, pid: %u, tid: %u }", pevent->event.read.pid, pevent->event.read.tid); /* @@ -252,7 +261,7 @@ static PyTypeObject pyrf_read_event__type = { .tp_repr = (reprfunc)pyrf_read_event__repr, }; -static char pyrf_sample_event__doc[] = PyDoc_STR("perf sample event object."); +static const char pyrf_sample_event__doc[] = PyDoc_STR("perf sample event object."); static PyMemberDef pyrf_sample_event__members[] = { sample_members @@ -260,7 +269,13 @@ static PyMemberDef pyrf_sample_event__members[] = { { .name = NULL, }, }; -static PyObject *pyrf_sample_event__repr(struct pyrf_event *pevent) +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; char *s; @@ -268,12 +283,98 @@ static PyObject *pyrf_sample_event__repr(struct pyrf_event *pevent) if (asprintf(&s, "{ type: sample }") < 0) { ret = PyErr_NoMemory(); } else { - ret = PyString_FromString(s); + ret = PyUnicode_FromString(s); free(s); } return ret; } +#ifdef HAVE_LIBTRACEEVENT +static bool is_tracepoint(const struct pyrf_event *pevent) +{ + return pevent->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; + PyObject *ret = NULL; + unsigned long long val; + unsigned int offset, len; + + if (field->flags & TEP_FIELD_IS_ARRAY) { + offset = field->offset; + len = field->size; + if (field->flags & TEP_FIELD_IS_DYNAMIC) { + val = tep_read_number(pevent, data + offset, len); + offset = val; + len = offset >> 16; + offset &= 0xffff; + if (tep_field_is_relative(field->flags)) + offset += field->offset + field->size; + } + if (field->flags & TEP_FIELD_IS_STRING && + is_printable_array(data + offset, len)) { + ret = PyUnicode_FromString((char *)data + offset); + } else { + ret = PyByteArray_FromStringAndSize((const char *) data + offset, len); + field->flags &= ~TEP_FIELD_IS_STRING; + } + } else { + val = tep_read_number(pevent, data + field->offset, + field->size); + if (field->flags & TEP_FIELD_IS_POINTER) + ret = PyLong_FromUnsignedLong((unsigned long) val); + else if (field->flags & TEP_FIELD_IS_SIGNED) + ret = PyLong_FromLong((long) val); + else + ret = PyLong_FromUnsignedLong((unsigned long) val); + } + + return ret; +} + +static PyObject* +get_tracepoint_field(struct pyrf_event *pevent, PyObject *attr_name) +{ + struct evsel *evsel = pevent->evsel; + struct tep_event *tp_format = evsel__tp_format(evsel); + struct tep_format_field *field; + + if (IS_ERR_OR_NULL(tp_format)) + return NULL; + + PyObject *obj = PyObject_Str(attr_name); + if (obj == NULL) + return NULL; + + const char *str = PyUnicode_AsUTF8(obj); + if (str == NULL) { + Py_DECREF(obj); + return NULL; + } + + field = tep_find_any_field(tp_format, str); + Py_DECREF(obj); + return field ? tracepoint_field(pevent, field) : NULL; +} +#endif /* HAVE_LIBTRACEEVENT */ + +static PyObject* +pyrf_sample_event__getattro(struct pyrf_event *pevent, PyObject *attr_name) +{ + PyObject *obj = NULL; + +#ifdef HAVE_LIBTRACEEVENT + if (is_tracepoint(pevent)) + obj = get_tracepoint_field(pevent, attr_name); +#endif + + return obj ?: PyObject_GenericGetAttr((PyObject *) pevent, attr_name); +} + static PyTypeObject pyrf_sample_event__type = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "perf.sample_event", @@ -282,6 +383,44 @@ static PyTypeObject pyrf_sample_event__type = { .tp_doc = pyrf_sample_event__doc, .tp_members = pyrf_sample_event__members, .tp_repr = (reprfunc)pyrf_sample_event__repr, + .tp_getattro = (getattrofunc) pyrf_sample_event__getattro, +}; + +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_record_switch, next_prev_pid, T_UINT, "next/prev pid"), + member_def(perf_record_switch, next_prev_tid, T_UINT, "next/prev tid"), + { .name = NULL, }, +}; + +static PyObject *pyrf_context_switch_event__repr(const struct pyrf_event *pevent) +{ + PyObject *ret; + char *s; + + if (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)) < 0) { + ret = PyErr_NoMemory(); + } else { + ret = PyUnicode_FromString(s); + free(s); + } + return ret; +} + +static PyTypeObject pyrf_context_switch_event__type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "perf.context_switch_event", + .tp_basicsize = sizeof(struct pyrf_event), + .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, + .tp_doc = pyrf_context_switch_event__doc, + .tp_members = pyrf_context_switch_event__members, + .tp_repr = (reprfunc)pyrf_context_switch_event__repr, }; static int pyrf_event__setup_types(void) @@ -293,7 +432,11 @@ static int pyrf_event__setup_types(void) pyrf_lost_event__type.tp_new = pyrf_read_event__type.tp_new = 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; @@ -315,6 +458,9 @@ static int pyrf_event__setup_types(void) err = PyType_Ready(&pyrf_sample_event__type); if (err < 0) goto out; + err = PyType_Ready(&pyrf_context_switch_event__type); + if (err < 0) + goto out; out: return err; } @@ -329,16 +475,31 @@ static PyTypeObject *pyrf_event__type[] = { [PERF_RECORD_FORK] = &pyrf_task_event__type, [PERF_RECORD_READ] = &pyrf_read_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, }; -static PyObject *pyrf_event__new(union perf_event *event) +static PyObject *pyrf_event__new(const union perf_event *event) { struct pyrf_event *pevent; PyTypeObject *ptype; - if (event->header.type < PERF_RECORD_MMAP || - event->header.type > PERF_RECORD_SAMPLE) + if ((event->header.type < PERF_RECORD_MMAP || + event->header.type > PERF_RECORD_SAMPLE) && + !(event->header.type == PERF_RECORD_SWITCH || + event->header.type == PERF_RECORD_SWITCH_CPU_WIDE)) { + PyErr_Format(PyExc_TypeError, "Unexpected header type %u", + event->header.type); + 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) { + PyErr_Format(PyExc_TypeError, "Unexpected event size: %zd < %u", + sizeof(pevent->event), event->header.size); return NULL; + } ptype = pyrf_event__type[event->header.type]; pevent = PyObject_New(struct pyrf_event, ptype); @@ -350,7 +511,7 @@ static PyObject *pyrf_event__new(union perf_event *event) struct pyrf_cpu_map { PyObject_HEAD - struct cpu_map *cpus; + struct perf_cpu_map *cpus; }; static int pyrf_cpu_map__init(struct pyrf_cpu_map *pcpus, @@ -363,7 +524,7 @@ static int pyrf_cpu_map__init(struct pyrf_cpu_map *pcpus, kwlist, &cpustr)) return -1; - pcpus->cpus = cpu_map__new(cpustr); + pcpus->cpus = perf_cpu_map__new(cpustr); if (pcpus->cpus == NULL) return -1; return 0; @@ -371,25 +532,27 @@ static int pyrf_cpu_map__init(struct pyrf_cpu_map *pcpus, static void pyrf_cpu_map__delete(struct pyrf_cpu_map *pcpus) { - cpu_map__delete(pcpus->cpus); - pcpus->ob_type->tp_free((PyObject*)pcpus); + perf_cpu_map__put(pcpus->cpus); + Py_TYPE(pcpus)->tp_free((PyObject*)pcpus); } static Py_ssize_t pyrf_cpu_map__length(PyObject *obj) { struct pyrf_cpu_map *pcpus = (void *)obj; - return pcpus->cpus->nr; + return perf_cpu_map__nr(pcpus->cpus); } static PyObject *pyrf_cpu_map__item(PyObject *obj, Py_ssize_t i) { struct pyrf_cpu_map *pcpus = (void *)obj; - if (i >= pcpus->cpus->nr) + if (i >= perf_cpu_map__nr(pcpus->cpus)) { + PyErr_SetString(PyExc_IndexError, "Index out of range"); return NULL; + } - return Py_BuildValue("i", pcpus->cpus->map[i]); + return Py_BuildValue("i", perf_cpu_map__cpu(pcpus->cpus, i).cpu); } static PySequenceMethods pyrf_cpu_map__sequence_methods = { @@ -397,7 +560,7 @@ static PySequenceMethods pyrf_cpu_map__sequence_methods = { .sq_item = pyrf_cpu_map__item, }; -static char pyrf_cpu_map__doc[] = PyDoc_STR("cpu map object."); +static const char pyrf_cpu_map__doc[] = PyDoc_STR("cpu map object."); static PyTypeObject pyrf_cpu_map__type = { PyVarObject_HEAD_INIT(NULL, 0) @@ -419,20 +582,20 @@ static int pyrf_cpu_map__setup_types(void) struct pyrf_thread_map { PyObject_HEAD - struct thread_map *threads; + struct perf_thread_map *threads; }; static int pyrf_thread_map__init(struct pyrf_thread_map *pthreads, PyObject *args, PyObject *kwargs) { - static char *kwlist[] = { "pid", "tid", "uid", NULL }; - int pid = -1, tid = -1, uid = UINT_MAX; + static char *kwlist[] = { "pid", "tid", NULL }; + int pid = -1, tid = -1; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|iii", - kwlist, &pid, &tid, &uid)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", + kwlist, &pid, &tid)) return -1; - pthreads->threads = thread_map__new(pid, tid, uid); + pthreads->threads = thread_map__new(pid, tid); if (pthreads->threads == NULL) return -1; return 0; @@ -440,25 +603,27 @@ static int pyrf_thread_map__init(struct pyrf_thread_map *pthreads, static void pyrf_thread_map__delete(struct pyrf_thread_map *pthreads) { - thread_map__delete(pthreads->threads); - pthreads->ob_type->tp_free((PyObject*)pthreads); + perf_thread_map__put(pthreads->threads); + Py_TYPE(pthreads)->tp_free((PyObject*)pthreads); } static Py_ssize_t pyrf_thread_map__length(PyObject *obj) { struct pyrf_thread_map *pthreads = (void *)obj; - return pthreads->threads->nr; + return perf_thread_map__nr(pthreads->threads); } static PyObject *pyrf_thread_map__item(PyObject *obj, Py_ssize_t i) { struct pyrf_thread_map *pthreads = (void *)obj; - if (i >= pthreads->threads->nr) + if (i >= perf_thread_map__nr(pthreads->threads)) { + PyErr_SetString(PyExc_IndexError, "Index out of range"); return NULL; + } - return Py_BuildValue("i", pthreads->threads->map[i]); + return Py_BuildValue("i", perf_thread_map__pid(pthreads->threads, i)); } static PySequenceMethods pyrf_thread_map__sequence_methods = { @@ -466,7 +631,7 @@ static PySequenceMethods pyrf_thread_map__sequence_methods = { .sq_item = pyrf_thread_map__item, }; -static char pyrf_thread_map__doc[] = PyDoc_STR("thread map object."); +static const char pyrf_thread_map__doc[] = PyDoc_STR("thread map object."); static PyTypeObject pyrf_thread_map__type = { PyVarObject_HEAD_INIT(NULL, 0) @@ -485,10 +650,299 @@ static int pyrf_thread_map__setup_types(void) return PyType_Ready(&pyrf_thread_map__type); } +/** + * A python wrapper for perf_pmus that are globally owned by the pmus.c code. + */ +struct pyrf_pmu { + PyObject_HEAD + + struct perf_pmu *pmu; +}; + +static void pyrf_pmu__delete(struct pyrf_pmu *ppmu) +{ + Py_TYPE(ppmu)->tp_free((PyObject *)ppmu); +} + +static PyObject *pyrf_pmu__name(PyObject *self) +{ + struct pyrf_pmu *ppmu = (void *)self; + + return PyUnicode_FromString(ppmu->pmu->name); +} + +static bool add_to_dict(PyObject *dict, const char *key, const char *value) +{ + PyObject *pkey, *pvalue; + bool ret; + + if (value == NULL) + return true; + + pkey = PyUnicode_FromString(key); + pvalue = PyUnicode_FromString(value); + + ret = pkey && pvalue && PyDict_SetItem(dict, pkey, pvalue) == 0; + Py_XDECREF(pkey); + Py_XDECREF(pvalue); + return ret; +} + +static int pyrf_pmu__events_cb(void *state, struct pmu_event_info *info) +{ + PyObject *py_list = state; + PyObject *dict = PyDict_New(); + + if (!dict) + return -ENOMEM; + + if (!add_to_dict(dict, "name", info->name) || + !add_to_dict(dict, "alias", info->alias) || + !add_to_dict(dict, "scale_unit", info->scale_unit) || + !add_to_dict(dict, "desc", info->desc) || + !add_to_dict(dict, "long_desc", info->long_desc) || + !add_to_dict(dict, "encoding_desc", info->encoding_desc) || + !add_to_dict(dict, "topic", info->topic) || + !add_to_dict(dict, "event_type_desc", info->event_type_desc) || + !add_to_dict(dict, "str", info->str) || + !add_to_dict(dict, "deprecated", info->deprecated ? "deprecated" : NULL) || + PyList_Append(py_list, dict) != 0) { + Py_DECREF(dict); + return -ENOMEM; + } + Py_DECREF(dict); + return 0; +} + +static PyObject *pyrf_pmu__events(PyObject *self) +{ + struct pyrf_pmu *ppmu = (void *)self; + PyObject *py_list = PyList_New(0); + int ret; + + if (!py_list) + return NULL; + + ret = perf_pmu__for_each_event(ppmu->pmu, + /*skip_duplicate_pmus=*/false, + py_list, + pyrf_pmu__events_cb); + if (ret) { + Py_DECREF(py_list); + errno = -ret; + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + return py_list; +} + +static PyObject *pyrf_pmu__repr(PyObject *self) +{ + struct pyrf_pmu *ppmu = (void *)self; + + return PyUnicode_FromFormat("pmu(%s)", ppmu->pmu->name); +} + +static const char pyrf_pmu__doc[] = PyDoc_STR("perf Performance Monitoring Unit (PMU) object."); + +static PyMethodDef pyrf_pmu__methods[] = { + { + .ml_name = "events", + .ml_meth = (PyCFunction)pyrf_pmu__events, + .ml_flags = METH_NOARGS, + .ml_doc = PyDoc_STR("Returns a sequence of events encoded as a dictionaries.") + }, + { + .ml_name = "name", + .ml_meth = (PyCFunction)pyrf_pmu__name, + .ml_flags = METH_NOARGS, + .ml_doc = PyDoc_STR("Name of the PMU including suffixes.") + }, + { .ml_name = NULL, } +}; + +/** The python type for a perf.pmu. */ +static PyTypeObject pyrf_pmu__type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "perf.pmu", + .tp_basicsize = sizeof(struct pyrf_pmu), + .tp_dealloc = (destructor)pyrf_pmu__delete, + .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, + .tp_doc = pyrf_pmu__doc, + .tp_methods = pyrf_pmu__methods, + .tp_str = pyrf_pmu__name, + .tp_repr = pyrf_pmu__repr, +}; + +static int pyrf_pmu__setup_types(void) +{ + pyrf_pmu__type.tp_new = PyType_GenericNew; + return PyType_Ready(&pyrf_pmu__type); +} + + +/** A python iterator for pmus that has no equivalent in the C code. */ +struct pyrf_pmu_iterator { + PyObject_HEAD + struct perf_pmu *pmu; +}; + +static void pyrf_pmu_iterator__dealloc(struct pyrf_pmu_iterator *self) +{ + Py_TYPE(self)->tp_free((PyObject *) self); +} + +static PyObject *pyrf_pmu_iterator__new(PyTypeObject *type, PyObject *args __maybe_unused, + PyObject *kwds __maybe_unused) +{ + struct pyrf_pmu_iterator *itr = (void *)type->tp_alloc(type, 0); + + if (itr != NULL) + itr->pmu = perf_pmus__scan(/*pmu=*/NULL); + + return (PyObject *) itr; +} + +static PyObject *pyrf_pmu_iterator__iter(PyObject *self) +{ + Py_INCREF(self); + return self; +} + +static PyObject *pyrf_pmu_iterator__iternext(PyObject *self) +{ + struct pyrf_pmu_iterator *itr = (void *)self; + struct pyrf_pmu *ppmu; + + if (itr->pmu == NULL) { + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } + // Create object to return. + ppmu = PyObject_New(struct pyrf_pmu, &pyrf_pmu__type); + if (ppmu) { + ppmu->pmu = itr->pmu; + // Advance iterator. + itr->pmu = perf_pmus__scan(itr->pmu); + } + return (PyObject *)ppmu; +} + +/** The python type for the PMU iterator. */ +static PyTypeObject pyrf_pmu_iterator__type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "pmus.iterator", + .tp_doc = "Iterator for the pmus string sequence.", + .tp_basicsize = sizeof(struct pyrf_pmu_iterator), + .tp_itemsize = 0, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_new = pyrf_pmu_iterator__new, + .tp_dealloc = (destructor) pyrf_pmu_iterator__dealloc, + .tp_iter = pyrf_pmu_iterator__iter, + .tp_iternext = pyrf_pmu_iterator__iternext, +}; + +static int pyrf_pmu_iterator__setup_types(void) +{ + return PyType_Ready(&pyrf_pmu_iterator__type); +} + +static PyObject *pyrf__pmus(PyObject *self, PyObject *args) +{ + // Calling the class creates an instance of the iterator. + return PyObject_CallObject((PyObject *) &pyrf_pmu_iterator__type, /*args=*/NULL); +} + +struct pyrf_counts_values { + PyObject_HEAD + + struct perf_counts_values values; +}; + +static const char pyrf_counts_values__doc[] = PyDoc_STR("perf counts values object."); + +static void pyrf_counts_values__delete(struct pyrf_counts_values *pcounts_values) +{ + Py_TYPE(pcounts_values)->tp_free((PyObject *)pcounts_values); +} + +#define counts_values_member_def(member, ptype, help) \ + { #member, ptype, \ + offsetof(struct pyrf_counts_values, values.member), \ + 0, help } + +static PyMemberDef pyrf_counts_values_members[] = { + counts_values_member_def(val, T_ULONG, "Value of event"), + counts_values_member_def(ena, T_ULONG, "Time for which enabled"), + counts_values_member_def(run, T_ULONG, "Time for which running"), + counts_values_member_def(id, T_ULONG, "Unique ID for an event"), + counts_values_member_def(lost, T_ULONG, "Num of lost samples"), + { .name = NULL, }, +}; + +static PyObject *pyrf_counts_values_get_values(struct pyrf_counts_values *self, void *closure) +{ + PyObject *vals = PyList_New(5); + + if (!vals) + return NULL; + for (int i = 0; i < 5; i++) + PyList_SetItem(vals, i, PyLong_FromLong(self->values.values[i])); + + return vals; +} + +static int pyrf_counts_values_set_values(struct pyrf_counts_values *self, PyObject *list, + void *closure) +{ + Py_ssize_t size; + PyObject *item = NULL; + + if (!PyList_Check(list)) { + PyErr_SetString(PyExc_TypeError, "Value assigned must be a list"); + return -1; + } + + size = PyList_Size(list); + for (Py_ssize_t i = 0; i < size; i++) { + item = PyList_GetItem(list, i); + if (!PyLong_Check(item)) { + PyErr_SetString(PyExc_TypeError, "List members should be numbers"); + return -1; + } + self->values.values[i] = PyLong_AsLong(item); + } + + return 0; +} + +static PyGetSetDef pyrf_counts_values_getset[] = { + {"values", (getter)pyrf_counts_values_get_values, (setter)pyrf_counts_values_set_values, + "Name field", NULL}, + { .name = NULL, }, +}; + +static PyTypeObject pyrf_counts_values__type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "perf.counts_values", + .tp_basicsize = sizeof(struct pyrf_counts_values), + .tp_dealloc = (destructor)pyrf_counts_values__delete, + .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, + .tp_doc = pyrf_counts_values__doc, + .tp_members = pyrf_counts_values_members, + .tp_getset = pyrf_counts_values_getset, +}; + +static int pyrf_counts_values__setup_types(void) +{ + pyrf_counts_values__type.tp_new = PyType_GenericNew; + return PyType_Ready(&pyrf_counts_values__type); +} + struct pyrf_evsel { PyObject_HEAD - struct perf_evsel evsel; + struct evsel evsel; }; static int pyrf_evsel__init(struct pyrf_evsel *pevsel, @@ -515,6 +969,7 @@ static int pyrf_evsel__init(struct pyrf_evsel *pevsel, "exclude_hv", "exclude_idle", "mmap", + "context_switch", "comm", "freq", "inherit_stat", @@ -540,6 +995,7 @@ static int pyrf_evsel__init(struct pyrf_evsel *pevsel, exclude_hv = 0, exclude_idle = 0, mmap = 0, + context_switch = 0, comm = 0, freq = 1, inherit_stat = 0, @@ -552,13 +1008,13 @@ static int pyrf_evsel__init(struct pyrf_evsel *pevsel, int idx = 0; if (!PyArg_ParseTupleAndKeywords(args, kwargs, - "|iKiKKiiiiiiiiiiiiiiiiiiiiiKK", kwlist, + "|iKiKKiiiiiiiiiiiiiiiiiiiiiiKK", kwlist, &attr.type, &attr.config, &attr.sample_freq, &sample_period, &attr.sample_type, &attr.read_format, &disabled, &inherit, &pinned, &exclusive, &exclude_user, &exclude_kernel, &exclude_hv, &exclude_idle, - &mmap, &comm, &freq, &inherit_stat, + &mmap, &context_switch, &comm, &freq, &inherit_stat, &enable_on_exec, &task, &watermark, &precise_ip, &mmap_data, &sample_id_all, &attr.wakeup_events, &attr.bp_type, @@ -582,6 +1038,7 @@ static int pyrf_evsel__init(struct pyrf_evsel *pevsel, attr.exclude_hv = exclude_hv; attr.exclude_idle = exclude_idle; attr.mmap = mmap; + attr.context_switch = context_switch; attr.comm = comm; attr.freq = freq; attr.inherit_stat = inherit_stat; @@ -591,23 +1048,24 @@ static int pyrf_evsel__init(struct pyrf_evsel *pevsel, attr.precise_ip = precise_ip; attr.mmap_data = mmap_data; attr.sample_id_all = sample_id_all; + attr.size = sizeof(attr); - perf_evsel__init(&pevsel->evsel, &attr, idx); + evsel__init(&pevsel->evsel, &attr, idx); return 0; } static void pyrf_evsel__delete(struct pyrf_evsel *pevsel) { - perf_evsel__exit(&pevsel->evsel); - pevsel->ob_type->tp_free((PyObject*)pevsel); + evsel__exit(&pevsel->evsel); + Py_TYPE(pevsel)->tp_free((PyObject*)pevsel); } static PyObject *pyrf_evsel__open(struct pyrf_evsel *pevsel, PyObject *args, PyObject *kwargs) { - struct perf_evsel *evsel = &pevsel->evsel; - struct cpu_map *cpus = NULL; - struct thread_map *threads = NULL; + struct evsel *evsel = &pevsel->evsel; + struct perf_cpu_map *cpus = NULL; + struct perf_thread_map *threads = NULL; PyObject *pcpus = NULL, *pthreads = NULL; int group = 0, inherit = 0; static char *kwlist[] = { "cpus", "threads", "group", "inherit", NULL }; @@ -622,12 +1080,12 @@ static PyObject *pyrf_evsel__open(struct pyrf_evsel *pevsel, if (pcpus != NULL) cpus = ((struct pyrf_cpu_map *)pcpus)->cpus; - evsel->attr.inherit = inherit; + evsel->core.attr.inherit = inherit; /* * This will group just the fds for this single evsel, to group * multiple events, use evlist.open(). */ - if (perf_evsel__open(evsel, cpus, threads) < 0) { + if (evsel__open(evsel, cpus, threads) < 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; } @@ -636,6 +1094,106 @@ static PyObject *pyrf_evsel__open(struct pyrf_evsel *pevsel, return Py_None; } +static PyObject *pyrf_evsel__cpus(struct pyrf_evsel *pevsel) +{ + 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(pevsel->evsel.core.cpus); + + return (PyObject *)pcpu_map; +} + +static PyObject *pyrf_evsel__threads(struct pyrf_evsel *pevsel) +{ + struct pyrf_thread_map *pthread_map = + PyObject_New(struct pyrf_thread_map, &pyrf_thread_map__type); + + if (pthread_map) + pthread_map->threads = perf_thread_map__get(pevsel->evsel.core.threads); + + return (PyObject *)pthread_map; +} + +/* + * Ensure evsel's counts and prev_raw_counts are allocated, the latter + * used by tool PMUs to compute the cumulative count as expected by + * stat's process_counter_values. + */ +static int evsel__ensure_counts(struct evsel *evsel) +{ + int nthreads, ncpus; + + if (evsel->counts != NULL) + return 0; + + nthreads = perf_thread_map__nr(evsel->core.threads); + ncpus = perf_cpu_map__nr(evsel->core.cpus); + + evsel->counts = perf_counts__new(ncpus, nthreads); + if (evsel->counts == NULL) + return -ENOMEM; + + evsel->prev_raw_counts = perf_counts__new(ncpus, nthreads); + if (evsel->prev_raw_counts == NULL) + return -ENOMEM; + + return 0; +} + +static PyObject *pyrf_evsel__read(struct pyrf_evsel *pevsel, + PyObject *args, PyObject *kwargs) +{ + struct evsel *evsel = &pevsel->evsel; + int cpu = 0, cpu_idx, thread = 0, thread_idx; + struct perf_counts_values *old_count, *new_count; + struct pyrf_counts_values *count_values = PyObject_New(struct pyrf_counts_values, + &pyrf_counts_values__type); + + if (!count_values) + return NULL; + + if (!PyArg_ParseTuple(args, "ii", &cpu, &thread)) + return NULL; + + cpu_idx = perf_cpu_map__idx(evsel->core.cpus, (struct perf_cpu){.cpu = cpu}); + if (cpu_idx < 0) { + PyErr_Format(PyExc_TypeError, "CPU %d is not part of evsel's CPUs", cpu); + return NULL; + } + thread_idx = perf_thread_map__idx(evsel->core.threads, thread); + if (thread_idx < 0) { + PyErr_Format(PyExc_TypeError, "Thread %d is not part of evsel's threads", + thread); + return NULL; + } + + if (evsel__ensure_counts(evsel)) + return PyErr_NoMemory(); + + /* Set up pointers to the old and newly read counter values. */ + old_count = perf_counts(evsel->prev_raw_counts, cpu_idx, thread_idx); + new_count = perf_counts(evsel->counts, cpu_idx, thread_idx); + /* Update the value in evsel->counts. */ + evsel__read_counter(evsel, cpu_idx, thread_idx); + /* Copy the value and turn it into the delta from old_count. */ + count_values->values = *new_count; + count_values->values.val -= old_count->val; + count_values->values.ena -= old_count->ena; + count_values->values.run -= old_count->run; + /* Save the new count over the old_count for the next read. */ + *old_count = *new_count; + return (PyObject *)count_values; +} + +static PyObject *pyrf_evsel__str(PyObject *self) +{ + struct pyrf_evsel *pevsel = (void *)self; + struct evsel *evsel = &pevsel->evsel; + + return PyUnicode_FromFormat("evsel(%s/%s/)", evsel__pmu_name(evsel), evsel__name(evsel)); +} + static PyMethodDef pyrf_evsel__methods[] = { { .ml_name = "open", @@ -643,10 +1201,50 @@ static PyMethodDef pyrf_evsel__methods[] = { .ml_flags = METH_VARARGS | METH_KEYWORDS, .ml_doc = PyDoc_STR("open the event selector file descriptor table.") }, + { + .ml_name = "cpus", + .ml_meth = (PyCFunction)pyrf_evsel__cpus, + .ml_flags = METH_NOARGS, + .ml_doc = PyDoc_STR("CPUs the event is to be used with.") + }, + { + .ml_name = "threads", + .ml_meth = (PyCFunction)pyrf_evsel__threads, + .ml_flags = METH_NOARGS, + .ml_doc = PyDoc_STR("threads the event is to be used with.") + }, + { + .ml_name = "read", + .ml_meth = (PyCFunction)pyrf_evsel__read, + .ml_flags = METH_VARARGS | METH_KEYWORDS, + .ml_doc = PyDoc_STR("read counters") + }, { .ml_name = NULL, } }; -static char pyrf_evsel__doc[] = PyDoc_STR("perf event selector list object."); +#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 = { PyVarObject_HEAD_INIT(NULL, 0) @@ -655,8 +1253,11 @@ 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, + .tp_repr = pyrf_evsel__str, }; static int pyrf_evsel__setup_types(void) @@ -668,35 +1269,217 @@ static int pyrf_evsel__setup_types(void) struct pyrf_evlist { PyObject_HEAD - struct perf_evlist evlist; + struct evlist evlist; }; static int pyrf_evlist__init(struct pyrf_evlist *pevlist, PyObject *args, PyObject *kwargs __maybe_unused) { PyObject *pcpus = NULL, *pthreads = NULL; - struct cpu_map *cpus; - struct thread_map *threads; + struct perf_cpu_map *cpus; + struct perf_thread_map *threads; if (!PyArg_ParseTuple(args, "OO", &pcpus, &pthreads)) return -1; threads = ((struct pyrf_thread_map *)pthreads)->threads; cpus = ((struct pyrf_cpu_map *)pcpus)->cpus; - perf_evlist__init(&pevlist->evlist, cpus, threads); + evlist__init(&pevlist->evlist, cpus, threads); return 0; } static void pyrf_evlist__delete(struct pyrf_evlist *pevlist) { - perf_evlist__exit(&pevlist->evlist); - pevlist->ob_type->tp_free((PyObject*)pevlist); + evlist__exit(&pevlist->evlist); + 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__metrics(struct pyrf_evlist *pevlist) +{ + PyObject *list = PyList_New(/*len=*/0); + struct rb_node *node; + + if (!list) + return NULL; + + for (node = rb_first_cached(&pevlist->evlist.metric_events.entries); node; + node = rb_next(node)) { + struct metric_event *me = container_of(node, struct metric_event, nd); + struct list_head *pos; + + list_for_each(pos, &me->head) { + struct metric_expr *expr = container_of(pos, struct metric_expr, nd); + PyObject *str = PyUnicode_FromString(expr->metric_name); + + if (!str || PyList_Append(list, str) != 0) { + Py_DECREF(list); + return NULL; + } + Py_DECREF(str); + } + } + return list; +} + +static int prepare_metric(const struct metric_expr *mexp, + const struct evsel *evsel, + struct expr_parse_ctx *pctx, + int cpu_idx, int thread_idx) +{ + struct evsel * const *metric_events = mexp->metric_events; + struct metric_ref *metric_refs = mexp->metric_refs; + + for (int i = 0; metric_events[i]; i++) { + struct evsel *cur = metric_events[i]; + double val, ena, run; + int ret, source_count = 0; + struct perf_counts_values *old_count, *new_count; + char *n = strdup(evsel__metric_id(cur)); + + if (!n) + return -ENOMEM; + + /* + * If there are multiple uncore PMUs and we're not reading the + * leader's stats, determine the stats for the appropriate + * uncore PMU. + */ + if (evsel && evsel->metric_leader && + evsel->pmu != evsel->metric_leader->pmu && + cur->pmu == evsel->metric_leader->pmu) { + struct evsel *pos; + + evlist__for_each_entry(evsel->evlist, pos) { + if (pos->pmu != evsel->pmu) + continue; + if (pos->metric_leader != cur) + continue; + cur = pos; + source_count = 1; + break; + } + } + + if (source_count == 0) + source_count = evsel__source_count(cur); + + ret = evsel__ensure_counts(cur); + if (ret) + return ret; + + /* Set up pointers to the old and newly read counter values. */ + old_count = perf_counts(cur->prev_raw_counts, cpu_idx, thread_idx); + new_count = perf_counts(cur->counts, cpu_idx, thread_idx); + /* Update the value in cur->counts. */ + evsel__read_counter(cur, cpu_idx, thread_idx); + + val = new_count->val - old_count->val; + ena = new_count->ena - old_count->ena; + run = new_count->run - old_count->run; + + if (ena != run && run != 0) + val = val * ena / run; + ret = expr__add_id_val_source_count(pctx, n, val, source_count); + if (ret) + return ret; + } + + for (int i = 0; metric_refs && metric_refs[i].metric_name; i++) { + int ret = expr__add_ref(pctx, &metric_refs[i]); + + if (ret) + return ret; + } + + return 0; +} + +static PyObject *pyrf_evlist__compute_metric(struct pyrf_evlist *pevlist, + PyObject *args, PyObject *kwargs) +{ + int ret, cpu = 0, cpu_idx = 0, thread = 0, thread_idx = 0; + const char *metric; + struct rb_node *node; + struct metric_expr *mexp = NULL; + struct expr_parse_ctx *pctx; + double result = 0; + struct evsel *metric_evsel = NULL; + + if (!PyArg_ParseTuple(args, "sii", &metric, &cpu, &thread)) + return NULL; + + for (node = rb_first_cached(&pevlist->evlist.metric_events.entries); + mexp == NULL && node; + node = rb_next(node)) { + struct metric_event *me = container_of(node, struct metric_event, nd); + struct list_head *pos; + + list_for_each(pos, &me->head) { + struct metric_expr *e = container_of(pos, struct metric_expr, nd); + struct evsel *pos2; + + if (strcmp(e->metric_name, metric)) + continue; + + if (e->metric_events[0] == NULL) + continue; + + evlist__for_each_entry(&pevlist->evlist, pos2) { + if (pos2->metric_leader != e->metric_events[0]) + continue; + cpu_idx = perf_cpu_map__idx(pos2->core.cpus, + (struct perf_cpu){.cpu = cpu}); + if (cpu_idx < 0) + continue; + + thread_idx = perf_thread_map__idx(pos2->core.threads, thread); + if (thread_idx < 0) + continue; + metric_evsel = pos2; + mexp = e; + goto done; + } + } + } +done: + if (!mexp) { + PyErr_Format(PyExc_TypeError, "Unknown metric '%s' for CPU '%d' and thread '%d'", + metric, cpu, thread); + return NULL; + } + + pctx = expr__ctx_new(); + if (!pctx) + return PyErr_NoMemory(); + + ret = prepare_metric(mexp, metric_evsel, pctx, cpu_idx, thread_idx); + if (ret) { + expr__ctx_free(pctx); + errno = -ret; + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + if (expr__parse(&result, pctx, mexp->metric_expr)) + result = 0.0; + + expr__ctx_free(pctx); + return PyFloat_FromDouble(result); } static PyObject *pyrf_evlist__mmap(struct pyrf_evlist *pevlist, PyObject *args, PyObject *kwargs) { - struct perf_evlist *evlist = &pevlist->evlist; + struct evlist *evlist = &pevlist->evlist; static char *kwlist[] = { "pages", "overwrite", NULL }; int pages = 128, overwrite = false; @@ -704,7 +1487,7 @@ static PyObject *pyrf_evlist__mmap(struct pyrf_evlist *pevlist, &pages, &overwrite)) return NULL; - if (perf_evlist__mmap(evlist, pages, overwrite) < 0) { + if (evlist__mmap(evlist, pages) < 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; } @@ -716,14 +1499,14 @@ static PyObject *pyrf_evlist__mmap(struct pyrf_evlist *pevlist, static PyObject *pyrf_evlist__poll(struct pyrf_evlist *pevlist, PyObject *args, PyObject *kwargs) { - struct perf_evlist *evlist = &pevlist->evlist; + struct evlist *evlist = &pevlist->evlist; static char *kwlist[] = { "timeout", NULL }; int timeout = -1, n; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &timeout)) return NULL; - n = poll(evlist->pollfd, evlist->nr_fds, timeout); + n = evlist__poll(evlist, timeout); if (n < 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; @@ -736,18 +1519,14 @@ static PyObject *pyrf_evlist__get_pollfd(struct pyrf_evlist *pevlist, PyObject *args __maybe_unused, PyObject *kwargs __maybe_unused) { - struct perf_evlist *evlist = &pevlist->evlist; + struct evlist *evlist = &pevlist->evlist; PyObject *list = PyList_New(0); int i; - for (i = 0; i < evlist->nr_fds; ++i) { + for (i = 0; i < evlist->core.pollfd.nr; ++i) { PyObject *file; - FILE *fp = fdopen(evlist->pollfd[i].fd, "r"); - - if (fp == NULL) - goto free_list; - - file = PyFile_FromFile(fp, "perf", "r", NULL); + file = PyFile_FromFd(evlist->core.pollfd.entries[i].fd, "perf", "r", -1, + NULL, NULL, NULL, 0); if (file == NULL) goto free_list; @@ -755,7 +1534,7 @@ static PyObject *pyrf_evlist__get_pollfd(struct pyrf_evlist *pevlist, Py_DECREF(file); goto free_list; } - + Py_DECREF(file); } @@ -769,49 +1548,88 @@ static PyObject *pyrf_evlist__add(struct pyrf_evlist *pevlist, PyObject *args, PyObject *kwargs __maybe_unused) { - struct perf_evlist *evlist = &pevlist->evlist; + struct evlist *evlist = &pevlist->evlist; PyObject *pevsel; - struct perf_evsel *evsel; + struct evsel *evsel; if (!PyArg_ParseTuple(args, "O", &pevsel)) return NULL; Py_INCREF(pevsel); evsel = &((struct pyrf_evsel *)pevsel)->evsel; - evsel->idx = evlist->nr_entries; - perf_evlist__add(evlist, evsel); + evsel->core.idx = evlist->core.nr_entries; + evlist__add(evlist, evsel); + + return Py_BuildValue("i", evlist->core.nr_entries); +} + +static struct mmap *get_md(struct evlist *evlist, int cpu) +{ + int i; + + for (i = 0; i < evlist->core.nr_mmaps; i++) { + struct mmap *md = &evlist->mmap[i]; - return Py_BuildValue("i", evlist->nr_entries); + if (md->core.cpu.cpu == cpu) + return md; + } + + return NULL; } static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, PyObject *args, PyObject *kwargs) { - struct perf_evlist *evlist = &pevlist->evlist; + struct evlist *evlist = &pevlist->evlist; union perf_event *event; int sample_id_all = 1, cpu; static char *kwlist[] = { "cpu", "sample_id_all", NULL }; + struct mmap *md; int err; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|i", kwlist, &cpu, &sample_id_all)) return NULL; - event = perf_evlist__mmap_read(evlist, cpu); + md = get_md(evlist, cpu); + if (!md) { + PyErr_Format(PyExc_TypeError, "Unknown CPU '%d'", cpu); + return NULL; + } + + if (perf_mmap__read_init(&md->core) < 0) + goto end; + + event = perf_mmap__read_event(&md->core); if (event != NULL) { PyObject *pyevent = pyrf_event__new(event); struct pyrf_event *pevent = (struct pyrf_event *)pyevent; + struct evsel *evsel; if (pyevent == NULL) return PyErr_NoMemory(); - err = perf_evlist__parse_sample(evlist, event, &pevent->sample); - if (err) + evsel = evlist__event2evsel(evlist, event); + if (!evsel) { + Py_DECREF(pyevent); + Py_INCREF(Py_None); + return Py_None; + } + + pevent->evsel = evsel; + + perf_mmap__consume(&md->core); + + err = evsel__parse_sample(evsel, &pevent->event, &pevent->sample); + if (err) { + Py_DECREF(pyevent); return PyErr_Format(PyExc_OSError, "perf: can't parse sample, err=%d", err); + } + return pyevent; } - +end: Py_INCREF(Py_None); return Py_None; } @@ -819,17 +1637,9 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, static PyObject *pyrf_evlist__open(struct pyrf_evlist *pevlist, PyObject *args, PyObject *kwargs) { - struct perf_evlist *evlist = &pevlist->evlist; - int group = 0; - static char *kwlist[] = { "group", NULL }; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOii", kwlist, &group)) - return NULL; - - if (group) - perf_evlist__set_leader(evlist); + struct evlist *evlist = &pevlist->evlist; - if (perf_evlist__open(evlist) < 0) { + if (evlist__open(evlist) < 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; } @@ -838,8 +1648,75 @@ static PyObject *pyrf_evlist__open(struct pyrf_evlist *pevlist, return Py_None; } +static PyObject *pyrf_evlist__close(struct pyrf_evlist *pevlist) +{ + struct evlist *evlist = &pevlist->evlist; + + evlist__close(evlist); + + Py_INCREF(Py_None); + 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); + 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 = "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 = "metrics", + .ml_meth = (PyCFunction)pyrf_evlist__metrics, + .ml_flags = METH_NOARGS, + .ml_doc = PyDoc_STR("List of metric names within the evlist.") + }, + { + .ml_name = "compute_metric", + .ml_meth = (PyCFunction)pyrf_evlist__compute_metric, + .ml_flags = METH_VARARGS | METH_KEYWORDS, + .ml_doc = PyDoc_STR("compute metric for given name, cpu and thread") + }, + { .ml_name = "mmap", .ml_meth = (PyCFunction)pyrf_evlist__mmap, .ml_flags = METH_VARARGS | METH_KEYWORDS, @@ -852,6 +1729,12 @@ static PyMethodDef pyrf_evlist__methods[] = { .ml_doc = PyDoc_STR("open the file descriptors.") }, { + .ml_name = "close", + .ml_meth = (PyCFunction)pyrf_evlist__close, + .ml_flags = METH_NOARGS, + .ml_doc = PyDoc_STR("close the file descriptors.") + }, + { .ml_name = "poll", .ml_meth = (PyCFunction)pyrf_evlist__poll, .ml_flags = METH_VARARGS | METH_KEYWORDS, @@ -875,6 +1758,24 @@ 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, + .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, } }; @@ -882,30 +1783,57 @@ static Py_ssize_t pyrf_evlist__length(PyObject *obj) { struct pyrf_evlist *pevlist = (void *)obj; - return pevlist->evlist.nr_entries; + return pevlist->evlist.core.nr_entries; } static PyObject *pyrf_evlist__item(PyObject *obj, Py_ssize_t i) { struct pyrf_evlist *pevlist = (void *)obj; - struct perf_evsel *pos; + struct evsel *pos; - if (i >= pevlist->evlist.nr_entries) + if (i >= pevlist->evlist.core.nr_entries) { + PyErr_SetString(PyExc_IndexError, "Index out of range"); return NULL; + } - list_for_each_entry(pos, &pevlist->evlist.entries, node) + evlist__for_each_entry(&pevlist->evlist, pos) { if (i-- == 0) break; + } return Py_BuildValue("O", container_of(pos, struct pyrf_evsel, evsel)); } +static PyObject *pyrf_evlist__str(PyObject *self) +{ + struct pyrf_evlist *pevlist = (void *)self; + struct evsel *pos; + struct strbuf sb = STRBUF_INIT; + bool first = true; + PyObject *result; + + strbuf_addstr(&sb, "evlist(["); + evlist__for_each_entry(&pevlist->evlist, pos) { + if (!first) + strbuf_addch(&sb, ','); + if (!pos->pmu) + strbuf_addstr(&sb, evsel__name(pos)); + else + strbuf_addf(&sb, "%s/%s/", pos->pmu->name, evsel__name(pos)); + first = false; + } + strbuf_addstr(&sb, "])"); + result = PyUnicode_FromString(sb.buf); + strbuf_release(&sb); + return result; +} + static PySequenceMethods pyrf_evlist__sequence_methods = { .sq_length = pyrf_evlist__length, .sq_item = pyrf_evlist__item, }; -static char pyrf_evlist__doc[] = PyDoc_STR("perf event selector list object."); +static const char pyrf_evlist__doc[] = PyDoc_STR("perf event selector list object."); static PyTypeObject pyrf_evlist__type = { PyVarObject_HEAD_INIT(NULL, 0) @@ -917,6 +1845,8 @@ static PyTypeObject pyrf_evlist__type = { .tp_doc = pyrf_evlist__doc, .tp_methods = pyrf_evlist__methods, .tp_init = (initproc)pyrf_evlist__init, + .tp_repr = pyrf_evlist__str, + .tp_str = pyrf_evlist__str, }; static int pyrf_evlist__setup_types(void) @@ -925,96 +1855,417 @@ static int pyrf_evlist__setup_types(void) return PyType_Ready(&pyrf_evlist__type); } -static struct { +#define PERF_CONST(name) { #name, PERF_##name } + +struct perf_constant { const char *name; int value; -} perf__constants[] = { - { "TYPE_HARDWARE", PERF_TYPE_HARDWARE }, - { "TYPE_SOFTWARE", PERF_TYPE_SOFTWARE }, - { "TYPE_TRACEPOINT", PERF_TYPE_TRACEPOINT }, - { "TYPE_HW_CACHE", PERF_TYPE_HW_CACHE }, - { "TYPE_RAW", PERF_TYPE_RAW }, - { "TYPE_BREAKPOINT", PERF_TYPE_BREAKPOINT }, - - { "COUNT_HW_CPU_CYCLES", PERF_COUNT_HW_CPU_CYCLES }, - { "COUNT_HW_INSTRUCTIONS", PERF_COUNT_HW_INSTRUCTIONS }, - { "COUNT_HW_CACHE_REFERENCES", PERF_COUNT_HW_CACHE_REFERENCES }, - { "COUNT_HW_CACHE_MISSES", PERF_COUNT_HW_CACHE_MISSES }, - { "COUNT_HW_BRANCH_INSTRUCTIONS", PERF_COUNT_HW_BRANCH_INSTRUCTIONS }, - { "COUNT_HW_BRANCH_MISSES", PERF_COUNT_HW_BRANCH_MISSES }, - { "COUNT_HW_BUS_CYCLES", PERF_COUNT_HW_BUS_CYCLES }, - { "COUNT_HW_CACHE_L1D", PERF_COUNT_HW_CACHE_L1D }, - { "COUNT_HW_CACHE_L1I", PERF_COUNT_HW_CACHE_L1I }, - { "COUNT_HW_CACHE_LL", PERF_COUNT_HW_CACHE_LL }, - { "COUNT_HW_CACHE_DTLB", PERF_COUNT_HW_CACHE_DTLB }, - { "COUNT_HW_CACHE_ITLB", PERF_COUNT_HW_CACHE_ITLB }, - { "COUNT_HW_CACHE_BPU", PERF_COUNT_HW_CACHE_BPU }, - { "COUNT_HW_CACHE_OP_READ", PERF_COUNT_HW_CACHE_OP_READ }, - { "COUNT_HW_CACHE_OP_WRITE", PERF_COUNT_HW_CACHE_OP_WRITE }, - { "COUNT_HW_CACHE_OP_PREFETCH", PERF_COUNT_HW_CACHE_OP_PREFETCH }, - { "COUNT_HW_CACHE_RESULT_ACCESS", PERF_COUNT_HW_CACHE_RESULT_ACCESS }, - { "COUNT_HW_CACHE_RESULT_MISS", PERF_COUNT_HW_CACHE_RESULT_MISS }, - - { "COUNT_HW_STALLED_CYCLES_FRONTEND", PERF_COUNT_HW_STALLED_CYCLES_FRONTEND }, - { "COUNT_HW_STALLED_CYCLES_BACKEND", PERF_COUNT_HW_STALLED_CYCLES_BACKEND }, - - { "COUNT_SW_CPU_CLOCK", PERF_COUNT_SW_CPU_CLOCK }, - { "COUNT_SW_TASK_CLOCK", PERF_COUNT_SW_TASK_CLOCK }, - { "COUNT_SW_PAGE_FAULTS", PERF_COUNT_SW_PAGE_FAULTS }, - { "COUNT_SW_CONTEXT_SWITCHES", PERF_COUNT_SW_CONTEXT_SWITCHES }, - { "COUNT_SW_CPU_MIGRATIONS", PERF_COUNT_SW_CPU_MIGRATIONS }, - { "COUNT_SW_PAGE_FAULTS_MIN", PERF_COUNT_SW_PAGE_FAULTS_MIN }, - { "COUNT_SW_PAGE_FAULTS_MAJ", PERF_COUNT_SW_PAGE_FAULTS_MAJ }, - { "COUNT_SW_ALIGNMENT_FAULTS", PERF_COUNT_SW_ALIGNMENT_FAULTS }, - { "COUNT_SW_EMULATION_FAULTS", PERF_COUNT_SW_EMULATION_FAULTS }, - - { "SAMPLE_IP", PERF_SAMPLE_IP }, - { "SAMPLE_TID", PERF_SAMPLE_TID }, - { "SAMPLE_TIME", PERF_SAMPLE_TIME }, - { "SAMPLE_ADDR", PERF_SAMPLE_ADDR }, - { "SAMPLE_READ", PERF_SAMPLE_READ }, - { "SAMPLE_CALLCHAIN", PERF_SAMPLE_CALLCHAIN }, - { "SAMPLE_ID", PERF_SAMPLE_ID }, - { "SAMPLE_CPU", PERF_SAMPLE_CPU }, - { "SAMPLE_PERIOD", PERF_SAMPLE_PERIOD }, - { "SAMPLE_STREAM_ID", PERF_SAMPLE_STREAM_ID }, - { "SAMPLE_RAW", PERF_SAMPLE_RAW }, - - { "FORMAT_TOTAL_TIME_ENABLED", PERF_FORMAT_TOTAL_TIME_ENABLED }, - { "FORMAT_TOTAL_TIME_RUNNING", PERF_FORMAT_TOTAL_TIME_RUNNING }, - { "FORMAT_ID", PERF_FORMAT_ID }, - { "FORMAT_GROUP", PERF_FORMAT_GROUP }, - - { "RECORD_MMAP", PERF_RECORD_MMAP }, - { "RECORD_LOST", PERF_RECORD_LOST }, - { "RECORD_COMM", PERF_RECORD_COMM }, - { "RECORD_EXIT", PERF_RECORD_EXIT }, - { "RECORD_THROTTLE", PERF_RECORD_THROTTLE }, - { "RECORD_UNTHROTTLE", PERF_RECORD_UNTHROTTLE }, - { "RECORD_FORK", PERF_RECORD_FORK }, - { "RECORD_READ", PERF_RECORD_READ }, - { "RECORD_SAMPLE", PERF_RECORD_SAMPLE }, +}; + +static const struct perf_constant perf__constants[] = { + PERF_CONST(TYPE_HARDWARE), + PERF_CONST(TYPE_SOFTWARE), + PERF_CONST(TYPE_TRACEPOINT), + PERF_CONST(TYPE_HW_CACHE), + PERF_CONST(TYPE_RAW), + PERF_CONST(TYPE_BREAKPOINT), + + PERF_CONST(COUNT_HW_CPU_CYCLES), + PERF_CONST(COUNT_HW_INSTRUCTIONS), + PERF_CONST(COUNT_HW_CACHE_REFERENCES), + PERF_CONST(COUNT_HW_CACHE_MISSES), + PERF_CONST(COUNT_HW_BRANCH_INSTRUCTIONS), + PERF_CONST(COUNT_HW_BRANCH_MISSES), + PERF_CONST(COUNT_HW_BUS_CYCLES), + PERF_CONST(COUNT_HW_CACHE_L1D), + PERF_CONST(COUNT_HW_CACHE_L1I), + PERF_CONST(COUNT_HW_CACHE_LL), + PERF_CONST(COUNT_HW_CACHE_DTLB), + PERF_CONST(COUNT_HW_CACHE_ITLB), + PERF_CONST(COUNT_HW_CACHE_BPU), + PERF_CONST(COUNT_HW_CACHE_OP_READ), + PERF_CONST(COUNT_HW_CACHE_OP_WRITE), + PERF_CONST(COUNT_HW_CACHE_OP_PREFETCH), + PERF_CONST(COUNT_HW_CACHE_RESULT_ACCESS), + PERF_CONST(COUNT_HW_CACHE_RESULT_MISS), + + PERF_CONST(COUNT_HW_STALLED_CYCLES_FRONTEND), + PERF_CONST(COUNT_HW_STALLED_CYCLES_BACKEND), + + PERF_CONST(COUNT_SW_CPU_CLOCK), + PERF_CONST(COUNT_SW_TASK_CLOCK), + PERF_CONST(COUNT_SW_PAGE_FAULTS), + PERF_CONST(COUNT_SW_CONTEXT_SWITCHES), + PERF_CONST(COUNT_SW_CPU_MIGRATIONS), + PERF_CONST(COUNT_SW_PAGE_FAULTS_MIN), + PERF_CONST(COUNT_SW_PAGE_FAULTS_MAJ), + PERF_CONST(COUNT_SW_ALIGNMENT_FAULTS), + PERF_CONST(COUNT_SW_EMULATION_FAULTS), + PERF_CONST(COUNT_SW_DUMMY), + + PERF_CONST(SAMPLE_IP), + PERF_CONST(SAMPLE_TID), + PERF_CONST(SAMPLE_TIME), + PERF_CONST(SAMPLE_ADDR), + PERF_CONST(SAMPLE_READ), + PERF_CONST(SAMPLE_CALLCHAIN), + PERF_CONST(SAMPLE_ID), + PERF_CONST(SAMPLE_CPU), + PERF_CONST(SAMPLE_PERIOD), + PERF_CONST(SAMPLE_STREAM_ID), + PERF_CONST(SAMPLE_RAW), + + PERF_CONST(FORMAT_TOTAL_TIME_ENABLED), + PERF_CONST(FORMAT_TOTAL_TIME_RUNNING), + PERF_CONST(FORMAT_ID), + PERF_CONST(FORMAT_GROUP), + + PERF_CONST(RECORD_MMAP), + PERF_CONST(RECORD_LOST), + PERF_CONST(RECORD_COMM), + PERF_CONST(RECORD_EXIT), + PERF_CONST(RECORD_THROTTLE), + PERF_CONST(RECORD_UNTHROTTLE), + PERF_CONST(RECORD_FORK), + PERF_CONST(RECORD_READ), + PERF_CONST(RECORD_SAMPLE), + PERF_CONST(RECORD_MMAP2), + PERF_CONST(RECORD_AUX), + PERF_CONST(RECORD_ITRACE_START), + PERF_CONST(RECORD_LOST_SAMPLES), + PERF_CONST(RECORD_SWITCH), + PERF_CONST(RECORD_SWITCH_CPU_WIDE), + + PERF_CONST(RECORD_MISC_SWITCH_OUT), { .name = NULL, }, }; +static PyObject *pyrf__tracepoint(struct pyrf_evsel *pevsel, + PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = { "sys", "name", NULL }; + char *sys = NULL; + char *name = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ss", kwlist, + &sys, &name)) + return NULL; + + return PyLong_FromLong(tp_pmu__id(sys, name)); +} + +static PyObject *pyrf_evsel__from_evsel(struct evsel *evsel) +{ + struct pyrf_evsel *pevsel = PyObject_New(struct pyrf_evsel, &pyrf_evsel__type); + + if (!pevsel) + return NULL; + + memset(&pevsel->evsel, 0, sizeof(pevsel->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; +} + +static int evlist__pos(struct evlist *evlist, struct evsel *evsel) +{ + struct evsel *pos; + int idx = 0; + + evlist__for_each_entry(evlist, pos) { + if (evsel == pos) + return idx; + idx++; + } + return -1; +} + +static struct evsel *evlist__at(struct evlist *evlist, int idx) +{ + struct evsel *pos; + int idx2 = 0; + + evlist__for_each_entry(evlist, pos) { + if (idx == idx2) + return pos; + idx2++; + } + return NULL; +} + +static PyObject *pyrf_evlist__from_evlist(struct evlist *evlist) +{ + struct pyrf_evlist *pevlist = PyObject_New(struct pyrf_evlist, &pyrf_evlist__type); + struct evsel *pos; + struct rb_node *node; + + if (!pevlist) + return NULL; + + memset(&pevlist->evlist, 0, sizeof(pevlist->evlist)); + evlist__init(&pevlist->evlist, evlist->core.all_cpus, evlist->core.threads); + evlist__for_each_entry(evlist, pos) { + struct pyrf_evsel *pevsel = (void *)pyrf_evsel__from_evsel(pos); + + evlist__add(&pevlist->evlist, &pevsel->evsel); + } + evlist__for_each_entry(&pevlist->evlist, pos) { + struct evsel *leader = evsel__leader(pos); + + if (pos != leader) { + int idx = evlist__pos(evlist, leader); + + if (idx >= 0) + evsel__set_leader(pos, evlist__at(&pevlist->evlist, idx)); + else if (leader == NULL) + evsel__set_leader(pos, pos); + } + + leader = pos->metric_leader; + + if (pos != leader) { + int idx = evlist__pos(evlist, leader); + + if (idx >= 0) + pos->metric_leader = evlist__at(&pevlist->evlist, idx); + else if (leader == NULL) + pos->metric_leader = pos; + } + } + metricgroup__copy_metric_events(&pevlist->evlist, /*cgrp=*/NULL, + &pevlist->evlist.metric_events, + &evlist->metric_events); + for (node = rb_first_cached(&pevlist->evlist.metric_events.entries); node; + node = rb_next(node)) { + struct metric_event *me = container_of(node, struct metric_event, nd); + struct list_head *mpos; + int idx = evlist__pos(evlist, me->evsel); + + if (idx >= 0) + me->evsel = evlist__at(&pevlist->evlist, idx); + list_for_each(mpos, &me->head) { + struct metric_expr *e = container_of(mpos, struct metric_expr, nd); + + for (int j = 0; e->metric_events[j]; j++) { + idx = evlist__pos(evlist, e->metric_events[j]); + if (idx >= 0) + e->metric_events[j] = evlist__at(&pevlist->evlist, idx); + } + } + } + return (PyObject *)pevlist; +} + +static PyObject *pyrf__parse_events(PyObject *self, PyObject *args) +{ + const char *input; + 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|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, cpus, threads); + if (parse_events(&evlist, input, &err)) { + parse_events_error__print(&err, input); + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + result = pyrf_evlist__from_evlist(&evlist); + evlist__exit(&evlist); + return result; +} + +static PyObject *pyrf__parse_metrics(PyObject *self, PyObject *args) +{ + const char *input, *pmu = NULL; + struct evlist evlist = {}; + PyObject *result; + PyObject *pcpus = NULL, *pthreads = NULL; + struct perf_cpu_map *cpus; + struct perf_thread_map *threads; + int ret; + + if (!PyArg_ParseTuple(args, "s|sOO", &input, &pmu, &pcpus, &pthreads)) + return NULL; + + threads = pthreads ? ((struct pyrf_thread_map *)pthreads)->threads : NULL; + cpus = pcpus ? ((struct pyrf_cpu_map *)pcpus)->cpus : NULL; + + evlist__init(&evlist, cpus, threads); + ret = metricgroup__parse_groups(&evlist, pmu ?: "all", input, + /*metric_no_group=*/ false, + /*metric_no_merge=*/ false, + /*metric_no_threshold=*/ true, + /*user_requested_cpu_list=*/ NULL, + /*system_wide=*/true, + /*hardware_aware_grouping=*/ false); + if (ret) { + errno = -ret; + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + result = pyrf_evlist__from_evlist(&evlist); + evlist__exit(&evlist); + return result; +} + +static PyObject *pyrf__metrics_groups(const struct pmu_metric *pm) +{ + PyObject *groups = PyList_New(/*len=*/0); + const char *mg = pm->metric_group; + + if (!groups) + return NULL; + + while (mg) { + PyObject *val = NULL; + const char *sep = strchr(mg, ';'); + size_t len = sep ? (size_t)(sep - mg) : strlen(mg); + + if (len > 0) { + val = PyUnicode_FromStringAndSize(mg, len); + if (val) + PyList_Append(groups, val); + + Py_XDECREF(val); + } + mg = sep ? sep + 1 : NULL; + } + return groups; +} + +static int pyrf__metrics_cb(const struct pmu_metric *pm, + const struct pmu_metrics_table *table __maybe_unused, + void *vdata) +{ + PyObject *py_list = vdata; + PyObject *dict = PyDict_New(); + PyObject *key = dict ? PyUnicode_FromString("MetricGroup") : NULL; + PyObject *value = key ? pyrf__metrics_groups(pm) : NULL; + + if (!value || PyDict_SetItem(dict, key, value) != 0) { + Py_XDECREF(key); + Py_XDECREF(value); + Py_XDECREF(dict); + return -ENOMEM; + } + + if (!add_to_dict(dict, "MetricName", pm->metric_name) || + !add_to_dict(dict, "PMU", pm->pmu) || + !add_to_dict(dict, "MetricExpr", pm->metric_expr) || + !add_to_dict(dict, "MetricThreshold", pm->metric_threshold) || + !add_to_dict(dict, "ScaleUnit", pm->unit) || + !add_to_dict(dict, "Compat", pm->compat) || + !add_to_dict(dict, "BriefDescription", pm->desc) || + !add_to_dict(dict, "PublicDescription", pm->long_desc) || + PyList_Append(py_list, dict) != 0) { + Py_DECREF(dict); + return -ENOMEM; + } + Py_DECREF(dict); + return 0; +} + +static PyObject *pyrf__metrics(PyObject *self, PyObject *args) +{ + const struct pmu_metrics_table *table = pmu_metrics_table__find(); + PyObject *list = PyList_New(/*len=*/0); + int ret; + + if (!list) + return NULL; + + ret = pmu_metrics_table__for_each_metric(table, pyrf__metrics_cb, list); + if (!ret) + ret = pmu_for_each_sys_metric(pyrf__metrics_cb, list); + + if (ret) { + Py_DECREF(list); + errno = -ret; + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + return list; +} + static PyMethodDef perf__methods[] = { + { + .ml_name = "metrics", + .ml_meth = (PyCFunction) pyrf__metrics, + .ml_flags = METH_NOARGS, + .ml_doc = PyDoc_STR( + "Returns a list of metrics represented as string values in dictionaries.") + }, + { + .ml_name = "tracepoint", + .ml_meth = (PyCFunction) pyrf__tracepoint, + .ml_flags = METH_VARARGS | METH_KEYWORDS, + .ml_doc = PyDoc_STR("Get tracepoint config.") + }, + { + .ml_name = "parse_events", + .ml_meth = (PyCFunction) pyrf__parse_events, + .ml_flags = METH_VARARGS, + .ml_doc = PyDoc_STR("Parse a string of events and return an evlist.") + }, + { + .ml_name = "parse_metrics", + .ml_meth = (PyCFunction) pyrf__parse_metrics, + .ml_flags = METH_VARARGS, + .ml_doc = PyDoc_STR( + "Parse a string of metrics or metric groups and return an evlist.") + }, + { + .ml_name = "pmus", + .ml_meth = (PyCFunction) pyrf__pmus, + .ml_flags = METH_NOARGS, + .ml_doc = PyDoc_STR("Returns a sequence of pmus.") + }, { .ml_name = NULL, } }; -PyMODINIT_FUNC initperf(void) +PyMODINIT_FUNC PyInit_perf(void) { PyObject *obj; int i; - PyObject *dict, *module = Py_InitModule("perf", perf__methods); + PyObject *dict; + static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "perf", /* m_name */ + "", /* m_doc */ + -1, /* m_size */ + perf__methods, /* m_methods */ + NULL, /* m_reload */ + NULL, /* m_traverse */ + NULL, /* m_clear */ + NULL, /* m_free */ + }; + PyObject *module = PyModule_Create(&moduledef); if (module == NULL || pyrf_event__setup_types() < 0 || pyrf_evlist__setup_types() < 0 || pyrf_evsel__setup_types() < 0 || pyrf_thread_map__setup_types() < 0 || - pyrf_cpu_map__setup_types() < 0) - return; + pyrf_cpu_map__setup_types() < 0 || + pyrf_pmu_iterator__setup_types() < 0 || + pyrf_pmu__setup_types() < 0 || + pyrf_counts_values__setup_types() < 0) + return module; + /* The page_size is placed in util object. */ page_size = sysconf(_SC_PAGE_SIZE); Py_INCREF(&pyrf_evlist__type); @@ -1023,18 +2274,48 @@ PyMODINIT_FUNC initperf(void) Py_INCREF(&pyrf_evsel__type); PyModule_AddObject(module, "evsel", (PyObject*)&pyrf_evsel__type); + Py_INCREF(&pyrf_mmap_event__type); + PyModule_AddObject(module, "mmap_event", (PyObject *)&pyrf_mmap_event__type); + + Py_INCREF(&pyrf_lost_event__type); + PyModule_AddObject(module, "lost_event", (PyObject *)&pyrf_lost_event__type); + + Py_INCREF(&pyrf_comm_event__type); + PyModule_AddObject(module, "comm_event", (PyObject *)&pyrf_comm_event__type); + + Py_INCREF(&pyrf_task_event__type); + PyModule_AddObject(module, "task_event", (PyObject *)&pyrf_task_event__type); + + Py_INCREF(&pyrf_throttle_event__type); + PyModule_AddObject(module, "throttle_event", (PyObject *)&pyrf_throttle_event__type); + + Py_INCREF(&pyrf_task_event__type); + PyModule_AddObject(module, "task_event", (PyObject *)&pyrf_task_event__type); + + Py_INCREF(&pyrf_read_event__type); + PyModule_AddObject(module, "read_event", (PyObject *)&pyrf_read_event__type); + + Py_INCREF(&pyrf_sample_event__type); + PyModule_AddObject(module, "sample_event", (PyObject *)&pyrf_sample_event__type); + + Py_INCREF(&pyrf_context_switch_event__type); + PyModule_AddObject(module, "switch_event", (PyObject *)&pyrf_context_switch_event__type); + Py_INCREF(&pyrf_thread_map__type); PyModule_AddObject(module, "thread_map", (PyObject*)&pyrf_thread_map__type); Py_INCREF(&pyrf_cpu_map__type); PyModule_AddObject(module, "cpu_map", (PyObject*)&pyrf_cpu_map__type); + Py_INCREF(&pyrf_counts_values__type); + PyModule_AddObject(module, "counts_values", (PyObject *)&pyrf_counts_values__type); + dict = PyModule_GetDict(module); if (dict == NULL) goto error; for (i = 0; perf__constants[i].name != NULL; i++) { - obj = PyInt_FromLong(perf__constants[i].value); + obj = PyLong_FromLong(perf__constants[i].value); if (obj == NULL) goto error; PyDict_SetItemString(dict, perf__constants[i].name, obj); @@ -1044,13 +2325,5 @@ PyMODINIT_FUNC initperf(void) error: if (PyErr_Occurred()) PyErr_SetString(PyExc_ImportError, "perf: Init failed!"); -} - -/* - * Dummy, to avoid dragging all the test_attr infrastructure in the python - * binding. - */ -void test_attr__open(struct perf_event_attr *attr, pid_t pid, int cpu, - int fd, int group_fd, unsigned long flags) -{ + return module; } |
