root/tools/perf/util/tp_pmu.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);
        }
        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((char *)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;
}