Commit | Line | Data |
---|---|---|
9b7c7728 IR |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <dirent.h> | |
3 | #include <errno.h> | |
4 | #include <stdio.h> | |
5 | #include <stdlib.h> | |
6 | #include <string.h> | |
00462d8e | 7 | #include <fcntl.h> |
9b7c7728 | 8 | #include <sys/param.h> |
00462d8e | 9 | #include <unistd.h> |
9b7c7728 IR |
10 | |
11 | #include <api/fs/tracing_path.h> | |
12 | #include <linux/stddef.h> | |
13 | #include <linux/perf_event.h> | |
14 | #include <linux/zalloc.h> | |
15 | #include <subcmd/pager.h> | |
16 | ||
17 | #include "build-id.h" | |
18 | #include "debug.h" | |
19 | #include "evsel.h" | |
20 | #include "metricgroup.h" | |
21 | #include "parse-events.h" | |
22 | #include "pmu.h" | |
1eaf496e | 23 | #include "pmus.h" |
9b7c7728 IR |
24 | #include "print-events.h" |
25 | #include "probe-file.h" | |
26 | #include "string2.h" | |
27 | #include "strlist.h" | |
9b7c7728 IR |
28 | #include "tracepoint.h" |
29 | #include "pfm.h" | |
9a1bc9ea | 30 | #include "thread_map.h" |
8ece26ad | 31 | #include "util.h" |
9b7c7728 IR |
32 | |
33 | #define MAX_NAME_LEN 100 | |
34 | ||
e5c6109f | 35 | /** Strings corresponding to enum perf_type_id. */ |
9b7c7728 IR |
36 | static const char * const event_type_descriptors[] = { |
37 | "Hardware event", | |
38 | "Software event", | |
39 | "Tracepoint event", | |
40 | "Hardware cache event", | |
41 | "Raw hardware event descriptor", | |
42 | "Hardware breakpoint", | |
43 | }; | |
44 | ||
45 | static const struct event_symbol event_symbols_tool[PERF_TOOL_MAX] = { | |
46 | [PERF_TOOL_DURATION_TIME] = { | |
47 | .symbol = "duration_time", | |
48 | .alias = "", | |
49 | }, | |
50 | [PERF_TOOL_USER_TIME] = { | |
51 | .symbol = "user_time", | |
52 | .alias = "", | |
53 | }, | |
54 | [PERF_TOOL_SYSTEM_TIME] = { | |
55 | .symbol = "system_time", | |
56 | .alias = "", | |
57 | }, | |
58 | }; | |
59 | ||
9b7c7728 IR |
60 | /* |
61 | * Print the events from <debugfs_mount_point>/tracing/events | |
62 | */ | |
00462d8e NK |
63 | void print_tracepoint_events(const struct print_callbacks *print_cb __maybe_unused, void *print_state __maybe_unused) |
64 | { | |
65 | char *events_path = get_tracing_file("events"); | |
66 | int events_fd = open(events_path, O_PATH); | |
8ece26ad IR |
67 | struct dirent **sys_namelist = NULL; |
68 | int sys_items; | |
00462d8e NK |
69 | |
70 | put_tracing_file(events_path); | |
71 | if (events_fd < 0) { | |
9d95c6be | 72 | pr_err("Error: failed to open tracing events directory\n"); |
00462d8e NK |
73 | return; |
74 | } | |
75 | ||
8ece26ad | 76 | sys_items = tracing_events__scandir_alphasort(&sys_namelist); |
d74060c0 IR |
77 | |
78 | for (int i = 0; i < sys_items; i++) { | |
79 | struct dirent *sys_dirent = sys_namelist[i]; | |
80 | struct dirent **evt_namelist = NULL; | |
00462d8e | 81 | int dir_fd; |
d74060c0 IR |
82 | int evt_items; |
83 | ||
84 | if (sys_dirent->d_type != DT_DIR || | |
85 | !strcmp(sys_dirent->d_name, ".") || | |
86 | !strcmp(sys_dirent->d_name, "..")) | |
7586d11d | 87 | goto next_sys; |
9b7c7728 | 88 | |
00462d8e NK |
89 | dir_fd = openat(events_fd, sys_dirent->d_name, O_PATH); |
90 | if (dir_fd < 0) | |
7586d11d | 91 | goto next_sys; |
9b7c7728 | 92 | |
00462d8e | 93 | evt_items = scandirat(events_fd, sys_dirent->d_name, &evt_namelist, NULL, alphasort); |
d74060c0 IR |
94 | for (int j = 0; j < evt_items; j++) { |
95 | struct dirent *evt_dirent = evt_namelist[j]; | |
96 | char evt_path[MAXPATHLEN]; | |
00462d8e | 97 | int evt_fd; |
d74060c0 IR |
98 | |
99 | if (evt_dirent->d_type != DT_DIR || | |
100 | !strcmp(evt_dirent->d_name, ".") || | |
101 | !strcmp(evt_dirent->d_name, "..")) | |
7586d11d | 102 | goto next_evt; |
9b7c7728 | 103 | |
00462d8e NK |
104 | snprintf(evt_path, sizeof(evt_path), "%s/id", evt_dirent->d_name); |
105 | evt_fd = openat(dir_fd, evt_path, O_RDONLY); | |
106 | if (evt_fd < 0) | |
7586d11d | 107 | goto next_evt; |
00462d8e | 108 | close(evt_fd); |
d74060c0 | 109 | |
9b7c7728 IR |
110 | snprintf(evt_path, MAXPATHLEN, "%s:%s", |
111 | sys_dirent->d_name, evt_dirent->d_name); | |
e5c6109f IR |
112 | print_cb->print_event(print_state, |
113 | /*topic=*/NULL, | |
114 | /*pmu_name=*/NULL, | |
115 | evt_path, | |
116 | /*event_alias=*/NULL, | |
117 | /*scale_unit=*/NULL, | |
118 | /*deprecated=*/false, | |
119 | "Tracepoint event", | |
120 | /*desc=*/NULL, | |
121 | /*long_desc=*/NULL, | |
d9dc8874 | 122 | /*encoding_desc=*/NULL); |
7586d11d NK |
123 | next_evt: |
124 | free(evt_namelist[j]); | |
9b7c7728 | 125 | } |
00462d8e | 126 | close(dir_fd); |
d74060c0 | 127 | free(evt_namelist); |
7586d11d NK |
128 | next_sys: |
129 | free(sys_namelist[i]); | |
9b7c7728 | 130 | } |
00462d8e | 131 | |
d74060c0 | 132 | free(sys_namelist); |
00462d8e NK |
133 | close(events_fd); |
134 | } | |
9b7c7728 | 135 | |
e5c6109f | 136 | void print_sdt_events(const struct print_callbacks *print_cb, void *print_state) |
9b7c7728 | 137 | { |
9b7c7728 | 138 | struct strlist *bidlist, *sdtlist; |
e5c6109f IR |
139 | struct str_node *bid_nd, *sdt_name, *next_sdt_name; |
140 | const char *last_sdt_name = NULL; | |
141 | ||
142 | /* | |
143 | * The implicitly sorted sdtlist will hold the tracepoint name followed | |
144 | * by @<buildid>. If the tracepoint name is unique (determined by | |
145 | * looking at the adjacent nodes) the @<buildid> is dropped otherwise | |
146 | * the executable path and buildid are added to the name. | |
147 | */ | |
148 | sdtlist = strlist__new(NULL, NULL); | |
9b7c7728 IR |
149 | if (!sdtlist) { |
150 | pr_debug("Failed to allocate new strlist for SDT\n"); | |
151 | return; | |
152 | } | |
153 | bidlist = build_id_cache__list_all(true); | |
154 | if (!bidlist) { | |
155 | pr_debug("Failed to get buildids: %d\n", errno); | |
156 | return; | |
157 | } | |
e5c6109f IR |
158 | strlist__for_each_entry(bid_nd, bidlist) { |
159 | struct probe_cache *pcache; | |
160 | struct probe_cache_entry *ent; | |
161 | ||
162 | pcache = probe_cache__new(bid_nd->s, NULL); | |
9b7c7728 IR |
163 | if (!pcache) |
164 | continue; | |
165 | list_for_each_entry(ent, &pcache->entries, node) { | |
e5c6109f IR |
166 | char buf[1024]; |
167 | ||
168 | snprintf(buf, sizeof(buf), "%s:%s@%s", | |
169 | ent->pev.group, ent->pev.event, bid_nd->s); | |
170 | strlist__add(sdtlist, buf); | |
9b7c7728 IR |
171 | } |
172 | probe_cache__delete(pcache); | |
173 | } | |
174 | strlist__delete(bidlist); | |
175 | ||
e5c6109f IR |
176 | strlist__for_each_entry(sdt_name, sdtlist) { |
177 | bool show_detail = false; | |
178 | char *bid = strchr(sdt_name->s, '@'); | |
179 | char *evt_name = NULL; | |
180 | ||
181 | if (bid) | |
182 | *(bid++) = '\0'; | |
183 | ||
184 | if (last_sdt_name && !strcmp(last_sdt_name, sdt_name->s)) { | |
185 | show_detail = true; | |
186 | } else { | |
187 | next_sdt_name = strlist__next(sdt_name); | |
188 | if (next_sdt_name) { | |
189 | char *bid2 = strchr(next_sdt_name->s, '@'); | |
190 | ||
191 | if (bid2) | |
192 | *bid2 = '\0'; | |
193 | if (strcmp(sdt_name->s, next_sdt_name->s) == 0) | |
194 | show_detail = true; | |
195 | if (bid2) | |
196 | *bid2 = '@'; | |
197 | } | |
9b7c7728 | 198 | } |
e5c6109f IR |
199 | last_sdt_name = sdt_name->s; |
200 | ||
9b7c7728 | 201 | if (show_detail) { |
e5c6109f IR |
202 | char *path = build_id_cache__origname(bid); |
203 | ||
204 | if (path) { | |
205 | if (asprintf(&evt_name, "%s@%s(%.12s)", sdt_name->s, path, bid) < 0) | |
206 | evt_name = NULL; | |
207 | free(path); | |
9b7c7728 | 208 | } |
9b7c7728 | 209 | } |
e5c6109f IR |
210 | print_cb->print_event(print_state, |
211 | /*topic=*/NULL, | |
212 | /*pmu_name=*/NULL, | |
213 | evt_name ?: sdt_name->s, | |
214 | /*event_alias=*/NULL, | |
215 | /*deprecated=*/false, | |
216 | /*scale_unit=*/NULL, | |
217 | "SDT event", | |
218 | /*desc=*/NULL, | |
219 | /*long_desc=*/NULL, | |
d9dc8874 | 220 | /*encoding_desc=*/NULL); |
e5c6109f IR |
221 | |
222 | free(evt_name); | |
9b7c7728 IR |
223 | } |
224 | strlist__delete(sdtlist); | |
225 | } | |
226 | ||
e2be0666 | 227 | bool is_event_supported(u8 type, u64 config) |
9a1bc9ea IR |
228 | { |
229 | bool ret = true; | |
9a1bc9ea IR |
230 | struct evsel *evsel; |
231 | struct perf_event_attr attr = { | |
232 | .type = type, | |
233 | .config = config, | |
234 | .disabled = 1, | |
235 | }; | |
236 | struct perf_thread_map *tmap = thread_map__new_by_tid(0); | |
237 | ||
238 | if (tmap == NULL) | |
239 | return false; | |
240 | ||
241 | evsel = evsel__new(&attr); | |
242 | if (evsel) { | |
25412c03 | 243 | ret = evsel__open(evsel, NULL, tmap) >= 0; |
9a1bc9ea | 244 | |
25412c03 | 245 | if (!ret) { |
9a1bc9ea | 246 | /* |
25412c03 | 247 | * The event may fail to open if the paranoid value |
9a1bc9ea | 248 | * /proc/sys/kernel/perf_event_paranoid is set to 2 |
25412c03 MR |
249 | * Re-run with exclude_kernel set; we don't do that by |
250 | * default as some ARM machines do not support it. | |
9a1bc9ea IR |
251 | */ |
252 | evsel->core.attr.exclude_kernel = 1; | |
253 | ret = evsel__open(evsel, NULL, tmap) >= 0; | |
254 | } | |
25412c03 MR |
255 | |
256 | if (!ret) { | |
257 | /* | |
258 | * The event may fail to open if the PMU requires | |
259 | * exclude_guest to be set (e.g. as the Apple M1 PMU | |
260 | * requires). | |
261 | * Re-run with exclude_guest set; we don't do that by | |
262 | * default as it's equally legitimate for another PMU | |
263 | * driver to require that exclude_guest is clear. | |
264 | */ | |
265 | evsel->core.attr.exclude_guest = 1; | |
266 | ret = evsel__open(evsel, NULL, tmap) >= 0; | |
267 | } | |
268 | ||
9a1bc9ea IR |
269 | evsel__delete(evsel); |
270 | } | |
271 | ||
272 | perf_thread_map__put(tmap); | |
273 | return ret; | |
274 | } | |
275 | ||
e5c6109f | 276 | int print_hwcache_events(const struct print_callbacks *print_cb, void *print_state) |
9b7c7728 | 277 | { |
d7f21df0 | 278 | struct perf_pmu *pmu = NULL; |
442eeb77 | 279 | const char *event_type_descriptor = event_type_descriptors[PERF_TYPE_HW_CACHE]; |
9b7c7728 | 280 | |
9d6a1df9 IR |
281 | /* |
282 | * Only print core PMUs, skipping uncore for performance and | |
283 | * PERF_TYPE_SOFTWARE that can succeed in opening legacy cache evenst. | |
284 | */ | |
285 | while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { | |
d7f21df0 IR |
286 | if (pmu->is_uncore || pmu->type == PERF_TYPE_SOFTWARE) |
287 | continue; | |
288 | ||
289 | for (int type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { | |
290 | for (int op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { | |
291 | /* skip invalid cache type */ | |
292 | if (!evsel__is_cache_op_valid(type, op)) | |
9b7c7728 | 293 | continue; |
d7f21df0 IR |
294 | |
295 | for (int res = 0; res < PERF_COUNT_HW_CACHE_RESULT_MAX; res++) { | |
296 | char name[64]; | |
297 | char alias_name[128]; | |
298 | __u64 config; | |
299 | int ret; | |
300 | ||
301 | __evsel__hw_cache_type_op_res_name(type, op, res, | |
302 | name, sizeof(name)); | |
303 | ||
304 | ret = parse_events__decode_legacy_cache(name, pmu->type, | |
305 | &config); | |
306 | if (ret || !is_event_supported(PERF_TYPE_HW_CACHE, config)) | |
307 | continue; | |
308 | snprintf(alias_name, sizeof(alias_name), "%s/%s/", | |
309 | pmu->name, name); | |
310 | print_cb->print_event(print_state, | |
311 | "cache", | |
312 | pmu->name, | |
313 | name, | |
314 | alias_name, | |
315 | /*scale_unit=*/NULL, | |
316 | /*deprecated=*/false, | |
317 | event_type_descriptor, | |
318 | /*desc=*/NULL, | |
319 | /*long_desc=*/NULL, | |
320 | /*encoding_desc=*/NULL); | |
9b7c7728 | 321 | } |
9b7c7728 IR |
322 | } |
323 | } | |
324 | } | |
3301b3fe | 325 | return 0; |
9b7c7728 IR |
326 | } |
327 | ||
e5c6109f | 328 | void print_tool_events(const struct print_callbacks *print_cb, void *print_state) |
9b7c7728 IR |
329 | { |
330 | // Start at 1 because the first enum entry means no tool event. | |
e5c6109f IR |
331 | for (int i = 1; i < PERF_TOOL_MAX; ++i) { |
332 | print_cb->print_event(print_state, | |
333 | "tool", | |
334 | /*pmu_name=*/NULL, | |
335 | event_symbols_tool[i].symbol, | |
336 | event_symbols_tool[i].alias, | |
337 | /*scale_unit=*/NULL, | |
338 | /*deprecated=*/false, | |
339 | "Tool event", | |
340 | /*desc=*/NULL, | |
341 | /*long_desc=*/NULL, | |
d9dc8874 | 342 | /*encoding_desc=*/NULL); |
e5c6109f | 343 | } |
9b7c7728 IR |
344 | } |
345 | ||
e5c6109f IR |
346 | void print_symbol_events(const struct print_callbacks *print_cb, void *print_state, |
347 | unsigned int type, const struct event_symbol *syms, | |
348 | unsigned int max) | |
9b7c7728 | 349 | { |
de3752a7 IR |
350 | struct strlist *evt_name_list = strlist__new(NULL, NULL); |
351 | struct str_node *nd; | |
9b7c7728 | 352 | |
de3752a7 IR |
353 | if (!evt_name_list) { |
354 | pr_debug("Failed to allocate new strlist for symbol events\n"); | |
355 | return; | |
356 | } | |
357 | for (unsigned int i = 0; i < max; i++) { | |
9b7c7728 IR |
358 | /* |
359 | * New attr.config still not supported here, the latest | |
360 | * example was PERF_COUNT_SW_CGROUP_SWITCHES | |
361 | */ | |
de3752a7 | 362 | if (syms[i].symbol == NULL) |
9b7c7728 IR |
363 | continue; |
364 | ||
9b7c7728 IR |
365 | if (!is_event_supported(type, i)) |
366 | continue; | |
367 | ||
de3752a7 IR |
368 | if (strlen(syms[i].alias)) { |
369 | char name[MAX_NAME_LEN]; | |
9b7c7728 | 370 | |
de3752a7 IR |
371 | snprintf(name, MAX_NAME_LEN, "%s OR %s", syms[i].symbol, syms[i].alias); |
372 | strlist__add(evt_name_list, name); | |
373 | } else | |
374 | strlist__add(evt_name_list, syms[i].symbol); | |
9b7c7728 IR |
375 | } |
376 | ||
de3752a7 | 377 | strlist__for_each_entry(nd, evt_name_list) { |
e5c6109f IR |
378 | char *alias = strstr(nd->s, " OR "); |
379 | ||
380 | if (alias) { | |
381 | *alias = '\0'; | |
382 | alias += 4; | |
9b7c7728 | 383 | } |
e5c6109f IR |
384 | print_cb->print_event(print_state, |
385 | /*topic=*/NULL, | |
386 | /*pmu_name=*/NULL, | |
387 | nd->s, | |
388 | alias, | |
389 | /*scale_unit=*/NULL, | |
390 | /*deprecated=*/false, | |
391 | event_type_descriptors[type], | |
392 | /*desc=*/NULL, | |
393 | /*long_desc=*/NULL, | |
d9dc8874 | 394 | /*encoding_desc=*/NULL); |
9b7c7728 | 395 | } |
de3752a7 | 396 | strlist__delete(evt_name_list); |
9b7c7728 IR |
397 | } |
398 | ||
399 | /* | |
400 | * Print the help text for the event symbols: | |
401 | */ | |
e5c6109f | 402 | void print_events(const struct print_callbacks *print_cb, void *print_state) |
9b7c7728 | 403 | { |
4f19fc18 IR |
404 | char *tmp; |
405 | ||
e5c6109f IR |
406 | print_symbol_events(print_cb, print_state, PERF_TYPE_HARDWARE, |
407 | event_symbols_hw, PERF_COUNT_HW_MAX); | |
408 | print_symbol_events(print_cb, print_state, PERF_TYPE_SOFTWARE, | |
409 | event_symbols_sw, PERF_COUNT_SW_MAX); | |
410 | ||
411 | print_tool_events(print_cb, print_state); | |
412 | ||
413 | print_hwcache_events(print_cb, print_state); | |
414 | ||
1eaf496e | 415 | perf_pmus__print_pmu_events(print_cb, print_state); |
e5c6109f IR |
416 | |
417 | print_cb->print_event(print_state, | |
418 | /*topic=*/NULL, | |
419 | /*pmu_name=*/NULL, | |
420 | "rNNN", | |
421 | /*event_alias=*/NULL, | |
422 | /*scale_unit=*/NULL, | |
423 | /*deprecated=*/false, | |
424 | event_type_descriptors[PERF_TYPE_RAW], | |
425 | /*desc=*/NULL, | |
426 | /*long_desc=*/NULL, | |
d9dc8874 | 427 | /*encoding_desc=*/NULL); |
e5c6109f | 428 | |
4f19fc18 IR |
429 | if (asprintf(&tmp, "%s/t1=v1[,t2=v2,t3 ...]/modifier", |
430 | perf_pmus__scan_core(/*pmu=*/NULL)->name) > 0) { | |
431 | print_cb->print_event(print_state, | |
432 | /*topic=*/NULL, | |
433 | /*pmu_name=*/NULL, | |
434 | tmp, | |
435 | /*event_alias=*/NULL, | |
436 | /*scale_unit=*/NULL, | |
437 | /*deprecated=*/false, | |
438 | event_type_descriptors[PERF_TYPE_RAW], | |
439 | "(see 'man perf-list' on how to encode it)", | |
440 | /*long_desc=*/NULL, | |
441 | /*encoding_desc=*/NULL); | |
442 | free(tmp); | |
443 | } | |
e5c6109f IR |
444 | |
445 | print_cb->print_event(print_state, | |
446 | /*topic=*/NULL, | |
447 | /*pmu_name=*/NULL, | |
448 | "mem:<addr>[/len][:access]", | |
449 | /*scale_unit=*/NULL, | |
450 | /*event_alias=*/NULL, | |
451 | /*deprecated=*/false, | |
452 | event_type_descriptors[PERF_TYPE_BREAKPOINT], | |
453 | /*desc=*/NULL, | |
454 | /*long_desc=*/NULL, | |
d9dc8874 | 455 | /*encoding_desc=*/NULL); |
e5c6109f IR |
456 | |
457 | print_tracepoint_events(print_cb, print_state); | |
458 | ||
459 | print_sdt_events(print_cb, print_state); | |
460 | ||
461 | metricgroup__print(print_cb, print_state); | |
462 | ||
463 | print_libpfm_events(print_cb, print_state); | |
9b7c7728 | 464 | } |