* [RFC PATCH 1/3] libperf: Introduce wrappers for perf structs to be exposed to python
2025-03-13 7:51 [RFC PATCH 0/3] Introduce a C extension module to allow libperf usage from python Gautam Menghani
@ 2025-03-13 7:51 ` Gautam Menghani
2025-03-13 7:51 ` [RFC PATCH 2/3] libperf: Introduce a C extension module for python Gautam Menghani
` (2 subsequent siblings)
3 siblings, 0 replies; 7+ messages in thread
From: Gautam Menghani @ 2025-03-13 7:51 UTC (permalink / raw)
To: peterz, mingo, acme, namhyung, mark.rutland, alexander.shishkin,
jolsa, irogers, adrian.hunter, kan.liang
Cc: Gautam Menghani, linux-perf-users, linux-kernel,
Madhavan Srinivasan
Create wrapper objects for the perf structs (which are used by libperf)
to be used by python. Also define other helper functions that will be
used by the C extension module for python.
Co-developed-by: Madhavan Srinivasan <maddy@linux.ibm.com>
Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
Signed-off-by: Gautam Menghani <gautam@linux.ibm.com>
---
tools/lib/perf/include/perf/py_perf.h | 431 ++++++++++++++++++++++++++
1 file changed, 431 insertions(+)
create mode 100644 tools/lib/perf/include/perf/py_perf.h
diff --git a/tools/lib/perf/include/perf/py_perf.h b/tools/lib/perf/include/perf/py_perf.h
new file mode 100644
index 000000000000..26eeb05eaf67
--- /dev/null
+++ b/tools/lib/perf/include/perf/py_perf.h
@@ -0,0 +1,431 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LIBPERF_PY_PERF_H
+#define __LIBPERF_PY_PERF_H
+
+#define PY_SSIZE_T_CLEAN
+#include <stdlib.h>
+#include <linux/perf_event.h>
+#include <linux/kernel.h>
+#include <perf/evlist.h>
+#include <internal/evlist.h>
+#include <perf/evsel.h>
+#include <perf/threadmap.h>
+#include <Python.h>
+
+// perf_thread_map declaration
+typedef struct {
+ PyObject_HEAD
+ struct perf_thread_map *thread_map;
+} py_perf_thread_map;
+
+static void py_perf_thread_map_dealloc(py_perf_thread_map *thread_map)
+{
+ free(thread_map->thread_map);
+ Py_DECREF(thread_map);
+ PyObject_Del((PyObject *)thread_map);
+}
+
+static PyTypeObject py_perf_thread_map_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "libperf.py_perf_thread_map",
+ .tp_doc = "Perf thread map object",
+ .tp_basicsize = sizeof(py_perf_thread_map),
+ .tp_dealloc = (destructor)py_perf_thread_map_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+};
+
+// perf_evsel declarations
+typedef struct {
+ PyObject_HEAD
+ struct perf_evsel *evsel;
+} py_perf_evsel;
+
+static void py_perf_evsel_dealloc(py_perf_evsel *evsel)
+{
+ Py_DECREF(evsel);
+ PyObject_Del((PyObject *)evsel);
+}
+
+static PyTypeObject py_perf_evsel_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "libperf.py_perf_evsel",
+ .tp_doc = "Perf evsel object",
+ .tp_basicsize = sizeof(py_perf_evsel),
+ .tp_dealloc = (destructor)py_perf_evsel_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+};
+
+// perf_evlist declarations
+typedef struct {
+ PyObject_HEAD
+ struct perf_evlist *evlist;
+} py_perf_evlist;
+
+static void py_perf_evlist_dealloc(py_perf_evlist *evlist)
+{
+ free(evlist->evlist);
+ Py_DECREF(evlist);
+ PyObject_Del((PyObject *)evlist);
+}
+
+typedef struct {
+ PyObject_HEAD
+ PyObject *evlist;
+ struct list_head *current;
+} py_perf_evlist_iterator;
+
+static PyObject *evlist_iterator_next(py_perf_evlist_iterator *iter) {
+
+ py_perf_evsel *pyperf_evsel = PyObject_New(py_perf_evsel, &py_perf_evsel_type);
+ struct list_head *head;
+
+ if (((py_perf_evlist *)(iter->evlist))->evlist == NULL) {
+ PyErr_SetString(PyExc_RuntimeError, "perf_evlist has been closed");
+ return NULL;
+ }
+ head = &((py_perf_evlist *)(iter->evlist))->evlist->entries;
+ if (iter->current == head) {
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+ }
+ pyperf_evsel->evsel = list_entry(iter->current, struct perf_evsel, node);
+ iter->current = iter->current->next;
+ Py_INCREF(iter->evlist);
+
+ return (PyObject *)pyperf_evsel;
+}
+
+static PyTypeObject py_perf_evlist_iterator_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "libperf.evlist_iterator_type",
+ .tp_basicsize = sizeof(py_perf_evlist_iterator),
+ .tp_itemsize = 0,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_doc = "evlist_iterator object",
+ .tp_iter = PyObject_SelfIter,
+ .tp_iternext = (iternextfunc) evlist_iterator_next,
+};
+
+static PyObject *py_perf_evlist_iter(py_perf_evlist *self) {
+ py_perf_evlist_iterator *iter = PyObject_New(py_perf_evlist_iterator, &py_perf_evlist_iterator_type);
+ if (!iter)
+ return NULL;
+ iter->current = self->evlist->entries.next;
+ iter->evlist = (PyObject *)self;
+ Py_INCREF(self);
+ return (PyObject *)iter;
+}
+
+static PyTypeObject py_perf_evlist_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "libperf.py_perf_evlist",
+ .tp_doc = "Perf evlist object",
+ .tp_basicsize = sizeof(py_perf_evlist),
+ .tp_dealloc = (destructor)py_perf_evlist_dealloc,
+ .tp_iter = (getiterfunc) py_perf_evlist_iter,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+};
+
+// perf_cpu_map declarations
+typedef struct {
+ PyObject_HEAD
+ struct perf_cpu_map *map;
+} py_perf_cpu_map;
+
+static void py_perf_cpu_map_dealloc(py_perf_cpu_map *cpu_map)
+{
+ free(cpu_map->map);
+ Py_DECREF(cpu_map);
+ PyObject_Del((PyObject *)cpu_map);
+}
+
+static PyTypeObject py_perf_cpu_map_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "libperf.py_perf_cpu_map",
+ .tp_doc = "Perf cpu_map object",
+ .tp_basicsize = sizeof(py_perf_cpu_map),
+ .tp_dealloc = (destructor)py_perf_cpu_map_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+};
+
+// perf_event_attr declarations
+typedef struct {
+ PyObject_HEAD
+ struct perf_event_attr *attr;
+} py_perf_event_attr;
+
+#define PY_STRUCT_GET_SET_FUNC_LONG(name, c_type, element) \
+static PyObject *py_##name##_##element##_get(py_##name *self, void *closure) \
+{ \
+ return PyLong_FromLong(self->c_type->element); \
+} \
+ \
+static int py_##name##_##element##_set(py_##name *self, PyObject *value, void *closure) \
+{ \
+ \
+ if (!PyLong_Check(value)) \
+ return -1; \
+ \
+ self->c_type->element = PyLong_AsLong(value); \
+ \
+ return 0; \
+}
+
+#define GET_SET_DEF(name, element) \
+ {#element, (getter)py_##name##_##element##_get, (setter)py_##name##_##element##_set, NULL, NULL}
+
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, type)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, size)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, config)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, sample_period)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, sample_freq)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, sample_type)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, read_format)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, disabled)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, inherit)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, pinned)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, exclusive)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, exclude_user)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, exclude_kernel)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, exclude_hv)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, exclude_idle)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, mmap)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, comm)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, freq)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, inherit_stat)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, enable_on_exec)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, task)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, watermark)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, precise_ip)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, mmap_data)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, sample_id_all)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, exclude_host)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, exclude_guest)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, exclude_callchain_kernel)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, exclude_callchain_user)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, mmap2)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, comm_exec)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, use_clockid)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, context_switch)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, write_backward)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, namespaces)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, ksymbol)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, bpf_event)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, aux_output)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, cgroup)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, text_poke)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, build_id)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, inherit_thread)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, remove_on_exec)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, sigtrap)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, __reserved_1)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, wakeup_events)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, wakeup_watermark)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, bp_type)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, bp_addr)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, kprobe_func)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, uprobe_path)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, config1)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, bp_len)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, kprobe_addr)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, probe_offset)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, config2)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, branch_sample_type)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, sample_regs_user)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, sample_stack_user)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, clockid)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, sample_regs_intr)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, aux_watermark)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, sample_max_stack)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, __reserved_2)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, aux_sample_size)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, __reserved_3)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_event_attr, attr, sig_data)
+
+
+static PyGetSetDef py_perf_event_attr_getset[] = {
+ GET_SET_DEF(perf_event_attr, type),
+ GET_SET_DEF(perf_event_attr, size),
+ GET_SET_DEF(perf_event_attr, config),
+ GET_SET_DEF(perf_event_attr, sample_period),
+ GET_SET_DEF(perf_event_attr, sample_freq),
+ GET_SET_DEF(perf_event_attr, sample_type),
+ GET_SET_DEF(perf_event_attr, read_format),
+ GET_SET_DEF(perf_event_attr, disabled),
+ GET_SET_DEF(perf_event_attr, inherit),
+ GET_SET_DEF(perf_event_attr, pinned),
+ GET_SET_DEF(perf_event_attr, exclusive),
+ GET_SET_DEF(perf_event_attr, exclude_user),
+ GET_SET_DEF(perf_event_attr, exclude_kernel),
+ GET_SET_DEF(perf_event_attr, exclude_hv),
+ GET_SET_DEF(perf_event_attr, exclude_idle),
+ GET_SET_DEF(perf_event_attr, mmap),
+ GET_SET_DEF(perf_event_attr, comm),
+ GET_SET_DEF(perf_event_attr, freq),
+ GET_SET_DEF(perf_event_attr, inherit_stat),
+ GET_SET_DEF(perf_event_attr, enable_on_exec),
+ GET_SET_DEF(perf_event_attr, task),
+ GET_SET_DEF(perf_event_attr, watermark),
+ GET_SET_DEF(perf_event_attr, precise_ip),
+ GET_SET_DEF(perf_event_attr, mmap_data),
+ GET_SET_DEF(perf_event_attr, sample_id_all),
+ GET_SET_DEF(perf_event_attr, exclude_host),
+ GET_SET_DEF(perf_event_attr, exclude_guest),
+ GET_SET_DEF(perf_event_attr, exclude_callchain_kernel),
+ GET_SET_DEF(perf_event_attr, exclude_callchain_user),
+ GET_SET_DEF(perf_event_attr, mmap2),
+ GET_SET_DEF(perf_event_attr, comm_exec),
+ GET_SET_DEF(perf_event_attr, use_clockid),
+ GET_SET_DEF(perf_event_attr, context_switch),
+ GET_SET_DEF(perf_event_attr, write_backward),
+ GET_SET_DEF(perf_event_attr, namespaces),
+ GET_SET_DEF(perf_event_attr, ksymbol),
+ GET_SET_DEF(perf_event_attr, bpf_event),
+ GET_SET_DEF(perf_event_attr, aux_output),
+ GET_SET_DEF(perf_event_attr, cgroup),
+ GET_SET_DEF(perf_event_attr, text_poke),
+ GET_SET_DEF(perf_event_attr, build_id),
+ GET_SET_DEF(perf_event_attr, inherit_thread),
+ GET_SET_DEF(perf_event_attr, remove_on_exec),
+ GET_SET_DEF(perf_event_attr, sigtrap),
+ GET_SET_DEF(perf_event_attr, __reserved_1),
+ GET_SET_DEF(perf_event_attr, wakeup_events),
+ GET_SET_DEF(perf_event_attr, wakeup_watermark),
+ GET_SET_DEF(perf_event_attr, bp_type),
+ GET_SET_DEF(perf_event_attr, bp_addr),
+ GET_SET_DEF(perf_event_attr, kprobe_func),
+ GET_SET_DEF(perf_event_attr, uprobe_path),
+ GET_SET_DEF(perf_event_attr, config1),
+ GET_SET_DEF(perf_event_attr, bp_len),
+ GET_SET_DEF(perf_event_attr, kprobe_addr),
+ GET_SET_DEF(perf_event_attr, probe_offset),
+ GET_SET_DEF(perf_event_attr, config2),
+ GET_SET_DEF(perf_event_attr, branch_sample_type),
+ GET_SET_DEF(perf_event_attr, sample_regs_user),
+ GET_SET_DEF(perf_event_attr, sample_stack_user),
+ GET_SET_DEF(perf_event_attr, clockid),
+ GET_SET_DEF(perf_event_attr, sample_regs_intr),
+ GET_SET_DEF(perf_event_attr, aux_watermark),
+ GET_SET_DEF(perf_event_attr, sample_max_stack),
+ GET_SET_DEF(perf_event_attr, __reserved_2),
+ GET_SET_DEF(perf_event_attr, aux_sample_size),
+ GET_SET_DEF(perf_event_attr, __reserved_3),
+ GET_SET_DEF(perf_event_attr, sig_data),
+ {NULL}
+};
+
+static PyObject *py_perf_event_attr_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ py_perf_event_attr *self = (py_perf_event_attr *) type->tp_alloc(type, 0);
+
+ if (!self)
+ return NULL;
+
+ self->attr = calloc(1, sizeof(struct perf_event_attr));
+
+ return (PyObject *)self;
+}
+
+static void py_perf_event_attr_dealloc(py_perf_event_attr *ctr)
+{
+ free(ctr->attr);
+ Py_DECREF(ctr);
+ PyObject_Del((PyObject *)ctr);
+}
+
+static PyTypeObject py_perf_event_attr_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "libperf.py_perf_event_attr",
+ .tp_doc = "perf event attribute structure object",
+ .tp_basicsize = sizeof(py_perf_event_attr),
+ .tp_new = py_perf_event_attr_new,
+ .tp_dealloc = (destructor)py_perf_event_attr_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT| Py_TPFLAGS_BASETYPE,
+ .tp_getset = py_perf_event_attr_getset,
+};
+
+// perf_counts_values declarations
+typedef struct {
+ PyObject_HEAD
+ struct perf_counts_values *values;
+} py_perf_counts_values;
+
+static void py_perf_counts_values_dealloc(py_perf_counts_values *values)
+{
+ free(values->values);
+ Py_DECREF(values);
+ PyObject_Del((PyObject *)values);
+}
+
+static PyObject * py_perf_counts_values_get_values(py_perf_counts_values *self, void *closure)
+{
+ PyObject *list = PyList_New(5);
+ if (!list)
+ return NULL;
+ for (int i = 0; i < 5; i++) {
+ PyList_SetItem(list, i, PyLong_FromLong(self->values->values[i]));
+ }
+ return list;
+}
+
+static int py_perf_counts_values_set_values(py_perf_counts_values *self, PyObject *value, void *closure)
+{
+ if (!PyLong_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "Values must be u64");
+ return -1;
+ }
+ for(int i = 0; i < 5; i++) {
+ self->values->values[i] = PyLong_AsLong(value);
+ }
+ return 0;
+}
+PY_STRUCT_GET_SET_FUNC_LONG(perf_counts_values, values, val)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_counts_values, values, ena)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_counts_values, values, run)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_counts_values, values, id)
+PY_STRUCT_GET_SET_FUNC_LONG(perf_counts_values, values, lost)
+
+static PyGetSetDef py_perf_counts_values_getsetters[] = {
+ GET_SET_DEF(perf_counts_values, val),
+ GET_SET_DEF(perf_counts_values, ena),
+ GET_SET_DEF(perf_counts_values, run),
+ GET_SET_DEF(perf_counts_values, id),
+ GET_SET_DEF(perf_counts_values, lost),
+ {"values", (getter)py_perf_counts_values_get_values, (setter)py_perf_counts_values_set_values,"values", NULL},
+ {NULL}
+};
+
+static PyObject *py_perf_counts_values_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ py_perf_counts_values *self = (py_perf_counts_values *) type->tp_alloc(type, 0);
+
+ if (!self)
+ return NULL;
+
+ self->values = calloc(1, sizeof(struct perf_counts_values));
+
+ return (PyObject *)self;
+}
+
+static PyTypeObject py_perf_counts_values_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "libperf.py_perf_counts_values",
+ .tp_doc = "Perf_counts_values object",
+ .tp_basicsize = sizeof(py_perf_counts_values),
+ .tp_new = py_perf_counts_values_new,
+ .tp_dealloc = (destructor)py_perf_counts_values_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_getset = py_perf_counts_values_getsetters,
+};
+
+static void python_push_type(const char *name, PyObject *module, PyTypeObject *type)
+{
+ if (PyType_Ready(type) == -1)
+ printf("python_push_type: failed to ready %s", name);
+
+ Py_INCREF(type);
+}
+
+LIBPERF_API PyMODINIT_FUNC PyInit_libperf(void);
+
+#endif /* __LIBPERF_PY_PERF_H */
--
2.47.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [RFC PATCH 2/3] libperf: Introduce a C extension module for python
2025-03-13 7:51 [RFC PATCH 0/3] Introduce a C extension module to allow libperf usage from python Gautam Menghani
2025-03-13 7:51 ` [RFC PATCH 1/3] libperf: Introduce wrappers for perf structs to be exposed to python Gautam Menghani
@ 2025-03-13 7:51 ` Gautam Menghani
2025-03-13 7:51 ` [RFC PATCH 3/3] libperf: Add counting.py example to demonstrate libperf usage from python Gautam Menghani
2025-03-13 14:12 ` [RFC PATCH 0/3] Introduce a C extension module to allow " John B. Wyatt IV
3 siblings, 0 replies; 7+ messages in thread
From: Gautam Menghani @ 2025-03-13 7:51 UTC (permalink / raw)
To: peterz, mingo, acme, namhyung, mark.rutland, alexander.shishkin,
jolsa, irogers, adrian.hunter, kan.liang
Cc: Gautam Menghani, linux-perf-users, linux-kernel,
Madhavan Srinivasan
Create a C extension module for libperf that will be used by python to
call the libperf functions.
Co-developed-by: Madhavan Srinivasan <maddy@linux.ibm.com>
Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
Signed-off-by: Gautam Menghani <gautam@linux.ibm.com>
---
tools/lib/perf/Build | 1 +
tools/lib/perf/Makefile | 12 +-
tools/lib/perf/libperf.map | 1 +
tools/lib/perf/py_perf.c | 261 +++++++++++++++++++++++++++++++++++++
4 files changed, 273 insertions(+), 2 deletions(-)
create mode 100644 tools/lib/perf/py_perf.c
diff --git a/tools/lib/perf/Build b/tools/lib/perf/Build
index e8f5b7fb9973..4869da30ffb5 100644
--- a/tools/lib/perf/Build
+++ b/tools/lib/perf/Build
@@ -7,6 +7,7 @@ libperf-y += mmap.o
libperf-y += zalloc.o
libperf-y += xyarray.o
libperf-y += lib.o
+libperf-y += py_perf.o
$(OUTPUT)zalloc.o: ../../lib/zalloc.c FORCE
$(call rule_mkdir)
diff --git a/tools/lib/perf/Makefile b/tools/lib/perf/Makefile
index e9a7ac2c062e..8e16492af882 100644
--- a/tools/lib/perf/Makefile
+++ b/tools/lib/perf/Makefile
@@ -48,20 +48,28 @@ else
CFLAGS := -g -Wall
endif
+# Check Python.h path
+ifeq (,$(shell which python-config))
+ $(error "Consider installing python-dev or python-devel")
+endif
+
+PYTHON_INCLUDE = $(shell python-config --includes)
+
INCLUDES = \
-I$(srctree)/tools/lib/perf/include \
-I$(srctree)/tools/lib/ \
-I$(srctree)/tools/include \
-I$(srctree)/tools/arch/$(SRCARCH)/include/ \
-I$(srctree)/tools/arch/$(SRCARCH)/include/uapi \
--I$(srctree)/tools/include/uapi
+-I$(srctree)/tools/include/uapi \
+-I$(PYTHON_INCLUDE)
# Append required CFLAGS
override CFLAGS += $(EXTRA_WARNINGS)
override CFLAGS += -Werror -Wall
override CFLAGS += -fPIC
override CFLAGS += $(INCLUDES)
-override CFLAGS += -fvisibility=hidden
+override CFLAGS += -fvisibility=hidden -Wno-redundant-decls
all:
diff --git a/tools/lib/perf/libperf.map b/tools/lib/perf/libperf.map
index fdd8304fe9d0..0488c0591404 100644
--- a/tools/lib/perf/libperf.map
+++ b/tools/lib/perf/libperf.map
@@ -57,6 +57,7 @@ LIBPERF_0.0.1 {
perf_mmap__read_done;
perf_mmap__read_event;
perf_counts_values__scale;
+ PyInit_libperf;
local:
*;
};
diff --git a/tools/lib/perf/py_perf.c b/tools/lib/perf/py_perf.c
new file mode 100644
index 000000000000..cf0fcb8cc6c1
--- /dev/null
+++ b/tools/lib/perf/py_perf.c
@@ -0,0 +1,261 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#include "include/perf/py_perf.h"
+#include <stdlib.h>
+#include <perf/threadmap.h>
+#include <perf/py_perf.h>
+#include <errno.h>
+#include <internal/threadmap.h>
+
+static PyObject *program_perf_thread_map__new_dummy(PyObject *self, PyObject *args)
+{
+ py_perf_thread_map *pythread_map = PyObject_New(py_perf_thread_map, &py_perf_thread_map_type);
+
+ pythread_map->thread_map = perf_thread_map__new_dummy();
+ if (!pythread_map->thread_map) {
+ Py_DECREF(pythread_map);
+ return Py_None;
+ }
+
+ return Py_BuildValue("O", pythread_map);
+}
+
+static PyObject *program_perf_thread_map__set_pid(PyObject *self, PyObject *args)
+{
+ py_perf_thread_map *pythread_map = NULL;
+ int idx, pid;
+
+ if (!PyArg_ParseTuple(args, "Oii", &pythread_map, &idx, &pid)) {
+ return NULL;
+ }
+
+ perf_thread_map__set_pid(pythread_map->thread_map, idx, pid);
+
+ return Py_None;
+}
+
+static PyObject *program_perf_evlist__new(PyObject *self, PyObject *args)
+{
+ py_perf_evlist *pyperf_evlist = PyObject_New(py_perf_evlist, &py_perf_evlist_type);
+
+ pyperf_evlist->evlist = perf_evlist__new();
+ if (!pyperf_evlist->evlist) {
+ Py_DECREF(pyperf_evlist);
+ return Py_None;
+ }
+
+ return Py_BuildValue("O", pyperf_evlist);
+}
+
+static PyObject *program_perf_evsel__new(PyObject *self, PyObject *args)
+{
+ struct perf_event_attr *attr;
+ PyObject *py_attr;
+ py_perf_evsel *pyperf_evsel = PyObject_New(py_perf_evsel, &py_perf_evsel_type);
+
+ if (!PyArg_ParseTuple(args, "O", &py_attr)) {
+ return NULL;
+ }
+
+ attr = (py_attr == Py_None)? NULL: ((py_perf_event_attr *)py_attr)->attr;
+ pyperf_evsel->evsel = perf_evsel__new(attr);
+ if (!pyperf_evsel->evsel) {
+ Py_DECREF(pyperf_evsel);
+ return Py_None;
+ }
+
+ return Py_BuildValue("O", pyperf_evsel);
+}
+
+static PyObject *program_perf_evlist__add(PyObject *self, PyObject *args)
+{
+ struct perf_evsel *evsel;
+ struct perf_evlist *evlist;
+ PyObject *pyevlist, *pyevsel;
+
+ if (!PyArg_ParseTuple(args, "OO", &pyevlist, &pyevsel)) {
+ return NULL;
+ }
+
+ evsel = (pyevsel == Py_None)? NULL: ((py_perf_evsel *)pyevsel)->evsel;
+ evlist = (pyevlist == Py_None)? NULL: ((py_perf_evlist *)pyevlist)->evlist;
+ perf_evlist__add(evlist, evsel);
+
+ return Py_None;
+}
+
+static PyObject *program_perf_evlist__set_maps(PyObject *self, PyObject *args)
+{
+ struct perf_thread_map *thread_map;
+ struct perf_evlist *evlist;
+ struct perf_cpu_map *cpu_map;
+ PyObject *pyevlist, *pythread_map, *pycpu_map;
+
+ if (!PyArg_ParseTuple(args, "OOO", &pyevlist, &pycpu_map, &pythread_map)) {
+ return NULL;
+ }
+
+ evlist = (pyevlist == Py_None)? NULL: ((py_perf_evlist *)pyevlist)->evlist;
+ cpu_map = (pycpu_map == Py_None)? NULL: ((py_perf_cpu_map *)pycpu_map)->map;
+ thread_map = (pythread_map == Py_None)? NULL: ((py_perf_thread_map *)pythread_map)->thread_map;
+ perf_evlist__set_maps(evlist, cpu_map, thread_map);
+
+ return Py_None;
+}
+
+static PyObject *program_perf_evlist__open(PyObject *self, PyObject *args)
+{
+ struct perf_evlist *evlist;
+ PyObject *pyevlist;
+
+ if (!PyArg_ParseTuple(args, "O", &pyevlist)) {
+ return NULL;
+ }
+ evlist = (pyevlist == Py_None)? NULL: ((py_perf_evlist *)pyevlist)->evlist;
+
+ return Py_BuildValue("i", perf_evlist__open(evlist));
+}
+
+static PyObject *program_perf_evlist__enable(PyObject *self, PyObject *args)
+{
+ struct perf_evlist *evlist;
+ PyObject *pyevlist;
+
+ if (!PyArg_ParseTuple(args, "O", &pyevlist)) {
+ return NULL;
+ }
+
+ evlist = (pyevlist == Py_None)? NULL: ((py_perf_evlist *)pyevlist)->evlist;
+ perf_evlist__enable(evlist);
+
+ return Py_None;
+}
+
+static PyObject *program_perf_evlist__disable(PyObject *self, PyObject *args)
+{
+ struct perf_evlist *evlist;
+ PyObject *pyevlist;
+
+ if (!PyArg_ParseTuple(args, "O", &pyevlist)) {
+ return NULL;
+ }
+ evlist = (pyevlist == Py_None)? NULL: ((py_perf_evlist *)pyevlist)->evlist;
+ perf_evlist__disable(evlist);
+
+ return Py_None;
+}
+
+static PyObject *program_perf_evsel__read(PyObject *self, PyObject *args)
+{
+ PyObject *pyevsel, *pyperf_counts_values;
+ struct perf_evsel *evsel;
+ struct perf_counts_values *values;
+ int cpu_map_idx, thread;
+
+ if (!PyArg_ParseTuple(args, "OiiO", &pyevsel, &cpu_map_idx, &thread, &pyperf_counts_values)) {
+ return NULL;
+ }
+ evsel = (pyevsel == Py_None)? NULL: ((py_perf_evsel *)pyevsel)->evsel;
+ values = (pyevsel == Py_None)? NULL: ((py_perf_counts_values *)pyperf_counts_values)->values;
+
+ return Py_BuildValue("i", perf_evsel__read(evsel, cpu_map_idx, thread, values));
+}
+
+static PyObject *program_perf_evlist__close(PyObject *self, PyObject *args)
+{
+ struct perf_evlist *evlist;
+ PyObject *pyevlist;
+
+ if (!PyArg_ParseTuple(args, "O", &pyevlist)) {
+ return NULL;
+ }
+ evlist = (pyevlist == Py_None)? NULL: ((py_perf_evlist *)pyevlist)->evlist;
+ perf_evlist__close(evlist);
+
+ return Py_None;
+}
+
+static PyObject *program_perf_evlist__delete(PyObject *self, PyObject *args)
+{
+ struct perf_evlist *evlist;
+ PyObject *pyevlist;
+
+ if (!PyArg_ParseTuple(args, "O", &pyevlist)) {
+ return NULL;
+ }
+ evlist = (pyevlist == Py_None)? NULL: ((py_perf_evlist *)pyevlist)->evlist;
+ perf_evlist__delete(evlist);
+
+ return Py_None;
+}
+
+static PyObject *program_perf_thread_map__put(PyObject *self, PyObject *args)
+{
+ struct perf_thread_map *map;
+ PyObject *pyperf_thread_map;
+
+ if (!PyArg_ParseTuple(args, "O", &pyperf_thread_map)) {
+ return NULL;
+ }
+ map = (pyperf_thread_map == Py_None)? NULL: ((py_perf_thread_map *)pyperf_thread_map)->thread_map;
+ perf_thread_map__put(map);
+
+ return Py_None;
+}
+
+static int libperf_print(enum libperf_print_level level,
+ const char *fmt, va_list ap)
+{
+ return vfprintf(stderr, fmt, ap);
+}
+
+static PyObject *program_libperf_init(PyObject *self, PyObject *args)
+{
+ libperf_init(libperf_print);
+ return Py_None;
+}
+
+PyMethodDef libperf_methods[] = {
+ {"perf_thread_map__new_dummy", program_perf_thread_map__new_dummy, METH_VARARGS, "Create a dummy thread map function variable"},
+ {"perf_thread_map__set_pid", program_perf_thread_map__set_pid, METH_VARARGS, "Set PID for a thread map"},
+ {"perf_evlist__new", program_perf_evlist__new, METH_VARARGS, "Create a perf evlist"},
+ {"perf_evsel__new", program_perf_evsel__new, METH_VARARGS, "Create a perf evsel"},
+ {"perf_evlist__add", program_perf_evlist__add, METH_VARARGS, "Add evsel to evlist"},
+ {"perf_evlist__set_maps", program_perf_evlist__set_maps, METH_VARARGS, "perf_evlist__set_maps"},
+ {"perf_evlist__open", program_perf_evlist__open, METH_VARARGS, "perf_evlist__set_maps"},
+ {"perf_evlist__enable", program_perf_evlist__enable, METH_VARARGS, "perf_evlist__enable"},
+ {"perf_evlist__disable", program_perf_evlist__disable, METH_VARARGS, "perf_evlist__disable"},
+ {"perf_evsel__read", program_perf_evsel__read, METH_VARARGS, "perf_evsel__read"},
+ {"perf_evlist__close", program_perf_evlist__close, METH_VARARGS, "perf_evlist__close"},
+ {"perf_evlist__delete", program_perf_evlist__delete, METH_VARARGS, "perf_evlist__delete"},
+ {"perf_thread_map__put", program_perf_thread_map__put, METH_VARARGS, "perf_thread_map__put"},
+ {"libperf_init", program_libperf_init, METH_VARARGS, "libperf init"},
+ {NULL, NULL, 0, NULL}
+};
+
+struct PyModuleDef libperf = {
+ PyModuleDef_HEAD_INIT,
+ "libperf",
+ "Extension module to expose libperf to python",
+ -1,
+ libperf_methods
+};
+
+PyMODINIT_FUNC PyInit_libperf(void) {
+ PyObject *m = PyModule_Create(&libperf);
+
+ if (!m)
+ return NULL;
+
+ python_push_type("py_perf_thread_map", m, &py_perf_thread_map_type);
+ python_push_type("py_perf_evlist", m, &py_perf_evlist_type);
+ python_push_type("evlist_iterator", m, &py_perf_evlist_iterator_type);
+ python_push_type("py_perf_evsel", m, &py_perf_evsel_type);
+ python_push_type("py_perf_cpu_map", m, &py_perf_cpu_map_type);
+ python_push_type("py_perf_event_attr", m, &py_perf_event_attr_type);
+ python_push_type("py_perf_counts_values", m, &py_perf_counts_values_type);
+
+ PyModule_AddObject(m, "perf_event_attr", (PyObject *) & py_perf_event_attr_type);
+ PyModule_AddObject(m, "perf_counts_values", (PyObject *) & py_perf_counts_values_type);
+
+ return m;
+}
--
2.47.0
^ permalink raw reply related [flat|nested] 7+ messages in thread