* [PATCH 1/4] perf python: Add support for perf_counts_values to return counter data
2025-05-01 9:36 [PATCH 0/4] perf python: Add missing infra pieces for counting perf events Gautam Menghani
@ 2025-05-01 9:36 ` Gautam Menghani
2025-05-01 9:36 ` [PATCH 2/4] perf python: Add evsel read method Gautam Menghani
` (2 subsequent siblings)
3 siblings, 0 replies; 11+ messages in thread
From: Gautam Menghani @ 2025-05-01 9:36 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, maddy
Add bindings for perf_counts_values struct to enable the python scripts
to read the counter data for an event.
Signed-off-by: Gautam Menghani <gautam@linux.ibm.com>
---
tools/perf/util/python.c | 92 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 91 insertions(+), 1 deletion(-)
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index f3c05da25b4a..011ee2e27797 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -626,6 +626,92 @@ static int pyrf_thread_map__setup_types(void)
return PyType_Ready(&pyrf_thread_map__type);
}
+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, Py_T_ULONG, "Value of event"),
+ counts_values_member_def(ena, Py_T_ULONG, "Time for which enabled"),
+ counts_values_member_def(run, Py_T_ULONG, "Time for which running"),
+ counts_values_member_def(id, Py_T_ULONG, "Unique ID for an event"),
+ counts_values_member_def(lost, Py_T_ULONG, "Num of lost samples"),
+ {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},
+ {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
@@ -1442,7 +1528,8 @@ PyMODINIT_FUNC PyInit_perf(void)
pyrf_evlist__setup_types() < 0 ||
pyrf_evsel__setup_types() < 0 ||
pyrf_thread_map__setup_types() < 0 ||
- pyrf_cpu_map__setup_types() < 0)
+ pyrf_cpu_map__setup_types() < 0 ||
+ pyrf_counts_values__setup_types() < 0)
return module;
/* The page_size is placed in util object. */
@@ -1487,6 +1574,9 @@ PyMODINIT_FUNC PyInit_perf(void)
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;
--
2.49.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 2/4] perf python: Add evsel read method
2025-05-01 9:36 [PATCH 0/4] perf python: Add missing infra pieces for counting perf events Gautam Menghani
2025-05-01 9:36 ` [PATCH 1/4] perf python: Add support for perf_counts_values to return counter data Gautam Menghani
@ 2025-05-01 9:36 ` Gautam Menghani
2025-05-01 9:36 ` [PATCH 3/4] perf python: Add evlist close and next methods Gautam Menghani
2025-05-01 9:36 ` [PATCH 4/4] perf python: Add counting.py as example for counting perf events Gautam Menghani
3 siblings, 0 replies; 11+ messages in thread
From: Gautam Menghani @ 2025-05-01 9:36 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, maddy
Add the evsel read method to enable reading counter data for the
given evsel from python.
Signed-off-by: Gautam Menghani <gautam@linux.ibm.com>
---
tools/perf/util/python.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index 011ee2e27797..5a4d2c9aaabd 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -867,6 +867,23 @@ static PyObject *pyrf_evsel__open(struct pyrf_evsel *pevsel,
return Py_None;
}
+static PyObject *pyrf_evsel__read(struct pyrf_evsel *pevsel,
+ PyObject *args, PyObject *kwargs)
+{
+ struct evsel *evsel = &pevsel->evsel;
+ int cpu_map_idx = 0, thread = 0;
+ struct perf_counts_values counts;
+ struct pyrf_counts_values *count_values = PyObject_New(struct pyrf_counts_values,
+ &pyrf_counts_values__type);
+
+ if (!PyArg_ParseTuple(args, "ii", &cpu_map_idx, &thread))
+ return NULL;
+
+ perf_evsel__read(&(evsel->core), cpu_map_idx, thread, &counts);
+ count_values->values = counts;
+ return (PyObject *)count_values;
+}
+
static PyObject *pyrf_evsel__str(PyObject *self)
{
struct pyrf_evsel *pevsel = (void *)self;
@@ -885,6 +902,12 @@ static PyMethodDef pyrf_evsel__methods[] = {
.ml_flags = METH_VARARGS | METH_KEYWORDS,
.ml_doc = PyDoc_STR("open the event selector file descriptor table.")
},
+ {
+ .ml_name = "read",
+ .ml_meth = (PyCFunction)pyrf_evsel__read,
+ .ml_flags = METH_VARARGS | METH_KEYWORDS,
+ .ml_doc = PyDoc_STR("read counters")
+ },
{ .ml_name = NULL, }
};
--
2.49.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 3/4] perf python: Add evlist close and next methods
2025-05-01 9:36 [PATCH 0/4] perf python: Add missing infra pieces for counting perf events Gautam Menghani
2025-05-01 9:36 ` [PATCH 1/4] perf python: Add support for perf_counts_values to return counter data Gautam Menghani
2025-05-01 9:36 ` [PATCH 2/4] perf python: Add evsel read method Gautam Menghani
@ 2025-05-01 9:36 ` Gautam Menghani
2025-05-01 15:49 ` Ian Rogers
2025-05-01 9:36 ` [PATCH 4/4] perf python: Add counting.py as example for counting perf events Gautam Menghani
3 siblings, 1 reply; 11+ messages in thread
From: Gautam Menghani @ 2025-05-01 9:36 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, maddy
Add support for the evlist close and next methods. The next method
enables iterating over the evsels in an evlist.
Signed-off-by: Gautam Menghani <gautam@linux.ibm.com>
---
tools/perf/util/python.c | 47 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 47 insertions(+)
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index 5a4d2c9aaabd..599cb37600f1 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -1163,6 +1163,16 @@ 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 = {
@@ -1202,6 +1212,31 @@ static PyObject *pyrf_evlist__enable(struct pyrf_evlist *pevlist)
return Py_None;
}
+static PyObject *pyrf_evlist__next(struct pyrf_evlist *pevlist,
+ PyObject *args, PyObject *kwargs)
+{
+ struct evlist *evlist = &pevlist->evlist;
+ PyObject *py_evsel;
+ struct perf_evsel *pevsel;
+ struct evsel *evsel;
+ struct pyrf_evsel *next_evsel = PyObject_New(struct pyrf_evsel, &pyrf_evsel__type);
+ static char *kwlist[] = { "evsel", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist,
+ &py_evsel))
+ return NULL;
+
+ pevsel = (py_evsel == Py_None) ? NULL : &(((struct pyrf_evsel *)py_evsel)->evsel.core);
+ pevsel = perf_evlist__next(&(evlist->core), pevsel);
+ if (pevsel != NULL) {
+ evsel = container_of(pevsel, struct evsel, core);
+ next_evsel = container_of(evsel, struct pyrf_evsel, evsel);
+ return (PyObject *) next_evsel;
+ }
+
+ return Py_None;
+}
+
static PyMethodDef pyrf_evlist__methods[] = {
{
.ml_name = "all_cpus",
@@ -1221,6 +1256,12 @@ static PyMethodDef pyrf_evlist__methods[] = {
.ml_flags = METH_VARARGS | METH_KEYWORDS,
.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,
@@ -1263,6 +1304,12 @@ static PyMethodDef pyrf_evlist__methods[] = {
.ml_flags = METH_NOARGS,
.ml_doc = PyDoc_STR("Enable the evsels in the evlist.")
},
+ {
+ .ml_name = "next",
+ .ml_meth = (PyCFunction)pyrf_evlist__next,
+ .ml_flags = METH_VARARGS | METH_KEYWORDS,
+ .ml_doc = PyDoc_STR("Return next evsel")
+ },
{ .ml_name = NULL, }
};
--
2.49.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 3/4] perf python: Add evlist close and next methods
2025-05-01 9:36 ` [PATCH 3/4] perf python: Add evlist close and next methods Gautam Menghani
@ 2025-05-01 15:49 ` Ian Rogers
2025-05-05 11:49 ` Gautam Menghani
0 siblings, 1 reply; 11+ messages in thread
From: Ian Rogers @ 2025-05-01 15:49 UTC (permalink / raw)
To: Gautam Menghani
Cc: peterz, mingo, acme, namhyung, mark.rutland, alexander.shishkin,
jolsa, adrian.hunter, kan.liang, linux-perf-users, linux-kernel,
maddy
On Thu, May 1, 2025 at 2:37 AM Gautam Menghani <gautam@linux.ibm.com> wrote:
>
> Add support for the evlist close and next methods. The next method
> enables iterating over the evsels in an evlist.
>
> Signed-off-by: Gautam Menghani <gautam@linux.ibm.com>
> ---
> tools/perf/util/python.c | 47 ++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 47 insertions(+)
>
> diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
> index 5a4d2c9aaabd..599cb37600f1 100644
> --- a/tools/perf/util/python.c
> +++ b/tools/perf/util/python.c
> @@ -1163,6 +1163,16 @@ 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 = {
> @@ -1202,6 +1212,31 @@ static PyObject *pyrf_evlist__enable(struct pyrf_evlist *pevlist)
> return Py_None;
> }
>
> +static PyObject *pyrf_evlist__next(struct pyrf_evlist *pevlist,
> + PyObject *args, PyObject *kwargs)
> +{
> + struct evlist *evlist = &pevlist->evlist;
> + PyObject *py_evsel;
> + struct perf_evsel *pevsel;
> + struct evsel *evsel;
> + struct pyrf_evsel *next_evsel = PyObject_New(struct pyrf_evsel, &pyrf_evsel__type);
> + static char *kwlist[] = { "evsel", NULL };
> +
> + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist,
> + &py_evsel))
> + return NULL;
> +
> + pevsel = (py_evsel == Py_None) ? NULL : &(((struct pyrf_evsel *)py_evsel)->evsel.core);
> + pevsel = perf_evlist__next(&(evlist->core), pevsel);
> + if (pevsel != NULL) {
> + evsel = container_of(pevsel, struct evsel, core);
> + next_evsel = container_of(evsel, struct pyrf_evsel, evsel);
> + return (PyObject *) next_evsel;
> + }
> +
> + return Py_None;
> +}
> +
Thanks for this! Have you looked at the existing iteration support?
There's an example here:
https://web.git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/python/tracepoint.py?h=perf-tools-next#n26
```
for ev in evlist:
ev.sample_type = ev.sample_type & ~perf.SAMPLE_IP
ev.read_format = 0
```
In the next patch you have:
```
evsel = evlist.next(None)
while evsel != None:
counts = evsel.read(0, 0)
print(counts.val, counts.ena, counts.run)
evsel = evlist.next(evsel)
```
I believe the former looks better. It also isn't clear to me if next
belongs on evlist or evsel.
Thanks,
Ian
> static PyMethodDef pyrf_evlist__methods[] = {
> {
> .ml_name = "all_cpus",
> @@ -1221,6 +1256,12 @@ static PyMethodDef pyrf_evlist__methods[] = {
> .ml_flags = METH_VARARGS | METH_KEYWORDS,
> .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,
> @@ -1263,6 +1304,12 @@ static PyMethodDef pyrf_evlist__methods[] = {
> .ml_flags = METH_NOARGS,
> .ml_doc = PyDoc_STR("Enable the evsels in the evlist.")
> },
> + {
> + .ml_name = "next",
> + .ml_meth = (PyCFunction)pyrf_evlist__next,
> + .ml_flags = METH_VARARGS | METH_KEYWORDS,
> + .ml_doc = PyDoc_STR("Return next evsel")
> + },
> { .ml_name = NULL, }
> };
>
> --
> 2.49.0
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 3/4] perf python: Add evlist close and next methods
2025-05-01 15:49 ` Ian Rogers
@ 2025-05-05 11:49 ` Gautam Menghani
2025-05-05 21:02 ` Ian Rogers
0 siblings, 1 reply; 11+ messages in thread
From: Gautam Menghani @ 2025-05-05 11:49 UTC (permalink / raw)
To: Ian Rogers
Cc: peterz, mingo, acme, namhyung, mark.rutland, alexander.shishkin,
jolsa, adrian.hunter, kan.liang, linux-perf-users, linux-kernel,
maddy
On Thu, May 01, 2025 at 08:49:08AM -0700, Ian Rogers wrote:
> On Thu, May 1, 2025 at 2:37 AM Gautam Menghani <gautam@linux.ibm.com> wrote:
> >
> > Add support for the evlist close and next methods. The next method
> > enables iterating over the evsels in an evlist.
> >
> > Signed-off-by: Gautam Menghani <gautam@linux.ibm.com>
> > ---
> > tools/perf/util/python.c | 47 ++++++++++++++++++++++++++++++++++++++++
> > 1 file changed, 47 insertions(+)
> >
> > diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
> > index 5a4d2c9aaabd..599cb37600f1 100644
> > --- a/tools/perf/util/python.c
> > +++ b/tools/perf/util/python.c
> > @@ -1163,6 +1163,16 @@ 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 = {
> > @@ -1202,6 +1212,31 @@ static PyObject *pyrf_evlist__enable(struct pyrf_evlist *pevlist)
> > return Py_None;
> > }
> >
> > +static PyObject *pyrf_evlist__next(struct pyrf_evlist *pevlist,
> > + PyObject *args, PyObject *kwargs)
> > +{
> > + struct evlist *evlist = &pevlist->evlist;
> > + PyObject *py_evsel;
> > + struct perf_evsel *pevsel;
> > + struct evsel *evsel;
> > + struct pyrf_evsel *next_evsel = PyObject_New(struct pyrf_evsel, &pyrf_evsel__type);
> > + static char *kwlist[] = { "evsel", NULL };
> > +
> > + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist,
> > + &py_evsel))
> > + return NULL;
> > +
> > + pevsel = (py_evsel == Py_None) ? NULL : &(((struct pyrf_evsel *)py_evsel)->evsel.core);
> > + pevsel = perf_evlist__next(&(evlist->core), pevsel);
> > + if (pevsel != NULL) {
> > + evsel = container_of(pevsel, struct evsel, core);
> > + next_evsel = container_of(evsel, struct pyrf_evsel, evsel);
> > + return (PyObject *) next_evsel;
> > + }
> > +
> > + return Py_None;
> > +}
> > +
>
> Thanks for this! Have you looked at the existing iteration support?
> There's an example here:
> https://web.git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/python/tracepoint.py?h=perf-tools-next#n26
> ```
> for ev in evlist:
> ev.sample_type = ev.sample_type & ~perf.SAMPLE_IP
> ev.read_format = 0
> ```
> In the next patch you have:
> ```
> evsel = evlist.next(None)
> while evsel != None:
> counts = evsel.read(0, 0)
> print(counts.val, counts.ena, counts.run)
> evsel = evlist.next(evsel)
> ```
> I believe the former looks better. It also isn't clear to me if next
> belongs on evlist or evsel.
Yes, the existing support would be the right way, I missed that. Will fix in
v2.
and regarding the next() function, I think we should keep it for evlist
because for the C code it's defined in the context of evlist, so would
avoid confusion. But since it is not needed for the iteration, should
I keep it in v2?
Thanks,
Gautam
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 3/4] perf python: Add evlist close and next methods
2025-05-05 11:49 ` Gautam Menghani
@ 2025-05-05 21:02 ` Ian Rogers
0 siblings, 0 replies; 11+ messages in thread
From: Ian Rogers @ 2025-05-05 21:02 UTC (permalink / raw)
To: Gautam Menghani
Cc: peterz, mingo, acme, namhyung, mark.rutland, alexander.shishkin,
jolsa, adrian.hunter, kan.liang, linux-perf-users, linux-kernel,
maddy
On Mon, May 5, 2025 at 4:49 AM Gautam Menghani <gautam@linux.ibm.com> wrote:
>
> On Thu, May 01, 2025 at 08:49:08AM -0700, Ian Rogers wrote:
> > On Thu, May 1, 2025 at 2:37 AM Gautam Menghani <gautam@linux.ibm.com> wrote:
> > >
> > > Add support for the evlist close and next methods. The next method
> > > enables iterating over the evsels in an evlist.
> > >
> > > Signed-off-by: Gautam Menghani <gautam@linux.ibm.com>
> > > ---
> > > tools/perf/util/python.c | 47 ++++++++++++++++++++++++++++++++++++++++
> > > 1 file changed, 47 insertions(+)
> > >
> > > diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
> > > index 5a4d2c9aaabd..599cb37600f1 100644
> > > --- a/tools/perf/util/python.c
> > > +++ b/tools/perf/util/python.c
> > > @@ -1163,6 +1163,16 @@ 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 = {
> > > @@ -1202,6 +1212,31 @@ static PyObject *pyrf_evlist__enable(struct pyrf_evlist *pevlist)
> > > return Py_None;
> > > }
> > >
> > > +static PyObject *pyrf_evlist__next(struct pyrf_evlist *pevlist,
> > > + PyObject *args, PyObject *kwargs)
> > > +{
> > > + struct evlist *evlist = &pevlist->evlist;
> > > + PyObject *py_evsel;
> > > + struct perf_evsel *pevsel;
> > > + struct evsel *evsel;
> > > + struct pyrf_evsel *next_evsel = PyObject_New(struct pyrf_evsel, &pyrf_evsel__type);
> > > + static char *kwlist[] = { "evsel", NULL };
> > > +
> > > + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist,
> > > + &py_evsel))
> > > + return NULL;
> > > +
> > > + pevsel = (py_evsel == Py_None) ? NULL : &(((struct pyrf_evsel *)py_evsel)->evsel.core);
> > > + pevsel = perf_evlist__next(&(evlist->core), pevsel);
> > > + if (pevsel != NULL) {
> > > + evsel = container_of(pevsel, struct evsel, core);
> > > + next_evsel = container_of(evsel, struct pyrf_evsel, evsel);
> > > + return (PyObject *) next_evsel;
> > > + }
> > > +
> > > + return Py_None;
> > > +}
> > > +
> >
> > Thanks for this! Have you looked at the existing iteration support?
> > There's an example here:
> > https://web.git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/python/tracepoint.py?h=perf-tools-next#n26
> > ```
> > for ev in evlist:
> > ev.sample_type = ev.sample_type & ~perf.SAMPLE_IP
> > ev.read_format = 0
> > ```
> > In the next patch you have:
> > ```
> > evsel = evlist.next(None)
> > while evsel != None:
> > counts = evsel.read(0, 0)
> > print(counts.val, counts.ena, counts.run)
> > evsel = evlist.next(evsel)
> > ```
> > I believe the former looks better. It also isn't clear to me if next
> > belongs on evlist or evsel.
>
> Yes, the existing support would be the right way, I missed that. Will fix in
> v2.
>
> and regarding the next() function, I think we should keep it for evlist
> because for the C code it's defined in the context of evlist, so would
> avoid confusion. But since it is not needed for the iteration, should
> I keep it in v2?
So the perf code is following the kernel style and using invasive data
structures a lot. The kernel does this as memory allocation is a pain
and can fail causing complicated error paths. It gets kind of crazy,
if you look at perf_event in the kernel it has like 11 invasive data
structures:
https://web.git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/include/linux/perf_event.h?h=perf-tools-next#n706
```
struct perf_event {
struct list_head event_entry;
struct list_head sibling_list;
struct list_head active_list;
struct rb_node group_node;
struct list_head migrate_entry;
struct hlist_node hlist_entry;
struct list_head active_entry;
struct list_head child_list;
struct list_head owner_entry;
struct list_head rb_entry;
struct list_head sb_list;
};
```
Managed languages like Python don't tend to use invasive data
structures and I'm not sure exposing next in this way makes sense. We
may want to use an array for evlist in the future, which we've done in
the past to make reference count accounting easier, for example:
https://lore.kernel.org/r/20240210031746.4057262-2-irogers@google.com
But if next is exposed then we'd need to support that for scripts that
may be using it. I think it is easier to avoid the problem by just not
adding the function.
Thanks,
Ian
> Thanks,
> Gautam
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 4/4] perf python: Add counting.py as example for counting perf events
2025-05-01 9:36 [PATCH 0/4] perf python: Add missing infra pieces for counting perf events Gautam Menghani
` (2 preceding siblings ...)
2025-05-01 9:36 ` [PATCH 3/4] perf python: Add evlist close and next methods Gautam Menghani
@ 2025-05-01 9:36 ` Gautam Menghani
2025-05-01 16:04 ` Ian Rogers
3 siblings, 1 reply; 11+ messages in thread
From: Gautam Menghani @ 2025-05-01 9:36 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, maddy
Add counting.py - a python version of counting.c to demonstrate
measuring and reading of counts for given perf events.
Signed-off-by: Gautam Menghani <gautam@linux.ibm.com>
---
tools/perf/python/counting.py | 41 +++++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
create mode 100755 tools/perf/python/counting.py
diff --git a/tools/perf/python/counting.py b/tools/perf/python/counting.py
new file mode 100755
index 000000000000..0c58907bd8bf
--- /dev/null
+++ b/tools/perf/python/counting.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+# -*- python -*-
+# -*- coding: utf-8 -*-
+
+import perf
+
+def main():
+ cpus = perf.cpu_map()
+ thread_map = perf.thread_map(-1)
+ evlist = perf.evlist(cpus, thread_map)
+
+ evsel1 = perf.evsel(type = perf.TYPE_SOFTWARE,
+ config = perf.COUNT_SW_CPU_CLOCK,
+ read_format = perf.FORMAT_TOTAL_TIME_ENABLED | perf.FORMAT_TOTAL_TIME_RUNNING,
+ disabled=1)
+ evlist.add(evsel1)
+
+ evsel2 = perf.evsel(type = perf.TYPE_SOFTWARE,
+ config = perf.COUNT_SW_TASK_CLOCK,
+ read_format = perf.FORMAT_TOTAL_TIME_ENABLED | perf.FORMAT_TOTAL_TIME_RUNNING,
+ disabled=1)
+ evlist.add(evsel2)
+
+ evlist.open()
+ evlist.enable()
+
+ count = 100000
+ while count > 0:
+ count -= 1
+
+ evlist.disable()
+ evsel = evlist.next(None)
+ while evsel != None:
+ counts = evsel.read(0, 0)
+ print(counts.val, counts.ena, counts.run)
+ evsel = evlist.next(evsel)
+ evlist.close()
+
+if __name__ == '__main__':
+ main()
--
2.49.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 4/4] perf python: Add counting.py as example for counting perf events
2025-05-01 9:36 ` [PATCH 4/4] perf python: Add counting.py as example for counting perf events Gautam Menghani
@ 2025-05-01 16:04 ` Ian Rogers
2025-05-01 16:11 ` Ian Rogers
2025-05-05 11:51 ` Gautam Menghani
0 siblings, 2 replies; 11+ messages in thread
From: Ian Rogers @ 2025-05-01 16:04 UTC (permalink / raw)
To: Gautam Menghani
Cc: peterz, mingo, acme, namhyung, mark.rutland, alexander.shishkin,
jolsa, adrian.hunter, kan.liang, linux-perf-users, linux-kernel,
maddy
On Thu, May 1, 2025 at 2:37 AM Gautam Menghani <gautam@linux.ibm.com> wrote:
>
> Add counting.py - a python version of counting.c to demonstrate
> measuring and reading of counts for given perf events.
>
> Signed-off-by: Gautam Menghani <gautam@linux.ibm.com>
> ---
> tools/perf/python/counting.py | 41 +++++++++++++++++++++++++++++++++++
> 1 file changed, 41 insertions(+)
> create mode 100755 tools/perf/python/counting.py
>
> diff --git a/tools/perf/python/counting.py b/tools/perf/python/counting.py
> new file mode 100755
> index 000000000000..0c58907bd8bf
> --- /dev/null
> +++ b/tools/perf/python/counting.py
> @@ -0,0 +1,41 @@
> +#!/usr/bin/env python3
> +# SPDX-License-Identifier: GPL-2.0
> +# -*- python -*-
> +# -*- coding: utf-8 -*-
> +
> +import perf
> +
> +def main():
> + cpus = perf.cpu_map()
> + thread_map = perf.thread_map(-1)
> + evlist = perf.evlist(cpus, thread_map)
> +
> + evsel1 = perf.evsel(type = perf.TYPE_SOFTWARE,
> + config = perf.COUNT_SW_CPU_CLOCK,
> + read_format = perf.FORMAT_TOTAL_TIME_ENABLED | perf.FORMAT_TOTAL_TIME_RUNNING,
> + disabled=1)
> + evlist.add(evsel1)
> +
> + evsel2 = perf.evsel(type = perf.TYPE_SOFTWARE,
> + config = perf.COUNT_SW_TASK_CLOCK,
> + read_format = perf.FORMAT_TOTAL_TIME_ENABLED | perf.FORMAT_TOTAL_TIME_RUNNING,
> + disabled=1)
> + evlist.add(evsel2)
Nice example! Would this be better as:
```
cpus = perf.cpu_map()
threads = perf.thread_map(-1)
evlist = perf.parse_events("cpu-clock,task-clock", cpus, threads)
```
If you run `perf stat -vv -e 'cpu-clock,task-clock' .. ` you can
double check the perf event attribute bits. For example in
tracepoint.py we remove the SAMPLE_IP:
https://web.git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/python/tracepoint.py?h=perf-tools-next#n27
> +
> + evlist.open()
> + evlist.enable()
> +
> + count = 100000
> + while count > 0:
> + count -= 1
> +
> + evlist.disable()
> + evsel = evlist.next(None)
> + while evsel != None:
> + counts = evsel.read(0, 0)
Rather than just reading on the first CPU and thread, perhaps change
to iterate over the cpus and threads? Something like:
```
for evsel in evlist:
for cpu in cpus:
for thread in threads:
counts = evsel.read(cpu, thread)
print(f"For {evsel} read val={counts.val}
enable={counts.ena} run ={counts.run}")
```
Thanks,
Ian
> + print(counts.val, counts.ena, counts.run)
> + evsel = evlist.next(evsel)
> + evlist.close()
> +
> +if __name__ == '__main__':
> + main()
> --
> 2.49.0
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 4/4] perf python: Add counting.py as example for counting perf events
2025-05-01 16:04 ` Ian Rogers
@ 2025-05-01 16:11 ` Ian Rogers
2025-05-05 11:51 ` Gautam Menghani
1 sibling, 0 replies; 11+ messages in thread
From: Ian Rogers @ 2025-05-01 16:11 UTC (permalink / raw)
To: Gautam Menghani
Cc: peterz, mingo, acme, namhyung, mark.rutland, alexander.shishkin,
jolsa, adrian.hunter, kan.liang, linux-perf-users, linux-kernel,
maddy
On Thu, May 1, 2025 at 9:04 AM Ian Rogers <irogers@google.com> wrote:
>
> On Thu, May 1, 2025 at 2:37 AM Gautam Menghani <gautam@linux.ibm.com> wrote:
> >
> > Add counting.py - a python version of counting.c to demonstrate
> > measuring and reading of counts for given perf events.
> >
> > Signed-off-by: Gautam Menghani <gautam@linux.ibm.com>
> > ---
> > tools/perf/python/counting.py | 41 +++++++++++++++++++++++++++++++++++
> > 1 file changed, 41 insertions(+)
> > create mode 100755 tools/perf/python/counting.py
> >
> > diff --git a/tools/perf/python/counting.py b/tools/perf/python/counting.py
> > new file mode 100755
> > index 000000000000..0c58907bd8bf
> > --- /dev/null
> > +++ b/tools/perf/python/counting.py
> > @@ -0,0 +1,41 @@
> > +#!/usr/bin/env python3
> > +# SPDX-License-Identifier: GPL-2.0
> > +# -*- python -*-
> > +# -*- coding: utf-8 -*-
> > +
> > +import perf
> > +
> > +def main():
> > + cpus = perf.cpu_map()
> > + thread_map = perf.thread_map(-1)
> > + evlist = perf.evlist(cpus, thread_map)
> > +
> > + evsel1 = perf.evsel(type = perf.TYPE_SOFTWARE,
> > + config = perf.COUNT_SW_CPU_CLOCK,
> > + read_format = perf.FORMAT_TOTAL_TIME_ENABLED | perf.FORMAT_TOTAL_TIME_RUNNING,
> > + disabled=1)
> > + evlist.add(evsel1)
> > +
> > + evsel2 = perf.evsel(type = perf.TYPE_SOFTWARE,
> > + config = perf.COUNT_SW_TASK_CLOCK,
> > + read_format = perf.FORMAT_TOTAL_TIME_ENABLED | perf.FORMAT_TOTAL_TIME_RUNNING,
> > + disabled=1)
> > + evlist.add(evsel2)
>
> Nice example! Would this be better as:
> ```
> cpus = perf.cpu_map()
> threads = perf.thread_map(-1)
> evlist = perf.parse_events("cpu-clock,task-clock", cpus, threads)
> ```
> If you run `perf stat -vv -e 'cpu-clock,task-clock' .. ` you can
> double check the perf event attribute bits. For example in
> tracepoint.py we remove the SAMPLE_IP:
> https://web.git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/python/tracepoint.py?h=perf-tools-next#n27
>
> > +
> > + evlist.open()
> > + evlist.enable()
> > +
> > + count = 100000
> > + while count > 0:
> > + count -= 1
> > +
> > + evlist.disable()
> > + evsel = evlist.next(None)
> > + while evsel != None:
> > + counts = evsel.read(0, 0)
>
> Rather than just reading on the first CPU and thread, perhaps change
> to iterate over the cpus and threads? Something like:
> ```
> for evsel in evlist:
> for cpu in cpus:
> for thread in threads:
> counts = evsel.read(cpu, thread)
> print(f"For {evsel} read val={counts.val}
> enable={counts.ena} run ={counts.run}")
> ```
Oh note, it is easy to confuse a thread or a CPU with the index into
the thread_map or cpu_map. For CPUs the two values of CPU and index
are often the same making it very easy to write code that works only
on CPU events (not events on things like memory controllers that
typically list a CPU per-socket). We added a struct perf_cpu to try to
avoid this kind of issue in the C code but in the python code
everything is back to being ints :-(
Thanks,
Ian
> Thanks,
> Ian
>
> > + print(counts.val, counts.ena, counts.run)
> > + evsel = evlist.next(evsel)
> > + evlist.close()
> > +
> > +if __name__ == '__main__':
> > + main()
> > --
> > 2.49.0
> >
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 4/4] perf python: Add counting.py as example for counting perf events
2025-05-01 16:04 ` Ian Rogers
2025-05-01 16:11 ` Ian Rogers
@ 2025-05-05 11:51 ` Gautam Menghani
1 sibling, 0 replies; 11+ messages in thread
From: Gautam Menghani @ 2025-05-05 11:51 UTC (permalink / raw)
To: Ian Rogers
Cc: peterz, mingo, acme, namhyung, mark.rutland, alexander.shishkin,
jolsa, adrian.hunter, kan.liang, linux-perf-users, linux-kernel,
maddy
On Thu, May 01, 2025 at 09:04:43AM -0700, Ian Rogers wrote:
> On Thu, May 1, 2025 at 2:37 AM Gautam Menghani <gautam@linux.ibm.com> wrote:
> >
> > Add counting.py - a python version of counting.c to demonstrate
> > measuring and reading of counts for given perf events.
> >
> > Signed-off-by: Gautam Menghani <gautam@linux.ibm.com>
> > ---
> > tools/perf/python/counting.py | 41 +++++++++++++++++++++++++++++++++++
> > 1 file changed, 41 insertions(+)
> > create mode 100755 tools/perf/python/counting.py
> >
> > diff --git a/tools/perf/python/counting.py b/tools/perf/python/counting.py
> > new file mode 100755
> > index 000000000000..0c58907bd8bf
> > --- /dev/null
> > +++ b/tools/perf/python/counting.py
> > @@ -0,0 +1,41 @@
> > +#!/usr/bin/env python3
> > +# SPDX-License-Identifier: GPL-2.0
> > +# -*- python -*-
> > +# -*- coding: utf-8 -*-
> > +
> > +import perf
> > +
> > +def main():
> > + cpus = perf.cpu_map()
> > + thread_map = perf.thread_map(-1)
> > + evlist = perf.evlist(cpus, thread_map)
> > +
> > + evsel1 = perf.evsel(type = perf.TYPE_SOFTWARE,
> > + config = perf.COUNT_SW_CPU_CLOCK,
> > + read_format = perf.FORMAT_TOTAL_TIME_ENABLED | perf.FORMAT_TOTAL_TIME_RUNNING,
> > + disabled=1)
> > + evlist.add(evsel1)
> > +
> > + evsel2 = perf.evsel(type = perf.TYPE_SOFTWARE,
> > + config = perf.COUNT_SW_TASK_CLOCK,
> > + read_format = perf.FORMAT_TOTAL_TIME_ENABLED | perf.FORMAT_TOTAL_TIME_RUNNING,
> > + disabled=1)
> > + evlist.add(evsel2)
>
> Nice example! Would this be better as:
> ```
> cpus = perf.cpu_map()
> threads = perf.thread_map(-1)
> evlist = perf.parse_events("cpu-clock,task-clock", cpus, threads)
> ```
> If you run `perf stat -vv -e 'cpu-clock,task-clock' .. ` you can
> double check the perf event attribute bits. For example in
> tracepoint.py we remove the SAMPLE_IP:
> https://web.git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/python/tracepoint.py?h=perf-tools-next#n27
Yes this is cleaner, will fix in v2.
>
> > +
> > + evlist.open()
> > + evlist.enable()
> > +
> > + count = 100000
> > + while count > 0:
> > + count -= 1
> > +
> > + evlist.disable()
> > + evsel = evlist.next(None)
> > + while evsel != None:
> > + counts = evsel.read(0, 0)
>
> Rather than just reading on the first CPU and thread, perhaps change
> to iterate over the cpus and threads? Something like:
> ```
> for evsel in evlist:
> for cpu in cpus:
> for thread in threads:
> counts = evsel.read(cpu, thread)
> print(f"For {evsel} read val={counts.val}
> enable={counts.ena} run ={counts.run}")
> ```
Ack, will do
Thanks,
Gautam
^ permalink raw reply [flat|nested] 11+ messages in thread