]> www.infradead.org Git - users/hch/misc.git/commitdiff
perf python: Add basic PMU abstraction and pmus sequence
authorIan Rogers <irogers@google.com>
Tue, 19 Aug 2025 01:39:33 +0000 (18:39 -0700)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Wed, 3 Sep 2025 15:34:54 +0000 (12:34 -0300)
Add an ability to iterate over PMUs and a basic PMU type then can just
show the PMU's name.

An example usage:
```
$ python
Python 3.12.9 (main, Feb  5 2025, 01:31:18) [GCC 14.2.0] on linux
>>> import perf
>>> list(perf.pmus())
[pmu(cpu), pmu(breakpoint), pmu(cstate_core), pmu(cstate_pkg),
pmu(hwmon_acpitz), pmu(hwmon_ac), pmu(hwmon_bat0),
pmu(hwmon_coretemp), pmu(hwmon_iwlwifi_1), pmu(hwmon_nvme),
pmu(hwmon_thinkpad), pmu(hwmon_ucsi_source_psy_usbc000_0),
pmu(hwmon_ucsi_source_psy_usbc000_0), pmu(i915), pmu(intel_bts),
pmu(intel_pt), pmu(kprobe), pmu(msr), pmu(power), pmu(software),
pmu(tool), pmu(tracepoint), pmu(uncore_arb), pmu(uncore_cbox_0),
pmu(uncore_cbox_1), pmu(uncore_cbox_2), pmu(uncore_cbox_3),
pmu(uncore_cbox_4), pmu(uncore_cbox_5), pmu(uncore_cbox_6),
pmu(uncore_cbox_7), pmu(uncore_clock), pmu(uncore_imc_free_running_0),
pmu(uncore_imc_free_running_1), pmu(uprobe)]
```

Committer testing:

One has to set PYTHONPATH to the build directory beforehand:

  $ export PYTHONPATH=/tmp/build/perf-tools-next/python/
  $ python
  Python 3.13.7 (main, Aug 14 2025, 00:00:00)
                 [GCC 15.2.1 20250808 (Red Hat 15.2.1-1)] on linux
  >>> import perf
  >>> list(perf.pmus())
  [pmu(cpu), pmu(amd_df), pmu(amd_iommu_0), pmu(amd_l3), pmu(amd_umc_0),
   pmu(breakpoint), pmu(hwmon_amdgpu), pmu(hwmon_amdgpu), pmu(hwmon_k10temp),
   pmu(hwmon_nvme), pmu(hwmon_r8169_0_e00_00), pmu(ibs_fetch), pmu(ibs_op),
   pmu(kprobe), pmu(msr), pmu(power), pmu(power_core), pmu(software),
   pmu(tool), pmu(tracepoint), pmu(uprobe)]
  >>>

Reviewed-by: Howard Chu <howardchu95@gmail.com>
Signed-off-by: Ian Rogers <irogers@google.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Chun-Tse Shao <ctshao@google.com>
Cc: Collin Funk <collin.funk1@gmail.com>
Cc: Dr. David Alan Gilbert <linux@treblig.org>
Cc: Gautam Menghani <gautam@linux.ibm.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@linaro.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Falcon <thomas.falcon@intel.com>
Cc: Thomas Richter <tmricht@linux.ibm.com>
Cc: Tiezhu Yang <yangtiezhu@loongson.cn>
Cc: Weilin Wang <weilin.wang@intel.com>
Cc: Xu Yang <xu.yang_2@nxp.com>
Link: https://lore.kernel.org/r/20250819013941.209033-4-irogers@google.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/util/python.c

index 127934af4828f92bbdc2240ee060a02fadc5e708..6f9728d365aeda4d0895df4d7a7a99e8c0d12245 100644 (file)
@@ -649,6 +649,138 @@ static int pyrf_thread_map__setup_types(void)
        return PyType_Ready(&pyrf_thread_map__type);
 }
 
+/**
+ * A python wrapper for perf_pmus that are globally owned by the pmus.c code.
+ */
+struct pyrf_pmu {
+       PyObject_HEAD
+
+       struct perf_pmu *pmu;
+};
+
+static void pyrf_pmu__delete(struct pyrf_pmu *ppmu)
+{
+       Py_TYPE(ppmu)->tp_free((PyObject *)ppmu);
+}
+
+static PyObject *pyrf_pmu__name(PyObject *self)
+{
+       struct pyrf_pmu *ppmu = (void *)self;
+
+       return PyUnicode_FromString(ppmu->pmu->name);
+}
+
+static PyObject *pyrf_pmu__repr(PyObject *self)
+{
+       struct pyrf_pmu *ppmu = (void *)self;
+
+       return PyUnicode_FromFormat("pmu(%s)", ppmu->pmu->name);
+}
+
+static const char pyrf_pmu__doc[] = PyDoc_STR("perf Performance Monitoring Unit (PMU) object.");
+
+static PyMethodDef pyrf_pmu__methods[] = {
+       {
+               .ml_name  = "name",
+               .ml_meth  = (PyCFunction)pyrf_pmu__name,
+               .ml_flags = METH_NOARGS,
+               .ml_doc   = PyDoc_STR("Name of the PMU including suffixes.")
+       },
+       { .ml_name = NULL, }
+};
+
+/** The python type for a perf.pmu. */
+static PyTypeObject pyrf_pmu__type = {
+       PyVarObject_HEAD_INIT(NULL, 0)
+       .tp_name        = "perf.pmu",
+       .tp_basicsize   = sizeof(struct pyrf_pmu),
+       .tp_dealloc     = (destructor)pyrf_pmu__delete,
+       .tp_flags       = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
+       .tp_doc         = pyrf_pmu__doc,
+       .tp_methods     = pyrf_pmu__methods,
+       .tp_str         = pyrf_pmu__name,
+       .tp_repr        = pyrf_pmu__repr,
+};
+
+static int pyrf_pmu__setup_types(void)
+{
+       pyrf_pmu__type.tp_new = PyType_GenericNew;
+       return PyType_Ready(&pyrf_pmu__type);
+}
+
+
+/** A python iterator for pmus that has no equivalent in the C code. */
+struct pyrf_pmu_iterator {
+       PyObject_HEAD
+       struct perf_pmu *pmu;
+};
+
+static void pyrf_pmu_iterator__dealloc(struct pyrf_pmu_iterator *self)
+{
+       Py_TYPE(self)->tp_free((PyObject *) self);
+}
+
+static PyObject *pyrf_pmu_iterator__new(PyTypeObject *type, PyObject *args __maybe_unused,
+                                       PyObject *kwds __maybe_unused)
+{
+       struct pyrf_pmu_iterator *itr = (void *)type->tp_alloc(type, 0);
+
+       if (itr != NULL)
+               itr->pmu = perf_pmus__scan(/*pmu=*/NULL);
+
+       return (PyObject *) itr;
+}
+
+static PyObject *pyrf_pmu_iterator__iter(PyObject *self)
+{
+       Py_INCREF(self);
+       return self;
+}
+
+static PyObject *pyrf_pmu_iterator__iternext(PyObject *self)
+{
+       struct pyrf_pmu_iterator *itr = (void *)self;
+       struct pyrf_pmu *ppmu;
+
+       if (itr->pmu == NULL) {
+               PyErr_SetNone(PyExc_StopIteration);
+               return NULL;
+       }
+       // Create object to return.
+       ppmu = PyObject_New(struct pyrf_pmu, &pyrf_pmu__type);
+       if (ppmu) {
+               ppmu->pmu = itr->pmu;
+               // Advance iterator.
+               itr->pmu = perf_pmus__scan(itr->pmu);
+       }
+       return (PyObject *)ppmu;
+}
+
+/** The python type for the PMU iterator. */
+static PyTypeObject pyrf_pmu_iterator__type = {
+       PyVarObject_HEAD_INIT(NULL, 0)
+       .tp_name = "pmus.iterator",
+       .tp_doc = "Iterator for the pmus string sequence.",
+       .tp_basicsize = sizeof(struct pyrf_pmu_iterator),
+       .tp_itemsize = 0,
+       .tp_flags = Py_TPFLAGS_DEFAULT,
+       .tp_new = pyrf_pmu_iterator__new,
+       .tp_dealloc = (destructor) pyrf_pmu_iterator__dealloc,
+       .tp_iter = pyrf_pmu_iterator__iter,
+       .tp_iternext = pyrf_pmu_iterator__iternext,
+};
+
+static int pyrf_pmu_iterator__setup_types(void)
+{
+       return PyType_Ready(&pyrf_pmu_iterator__type);
+}
+
+static PyObject *pyrf__pmus(PyObject *self, PyObject *args)
+{
+       // Calling the class creates an instance of the iterator.
+       return PyObject_CallObject((PyObject *) &pyrf_pmu_iterator__type, /*args=*/NULL);
+}
+
 struct pyrf_counts_values {
        PyObject_HEAD
 
@@ -1701,6 +1833,12 @@ static PyMethodDef perf__methods[] = {
                .ml_flags = METH_VARARGS,
                .ml_doc   = PyDoc_STR("Parse a string of events and return an evlist.")
        },
+       {
+               .ml_name  = "pmus",
+               .ml_meth  = (PyCFunction) pyrf__pmus,
+               .ml_flags = METH_NOARGS,
+               .ml_doc   = PyDoc_STR("Returns a sequence of pmus.")
+       },
        { .ml_name = NULL, }
 };
 
@@ -1728,6 +1866,8 @@ PyMODINIT_FUNC PyInit_perf(void)
            pyrf_evsel__setup_types() < 0 ||
            pyrf_thread_map__setup_types() < 0 ||
            pyrf_cpu_map__setup_types() < 0 ||
+           pyrf_pmu_iterator__setup_types() < 0 ||
+           pyrf_pmu__setup_types() < 0 ||
            pyrf_counts_values__setup_types() < 0)
                return module;