mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

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). $ sudo perf list -v tracepoint List of pre-defined events (to be used in -e or -M): alarmtimer:alarmtimer_cancel [Tracepoint event] [name: alarmtimer_cancel ID: 416 format: field:unsigned short common_type; offset:0; size:2; signed:0; field:unsigned char common_flags; offset:2; size:1; signed:0; field:unsigned char common_preempt_count; offset:3; size:1; signed:0; field:int common_pid; offset:4; size:4; signed:1; field:void * alarm; offset:8; size:8; signed:0; field:unsigned char alarm_type; offset:16; size:1; signed:0; field:s64 expires; offset:24; size:8; signed:1; field:s64 now; offset:32; size:8; signed:1; print fmt: "alarmtimer:%p type:%s expires:%llu now:%llu",REC->alarm,__print_flags((1 << REC->alarm_type)," | ",{ 1 << 0, "REALTIME" },{ 1 << 1,"BOOTTIME" },{ 1 << 3,"REALTIME Freezer" },{ 1 << 4,"BOOTTIME Freezer" }),REC->expires,REC->now . Unit: tracepoint] alarmtimer:alarmtimer_fired [Tracepoint event] [name: alarmtimer_fired ID: 418 ... 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-6-irogers@google.com Signed-off-by: Namhyung Kim <namhyung@kernel.org>
210 lines
4.8 KiB
C
210 lines
4.8 KiB
C
// 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>
|
|
#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;
|
|
}
|
|
|
|
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;
|
|
|
|
colon = strchr(name, ':');
|
|
if (colon == NULL)
|
|
return false;
|
|
|
|
dup_name = strdup(name);
|
|
if (!dup_name)
|
|
return false;
|
|
|
|
colon = dup_name + (colon - name);
|
|
*colon = '\0';
|
|
id = tp_pmu__id(dup_name, colon + 1);
|
|
free(dup_name);
|
|
return id >= 0;
|
|
}
|