]>
Commit | Line | Data |
---|---|---|
cd82a32e JO |
1 | #include <linux/list.h> |
2 | #include <sys/types.h> | |
cd82a32e JO |
3 | #include <unistd.h> |
4 | #include <stdio.h> | |
5 | #include <dirent.h> | |
4299a549 | 6 | #include "fs.h" |
410136f5 | 7 | #include <locale.h> |
cd82a32e JO |
8 | #include "util.h" |
9 | #include "pmu.h" | |
10 | #include "parse-events.h" | |
7ae92e74 | 11 | #include "cpumap.h" |
cd82a32e | 12 | |
410136f5 SE |
13 | #define UNIT_MAX_LEN 31 /* max length for event unit name */ |
14 | ||
ab1bf653 ACM |
15 | struct perf_pmu_alias { |
16 | char *name; | |
17 | struct list_head terms; | |
18 | struct list_head list; | |
410136f5 SE |
19 | char unit[UNIT_MAX_LEN+1]; |
20 | double scale; | |
ab1bf653 ACM |
21 | }; |
22 | ||
23 | struct perf_pmu_format { | |
24 | char *name; | |
25 | int value; | |
26 | DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS); | |
27 | struct list_head list; | |
28 | }; | |
29 | ||
50a9667c RR |
30 | #define EVENT_SOURCE_DEVICE_PATH "/bus/event_source/devices/" |
31 | ||
cd82a32e JO |
32 | int perf_pmu_parse(struct list_head *list, char *name); |
33 | extern FILE *perf_pmu_in; | |
34 | ||
35 | static LIST_HEAD(pmus); | |
36 | ||
37 | /* | |
38 | * Parse & process all the sysfs attributes located under | |
39 | * the directory specified in 'dir' parameter. | |
40 | */ | |
cff7f956 | 41 | int perf_pmu__format_parse(char *dir, struct list_head *head) |
cd82a32e JO |
42 | { |
43 | struct dirent *evt_ent; | |
44 | DIR *format_dir; | |
45 | int ret = 0; | |
46 | ||
47 | format_dir = opendir(dir); | |
48 | if (!format_dir) | |
49 | return -EINVAL; | |
50 | ||
51 | while (!ret && (evt_ent = readdir(format_dir))) { | |
52 | char path[PATH_MAX]; | |
53 | char *name = evt_ent->d_name; | |
54 | FILE *file; | |
55 | ||
56 | if (!strcmp(name, ".") || !strcmp(name, "..")) | |
57 | continue; | |
58 | ||
59 | snprintf(path, PATH_MAX, "%s/%s", dir, name); | |
60 | ||
61 | ret = -EINVAL; | |
62 | file = fopen(path, "r"); | |
63 | if (!file) | |
64 | break; | |
65 | ||
66 | perf_pmu_in = file; | |
67 | ret = perf_pmu_parse(head, name); | |
68 | fclose(file); | |
69 | } | |
70 | ||
71 | closedir(format_dir); | |
72 | return ret; | |
73 | } | |
74 | ||
75 | /* | |
76 | * Reading/parsing the default pmu format definition, which should be | |
77 | * located at: | |
78 | * /sys/bus/event_source/devices/<dev>/format as sysfs group attributes. | |
79 | */ | |
b6b96fb4 | 80 | static int pmu_format(const char *name, struct list_head *format) |
cd82a32e JO |
81 | { |
82 | struct stat st; | |
83 | char path[PATH_MAX]; | |
cf38fada | 84 | const char *sysfs = sysfs__mountpoint(); |
cd82a32e | 85 | |
cd82a32e JO |
86 | if (!sysfs) |
87 | return -1; | |
88 | ||
89 | snprintf(path, PATH_MAX, | |
50a9667c | 90 | "%s" EVENT_SOURCE_DEVICE_PATH "%s/format", sysfs, name); |
cd82a32e JO |
91 | |
92 | if (stat(path, &st) < 0) | |
9bc8f9fe | 93 | return 0; /* no error if format does not exist */ |
cd82a32e | 94 | |
cff7f956 | 95 | if (perf_pmu__format_parse(path, format)) |
cd82a32e JO |
96 | return -1; |
97 | ||
98 | return 0; | |
99 | } | |
100 | ||
410136f5 SE |
101 | static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char *name) |
102 | { | |
103 | struct stat st; | |
104 | ssize_t sret; | |
105 | char scale[128]; | |
106 | int fd, ret = -1; | |
107 | char path[PATH_MAX]; | |
108 | char *lc; | |
109 | ||
110 | snprintf(path, PATH_MAX, "%s/%s.scale", dir, name); | |
111 | ||
112 | fd = open(path, O_RDONLY); | |
113 | if (fd == -1) | |
114 | return -1; | |
115 | ||
116 | if (fstat(fd, &st) < 0) | |
117 | goto error; | |
118 | ||
119 | sret = read(fd, scale, sizeof(scale)-1); | |
120 | if (sret < 0) | |
121 | goto error; | |
122 | ||
123 | scale[sret] = '\0'; | |
124 | /* | |
125 | * save current locale | |
126 | */ | |
127 | lc = setlocale(LC_NUMERIC, NULL); | |
128 | ||
129 | /* | |
130 | * force to C locale to ensure kernel | |
131 | * scale string is converted correctly. | |
132 | * kernel uses default C locale. | |
133 | */ | |
134 | setlocale(LC_NUMERIC, "C"); | |
135 | ||
136 | alias->scale = strtod(scale, NULL); | |
137 | ||
138 | /* restore locale */ | |
139 | setlocale(LC_NUMERIC, lc); | |
140 | ||
141 | ret = 0; | |
142 | error: | |
143 | close(fd); | |
144 | return ret; | |
145 | } | |
146 | ||
147 | static int perf_pmu__parse_unit(struct perf_pmu_alias *alias, char *dir, char *name) | |
148 | { | |
149 | char path[PATH_MAX]; | |
150 | ssize_t sret; | |
151 | int fd; | |
152 | ||
153 | snprintf(path, PATH_MAX, "%s/%s.unit", dir, name); | |
154 | ||
155 | fd = open(path, O_RDONLY); | |
156 | if (fd == -1) | |
157 | return -1; | |
158 | ||
159 | sret = read(fd, alias->unit, UNIT_MAX_LEN); | |
160 | if (sret < 0) | |
161 | goto error; | |
162 | ||
163 | close(fd); | |
164 | ||
165 | alias->unit[sret] = '\0'; | |
166 | ||
167 | return 0; | |
168 | error: | |
169 | close(fd); | |
170 | alias->unit[0] = '\0'; | |
171 | return -1; | |
172 | } | |
173 | ||
174 | static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FILE *file) | |
a6146d50 | 175 | { |
5c6ccc37 | 176 | struct perf_pmu_alias *alias; |
a6146d50 ZY |
177 | char buf[256]; |
178 | int ret; | |
179 | ||
180 | ret = fread(buf, 1, sizeof(buf), file); | |
181 | if (ret == 0) | |
182 | return -EINVAL; | |
183 | buf[ret] = 0; | |
184 | ||
185 | alias = malloc(sizeof(*alias)); | |
186 | if (!alias) | |
187 | return -ENOMEM; | |
188 | ||
189 | INIT_LIST_HEAD(&alias->terms); | |
410136f5 SE |
190 | alias->scale = 1.0; |
191 | alias->unit[0] = '\0'; | |
192 | ||
a6146d50 ZY |
193 | ret = parse_events_terms(&alias->terms, buf); |
194 | if (ret) { | |
195 | free(alias); | |
196 | return ret; | |
197 | } | |
198 | ||
199 | alias->name = strdup(name); | |
410136f5 SE |
200 | /* |
201 | * load unit name and scale if available | |
202 | */ | |
203 | perf_pmu__parse_unit(alias, dir, name); | |
204 | perf_pmu__parse_scale(alias, dir, name); | |
205 | ||
a6146d50 | 206 | list_add_tail(&alias->list, list); |
410136f5 | 207 | |
a6146d50 ZY |
208 | return 0; |
209 | } | |
210 | ||
211 | /* | |
212 | * Process all the sysfs attributes located under the directory | |
213 | * specified in 'dir' parameter. | |
214 | */ | |
215 | static int pmu_aliases_parse(char *dir, struct list_head *head) | |
216 | { | |
217 | struct dirent *evt_ent; | |
218 | DIR *event_dir; | |
410136f5 | 219 | size_t len; |
a6146d50 ZY |
220 | int ret = 0; |
221 | ||
222 | event_dir = opendir(dir); | |
223 | if (!event_dir) | |
224 | return -EINVAL; | |
225 | ||
226 | while (!ret && (evt_ent = readdir(event_dir))) { | |
227 | char path[PATH_MAX]; | |
228 | char *name = evt_ent->d_name; | |
229 | FILE *file; | |
230 | ||
231 | if (!strcmp(name, ".") || !strcmp(name, "..")) | |
232 | continue; | |
233 | ||
410136f5 SE |
234 | /* |
235 | * skip .unit and .scale info files | |
236 | * parsed in perf_pmu__new_alias() | |
237 | */ | |
238 | len = strlen(name); | |
239 | if (len > 5 && !strcmp(name + len - 5, ".unit")) | |
240 | continue; | |
241 | if (len > 6 && !strcmp(name + len - 6, ".scale")) | |
242 | continue; | |
243 | ||
a6146d50 ZY |
244 | snprintf(path, PATH_MAX, "%s/%s", dir, name); |
245 | ||
246 | ret = -EINVAL; | |
247 | file = fopen(path, "r"); | |
248 | if (!file) | |
249 | break; | |
410136f5 SE |
250 | |
251 | ret = perf_pmu__new_alias(head, dir, name, file); | |
a6146d50 ZY |
252 | fclose(file); |
253 | } | |
254 | ||
255 | closedir(event_dir); | |
256 | return ret; | |
257 | } | |
258 | ||
259 | /* | |
260 | * Reading the pmu event aliases definition, which should be located at: | |
261 | * /sys/bus/event_source/devices/<dev>/events as sysfs group attributes. | |
262 | */ | |
b6b96fb4 | 263 | static int pmu_aliases(const char *name, struct list_head *head) |
a6146d50 ZY |
264 | { |
265 | struct stat st; | |
266 | char path[PATH_MAX]; | |
cf38fada | 267 | const char *sysfs = sysfs__mountpoint(); |
a6146d50 | 268 | |
a6146d50 ZY |
269 | if (!sysfs) |
270 | return -1; | |
271 | ||
272 | snprintf(path, PATH_MAX, | |
273 | "%s/bus/event_source/devices/%s/events", sysfs, name); | |
274 | ||
275 | if (stat(path, &st) < 0) | |
3fded963 | 276 | return 0; /* no error if 'events' does not exist */ |
a6146d50 ZY |
277 | |
278 | if (pmu_aliases_parse(path, head)) | |
279 | return -1; | |
280 | ||
281 | return 0; | |
282 | } | |
283 | ||
5c6ccc37 | 284 | static int pmu_alias_terms(struct perf_pmu_alias *alias, |
a6146d50 ZY |
285 | struct list_head *terms) |
286 | { | |
6cee6cd3 | 287 | struct parse_events_term *term, *clone; |
a6146d50 ZY |
288 | LIST_HEAD(list); |
289 | int ret; | |
290 | ||
291 | list_for_each_entry(term, &alias->terms, list) { | |
6cee6cd3 | 292 | ret = parse_events_term__clone(&clone, term); |
a6146d50 ZY |
293 | if (ret) { |
294 | parse_events__free_terms(&list); | |
295 | return ret; | |
296 | } | |
297 | list_add_tail(&clone->list, &list); | |
298 | } | |
299 | list_splice(&list, terms); | |
300 | return 0; | |
301 | } | |
302 | ||
cd82a32e JO |
303 | /* |
304 | * Reading/parsing the default pmu type value, which should be | |
305 | * located at: | |
306 | * /sys/bus/event_source/devices/<dev>/type as sysfs attribute. | |
307 | */ | |
b6b96fb4 | 308 | static int pmu_type(const char *name, __u32 *type) |
cd82a32e JO |
309 | { |
310 | struct stat st; | |
311 | char path[PATH_MAX]; | |
cd82a32e JO |
312 | FILE *file; |
313 | int ret = 0; | |
cf38fada | 314 | const char *sysfs = sysfs__mountpoint(); |
cd82a32e | 315 | |
cd82a32e JO |
316 | if (!sysfs) |
317 | return -1; | |
318 | ||
319 | snprintf(path, PATH_MAX, | |
50a9667c | 320 | "%s" EVENT_SOURCE_DEVICE_PATH "%s/type", sysfs, name); |
cd82a32e JO |
321 | |
322 | if (stat(path, &st) < 0) | |
323 | return -1; | |
324 | ||
325 | file = fopen(path, "r"); | |
326 | if (!file) | |
327 | return -EINVAL; | |
328 | ||
329 | if (1 != fscanf(file, "%u", type)) | |
330 | ret = -1; | |
331 | ||
332 | fclose(file); | |
333 | return ret; | |
334 | } | |
335 | ||
50a9667c RR |
336 | /* Add all pmus in sysfs to pmu list: */ |
337 | static void pmu_read_sysfs(void) | |
338 | { | |
339 | char path[PATH_MAX]; | |
50a9667c RR |
340 | DIR *dir; |
341 | struct dirent *dent; | |
cf38fada | 342 | const char *sysfs = sysfs__mountpoint(); |
50a9667c | 343 | |
50a9667c RR |
344 | if (!sysfs) |
345 | return; | |
346 | ||
347 | snprintf(path, PATH_MAX, | |
348 | "%s" EVENT_SOURCE_DEVICE_PATH, sysfs); | |
349 | ||
350 | dir = opendir(path); | |
351 | if (!dir) | |
352 | return; | |
353 | ||
354 | while ((dent = readdir(dir))) { | |
355 | if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) | |
356 | continue; | |
357 | /* add to static LIST_HEAD(pmus): */ | |
358 | perf_pmu__find(dent->d_name); | |
359 | } | |
360 | ||
361 | closedir(dir); | |
362 | } | |
363 | ||
b6b96fb4 | 364 | static struct cpu_map *pmu_cpumask(const char *name) |
7ae92e74 YZ |
365 | { |
366 | struct stat st; | |
367 | char path[PATH_MAX]; | |
7ae92e74 YZ |
368 | FILE *file; |
369 | struct cpu_map *cpus; | |
cf38fada | 370 | const char *sysfs = sysfs__mountpoint(); |
7ae92e74 | 371 | |
7ae92e74 YZ |
372 | if (!sysfs) |
373 | return NULL; | |
374 | ||
375 | snprintf(path, PATH_MAX, | |
376 | "%s/bus/event_source/devices/%s/cpumask", sysfs, name); | |
377 | ||
378 | if (stat(path, &st) < 0) | |
379 | return NULL; | |
380 | ||
381 | file = fopen(path, "r"); | |
382 | if (!file) | |
383 | return NULL; | |
384 | ||
385 | cpus = cpu_map__read(file); | |
386 | fclose(file); | |
387 | return cpus; | |
388 | } | |
389 | ||
b6b96fb4 | 390 | static struct perf_pmu *pmu_lookup(const char *name) |
cd82a32e JO |
391 | { |
392 | struct perf_pmu *pmu; | |
393 | LIST_HEAD(format); | |
a6146d50 | 394 | LIST_HEAD(aliases); |
cd82a32e JO |
395 | __u32 type; |
396 | ||
397 | /* | |
398 | * The pmu data we store & need consists of the pmu | |
399 | * type value and format definitions. Load both right | |
400 | * now. | |
401 | */ | |
402 | if (pmu_format(name, &format)) | |
403 | return NULL; | |
404 | ||
3fded963 JO |
405 | if (pmu_aliases(name, &aliases)) |
406 | return NULL; | |
407 | ||
cd82a32e JO |
408 | if (pmu_type(name, &type)) |
409 | return NULL; | |
410 | ||
411 | pmu = zalloc(sizeof(*pmu)); | |
412 | if (!pmu) | |
413 | return NULL; | |
414 | ||
7ae92e74 YZ |
415 | pmu->cpus = pmu_cpumask(name); |
416 | ||
cd82a32e | 417 | INIT_LIST_HEAD(&pmu->format); |
a6146d50 | 418 | INIT_LIST_HEAD(&pmu->aliases); |
cd82a32e | 419 | list_splice(&format, &pmu->format); |
a6146d50 | 420 | list_splice(&aliases, &pmu->aliases); |
cd82a32e JO |
421 | pmu->name = strdup(name); |
422 | pmu->type = type; | |
9bc8f9fe | 423 | list_add_tail(&pmu->list, &pmus); |
cd82a32e JO |
424 | return pmu; |
425 | } | |
426 | ||
b6b96fb4 | 427 | static struct perf_pmu *pmu_find(const char *name) |
cd82a32e JO |
428 | { |
429 | struct perf_pmu *pmu; | |
430 | ||
431 | list_for_each_entry(pmu, &pmus, list) | |
432 | if (!strcmp(pmu->name, name)) | |
433 | return pmu; | |
434 | ||
435 | return NULL; | |
436 | } | |
437 | ||
50a9667c RR |
438 | struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu) |
439 | { | |
440 | /* | |
441 | * pmu iterator: If pmu is NULL, we start at the begin, | |
442 | * otherwise return the next pmu. Returns NULL on end. | |
443 | */ | |
444 | if (!pmu) { | |
445 | pmu_read_sysfs(); | |
446 | pmu = list_prepare_entry(pmu, &pmus, list); | |
447 | } | |
448 | list_for_each_entry_continue(pmu, &pmus, list) | |
449 | return pmu; | |
450 | return NULL; | |
451 | } | |
452 | ||
b6b96fb4 | 453 | struct perf_pmu *perf_pmu__find(const char *name) |
cd82a32e JO |
454 | { |
455 | struct perf_pmu *pmu; | |
456 | ||
457 | /* | |
458 | * Once PMU is loaded it stays in the list, | |
459 | * so we keep us from multiple reading/parsing | |
460 | * the pmu format definitions. | |
461 | */ | |
462 | pmu = pmu_find(name); | |
463 | if (pmu) | |
464 | return pmu; | |
465 | ||
466 | return pmu_lookup(name); | |
467 | } | |
468 | ||
5c6ccc37 | 469 | static struct perf_pmu_format * |
cd82a32e JO |
470 | pmu_find_format(struct list_head *formats, char *name) |
471 | { | |
5c6ccc37 | 472 | struct perf_pmu_format *format; |
cd82a32e JO |
473 | |
474 | list_for_each_entry(format, formats, list) | |
475 | if (!strcmp(format->name, name)) | |
476 | return format; | |
477 | ||
478 | return NULL; | |
479 | } | |
480 | ||
481 | /* | |
482 | * Returns value based on the format definition (format parameter) | |
483 | * and unformated value (value parameter). | |
484 | * | |
485 | * TODO maybe optimize a little ;) | |
486 | */ | |
487 | static __u64 pmu_format_value(unsigned long *format, __u64 value) | |
488 | { | |
489 | unsigned long fbit, vbit; | |
490 | __u64 v = 0; | |
491 | ||
492 | for (fbit = 0, vbit = 0; fbit < PERF_PMU_FORMAT_BITS; fbit++) { | |
493 | ||
494 | if (!test_bit(fbit, format)) | |
495 | continue; | |
496 | ||
497 | if (!(value & (1llu << vbit++))) | |
498 | continue; | |
499 | ||
500 | v |= (1llu << fbit); | |
501 | } | |
502 | ||
503 | return v; | |
504 | } | |
505 | ||
506 | /* | |
507 | * Setup one of config[12] attr members based on the | |
508 | * user input data - temr parameter. | |
509 | */ | |
510 | static int pmu_config_term(struct list_head *formats, | |
511 | struct perf_event_attr *attr, | |
6cee6cd3 | 512 | struct parse_events_term *term) |
cd82a32e | 513 | { |
5c6ccc37 | 514 | struct perf_pmu_format *format; |
cd82a32e JO |
515 | __u64 *vp; |
516 | ||
517 | /* | |
518 | * Support only for hardcoded and numnerial terms. | |
519 | * Hardcoded terms should be already in, so nothing | |
520 | * to be done for them. | |
521 | */ | |
522 | if (parse_events__is_hardcoded_term(term)) | |
523 | return 0; | |
524 | ||
16fa7e82 | 525 | if (term->type_val != PARSE_EVENTS__TERM_TYPE_NUM) |
cd82a32e JO |
526 | return -EINVAL; |
527 | ||
528 | format = pmu_find_format(formats, term->config); | |
529 | if (!format) | |
530 | return -EINVAL; | |
531 | ||
532 | switch (format->value) { | |
533 | case PERF_PMU_FORMAT_VALUE_CONFIG: | |
534 | vp = &attr->config; | |
535 | break; | |
536 | case PERF_PMU_FORMAT_VALUE_CONFIG1: | |
537 | vp = &attr->config1; | |
538 | break; | |
539 | case PERF_PMU_FORMAT_VALUE_CONFIG2: | |
540 | vp = &attr->config2; | |
541 | break; | |
542 | default: | |
543 | return -EINVAL; | |
544 | } | |
545 | ||
16fa7e82 JO |
546 | /* |
547 | * XXX If we ever decide to go with string values for | |
548 | * non-hardcoded terms, here's the place to translate | |
549 | * them into value. | |
550 | */ | |
cd82a32e JO |
551 | *vp |= pmu_format_value(format->bits, term->val.num); |
552 | return 0; | |
553 | } | |
554 | ||
cff7f956 JO |
555 | int perf_pmu__config_terms(struct list_head *formats, |
556 | struct perf_event_attr *attr, | |
557 | struct list_head *head_terms) | |
cd82a32e | 558 | { |
6cee6cd3 | 559 | struct parse_events_term *term; |
cd82a32e | 560 | |
6b5fc39b | 561 | list_for_each_entry(term, head_terms, list) |
cd82a32e JO |
562 | if (pmu_config_term(formats, attr, term)) |
563 | return -EINVAL; | |
564 | ||
565 | return 0; | |
566 | } | |
567 | ||
568 | /* | |
569 | * Configures event's 'attr' parameter based on the: | |
570 | * 1) users input - specified in terms parameter | |
571 | * 2) pmu format definitions - specified by pmu parameter | |
572 | */ | |
573 | int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, | |
574 | struct list_head *head_terms) | |
575 | { | |
576 | attr->type = pmu->type; | |
cff7f956 | 577 | return perf_pmu__config_terms(&pmu->format, attr, head_terms); |
cd82a32e JO |
578 | } |
579 | ||
5c6ccc37 ACM |
580 | static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu, |
581 | struct parse_events_term *term) | |
a6146d50 | 582 | { |
5c6ccc37 | 583 | struct perf_pmu_alias *alias; |
a6146d50 ZY |
584 | char *name; |
585 | ||
586 | if (parse_events__is_hardcoded_term(term)) | |
587 | return NULL; | |
588 | ||
589 | if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM) { | |
590 | if (term->val.num != 1) | |
591 | return NULL; | |
592 | if (pmu_find_format(&pmu->format, term->config)) | |
593 | return NULL; | |
594 | name = term->config; | |
595 | } else if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) { | |
596 | if (strcasecmp(term->config, "event")) | |
597 | return NULL; | |
598 | name = term->val.str; | |
599 | } else { | |
600 | return NULL; | |
601 | } | |
602 | ||
603 | list_for_each_entry(alias, &pmu->aliases, list) { | |
604 | if (!strcasecmp(alias->name, name)) | |
605 | return alias; | |
606 | } | |
607 | return NULL; | |
608 | } | |
609 | ||
410136f5 SE |
610 | |
611 | static int check_unit_scale(struct perf_pmu_alias *alias, | |
612 | char **unit, double *scale) | |
613 | { | |
614 | /* | |
615 | * Only one term in event definition can | |
616 | * define unit and scale, fail if there's | |
617 | * more than one. | |
618 | */ | |
619 | if ((*unit && alias->unit) || | |
620 | (*scale && alias->scale)) | |
621 | return -EINVAL; | |
622 | ||
623 | if (alias->unit) | |
624 | *unit = alias->unit; | |
625 | ||
626 | if (alias->scale) | |
627 | *scale = alias->scale; | |
628 | ||
629 | return 0; | |
630 | } | |
631 | ||
a6146d50 ZY |
632 | /* |
633 | * Find alias in the terms list and replace it with the terms | |
634 | * defined for the alias | |
635 | */ | |
410136f5 SE |
636 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, |
637 | char **unit, double *scale) | |
a6146d50 | 638 | { |
6cee6cd3 | 639 | struct parse_events_term *term, *h; |
5c6ccc37 | 640 | struct perf_pmu_alias *alias; |
a6146d50 ZY |
641 | int ret; |
642 | ||
410136f5 SE |
643 | *unit = NULL; |
644 | *scale = 0; | |
645 | ||
a6146d50 ZY |
646 | list_for_each_entry_safe(term, h, head_terms, list) { |
647 | alias = pmu_find_alias(pmu, term); | |
648 | if (!alias) | |
649 | continue; | |
650 | ret = pmu_alias_terms(alias, &term->list); | |
651 | if (ret) | |
652 | return ret; | |
410136f5 SE |
653 | |
654 | ret = check_unit_scale(alias, unit, scale); | |
655 | if (ret) | |
656 | return ret; | |
657 | ||
a6146d50 ZY |
658 | list_del(&term->list); |
659 | free(term); | |
660 | } | |
661 | return 0; | |
662 | } | |
663 | ||
cd82a32e JO |
664 | int perf_pmu__new_format(struct list_head *list, char *name, |
665 | int config, unsigned long *bits) | |
666 | { | |
5c6ccc37 | 667 | struct perf_pmu_format *format; |
cd82a32e JO |
668 | |
669 | format = zalloc(sizeof(*format)); | |
670 | if (!format) | |
671 | return -ENOMEM; | |
672 | ||
673 | format->name = strdup(name); | |
674 | format->value = config; | |
675 | memcpy(format->bits, bits, sizeof(format->bits)); | |
676 | ||
677 | list_add_tail(&format->list, list); | |
678 | return 0; | |
679 | } | |
680 | ||
681 | void perf_pmu__set_format(unsigned long *bits, long from, long to) | |
682 | { | |
683 | long b; | |
684 | ||
685 | if (!to) | |
686 | to = from; | |
687 | ||
15268138 | 688 | memset(bits, 0, BITS_TO_BYTES(PERF_PMU_FORMAT_BITS)); |
cd82a32e JO |
689 | for (b = from; b <= to; b++) |
690 | set_bit(b, bits); | |
691 | } | |
dc098b35 AK |
692 | |
693 | static char *format_alias(char *buf, int len, struct perf_pmu *pmu, | |
694 | struct perf_pmu_alias *alias) | |
695 | { | |
696 | snprintf(buf, len, "%s/%s/", pmu->name, alias->name); | |
697 | return buf; | |
698 | } | |
699 | ||
700 | static char *format_alias_or(char *buf, int len, struct perf_pmu *pmu, | |
701 | struct perf_pmu_alias *alias) | |
702 | { | |
703 | snprintf(buf, len, "%s OR %s/%s/", alias->name, pmu->name, alias->name); | |
704 | return buf; | |
705 | } | |
706 | ||
707 | static int cmp_string(const void *a, const void *b) | |
708 | { | |
709 | const char * const *as = a; | |
710 | const char * const *bs = b; | |
711 | return strcmp(*as, *bs); | |
712 | } | |
713 | ||
714 | void print_pmu_events(const char *event_glob, bool name_only) | |
715 | { | |
716 | struct perf_pmu *pmu; | |
717 | struct perf_pmu_alias *alias; | |
718 | char buf[1024]; | |
719 | int printed = 0; | |
720 | int len, j; | |
721 | char **aliases; | |
722 | ||
723 | pmu = NULL; | |
724 | len = 0; | |
725 | while ((pmu = perf_pmu__scan(pmu)) != NULL) | |
726 | list_for_each_entry(alias, &pmu->aliases, list) | |
727 | len++; | |
728 | aliases = malloc(sizeof(char *) * len); | |
729 | if (!aliases) | |
730 | return; | |
731 | pmu = NULL; | |
732 | j = 0; | |
733 | while ((pmu = perf_pmu__scan(pmu)) != NULL) | |
734 | list_for_each_entry(alias, &pmu->aliases, list) { | |
735 | char *name = format_alias(buf, sizeof(buf), pmu, alias); | |
736 | bool is_cpu = !strcmp(pmu->name, "cpu"); | |
737 | ||
738 | if (event_glob != NULL && | |
739 | !(strglobmatch(name, event_glob) || | |
740 | (!is_cpu && strglobmatch(alias->name, | |
741 | event_glob)))) | |
742 | continue; | |
743 | aliases[j] = name; | |
744 | if (is_cpu && !name_only) | |
745 | aliases[j] = format_alias_or(buf, sizeof(buf), | |
746 | pmu, alias); | |
747 | aliases[j] = strdup(aliases[j]); | |
748 | j++; | |
749 | } | |
750 | len = j; | |
751 | qsort(aliases, len, sizeof(char *), cmp_string); | |
752 | for (j = 0; j < len; j++) { | |
753 | if (name_only) { | |
754 | printf("%s ", aliases[j]); | |
755 | continue; | |
756 | } | |
757 | printf(" %-50s [Kernel PMU event]\n", aliases[j]); | |
74cf249d | 758 | zfree(&aliases[j]); |
dc098b35 AK |
759 | printed++; |
760 | } | |
761 | if (printed) | |
762 | printf("\n"); | |
763 | free(aliases); | |
764 | } | |
4cabc3d1 AK |
765 | |
766 | bool pmu_have_event(const char *pname, const char *name) | |
767 | { | |
768 | struct perf_pmu *pmu; | |
769 | struct perf_pmu_alias *alias; | |
770 | ||
771 | pmu = NULL; | |
772 | while ((pmu = perf_pmu__scan(pmu)) != NULL) { | |
773 | if (strcmp(pname, pmu->name)) | |
774 | continue; | |
775 | list_for_each_entry(alias, &pmu->aliases, list) | |
776 | if (!strcmp(alias->name, name)) | |
777 | return true; | |
778 | } | |
779 | return false; | |
780 | } |