pmus.c (003be8c4f71753092bbb86fa9d7ad26dd9fb98db) pmus.c (1eaf496ed386934f1c2439a120fe84a05194f91a)
1// SPDX-License-Identifier: GPL-2.0
2#include <linux/list.h>
1// SPDX-License-Identifier: GPL-2.0
2#include <linux/list.h>
3#include <linux/zalloc.h>
4#include <subcmd/pager.h>
5#include <sys/types.h>
6#include <dirent.h>
3#include <string.h>
7#include <string.h>
8#include <unistd.h>
9#include "debug.h"
10#include "evsel.h"
4#include "pmus.h"
5#include "pmu.h"
11#include "pmus.h"
12#include "pmu.h"
13#include "print-events.h"
6
14
7LIST_HEAD(pmus);
15static LIST_HEAD(pmus);
8
16
17void perf_pmus__destroy(void)
18{
19 struct perf_pmu *pmu, *tmp;
20
21 list_for_each_entry_safe(pmu, tmp, &pmus, list) {
22 list_del(&pmu->list);
23
24 perf_pmu__delete(pmu);
25 }
26}
27
28static struct perf_pmu *pmu_find(const char *name)
29{
30 struct perf_pmu *pmu;
31
32 list_for_each_entry(pmu, &pmus, list) {
33 if (!strcmp(pmu->name, name) ||
34 (pmu->alias_name && !strcmp(pmu->alias_name, name)))
35 return pmu;
36 }
37
38 return NULL;
39}
40
41struct perf_pmu *perf_pmus__find(const char *name)
42{
43 struct perf_pmu *pmu;
44 int dirfd;
45
46 /*
47 * Once PMU is loaded it stays in the list,
48 * so we keep us from multiple reading/parsing
49 * the pmu format definitions.
50 */
51 pmu = pmu_find(name);
52 if (pmu)
53 return pmu;
54
55 dirfd = perf_pmu__event_source_devices_fd();
56 pmu = perf_pmu__lookup(&pmus, dirfd, name);
57 close(dirfd);
58
59 return pmu;
60}
61
62static struct perf_pmu *perf_pmu__find2(int dirfd, const char *name)
63{
64 struct perf_pmu *pmu;
65
66 /*
67 * Once PMU is loaded it stays in the list,
68 * so we keep us from multiple reading/parsing
69 * the pmu format definitions.
70 */
71 pmu = pmu_find(name);
72 if (pmu)
73 return pmu;
74
75 return perf_pmu__lookup(&pmus, dirfd, name);
76}
77
78/* Add all pmus in sysfs to pmu list: */
79static void pmu_read_sysfs(void)
80{
81 int fd;
82 DIR *dir;
83 struct dirent *dent;
84
85 fd = perf_pmu__event_source_devices_fd();
86 if (fd < 0)
87 return;
88
89 dir = fdopendir(fd);
90 if (!dir)
91 return;
92
93 while ((dent = readdir(dir))) {
94 if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
95 continue;
96 /* add to static LIST_HEAD(pmus): */
97 perf_pmu__find2(fd, dent->d_name);
98 }
99
100 closedir(dir);
101}
102
103struct perf_pmu *perf_pmus__find_by_type(unsigned int type)
104{
105 struct perf_pmu *pmu;
106
107 list_for_each_entry(pmu, &pmus, list)
108 if (pmu->type == type)
109 return pmu;
110
111 return NULL;
112}
113
114struct perf_pmu *perf_pmus__scan(struct perf_pmu *pmu)
115{
116 /*
117 * pmu iterator: If pmu is NULL, we start at the begin,
118 * otherwise return the next pmu. Returns NULL on end.
119 */
120 if (!pmu) {
121 pmu_read_sysfs();
122 pmu = list_prepare_entry(pmu, &pmus, list);
123 }
124 list_for_each_entry_continue(pmu, &pmus, list)
125 return pmu;
126 return NULL;
127}
128
9const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str)
10{
11 struct perf_pmu *pmu = NULL;
12
129const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str)
130{
131 struct perf_pmu *pmu = NULL;
132
13 while ((pmu = perf_pmu__scan(pmu)) != NULL) {
133 while ((pmu = perf_pmus__scan(pmu)) != NULL) {
14 if (!strcmp(pmu->name, str))
15 return pmu;
16 /* Ignore "uncore_" prefix. */
17 if (!strncmp(pmu->name, "uncore_", 7)) {
18 if (!strcmp(pmu->name + 7, str))
19 return pmu;
20 }
21 /* Ignore "cpu_" prefix on Intel hybrid PMUs. */
22 if (!strncmp(pmu->name, "cpu_", 4)) {
23 if (!strcmp(pmu->name + 4, str))
24 return pmu;
25 }
26 }
27 return NULL;
28}
134 if (!strcmp(pmu->name, str))
135 return pmu;
136 /* Ignore "uncore_" prefix. */
137 if (!strncmp(pmu->name, "uncore_", 7)) {
138 if (!strcmp(pmu->name + 7, str))
139 return pmu;
140 }
141 /* Ignore "cpu_" prefix on Intel hybrid PMUs. */
142 if (!strncmp(pmu->name, "cpu_", 4)) {
143 if (!strcmp(pmu->name + 4, str))
144 return pmu;
145 }
146 }
147 return NULL;
148}
149
150int perf_pmus__num_mem_pmus(void)
151{
152 struct perf_pmu *pmu = NULL;
153 int count = 0;
154
155 while ((pmu = perf_pmus__scan(pmu)) != NULL) {
156 if (perf_pmu__is_mem_pmu(pmu))
157 count++;
158 }
159 return count;
160}
161
162/** Struct for ordering events as output in perf list. */
163struct sevent {
164 /** PMU for event. */
165 const struct perf_pmu *pmu;
166 /**
167 * Optional event for name, desc, etc. If not present then this is a
168 * selectable PMU and the event name is shown as "//".
169 */
170 const struct perf_pmu_alias *event;
171 /** Is the PMU for the CPU? */
172 bool is_cpu;
173};
174
175static int cmp_sevent(const void *a, const void *b)
176{
177 const struct sevent *as = a;
178 const struct sevent *bs = b;
179 const char *a_pmu_name = NULL, *b_pmu_name = NULL;
180 const char *a_name = "//", *a_desc = NULL, *a_topic = "";
181 const char *b_name = "//", *b_desc = NULL, *b_topic = "";
182 int ret;
183
184 if (as->event) {
185 a_name = as->event->name;
186 a_desc = as->event->desc;
187 a_topic = as->event->topic ?: "";
188 a_pmu_name = as->event->pmu_name;
189 }
190 if (bs->event) {
191 b_name = bs->event->name;
192 b_desc = bs->event->desc;
193 b_topic = bs->event->topic ?: "";
194 b_pmu_name = bs->event->pmu_name;
195 }
196 /* Put extra events last. */
197 if (!!a_desc != !!b_desc)
198 return !!a_desc - !!b_desc;
199
200 /* Order by topics. */
201 ret = strcmp(a_topic, b_topic);
202 if (ret)
203 return ret;
204
205 /* Order CPU core events to be first */
206 if (as->is_cpu != bs->is_cpu)
207 return as->is_cpu ? -1 : 1;
208
209 /* Order by PMU name. */
210 if (as->pmu != bs->pmu) {
211 a_pmu_name = a_pmu_name ?: (as->pmu->name ?: "");
212 b_pmu_name = b_pmu_name ?: (bs->pmu->name ?: "");
213 ret = strcmp(a_pmu_name, b_pmu_name);
214 if (ret)
215 return ret;
216 }
217
218 /* Order by event name. */
219 return strcmp(a_name, b_name);
220}
221
222static bool pmu_alias_is_duplicate(struct sevent *alias_a,
223 struct sevent *alias_b)
224{
225 const char *a_pmu_name = NULL, *b_pmu_name = NULL;
226 const char *a_name = "//", *b_name = "//";
227
228
229 if (alias_a->event) {
230 a_name = alias_a->event->name;
231 a_pmu_name = alias_a->event->pmu_name;
232 }
233 if (alias_b->event) {
234 b_name = alias_b->event->name;
235 b_pmu_name = alias_b->event->pmu_name;
236 }
237
238 /* Different names -> never duplicates */
239 if (strcmp(a_name, b_name))
240 return false;
241
242 /* Don't remove duplicates for different PMUs */
243 a_pmu_name = a_pmu_name ?: (alias_a->pmu->name ?: "");
244 b_pmu_name = b_pmu_name ?: (alias_b->pmu->name ?: "");
245 return strcmp(a_pmu_name, b_pmu_name) == 0;
246}
247
248static int sub_non_neg(int a, int b)
249{
250 if (b > a)
251 return 0;
252 return a - b;
253}
254
255static char *format_alias(char *buf, int len, const struct perf_pmu *pmu,
256 const struct perf_pmu_alias *alias)
257{
258 struct parse_events_term *term;
259 int used = snprintf(buf, len, "%s/%s", pmu->name, alias->name);
260
261 list_for_each_entry(term, &alias->terms, list) {
262 if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR)
263 used += snprintf(buf + used, sub_non_neg(len, used),
264 ",%s=%s", term->config,
265 term->val.str);
266 }
267
268 if (sub_non_neg(len, used) > 0) {
269 buf[used] = '/';
270 used++;
271 }
272 if (sub_non_neg(len, used) > 0) {
273 buf[used] = '\0';
274 used++;
275 } else
276 buf[len - 1] = '\0';
277
278 return buf;
279}
280
281void perf_pmus__print_pmu_events(const struct print_callbacks *print_cb, void *print_state)
282{
283 struct perf_pmu *pmu;
284 struct perf_pmu_alias *event;
285 char buf[1024];
286 int printed = 0;
287 int len, j;
288 struct sevent *aliases;
289
290 pmu = NULL;
291 len = 0;
292 while ((pmu = perf_pmus__scan(pmu)) != NULL) {
293 list_for_each_entry(event, &pmu->aliases, list)
294 len++;
295 if (pmu->selectable)
296 len++;
297 }
298 aliases = zalloc(sizeof(struct sevent) * len);
299 if (!aliases) {
300 pr_err("FATAL: not enough memory to print PMU events\n");
301 return;
302 }
303 pmu = NULL;
304 j = 0;
305 while ((pmu = perf_pmus__scan(pmu)) != NULL) {
306 bool is_cpu = pmu->is_core;
307
308 list_for_each_entry(event, &pmu->aliases, list) {
309 aliases[j].event = event;
310 aliases[j].pmu = pmu;
311 aliases[j].is_cpu = is_cpu;
312 j++;
313 }
314 if (pmu->selectable) {
315 aliases[j].event = NULL;
316 aliases[j].pmu = pmu;
317 aliases[j].is_cpu = is_cpu;
318 j++;
319 }
320 }
321 len = j;
322 qsort(aliases, len, sizeof(struct sevent), cmp_sevent);
323 for (j = 0; j < len; j++) {
324 const char *name, *alias = NULL, *scale_unit = NULL,
325 *desc = NULL, *long_desc = NULL,
326 *encoding_desc = NULL, *topic = NULL,
327 *pmu_name = NULL;
328 bool deprecated = false;
329 size_t buf_used;
330
331 /* Skip duplicates */
332 if (j > 0 && pmu_alias_is_duplicate(&aliases[j], &aliases[j - 1]))
333 continue;
334
335 if (!aliases[j].event) {
336 /* A selectable event. */
337 pmu_name = aliases[j].pmu->name;
338 buf_used = snprintf(buf, sizeof(buf), "%s//", pmu_name) + 1;
339 name = buf;
340 } else {
341 if (aliases[j].event->desc) {
342 name = aliases[j].event->name;
343 buf_used = 0;
344 } else {
345 name = format_alias(buf, sizeof(buf), aliases[j].pmu,
346 aliases[j].event);
347 if (aliases[j].is_cpu) {
348 alias = name;
349 name = aliases[j].event->name;
350 }
351 buf_used = strlen(buf) + 1;
352 }
353 pmu_name = aliases[j].event->pmu_name ?: (aliases[j].pmu->name ?: "");
354 if (strlen(aliases[j].event->unit) || aliases[j].event->scale != 1.0) {
355 scale_unit = buf + buf_used;
356 buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used,
357 "%G%s", aliases[j].event->scale,
358 aliases[j].event->unit) + 1;
359 }
360 desc = aliases[j].event->desc;
361 long_desc = aliases[j].event->long_desc;
362 topic = aliases[j].event->topic;
363 encoding_desc = buf + buf_used;
364 buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used,
365 "%s/%s/", pmu_name, aliases[j].event->str) + 1;
366 deprecated = aliases[j].event->deprecated;
367 }
368 print_cb->print_event(print_state,
369 pmu_name,
370 topic,
371 name,
372 alias,
373 scale_unit,
374 deprecated,
375 "Kernel PMU event",
376 desc,
377 long_desc,
378 encoding_desc);
379 }
380 if (printed && pager_in_use())
381 printf("\n");
382
383 zfree(&aliases);
384}
385
386bool perf_pmus__have_event(const char *pname, const char *name)
387{
388 struct perf_pmu *pmu = perf_pmus__find(pname);
389
390 return pmu && perf_pmu__have_event(pmu, name);
391}
392
393bool perf_pmus__has_hybrid(void)
394{
395 static bool hybrid_scanned, has_hybrid;
396
397 if (!hybrid_scanned) {
398 struct perf_pmu *pmu = NULL;
399
400 while ((pmu = perf_pmus__scan(pmu)) != NULL) {
401 if (pmu->is_core && is_pmu_hybrid(pmu->name)) {
402 has_hybrid = true;
403 break;
404 }
405 }
406 hybrid_scanned = true;
407 }
408 return has_hybrid;
409}
410
411struct perf_pmu *evsel__find_pmu(const struct evsel *evsel)
412{
413 struct perf_pmu *pmu = evsel->pmu;
414
415 if (!pmu) {
416 pmu = perf_pmus__find_by_type(evsel->core.attr.type);
417 ((struct evsel *)evsel)->pmu = pmu;
418 }
419 return pmu;
420}