* [RFC PATCH 0/3] Introduce a C extension module to allow libperf usage from python
@ 2025-03-13 7:51 Gautam Menghani
2025-03-13 7:51 ` [RFC PATCH 1/3] libperf: Introduce wrappers for perf structs to be exposed to python Gautam Menghani
` (3 more replies)
0 siblings, 4 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
In this RFC series, we are introducing a C extension module to allow
python programs to call the libperf API functions. Currently libperf can
be used by C programs, but expanding the support to python is beneficial
for python users.
The structure of the patch series is as follows:
1. Patch 1 : Create wrappers for the perf structs which are used by
examples/counting.c
2. Patch 2: Create the C extension module that maps and exposes the
libperf functions to python programs
2. Patch 3: A python variant of counting.c - counting.py to demonstrate
the usage of libperf from python
We have not added support for entire libperf, as we want to get
community feedback on the approach taken in this series.
Gautam Menghani (3):
libperf: Introduce wrappers for perf structs to be exposed to python
libperf: Introduce a C extension module for python
libperf: Add counting.py example to demonstrate libperf usage from
python
tools/lib/perf/Build | 1 +
.../perf/Documentation/examples/counting.py | 74 +++
tools/lib/perf/Makefile | 12 +-
tools/lib/perf/include/perf/py_perf.h | 431 ++++++++++++++++++
tools/lib/perf/libperf.map | 1 +
tools/lib/perf/py_perf.c | 262 +++++++++++
6 files changed, 779 insertions(+), 2 deletions(-)
create mode 100755 tools/lib/perf/Documentation/examples/counting.py
create mode 100644 tools/lib/perf/include/perf/py_perf.h
create mode 100644 tools/lib/perf/py_perf.c
--
2.47.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [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
* [RFC PATCH 3/3] libperf: Add counting.py example to demonstrate libperf usage from 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 ` [RFC PATCH 2/3] libperf: Introduce a C extension module for python Gautam Menghani
@ 2025-03-13 7:51 ` 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
Add a counting.py example to demonstrate usage of libperf from python
using the C extension module support.
Example usage:
$ sudo ./counting.py
count 7903256, enabled 7903670, run 7903670
count 7902787, enabled 7902787, run 7902787
Signed-off-by: Gautam Menghani <gautam@linux.ibm.com>
---
.../perf/Documentation/examples/counting.py | 74 +++++++++++++++++++
1 file changed, 74 insertions(+)
create mode 100755 tools/lib/perf/Documentation/examples/counting.py
diff --git a/tools/lib/perf/Documentation/examples/counting.py b/tools/lib/perf/Documentation/examples/counting.py
new file mode 100755
index 000000000000..887111bf2e04
--- /dev/null
+++ b/tools/lib/perf/Documentation/examples/counting.py
@@ -0,0 +1,74 @@
+#!/usr/bin/python3
+
+import sys
+sys.path.append('../../')
+from libperf import *
+
+# software ids
+PERF_COUNT_SW_CPU_CLOCK = 0
+PERF_COUNT_SW_TASK_CLOCK = 1
+
+# Perf event types
+PERF_TYPE_HARDWARE = 0
+PERF_TYPE_SOFTWARE = 1
+PERF_TYPE_TRACEPOINT = 2
+PERF_TYPE_HW_CACHE = 3
+
+# perf_event_attr_read format
+PERF_FORMAT_TOTAL_TIME_ENABLED = 1 << 0
+PERF_FORMAT_TOTAL_TIME_RUNNING = 1 << 1
+PERF_FORMAT_ID = 1 << 2
+PERF_FORMAT_GROUP = 1 << 3
+PERF_FORMAT_LOST = 1 << 4
+
+# Perf sample identifier
+PERF_SAMPLE_IDENTIFIER = 1 << 16
+
+def get_attr(config):
+ attr = perf_event_attr()
+ attr.type = PERF_TYPE_SOFTWARE
+ attr.config = config
+ attr.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED|PERF_FORMAT_TOTAL_TIME_RUNNING
+ attr.disabled = 1
+ attr.size = 136
+ attr.sample_type = PERF_SAMPLE_IDENTIFIER
+ return attr
+
+libperf_init(None)
+threads = perf_thread_map__new_dummy()
+assert(threads)
+perf_thread_map__set_pid(threads, 0, 0)
+
+evlist = perf_evlist__new()
+assert(evlist)
+
+attr1 = get_attr(PERF_COUNT_SW_CPU_CLOCK)
+evsel = perf_evsel__new(attr1)
+assert(evsel)
+perf_evlist__add(evlist, evsel)
+
+attr2 = get_attr(PERF_COUNT_SW_TASK_CLOCK)
+evsel = perf_evsel__new(attr2)
+assert(evsel)
+perf_evlist__add(evlist, evsel)
+
+perf_evlist__set_maps(evlist, None, threads)
+rc = perf_evlist__open(evlist)
+if rc != 0:
+ print("failed to open evsel: ", rc)
+
+perf_evlist__enable(evlist)
+
+count = 100000
+while count >= 0:
+ count-=1
+
+perf_evlist__disable(evlist)
+c = perf_counts_values()
+for sel in evlist:
+ perf_evsel__read(sel, 0, 0, c);
+ print("count %lu, enabled %lu, run %lu" %(c.val, c.ena, c.run))
+
+perf_evlist__close(evlist);
+perf_evlist__delete(evlist);
+perf_thread_map__put(threads);
--
2.47.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [RFC PATCH 0/3] Introduce a C extension module to allow libperf usage from python
2025-03-13 7:51 [RFC PATCH 0/3] Introduce a C extension module to allow libperf usage from python Gautam Menghani
` (2 preceding siblings ...)
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 ` John B. Wyatt IV
2025-03-13 16:01 ` Ian Rogers
3 siblings, 1 reply; 7+ messages in thread
From: John B. Wyatt IV @ 2025-03-13 14:12 UTC (permalink / raw)
To: Gautam Menghani
Cc: peterz, mingo, acme, namhyung, mark.rutland, alexander.shishkin,
jolsa, irogers, adrian.hunter, kan.liang, linux-perf-users,
linux-kernel
Hello Gautam
On Thu, Mar 13, 2025 at 01:21:21PM +0530, Gautam Menghani wrote:
> In this RFC series, we are introducing a C extension module to allow
> python programs to call the libperf API functions. Currently libperf can
> be used by C programs, but expanding the support to python is beneficial
> for python users.
>
> The structure of the patch series is as follows:
> 1. Patch 1 : Create wrappers for the perf structs which are used by
> examples/counting.c
>
> 2. Patch 2: Create the C extension module that maps and exposes the
> libperf functions to python programs
May I ask why you are not using SWIG? With libcpupower the kernel has
already been using SWIG to generate Python bindings for a C user-space API.
This has several advantages including a much smaller footprint (you only
need to copy the header definitions into a .swg file), can generate for
several languages (Perl, Ruby, Java and C#), and SWIG is an active,
tested, and mature piece of software code that has been around for
almost as long as the Linux kernel.
Python bindings including the makefile as an example:
https://elixir.bootlin.com/linux/v6.13.6/source/tools/power/cpupower/bindings/python
How to use the bindings in a script:
https://elixir.bootlin.com/linux/v6.13.6/source/tools/power/cpupower/bindings/python/test_raw_pylibcpupower.py
Original discussion:
https://lore.kernel.org/linux-pm/20240724221122.54601-1-jwyatt@redhat.com/
SWIG has been pretty useful as it helped me find two issues in the
libcpupower API that have been around for over 10 years:
https://lore.kernel.org/linux-pm/20240905021916.15938-1-jwyatt@redhat.com/T/#mf04b4ba93f79fe68c20c1d88d8ed966164a1c7d7
https://lore.kernel.org/linux-pm/20250305210901.24177-1-jwyatt@redhat.com/
>
> 2. Patch 3: A python variant of counting.c - counting.py to demonstrate
> the usage of libperf from python
>
> We have not added support for entire libperf, as we want to get
> community feedback on the approach taken in this series.
>
> Gautam Menghani (3):
> libperf: Introduce wrappers for perf structs to be exposed to python
> libperf: Introduce a C extension module for python
> libperf: Add counting.py example to demonstrate libperf usage from
> python
>
> tools/lib/perf/Build | 1 +
> .../perf/Documentation/examples/counting.py | 74 +++
> tools/lib/perf/Makefile | 12 +-
> tools/lib/perf/include/perf/py_perf.h | 431 ++++++++++++++++++
> tools/lib/perf/libperf.map | 1 +
> tools/lib/perf/py_perf.c | 262 +++++++++++
> 6 files changed, 779 insertions(+), 2 deletions(-)
> create mode 100755 tools/lib/perf/Documentation/examples/counting.py
> create mode 100644 tools/lib/perf/include/perf/py_perf.h
> create mode 100644 tools/lib/perf/py_perf.c
>
> --
> 2.47.0
>
--
Sincerely,
John Wyatt
Software Engineer, Core Kernel
Red Hat
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC PATCH 0/3] Introduce a C extension module to allow libperf usage from python
2025-03-13 14:12 ` [RFC PATCH 0/3] Introduce a C extension module to allow " John B. Wyatt IV
@ 2025-03-13 16:01 ` Ian Rogers
2025-03-14 8:20 ` Gautam Menghani
0 siblings, 1 reply; 7+ messages in thread
From: Ian Rogers @ 2025-03-13 16:01 UTC (permalink / raw)
To: John B. Wyatt IV
Cc: Gautam Menghani, peterz, mingo, acme, namhyung, mark.rutland,
alexander.shishkin, jolsa, adrian.hunter, kan.liang,
linux-perf-users, linux-kernel
On Thu, Mar 13, 2025 at 7:12 AM John B. Wyatt IV <jwyatt@redhat.com> wrote:
>
> Hello Gautam
>
> On Thu, Mar 13, 2025 at 01:21:21PM +0530, Gautam Menghani wrote:
> > In this RFC series, we are introducing a C extension module to allow
> > python programs to call the libperf API functions. Currently libperf can
> > be used by C programs, but expanding the support to python is beneficial
> > for python users.
> >
> > The structure of the patch series is as follows:
> > 1. Patch 1 : Create wrappers for the perf structs which are used by
> > examples/counting.c
> >
> > 2. Patch 2: Create the C extension module that maps and exposes the
> > libperf functions to python programs
>
> May I ask why you are not using SWIG? With libcpupower the kernel has
> already been using SWIG to generate Python bindings for a C user-space API.
>
> This has several advantages including a much smaller footprint (you only
> need to copy the header definitions into a .swg file), can generate for
> several languages (Perl, Ruby, Java and C#), and SWIG is an active,
> tested, and mature piece of software code that has been around for
> almost as long as the Linux kernel.
>
> Python bindings including the makefile as an example:
> https://elixir.bootlin.com/linux/v6.13.6/source/tools/power/cpupower/bindings/python
>
> How to use the bindings in a script:
> https://elixir.bootlin.com/linux/v6.13.6/source/tools/power/cpupower/bindings/python/test_raw_pylibcpupower.py
>
> Original discussion:
> https://lore.kernel.org/linux-pm/20240724221122.54601-1-jwyatt@redhat.com/
>
> SWIG has been pretty useful as it helped me find two issues in the
> libcpupower API that have been around for over 10 years:
> https://lore.kernel.org/linux-pm/20240905021916.15938-1-jwyatt@redhat.com/T/#mf04b4ba93f79fe68c20c1d88d8ed966164a1c7d7
> https://lore.kernel.org/linux-pm/20250305210901.24177-1-jwyatt@redhat.com/
So I think we should probably get rid of libperf and re-integrate it
back into the perf code. There are issues in the code, like removing a
list element:
https://web.git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/lib/perf/evlist.c?h=tmp.perf-tools-next#n58
just leaks the removed element. Deleting the element means that the
container evsel, rather than the libperf perf_evsel, leaks things like
the name variable. We can add yet more call backs and complexity but
I'm not sure what we're winning. Perhaps we can move things the other
way, perf code into libperf, like machine, session, pmus, .. I'd
prefer if we were to do that we refactored the code and adopted the
same license as libbpf as both libraries have similar packaging
issues. The viral GPLv2 on libperf is something of an issue. Perhaps
we can also migrate this code to Rust.
SWIG is fine, there is also CLIF, I'm way of dependencies as even a
python dependency in the perf build is optional.
We already have perf python bindings, and I've been working to extend
those for example in:
https://lore.kernel.org/lkml/20250228222308.626803-1-irogers@google.com/
and I've been working to expand those for things like hybrid CPUs. It
seems a shame to reinvent all of that logic again on top of libperf.
Thanks,
Ian
> >
> > 2. Patch 3: A python variant of counting.c - counting.py to demonstrate
> > the usage of libperf from python
> >
> > We have not added support for entire libperf, as we want to get
> > community feedback on the approach taken in this series.
> >
> > Gautam Menghani (3):
> > libperf: Introduce wrappers for perf structs to be exposed to python
> > libperf: Introduce a C extension module for python
> > libperf: Add counting.py example to demonstrate libperf usage from
> > python
> >
> > tools/lib/perf/Build | 1 +
> > .../perf/Documentation/examples/counting.py | 74 +++
> > tools/lib/perf/Makefile | 12 +-
> > tools/lib/perf/include/perf/py_perf.h | 431 ++++++++++++++++++
> > tools/lib/perf/libperf.map | 1 +
> > tools/lib/perf/py_perf.c | 262 +++++++++++
> > 6 files changed, 779 insertions(+), 2 deletions(-)
> > create mode 100755 tools/lib/perf/Documentation/examples/counting.py
> > create mode 100644 tools/lib/perf/include/perf/py_perf.h
> > create mode 100644 tools/lib/perf/py_perf.c
> >
> > --
> > 2.47.0
> >
>
> --
> Sincerely,
> John Wyatt
> Software Engineer, Core Kernel
> Red Hat
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC PATCH 0/3] Introduce a C extension module to allow libperf usage from python
2025-03-13 16:01 ` Ian Rogers
@ 2025-03-14 8:20 ` Gautam Menghani
0 siblings, 0 replies; 7+ messages in thread
From: Gautam Menghani @ 2025-03-14 8:20 UTC (permalink / raw)
To: Ian Rogers
Cc: John B. Wyatt IV, peterz, mingo, acme, namhyung, mark.rutland,
alexander.shishkin, jolsa, adrian.hunter, kan.liang,
linux-perf-users, linux-kernel, maddy
On Thu, Mar 13, 2025 at 09:01:10AM -0700, Ian Rogers wrote:
> On Thu, Mar 13, 2025 at 7:12 AM John B. Wyatt IV <jwyatt@redhat.com> wrote:
> >
> > Hello Gautam
> >
> > On Thu, Mar 13, 2025 at 01:21:21PM +0530, Gautam Menghani wrote:
> > > In this RFC series, we are introducing a C extension module to allow
> > > python programs to call the libperf API functions. Currently libperf can
> > > be used by C programs, but expanding the support to python is beneficial
> > > for python users.
> > >
> > > The structure of the patch series is as follows:
> > > 1. Patch 1 : Create wrappers for the perf structs which are used by
> > > examples/counting.c
> > >
> > > 2. Patch 2: Create the C extension module that maps and exposes the
> > > libperf functions to python programs
> >
> > May I ask why you are not using SWIG? With libcpupower the kernel has
> > already been using SWIG to generate Python bindings for a C user-space API.
> >
> > This has several advantages including a much smaller footprint (you only
> > need to copy the header definitions into a .swg file), can generate for
> > several languages (Perl, Ruby, Java and C#), and SWIG is an active,
> > tested, and mature piece of software code that has been around for
> > almost as long as the Linux kernel.
> >
> > Python bindings including the makefile as an example:
> > https://elixir.bootlin.com/linux/v6.13.6/source/tools/power/cpupower/bindings/python
> >
> > How to use the bindings in a script:
> > https://elixir.bootlin.com/linux/v6.13.6/source/tools/power/cpupower/bindings/python/test_raw_pylibcpupower.py
> >
> > Original discussion:
> > https://lore.kernel.org/linux-pm/20240724221122.54601-1-jwyatt@redhat.com/
> >
> > SWIG has been pretty useful as it helped me find two issues in the
> > libcpupower API that have been around for over 10 years:
> > https://lore.kernel.org/linux-pm/20240905021916.15938-1-jwyatt@redhat.com/T/#mf04b4ba93f79fe68c20c1d88d8ed966164a1c7d7
> > https://lore.kernel.org/linux-pm/20250305210901.24177-1-jwyatt@redhat.com/
>
> So I think we should probably get rid of libperf and re-integrate it
> back into the perf code. There are issues in the code, like removing a
> list element:
> https://web.git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/lib/perf/evlist.c?h=tmp.perf-tools-next#n58
> just leaks the removed element. Deleting the element means that the
> container evsel, rather than the libperf perf_evsel, leaks things like
> the name variable. We can add yet more call backs and complexity but
> I'm not sure what we're winning. Perhaps we can move things the other
> way, perf code into libperf, like machine, session, pmus, .. I'd
> prefer if we were to do that we refactored the code and adopted the
> same license as libbpf as both libraries have similar packaging
> issues. The viral GPLv2 on libperf is something of an issue. Perhaps
> we can also migrate this code to Rust.
>
> SWIG is fine, there is also CLIF, I'm way of dependencies as even a
> python dependency in the perf build is optional.
>
> We already have perf python bindings, and I've been working to extend
> those for example in:
> https://lore.kernel.org/lkml/20250228222308.626803-1-irogers@google.com/
> and I've been working to expand those for things like hybrid CPUs. It
> seems a shame to reinvent all of that logic again on top of libperf.
Thanks for the feedback and pointers. We'll look more into this
approach.
Thanks,
Gautam
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2025-03-14 8:21 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [RFC PATCH 2/3] libperf: Introduce a C extension module for python 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
2025-03-13 16:01 ` Ian Rogers
2025-03-14 8:20 ` Gautam Menghani
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).