perf tp_pmu: Factor existing tracepoint logic to new file

Start the creation of a tracepoint PMU abstraction. Tracepoint events
don't follow the regular sysfs perf conventions. Eventually the new
PMU abstraction will bridge the gap so tracepoint events look more
like regular perf ones.

Signed-off-by: Ian Rogers <irogers@google.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Link: https://lore.kernel.org/r/20250725185202.68671-5-irogers@google.com
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
This commit is contained in:
Ian Rogers
2025-07-26 16:31:43 -07:00
committed by Namhyung Kim
parent 6e9fa4131a
commit d002aab87d
5 changed files with 172 additions and 108 deletions
+1
View File
@@ -88,6 +88,7 @@ perf-util-y += pmu-bison.o
perf-util-y += drm_pmu.o
perf-util-y += hwmon_pmu.o
perf-util-y += tool_pmu.o
perf-util-y += tp_pmu.o
perf-util-y += svghelper.o
perf-util-y += trace-event-info.o
perf-util-y += trace-event-scripting.o
+2 -19
View File
@@ -60,6 +60,7 @@
#include "drm_pmu.h"
#include "hwmon_pmu.h"
#include "tool_pmu.h"
#include "tp_pmu.h"
#include "rlimit.h"
#include "../perf-sys.h"
#include "util/parse-branch-options.h"
@@ -572,24 +573,6 @@ out_err:
return NULL;
}
static int trace_event__id(const char *sys, const char *name)
{
char *tp_dir = get_events_file(sys);
char path[PATH_MAX];
int id, err;
if (!tp_dir)
return -1;
scnprintf(path, PATH_MAX, "%s/%s/id", tp_dir, name);
put_events_file(tp_dir);
err = filename__read_int(path, &id);
if (err)
return err;
return id;
}
/*
* Returns pointer with encoded error via <linux/err.h> interface.
*/
@@ -623,7 +606,7 @@ struct evsel *evsel__newtp_idx(const char *sys, const char *name, int idx, bool
event_attr_init(&attr);
if (format) {
id = trace_event__id(sys, name);
id = tp_pmu__id(sys, name);
if (id < 0) {
err = id;
goto out_free;
+62 -89
View File
@@ -17,13 +17,12 @@
#include "string2.h"
#include "strbuf.h"
#include "debug.h"
#include <api/fs/tracing_path.h>
#include <api/io_dir.h>
#include <perf/cpumap.h>
#include <util/parse-events-bison.h>
#include <util/parse-events-flex.h>
#include "pmu.h"
#include "pmus.h"
#include "tp_pmu.h"
#include "asm/bug.h"
#include "ui/ui.h"
#include "util/parse-branch-options.h"
@@ -33,6 +32,7 @@
#include "util/stat.h"
#include "util/util.h"
#include "tracepoint.h"
#include <api/fs/tracing_path.h>
#define MAX_NAME_LEN 100
@@ -599,105 +599,82 @@ static int add_tracepoint(struct parse_events_state *parse_state,
return 0;
}
static int add_tracepoint_multi_event(struct parse_events_state *parse_state,
struct list_head *list,
const char *sys_name, const char *evt_name,
struct parse_events_error *err,
struct parse_events_terms *head_config, YYLTYPE *loc)
struct add_tracepoint_multi_args {
struct parse_events_state *parse_state;
struct list_head *list;
const char *sys_glob;
const char *evt_glob;
struct parse_events_error *err;
struct parse_events_terms *head_config;
YYLTYPE *loc;
int found;
};
static int add_tracepoint_multi_event_cb(void *state, const char *sys_name, const char *evt_name)
{
char *evt_path;
struct io_dirent64 *evt_ent;
struct io_dir evt_dir;
int ret = 0, found = 0;
struct add_tracepoint_multi_args *args = state;
int ret;
evt_path = get_events_file(sys_name);
if (!evt_path) {
tracepoint_error(err, errno, sys_name, evt_name, loc->first_column);
return -1;
}
io_dir__init(&evt_dir, open(evt_path, O_CLOEXEC | O_DIRECTORY | O_RDONLY));
if (evt_dir.dirfd < 0) {
put_events_file(evt_path);
tracepoint_error(err, errno, sys_name, evt_name, loc->first_column);
return -1;
}
if (!strglobmatch(evt_name, args->evt_glob))
return 0;
while (!ret && (evt_ent = io_dir__readdir(&evt_dir))) {
if (!strcmp(evt_ent->d_name, ".")
|| !strcmp(evt_ent->d_name, "..")
|| !strcmp(evt_ent->d_name, "enable")
|| !strcmp(evt_ent->d_name, "filter"))
continue;
args->found++;
ret = add_tracepoint(args->parse_state, args->list, sys_name, evt_name,
args->err, args->head_config, args->loc);
if (!strglobmatch(evt_ent->d_name, evt_name))
continue;
found++;
ret = add_tracepoint(parse_state, list, sys_name, evt_ent->d_name,
err, head_config, loc);
}
if (!found) {
tracepoint_error(err, ENOENT, sys_name, evt_name, loc->first_column);
ret = -1;
}
put_events_file(evt_path);
close(evt_dir.dirfd);
return ret;
}
static int add_tracepoint_event(struct parse_events_state *parse_state,
struct list_head *list,
const char *sys_name, const char *evt_name,
struct parse_events_error *err,
struct parse_events_terms *head_config, YYLTYPE *loc)
static int add_tracepoint_multi_event(struct add_tracepoint_multi_args *args, const char *sys_name)
{
return strpbrk(evt_name, "*?") ?
add_tracepoint_multi_event(parse_state, list, sys_name, evt_name,
err, head_config, loc) :
add_tracepoint(parse_state, list, sys_name, evt_name,
err, head_config, loc);
if (strpbrk(args->evt_glob, "*?") == NULL) {
/* Not a glob. */
args->found++;
return add_tracepoint(args->parse_state, args->list, sys_name, args->evt_glob,
args->err, args->head_config, args->loc);
}
return tp_pmu__for_each_tp_event(sys_name, args, add_tracepoint_multi_event_cb);
}
static int add_tracepoint_multi_sys_cb(void *state, const char *sys_name)
{
struct add_tracepoint_multi_args *args = state;
if (!strglobmatch(sys_name, args->sys_glob))
return 0;
return add_tracepoint_multi_event(args, sys_name);
}
static int add_tracepoint_multi_sys(struct parse_events_state *parse_state,
struct list_head *list,
const char *sys_name, const char *evt_name,
const char *sys_glob, const char *evt_glob,
struct parse_events_error *err,
struct parse_events_terms *head_config, YYLTYPE *loc)
{
struct io_dirent64 *events_ent;
struct io_dir events_dir;
int ret = 0;
char *events_dir_path = get_tracing_file("events");
struct add_tracepoint_multi_args args = {
.parse_state = parse_state,
.list = list,
.sys_glob = sys_glob,
.evt_glob = evt_glob,
.err = err,
.head_config = head_config,
.loc = loc,
.found = 0,
};
int ret;
if (!events_dir_path) {
tracepoint_error(err, errno, sys_name, evt_name, loc->first_column);
return -1;
if (strpbrk(sys_glob, "*?") == NULL) {
/* Not a glob. */
ret = add_tracepoint_multi_event(&args, sys_glob);
} else {
ret = tp_pmu__for_each_tp_sys(&args, add_tracepoint_multi_sys_cb);
}
io_dir__init(&events_dir, open(events_dir_path, O_CLOEXEC | O_DIRECTORY | O_RDONLY));
put_events_file(events_dir_path);
if (events_dir.dirfd < 0) {
tracepoint_error(err, errno, sys_name, evt_name, loc->first_column);
return -1;
if (args.found == 0) {
tracepoint_error(err, ENOENT, sys_glob, evt_glob, loc->first_column);
return -ENOENT;
}
while (!ret && (events_ent = io_dir__readdir(&events_dir))) {
if (!strcmp(events_ent->d_name, ".")
|| !strcmp(events_ent->d_name, "..")
|| !strcmp(events_ent->d_name, "enable")
|| !strcmp(events_ent->d_name, "header_event")
|| !strcmp(events_ent->d_name, "header_page"))
continue;
if (!strglobmatch(events_ent->d_name, sys_name))
continue;
ret = add_tracepoint_event(parse_state, list, events_ent->d_name,
evt_name, err, head_config, loc);
}
close(events_dir.dirfd);
return ret;
}
@@ -1406,12 +1383,8 @@ int parse_events_add_tracepoint(struct parse_events_state *parse_state,
return -EINVAL;
}
if (strpbrk(sys, "*?"))
return add_tracepoint_multi_sys(parse_state, list, sys, event,
err, head_config, loc);
else
return add_tracepoint_event(parse_state, list, sys, event,
err, head_config, loc);
return add_tracepoint_multi_sys(parse_state, list, sys, event,
err, head_config, loc);
}
static int __parse_events_add_numeric(struct parse_events_state *parse_state,
+95
View File
@@ -0,0 +1,95 @@
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
#include "tp_pmu.h"
#include <api/fs/fs.h>
#include <api/fs/tracing_path.h>
#include <api/io_dir.h>
#include <linux/kernel.h>
#include <errno.h>
#include <string.h>
int tp_pmu__id(const char *sys, const char *name)
{
char *tp_dir = get_events_file(sys);
char path[PATH_MAX];
int id, err;
if (!tp_dir)
return -1;
scnprintf(path, PATH_MAX, "%s/%s/id", tp_dir, name);
put_events_file(tp_dir);
err = filename__read_int(path, &id);
if (err)
return err;
return id;
}
int tp_pmu__for_each_tp_event(const char *sys, void *state, tp_event_callback cb)
{
char *evt_path;
struct io_dirent64 *evt_ent;
struct io_dir evt_dir;
int ret = 0;
evt_path = get_events_file(sys);
if (!evt_path)
return -errno;
io_dir__init(&evt_dir, open(evt_path, O_CLOEXEC | O_DIRECTORY | O_RDONLY));
if (evt_dir.dirfd < 0) {
ret = -errno;
put_events_file(evt_path);
return ret;
}
put_events_file(evt_path);
while (!ret && (evt_ent = io_dir__readdir(&evt_dir))) {
if (!strcmp(evt_ent->d_name, ".")
|| !strcmp(evt_ent->d_name, "..")
|| !strcmp(evt_ent->d_name, "enable")
|| !strcmp(evt_ent->d_name, "filter"))
continue;
ret = cb(state, sys, evt_ent->d_name);
if (ret)
break;
}
close(evt_dir.dirfd);
return ret;
}
int tp_pmu__for_each_tp_sys(void *state, tp_sys_callback cb)
{
struct io_dirent64 *events_ent;
struct io_dir events_dir;
int ret = 0;
char *events_dir_path = get_tracing_file("events");
if (!events_dir_path)
return -errno;
io_dir__init(&events_dir, open(events_dir_path, O_CLOEXEC | O_DIRECTORY | O_RDONLY));
if (events_dir.dirfd < 0) {
ret = -errno;
put_events_file(events_dir_path);
return ret;
}
put_events_file(events_dir_path);
while (!ret && (events_ent = io_dir__readdir(&events_dir))) {
if (!strcmp(events_ent->d_name, ".") ||
!strcmp(events_ent->d_name, "..") ||
!strcmp(events_ent->d_name, "enable") ||
!strcmp(events_ent->d_name, "header_event") ||
!strcmp(events_ent->d_name, "header_page"))
continue;
ret = cb(state, events_ent->d_name);
if (ret)
break;
}
close(events_dir.dirfd);
return ret;
}
+12
View File
@@ -0,0 +1,12 @@
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
#ifndef __TP_PMU_H
#define __TP_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);
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);
#endif /* __TP_PMU_H */