Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2#include "tp_pmu.h"
3#include "pmus.h"
4#include <api/fs/fs.h>
5#include <api/fs/tracing_path.h>
6#include <api/io_dir.h>
7#include <linux/kernel.h>
8#include <errno.h>
9#include <string.h>
10
11int tp_pmu__id(const char *sys, const char *name)
12{
13 char *tp_dir = get_events_file(sys);
14 char path[PATH_MAX];
15 int id, err;
16
17 if (!tp_dir)
18 return -1;
19
20 scnprintf(path, PATH_MAX, "%s/%s/id", tp_dir, name);
21 put_events_file(tp_dir);
22 err = filename__read_int(path, &id);
23 if (err)
24 return err;
25
26 return id;
27}
28
29
30int tp_pmu__for_each_tp_event(const char *sys, void *state, tp_event_callback cb)
31{
32 char *evt_path;
33 struct io_dirent64 *evt_ent;
34 struct io_dir evt_dir;
35 int ret = 0;
36
37 evt_path = get_events_file(sys);
38 if (!evt_path)
39 return -errno;
40
41 io_dir__init(&evt_dir, open(evt_path, O_CLOEXEC | O_DIRECTORY | O_RDONLY));
42 if (evt_dir.dirfd < 0) {
43 ret = -errno;
44 put_events_file(evt_path);
45 return ret;
46 }
47 put_events_file(evt_path);
48
49 while (!ret && (evt_ent = io_dir__readdir(&evt_dir))) {
50 if (!strcmp(evt_ent->d_name, ".")
51 || !strcmp(evt_ent->d_name, "..")
52 || !strcmp(evt_ent->d_name, "enable")
53 || !strcmp(evt_ent->d_name, "filter"))
54 continue;
55
56 ret = cb(state, sys, evt_ent->d_name);
57 if (ret)
58 break;
59 }
60 close(evt_dir.dirfd);
61 return ret;
62}
63
64int tp_pmu__for_each_tp_sys(void *state, tp_sys_callback cb)
65{
66 struct io_dirent64 *events_ent;
67 struct io_dir events_dir;
68 int ret = 0;
69 char *events_dir_path = get_tracing_file("events");
70
71 if (!events_dir_path)
72 return -errno;
73
74 io_dir__init(&events_dir, open(events_dir_path, O_CLOEXEC | O_DIRECTORY | O_RDONLY));
75 if (events_dir.dirfd < 0) {
76 ret = -errno;
77 put_events_file(events_dir_path);
78 return ret;
79 }
80 put_events_file(events_dir_path);
81
82 while (!ret && (events_ent = io_dir__readdir(&events_dir))) {
83 if (!strcmp(events_ent->d_name, ".") ||
84 !strcmp(events_ent->d_name, "..") ||
85 !strcmp(events_ent->d_name, "enable") ||
86 !strcmp(events_ent->d_name, "header_event") ||
87 !strcmp(events_ent->d_name, "header_page"))
88 continue;
89
90 ret = cb(state, events_ent->d_name);
91 }
92 close(events_dir.dirfd);
93 return ret;
94}
95
96bool perf_pmu__is_tracepoint(const struct perf_pmu *pmu)
97{
98 return pmu->type == PERF_TYPE_TRACEPOINT;
99}
100
101struct for_each_event_args {
102 void *state;
103 pmu_event_callback cb;
104 const struct perf_pmu *pmu;
105};
106
107static int for_each_event_cb(void *state, const char *sys_name, const char *evt_name)
108{
109 struct for_each_event_args *args = state;
110 char name[2 * FILENAME_MAX + 2];
111 /* 16 possible hex digits and 22 other characters and \0. */
112 char encoding[16 + 22];
113 char *format = NULL;
114 size_t format_size;
115 struct pmu_event_info info = {
116 .pmu = args->pmu,
117 .pmu_name = args->pmu->name,
118 .event_type_desc = "Tracepoint event",
119 };
120 char *tp_dir = get_events_file(sys_name);
121 char path[PATH_MAX];
122 int id, err;
123
124 if (!tp_dir)
125 return -1;
126
127 scnprintf(path, sizeof(path), "%s/%s/id", tp_dir, evt_name);
128 err = filename__read_int(path, &id);
129 if (err == 0) {
130 snprintf(encoding, sizeof(encoding), "tracepoint/config=0x%x/", id);
131 info.encoding_desc = encoding;
132 }
133
134 scnprintf(path, sizeof(path), "%s/%s/format", tp_dir, evt_name);
135 put_events_file(tp_dir);
136 err = filename__read_str(path, &format, &format_size);
137 if (err == 0) {
138 info.long_desc = format;
139 for (size_t i = 0 ; i < format_size; i++) {
140 /* Swap tabs to spaces due to some rendering issues. */
141 if (format[i] == '\t')
142 format[i] = ' ';
143 }
144 }
145 snprintf(name, sizeof(name), "%s:%s", sys_name, evt_name);
146 info.name = name;
147 err = args->cb(args->state, &info);
148 free(format);
149 return err;
150}
151
152static int for_each_event_sys_cb(void *state, const char *sys_name)
153{
154 return tp_pmu__for_each_tp_event(sys_name, state, for_each_event_cb);
155}
156
157int tp_pmu__for_each_event(struct perf_pmu *pmu, void *state, pmu_event_callback cb)
158{
159 struct for_each_event_args args = {
160 .state = state,
161 .cb = cb,
162 .pmu = pmu,
163 };
164
165 return tp_pmu__for_each_tp_sys(&args, for_each_event_sys_cb);
166}
167
168static int num_events_cb(void *state, const char *sys_name __maybe_unused,
169 const char *evt_name __maybe_unused)
170{
171 size_t *count = state;
172
173 (*count)++;
174 return 0;
175}
176
177static int num_events_sys_cb(void *state, const char *sys_name)
178{
179 return tp_pmu__for_each_tp_event(sys_name, state, num_events_cb);
180}
181
182size_t tp_pmu__num_events(struct perf_pmu *pmu __maybe_unused)
183{
184 size_t count = 0;
185
186 tp_pmu__for_each_tp_sys(&count, num_events_sys_cb);
187 return count;
188}
189
190bool tp_pmu__have_event(struct perf_pmu *pmu __maybe_unused, const char *name)
191{
192 char *dup_name, *colon;
193 int id;
194
195 colon = strchr(name, ':');
196 if (colon == NULL)
197 return false;
198
199 dup_name = strdup(name);
200 if (!dup_name)
201 return false;
202
203 colon = dup_name + (colon - name);
204 *colon = '\0';
205 id = tp_pmu__id(dup_name, colon + 1);
206 free(dup_name);
207 return id >= 0;
208}