1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/list.h> |
3 | #include <linux/list_sort.h> |
4 | #include <linux/string.h> |
5 | #include <linux/zalloc.h> |
6 | #include <subcmd/pager.h> |
7 | #include <sys/types.h> |
8 | #include <ctype.h> |
9 | #include <dirent.h> |
10 | #include <pthread.h> |
11 | #include <string.h> |
12 | #include <unistd.h> |
13 | #include "cpumap.h" |
14 | #include "debug.h" |
15 | #include "evsel.h" |
16 | #include "pmus.h" |
17 | #include "pmu.h" |
18 | #include "print-events.h" |
19 | |
20 | /* |
21 | * core_pmus: A PMU belongs to core_pmus if it's name is "cpu" or it's sysfs |
22 | * directory contains "cpus" file. All PMUs belonging to core_pmus |
23 | * must have pmu->is_core=1. If there are more than one PMU in |
24 | * this list, perf interprets it as a heterogeneous platform. |
25 | * (FWIW, certain ARM platforms having heterogeneous cores uses |
26 | * homogeneous PMU, and thus they are treated as homogeneous |
27 | * platform by perf because core_pmus will have only one entry) |
28 | * other_pmus: All other PMUs which are not part of core_pmus list. It doesn't |
29 | * matter whether PMU is present per SMT-thread or outside of the |
30 | * core in the hw. For e.g., an instance of AMD ibs_fetch// and |
31 | * ibs_op// PMUs is present in each hw SMT thread, however they |
32 | * are captured under other_pmus. PMUs belonging to other_pmus |
33 | * must have pmu->is_core=0 but pmu->is_uncore could be 0 or 1. |
34 | */ |
35 | static LIST_HEAD(core_pmus); |
36 | static LIST_HEAD(other_pmus); |
37 | static bool read_sysfs_core_pmus; |
38 | static bool read_sysfs_all_pmus; |
39 | |
40 | static void pmu_read_sysfs(bool core_only); |
41 | |
42 | int pmu_name_len_no_suffix(const char *str, unsigned long *num) |
43 | { |
44 | int orig_len, len; |
45 | |
46 | orig_len = len = strlen(str); |
47 | |
48 | /* Non-uncore PMUs have their full length, for example, i915. */ |
49 | if (!strstarts(str, prefix: "uncore_" )) |
50 | return len; |
51 | |
52 | /* |
53 | * Count trailing digits and '_', if '_{num}' suffix isn't present use |
54 | * the full length. |
55 | */ |
56 | while (len > 0 && isdigit(c: str[len - 1])) |
57 | len--; |
58 | |
59 | if (len > 0 && len != orig_len && str[len - 1] == '_') { |
60 | if (num) |
61 | *num = strtoul(&str[len], NULL, 10); |
62 | return len - 1; |
63 | } |
64 | return orig_len; |
65 | } |
66 | |
67 | void perf_pmus__destroy(void) |
68 | { |
69 | struct perf_pmu *pmu, *tmp; |
70 | |
71 | list_for_each_entry_safe(pmu, tmp, &core_pmus, list) { |
72 | list_del(entry: &pmu->list); |
73 | |
74 | perf_pmu__delete(pmu); |
75 | } |
76 | list_for_each_entry_safe(pmu, tmp, &other_pmus, list) { |
77 | list_del(entry: &pmu->list); |
78 | |
79 | perf_pmu__delete(pmu); |
80 | } |
81 | read_sysfs_core_pmus = false; |
82 | read_sysfs_all_pmus = false; |
83 | } |
84 | |
85 | static struct perf_pmu *pmu_find(const char *name) |
86 | { |
87 | struct perf_pmu *pmu; |
88 | |
89 | list_for_each_entry(pmu, &core_pmus, list) { |
90 | if (!strcmp(pmu->name, name) || |
91 | (pmu->alias_name && !strcmp(pmu->alias_name, name))) |
92 | return pmu; |
93 | } |
94 | list_for_each_entry(pmu, &other_pmus, list) { |
95 | if (!strcmp(pmu->name, name) || |
96 | (pmu->alias_name && !strcmp(pmu->alias_name, name))) |
97 | return pmu; |
98 | } |
99 | |
100 | return NULL; |
101 | } |
102 | |
103 | struct perf_pmu *perf_pmus__find(const char *name) |
104 | { |
105 | struct perf_pmu *pmu; |
106 | int dirfd; |
107 | bool core_pmu; |
108 | |
109 | /* |
110 | * Once PMU is loaded it stays in the list, |
111 | * so we keep us from multiple reading/parsing |
112 | * the pmu format definitions. |
113 | */ |
114 | pmu = pmu_find(name); |
115 | if (pmu) |
116 | return pmu; |
117 | |
118 | if (read_sysfs_all_pmus) |
119 | return NULL; |
120 | |
121 | core_pmu = is_pmu_core(name); |
122 | if (core_pmu && read_sysfs_core_pmus) |
123 | return NULL; |
124 | |
125 | dirfd = perf_pmu__event_source_devices_fd(); |
126 | pmu = perf_pmu__lookup(pmus: core_pmu ? &core_pmus : &other_pmus, dirfd, lookup_name: name); |
127 | close(dirfd); |
128 | |
129 | if (!pmu) { |
130 | /* |
131 | * Looking up an inidividual PMU failed. This may mean name is |
132 | * an alias, so read the PMUs from sysfs and try to find again. |
133 | */ |
134 | pmu_read_sysfs(core_only: core_pmu); |
135 | pmu = pmu_find(name); |
136 | } |
137 | return pmu; |
138 | } |
139 | |
140 | static struct perf_pmu *perf_pmu__find2(int dirfd, const char *name) |
141 | { |
142 | struct perf_pmu *pmu; |
143 | bool core_pmu; |
144 | |
145 | /* |
146 | * Once PMU is loaded it stays in the list, |
147 | * so we keep us from multiple reading/parsing |
148 | * the pmu format definitions. |
149 | */ |
150 | pmu = pmu_find(name); |
151 | if (pmu) |
152 | return pmu; |
153 | |
154 | if (read_sysfs_all_pmus) |
155 | return NULL; |
156 | |
157 | core_pmu = is_pmu_core(name); |
158 | if (core_pmu && read_sysfs_core_pmus) |
159 | return NULL; |
160 | |
161 | return perf_pmu__lookup(pmus: core_pmu ? &core_pmus : &other_pmus, dirfd, lookup_name: name); |
162 | } |
163 | |
164 | static int pmus_cmp(void *priv __maybe_unused, |
165 | const struct list_head *lhs, const struct list_head *rhs) |
166 | { |
167 | unsigned long lhs_num = 0, rhs_num = 0; |
168 | struct perf_pmu *lhs_pmu = container_of(lhs, struct perf_pmu, list); |
169 | struct perf_pmu *rhs_pmu = container_of(rhs, struct perf_pmu, list); |
170 | const char *lhs_pmu_name = lhs_pmu->name ?: "" ; |
171 | const char *rhs_pmu_name = rhs_pmu->name ?: "" ; |
172 | int lhs_pmu_name_len = pmu_name_len_no_suffix(str: lhs_pmu_name, num: &lhs_num); |
173 | int rhs_pmu_name_len = pmu_name_len_no_suffix(str: rhs_pmu_name, num: &rhs_num); |
174 | int ret = strncmp(lhs_pmu_name, rhs_pmu_name, |
175 | lhs_pmu_name_len < rhs_pmu_name_len ? lhs_pmu_name_len : rhs_pmu_name_len); |
176 | |
177 | if (lhs_pmu_name_len != rhs_pmu_name_len || ret != 0 || lhs_pmu_name_len == 0) |
178 | return ret; |
179 | |
180 | return lhs_num < rhs_num ? -1 : (lhs_num > rhs_num ? 1 : 0); |
181 | } |
182 | |
183 | /* Add all pmus in sysfs to pmu list: */ |
184 | static void pmu_read_sysfs(bool core_only) |
185 | { |
186 | int fd; |
187 | DIR *dir; |
188 | struct dirent *dent; |
189 | |
190 | if (read_sysfs_all_pmus || (core_only && read_sysfs_core_pmus)) |
191 | return; |
192 | |
193 | fd = perf_pmu__event_source_devices_fd(); |
194 | if (fd < 0) |
195 | return; |
196 | |
197 | dir = fdopendir(fd); |
198 | if (!dir) { |
199 | close(fd); |
200 | return; |
201 | } |
202 | |
203 | while ((dent = readdir(dir))) { |
204 | if (!strcmp(dent->d_name, "." ) || !strcmp(dent->d_name, ".." )) |
205 | continue; |
206 | if (core_only && !is_pmu_core(name: dent->d_name)) |
207 | continue; |
208 | /* add to static LIST_HEAD(core_pmus) or LIST_HEAD(other_pmus): */ |
209 | perf_pmu__find2(dirfd: fd, name: dent->d_name); |
210 | } |
211 | |
212 | closedir(dir); |
213 | if (list_empty(head: &core_pmus)) { |
214 | if (!perf_pmu__create_placeholder_core_pmu(core_pmus: &core_pmus)) |
215 | pr_err("Failure to set up any core PMUs\n" ); |
216 | } |
217 | list_sort(NULL, head: &core_pmus, cmp: pmus_cmp); |
218 | list_sort(NULL, head: &other_pmus, cmp: pmus_cmp); |
219 | if (!list_empty(head: &core_pmus)) { |
220 | read_sysfs_core_pmus = true; |
221 | if (!core_only) |
222 | read_sysfs_all_pmus = true; |
223 | } |
224 | } |
225 | |
226 | static struct perf_pmu *__perf_pmus__find_by_type(unsigned int type) |
227 | { |
228 | struct perf_pmu *pmu; |
229 | |
230 | list_for_each_entry(pmu, &core_pmus, list) { |
231 | if (pmu->type == type) |
232 | return pmu; |
233 | } |
234 | |
235 | list_for_each_entry(pmu, &other_pmus, list) { |
236 | if (pmu->type == type) |
237 | return pmu; |
238 | } |
239 | return NULL; |
240 | } |
241 | |
242 | struct perf_pmu *perf_pmus__find_by_type(unsigned int type) |
243 | { |
244 | struct perf_pmu *pmu = __perf_pmus__find_by_type(type); |
245 | |
246 | if (pmu || read_sysfs_all_pmus) |
247 | return pmu; |
248 | |
249 | pmu_read_sysfs(/*core_only=*/false); |
250 | pmu = __perf_pmus__find_by_type(type); |
251 | return pmu; |
252 | } |
253 | |
254 | /* |
255 | * pmu iterator: If pmu is NULL, we start at the begin, otherwise return the |
256 | * next pmu. Returns NULL on end. |
257 | */ |
258 | struct perf_pmu *perf_pmus__scan(struct perf_pmu *pmu) |
259 | { |
260 | bool use_core_pmus = !pmu || pmu->is_core; |
261 | |
262 | if (!pmu) { |
263 | pmu_read_sysfs(/*core_only=*/false); |
264 | pmu = list_prepare_entry(pmu, &core_pmus, list); |
265 | } |
266 | if (use_core_pmus) { |
267 | list_for_each_entry_continue(pmu, &core_pmus, list) |
268 | return pmu; |
269 | |
270 | pmu = NULL; |
271 | pmu = list_prepare_entry(pmu, &other_pmus, list); |
272 | } |
273 | list_for_each_entry_continue(pmu, &other_pmus, list) |
274 | return pmu; |
275 | return NULL; |
276 | } |
277 | |
278 | struct perf_pmu *perf_pmus__scan_core(struct perf_pmu *pmu) |
279 | { |
280 | if (!pmu) { |
281 | pmu_read_sysfs(/*core_only=*/true); |
282 | return list_first_entry_or_null(&core_pmus, typeof(*pmu), list); |
283 | } |
284 | list_for_each_entry_continue(pmu, &core_pmus, list) |
285 | return pmu; |
286 | |
287 | return NULL; |
288 | } |
289 | |
290 | static struct perf_pmu *perf_pmus__scan_skip_duplicates(struct perf_pmu *pmu) |
291 | { |
292 | bool use_core_pmus = !pmu || pmu->is_core; |
293 | int last_pmu_name_len = 0; |
294 | const char *last_pmu_name = (pmu && pmu->name) ? pmu->name : "" ; |
295 | |
296 | if (!pmu) { |
297 | pmu_read_sysfs(/*core_only=*/false); |
298 | pmu = list_prepare_entry(pmu, &core_pmus, list); |
299 | } else |
300 | last_pmu_name_len = pmu_name_len_no_suffix(str: pmu->name ?: "" , NULL); |
301 | |
302 | if (use_core_pmus) { |
303 | list_for_each_entry_continue(pmu, &core_pmus, list) { |
304 | int pmu_name_len = pmu_name_len_no_suffix(str: pmu->name ?: "" , /*num=*/NULL); |
305 | |
306 | if (last_pmu_name_len == pmu_name_len && |
307 | !strncmp(last_pmu_name, pmu->name ?: "" , pmu_name_len)) |
308 | continue; |
309 | |
310 | return pmu; |
311 | } |
312 | pmu = NULL; |
313 | pmu = list_prepare_entry(pmu, &other_pmus, list); |
314 | } |
315 | list_for_each_entry_continue(pmu, &other_pmus, list) { |
316 | int pmu_name_len = pmu_name_len_no_suffix(str: pmu->name ?: "" , /*num=*/NULL); |
317 | |
318 | if (last_pmu_name_len == pmu_name_len && |
319 | !strncmp(last_pmu_name, pmu->name ?: "" , pmu_name_len)) |
320 | continue; |
321 | |
322 | return pmu; |
323 | } |
324 | return NULL; |
325 | } |
326 | |
327 | const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str) |
328 | { |
329 | struct perf_pmu *pmu = NULL; |
330 | |
331 | while ((pmu = perf_pmus__scan(pmu)) != NULL) { |
332 | if (!strcmp(pmu->name, str)) |
333 | return pmu; |
334 | /* Ignore "uncore_" prefix. */ |
335 | if (!strncmp(pmu->name, "uncore_" , 7)) { |
336 | if (!strcmp(pmu->name + 7, str)) |
337 | return pmu; |
338 | } |
339 | /* Ignore "cpu_" prefix on Intel hybrid PMUs. */ |
340 | if (!strncmp(pmu->name, "cpu_" , 4)) { |
341 | if (!strcmp(pmu->name + 4, str)) |
342 | return pmu; |
343 | } |
344 | } |
345 | return NULL; |
346 | } |
347 | |
348 | /** Struct for ordering events as output in perf list. */ |
349 | struct sevent { |
350 | /** PMU for event. */ |
351 | const struct perf_pmu *pmu; |
352 | const char *name; |
353 | const char* alias; |
354 | const char *scale_unit; |
355 | const char *desc; |
356 | const char *long_desc; |
357 | const char *encoding_desc; |
358 | const char *topic; |
359 | const char *pmu_name; |
360 | bool deprecated; |
361 | }; |
362 | |
363 | static int cmp_sevent(const void *a, const void *b) |
364 | { |
365 | const struct sevent *as = a; |
366 | const struct sevent *bs = b; |
367 | bool a_iscpu, b_iscpu; |
368 | int ret; |
369 | |
370 | /* Put extra events last. */ |
371 | if (!!as->desc != !!bs->desc) |
372 | return !!as->desc - !!bs->desc; |
373 | |
374 | /* Order by topics. */ |
375 | ret = strcmp(as->topic ?: "" , bs->topic ?: "" ); |
376 | if (ret) |
377 | return ret; |
378 | |
379 | /* Order CPU core events to be first */ |
380 | a_iscpu = as->pmu ? as->pmu->is_core : true; |
381 | b_iscpu = bs->pmu ? bs->pmu->is_core : true; |
382 | if (a_iscpu != b_iscpu) |
383 | return a_iscpu ? -1 : 1; |
384 | |
385 | /* Order by PMU name. */ |
386 | if (as->pmu != bs->pmu) { |
387 | ret = strcmp(as->pmu_name ?: "" , bs->pmu_name ?: "" ); |
388 | if (ret) |
389 | return ret; |
390 | } |
391 | |
392 | /* Order by event name. */ |
393 | return strcmp(as->name, bs->name); |
394 | } |
395 | |
396 | static bool pmu_alias_is_duplicate(struct sevent *a, struct sevent *b) |
397 | { |
398 | /* Different names -> never duplicates */ |
399 | if (strcmp(a->name ?: "//" , b->name ?: "//" )) |
400 | return false; |
401 | |
402 | /* Don't remove duplicates for different PMUs */ |
403 | return strcmp(a->pmu_name, b->pmu_name) == 0; |
404 | } |
405 | |
406 | struct events_callback_state { |
407 | struct sevent *aliases; |
408 | size_t aliases_len; |
409 | size_t index; |
410 | }; |
411 | |
412 | static int perf_pmus__print_pmu_events__callback(void *vstate, |
413 | struct pmu_event_info *info) |
414 | { |
415 | struct events_callback_state *state = vstate; |
416 | struct sevent *s; |
417 | |
418 | if (state->index >= state->aliases_len) { |
419 | pr_err("Unexpected event %s/%s/\n" , info->pmu->name, info->name); |
420 | return 1; |
421 | } |
422 | s = &state->aliases[state->index]; |
423 | s->pmu = info->pmu; |
424 | #define COPY_STR(str) s->str = info->str ? strdup(info->str) : NULL |
425 | COPY_STR(name); |
426 | COPY_STR(alias); |
427 | COPY_STR(scale_unit); |
428 | COPY_STR(desc); |
429 | COPY_STR(long_desc); |
430 | COPY_STR(encoding_desc); |
431 | COPY_STR(topic); |
432 | COPY_STR(pmu_name); |
433 | #undef COPY_STR |
434 | s->deprecated = info->deprecated; |
435 | state->index++; |
436 | return 0; |
437 | } |
438 | |
439 | void perf_pmus__print_pmu_events(const struct print_callbacks *print_cb, void *print_state) |
440 | { |
441 | struct perf_pmu *pmu; |
442 | int printed = 0; |
443 | int len; |
444 | struct sevent *aliases; |
445 | struct events_callback_state state; |
446 | bool skip_duplicate_pmus = print_cb->skip_duplicate_pmus(print_state); |
447 | struct perf_pmu *(*scan_fn)(struct perf_pmu *); |
448 | |
449 | if (skip_duplicate_pmus) |
450 | scan_fn = perf_pmus__scan_skip_duplicates; |
451 | else |
452 | scan_fn = perf_pmus__scan; |
453 | |
454 | pmu = NULL; |
455 | len = 0; |
456 | while ((pmu = scan_fn(pmu)) != NULL) |
457 | len += perf_pmu__num_events(pmu); |
458 | |
459 | aliases = zalloc(sizeof(struct sevent) * len); |
460 | if (!aliases) { |
461 | pr_err("FATAL: not enough memory to print PMU events\n" ); |
462 | return; |
463 | } |
464 | pmu = NULL; |
465 | state = (struct events_callback_state) { |
466 | .aliases = aliases, |
467 | .aliases_len = len, |
468 | .index = 0, |
469 | }; |
470 | while ((pmu = scan_fn(pmu)) != NULL) { |
471 | perf_pmu__for_each_event(pmu, skip_duplicate_pmus, state: &state, |
472 | cb: perf_pmus__print_pmu_events__callback); |
473 | } |
474 | qsort(aliases, len, sizeof(struct sevent), cmp_sevent); |
475 | for (int j = 0; j < len; j++) { |
476 | /* Skip duplicates */ |
477 | if (j > 0 && pmu_alias_is_duplicate(a: &aliases[j], b: &aliases[j - 1])) |
478 | continue; |
479 | |
480 | print_cb->print_event(print_state, |
481 | aliases[j].pmu_name, |
482 | aliases[j].topic, |
483 | aliases[j].name, |
484 | aliases[j].alias, |
485 | aliases[j].scale_unit, |
486 | aliases[j].deprecated, |
487 | "Kernel PMU event" , |
488 | aliases[j].desc, |
489 | aliases[j].long_desc, |
490 | aliases[j].encoding_desc); |
491 | zfree(&aliases[j].name); |
492 | zfree(&aliases[j].alias); |
493 | zfree(&aliases[j].scale_unit); |
494 | zfree(&aliases[j].desc); |
495 | zfree(&aliases[j].long_desc); |
496 | zfree(&aliases[j].encoding_desc); |
497 | zfree(&aliases[j].topic); |
498 | zfree(&aliases[j].pmu_name); |
499 | } |
500 | if (printed && pager_in_use()) |
501 | printf("\n" ); |
502 | |
503 | zfree(&aliases); |
504 | } |
505 | |
506 | bool perf_pmus__have_event(const char *pname, const char *name) |
507 | { |
508 | struct perf_pmu *pmu = perf_pmus__find(name: pname); |
509 | |
510 | return pmu && perf_pmu__have_event(pmu, name); |
511 | } |
512 | |
513 | int perf_pmus__num_core_pmus(void) |
514 | { |
515 | static int count; |
516 | |
517 | if (!count) { |
518 | struct perf_pmu *pmu = NULL; |
519 | |
520 | while ((pmu = perf_pmus__scan_core(pmu)) != NULL) |
521 | count++; |
522 | } |
523 | return count; |
524 | } |
525 | |
526 | static bool __perf_pmus__supports_extended_type(void) |
527 | { |
528 | struct perf_pmu *pmu = NULL; |
529 | |
530 | if (perf_pmus__num_core_pmus() <= 1) |
531 | return false; |
532 | |
533 | while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { |
534 | if (!is_event_supported(type: PERF_TYPE_HARDWARE, config: PERF_COUNT_HW_CPU_CYCLES | ((__u64)pmu->type << PERF_PMU_TYPE_SHIFT))) |
535 | return false; |
536 | } |
537 | |
538 | return true; |
539 | } |
540 | |
541 | static bool perf_pmus__do_support_extended_type; |
542 | |
543 | static void perf_pmus__init_supports_extended_type(void) |
544 | { |
545 | perf_pmus__do_support_extended_type = __perf_pmus__supports_extended_type(); |
546 | } |
547 | |
548 | bool perf_pmus__supports_extended_type(void) |
549 | { |
550 | static pthread_once_t extended_type_once = PTHREAD_ONCE_INIT; |
551 | |
552 | pthread_once(&extended_type_once, perf_pmus__init_supports_extended_type); |
553 | |
554 | return perf_pmus__do_support_extended_type; |
555 | } |
556 | |
557 | char *perf_pmus__default_pmu_name(void) |
558 | { |
559 | int fd; |
560 | DIR *dir; |
561 | struct dirent *dent; |
562 | char *result = NULL; |
563 | |
564 | if (!list_empty(head: &core_pmus)) |
565 | return strdup(list_first_entry(&core_pmus, struct perf_pmu, list)->name); |
566 | |
567 | fd = perf_pmu__event_source_devices_fd(); |
568 | if (fd < 0) |
569 | return strdup("cpu" ); |
570 | |
571 | dir = fdopendir(fd); |
572 | if (!dir) { |
573 | close(fd); |
574 | return strdup("cpu" ); |
575 | } |
576 | |
577 | while ((dent = readdir(dir))) { |
578 | if (!strcmp(dent->d_name, "." ) || !strcmp(dent->d_name, ".." )) |
579 | continue; |
580 | if (is_pmu_core(name: dent->d_name)) { |
581 | result = strdup(dent->d_name); |
582 | break; |
583 | } |
584 | } |
585 | |
586 | closedir(dir); |
587 | return result ?: strdup("cpu" ); |
588 | } |
589 | |
590 | struct perf_pmu *evsel__find_pmu(const struct evsel *evsel) |
591 | { |
592 | struct perf_pmu *pmu = evsel->pmu; |
593 | |
594 | if (!pmu) { |
595 | pmu = perf_pmus__find_by_type(type: evsel->core.attr.type); |
596 | ((struct evsel *)evsel)->pmu = pmu; |
597 | } |
598 | return pmu; |
599 | } |
600 | |
601 | struct perf_pmu *perf_pmus__find_core_pmu(void) |
602 | { |
603 | return perf_pmus__scan_core(NULL); |
604 | } |
605 | |