[PATCH v1 13/15] perf tp_pmu: Add event APIs

From: Ian Rogers
Date: Tue Jun 10 2025 - 23:49:10 EST


Add event APIs for the tracepoint PMU allowing things like perf list
to function using it. For perf list add the tracepoint format in the
long description (shown with -v).

Signed-off-by: Ian Rogers <irogers@xxxxxxxxxx>
---
tools/perf/util/pmu.c | 7 +++
tools/perf/util/tp_pmu.c | 114 +++++++++++++++++++++++++++++++++++++++
tools/perf/util/tp_pmu.h | 7 +++
3 files changed, 128 insertions(+)

diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 4164c2278051..d4be0195ea74 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -23,6 +23,7 @@
#include "hwmon_pmu.h"
#include "pmus.h"
#include "tool_pmu.h"
+#include "tp_pmu.h"
#include <util/pmu-bison.h>
#include <util/pmu-flex.h>
#include "parse-events.h"
@@ -1977,6 +1978,8 @@ bool perf_pmu__have_event(struct perf_pmu *pmu, const char *name)
return false;
if (perf_pmu__is_tool(pmu) && tool_pmu__skip_event(name))
return false;
+ if (perf_pmu__is_tracepoint(pmu))
+ return tp_pmu__have_event(pmu, name);
if (perf_pmu__is_hwmon(pmu))
return hwmon_pmu__have_event(pmu, name);
if (perf_pmu__find_alias(pmu, name, /*load=*/ true) != NULL)
@@ -1990,6 +1993,8 @@ size_t perf_pmu__num_events(struct perf_pmu *pmu)
{
size_t nr;

+ if (perf_pmu__is_tracepoint(pmu))
+ return tp_pmu__num_events(pmu);
if (perf_pmu__is_hwmon(pmu))
return hwmon_pmu__num_events(pmu);

@@ -2058,6 +2063,8 @@ int perf_pmu__for_each_event(struct perf_pmu *pmu, bool skip_duplicate_pmus,
struct hashmap_entry *entry;
size_t bkt;

+ if (perf_pmu__is_tracepoint(pmu))
+ return tp_pmu__for_each_event(pmu, state, cb);
if (perf_pmu__is_hwmon(pmu))
return hwmon_pmu__for_each_event(pmu, state, cb);

diff --git a/tools/perf/util/tp_pmu.c b/tools/perf/util/tp_pmu.c
index fd83164f8763..9d68a1da17f6 100644
--- a/tools/perf/util/tp_pmu.c
+++ b/tools/perf/util/tp_pmu.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
#include "tp_pmu.h"
+#include "pmus.h"
#include <api/fs/fs.h>
#include <api/fs/tracing_path.h>
#include <api/io_dir.h>
@@ -93,3 +94,116 @@ int tp_pmu__for_each_tp_sys(void *state, tp_sys_callback cb)
close(events_dir.dirfd);
return ret;
}
+
+bool perf_pmu__is_tracepoint(const struct perf_pmu *pmu)
+{
+ return pmu->type == PERF_TYPE_TRACEPOINT;
+}
+
+struct for_each_event_args {
+ void *state;
+ pmu_event_callback cb;
+ const struct perf_pmu *pmu;
+};
+
+static int for_each_event_cb(void *state, const char *sys_name, const char *evt_name)
+{
+ struct for_each_event_args *args = state;
+ char name[2 * FILENAME_MAX + 2];
+ /* 16 possible hex digits and 22 other characters and \0. */
+ char encoding[16 + 22];
+ char *format = NULL;
+ size_t format_size;
+ struct pmu_event_info info = {
+ .pmu = args->pmu,
+ .pmu_name = args->pmu->name,
+ .event_type_desc = "Tracepoint event",
+ };
+ char *tp_dir = get_events_file(sys_name);
+ char path[PATH_MAX];
+ int id, err;
+
+ if (!tp_dir)
+ return -1;
+
+ scnprintf(path, sizeof(path), "%s/%s/id", tp_dir, evt_name);
+ err = filename__read_int(path, &id);
+ if (err == 0) {
+ snprintf(encoding, sizeof(encoding), "tracepoint/config=0x%x/", id);
+ info.encoding_desc = encoding;
+ }
+
+ scnprintf(path, sizeof(path), "%s/%s/format", tp_dir, evt_name);
+ put_events_file(tp_dir);
+ err = filename__read_str(path, &format, &format_size);
+ if (err == 0) {
+ info.long_desc = format;
+ for (size_t i = 0 ; i < format_size; i++) {
+ /* Swap tabs to spaces due to some rendering issues. */
+ if (format[i] == '\t')
+ format[i] = ' ';
+ }
+ }
+ snprintf(name, sizeof(name), "%s:%s", sys_name, evt_name);
+ info.name = name;
+ err = args->cb(args->state, &info);
+ free(format);
+ return err;
+}
+
+static int for_each_event_sys_cb(void *state, const char *sys_name)
+{
+ return tp_pmu__for_each_tp_event(sys_name, state, for_each_event_cb);
+}
+
+int tp_pmu__for_each_event(struct perf_pmu *pmu, void *state, pmu_event_callback cb)
+{
+ struct for_each_event_args args = {
+ .state = state,
+ .cb = cb,
+ .pmu = pmu,
+ };
+
+ return tp_pmu__for_each_tp_sys(&args, for_each_event_sys_cb);
+}
+
+static int num_events_cb(void *state, const char *sys_name __maybe_unused,
+ const char *evt_name __maybe_unused)
+{
+ size_t *count = state;
+
+ (*count)++;
+ return 0;
+}
+
+static int num_events_sys_cb(void *state, const char *sys_name)
+{
+ return tp_pmu__for_each_tp_event(sys_name, state, num_events_cb);
+}
+
+size_t tp_pmu__num_events(struct perf_pmu *pmu __maybe_unused)
+{
+ size_t count = 0;
+
+ tp_pmu__for_each_tp_sys(&count, num_events_sys_cb);
+ return count;
+}
+
+bool tp_pmu__have_event(struct perf_pmu *pmu __maybe_unused, const char *name)
+{
+ char *dup_name, *colon;
+ int id;
+
+ if (strchr(name, ':') == NULL)
+ return false;
+
+ dup_name = strdup(name);
+ if (!dup_name)
+ return false;
+
+ colon = strchr(dup_name, ':');
+ *colon = '\0';
+ id = tp_pmu__id(dup_name, colon + 1);
+ free(dup_name);
+ return id >= 0;
+}
diff --git a/tools/perf/util/tp_pmu.h b/tools/perf/util/tp_pmu.h
index 49537303bd73..30456bd6943d 100644
--- a/tools/perf/util/tp_pmu.h
+++ b/tools/perf/util/tp_pmu.h
@@ -2,6 +2,8 @@
#ifndef __TP_PMU_H
#define __TP_PMU_H

+#include "pmu.h"
+
typedef int (*tp_sys_callback)(void *state, const char *sys_name);
typedef int (*tp_event_callback)(void *state, const char *sys_name, const char *evt_name);

@@ -9,4 +11,9 @@ int tp_pmu__id(const char *sys, const char *name);
int tp_pmu__for_each_tp_event(const char *sys, void *state, tp_event_callback cb);
int tp_pmu__for_each_tp_sys(void *state, tp_sys_callback cb);

+bool perf_pmu__is_tracepoint(const struct perf_pmu *pmu);
+int tp_pmu__for_each_event(struct perf_pmu *pmu, void *state, pmu_event_callback cb);
+size_t tp_pmu__num_events(struct perf_pmu *pmu);
+bool tp_pmu__have_event(struct perf_pmu *pmu, const char *name);
+
#endif /* __TP_PMU_H */
--
2.50.0.rc0.642.g800a2b2222-goog