.. | .. |
---|
1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
---|
2 | 2 | #include <linux/list.h> |
---|
3 | 3 | #include <linux/compiler.h> |
---|
| 4 | +#include <linux/string.h> |
---|
| 5 | +#include <linux/zalloc.h> |
---|
| 6 | +#include <subcmd/pager.h> |
---|
4 | 7 | #include <sys/types.h> |
---|
5 | 8 | #include <errno.h> |
---|
6 | 9 | #include <fcntl.h> |
---|
.. | .. |
---|
13 | 16 | #include <api/fs/fs.h> |
---|
14 | 17 | #include <locale.h> |
---|
15 | 18 | #include <regex.h> |
---|
16 | | -#include "util.h" |
---|
| 19 | +#include <perf/cpumap.h> |
---|
| 20 | +#include "debug.h" |
---|
| 21 | +#include "evsel.h" |
---|
17 | 22 | #include "pmu.h" |
---|
18 | 23 | #include "parse-events.h" |
---|
19 | | -#include "cpumap.h" |
---|
20 | 24 | #include "header.h" |
---|
21 | | -#include "pmu-events/pmu-events.h" |
---|
22 | | -#include "cache.h" |
---|
23 | 25 | #include "string2.h" |
---|
| 26 | +#include "strbuf.h" |
---|
| 27 | +#include "fncache.h" |
---|
| 28 | + |
---|
| 29 | +struct perf_pmu perf_pmu__fake; |
---|
24 | 30 | |
---|
25 | 31 | struct perf_pmu_format { |
---|
26 | 32 | char *name; |
---|
.. | .. |
---|
28 | 34 | DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS); |
---|
29 | 35 | struct list_head list; |
---|
30 | 36 | }; |
---|
31 | | - |
---|
32 | | -#define EVENT_SOURCE_DEVICE_PATH "/bus/event_source/devices/" |
---|
33 | 37 | |
---|
34 | 38 | int perf_pmu_parse(struct list_head *list, char *name); |
---|
35 | 39 | extern FILE *perf_pmu_in; |
---|
.. | .. |
---|
81 | 85 | */ |
---|
82 | 86 | static int pmu_format(const char *name, struct list_head *format) |
---|
83 | 87 | { |
---|
84 | | - struct stat st; |
---|
85 | 88 | char path[PATH_MAX]; |
---|
86 | 89 | const char *sysfs = sysfs__mountpoint(); |
---|
87 | 90 | |
---|
.. | .. |
---|
91 | 94 | snprintf(path, PATH_MAX, |
---|
92 | 95 | "%s" EVENT_SOURCE_DEVICE_PATH "%s/format", sysfs, name); |
---|
93 | 96 | |
---|
94 | | - if (stat(path, &st) < 0) |
---|
95 | | - return 0; /* no error if format does not exist */ |
---|
| 97 | + if (!file_available(path)) |
---|
| 98 | + return 0; |
---|
96 | 99 | |
---|
97 | 100 | if (perf_pmu__format_parse(path, format)) |
---|
98 | 101 | return -1; |
---|
.. | .. |
---|
100 | 103 | return 0; |
---|
101 | 104 | } |
---|
102 | 105 | |
---|
103 | | -static int convert_scale(const char *scale, char **end, double *sval) |
---|
| 106 | +int perf_pmu__convert_scale(const char *scale, char **end, double *sval) |
---|
104 | 107 | { |
---|
105 | 108 | char *lc; |
---|
106 | 109 | int ret = 0; |
---|
.. | .. |
---|
163 | 166 | else |
---|
164 | 167 | scale[sret] = '\0'; |
---|
165 | 168 | |
---|
166 | | - ret = convert_scale(scale, NULL, &alias->scale); |
---|
| 169 | + ret = perf_pmu__convert_scale(scale, NULL, &alias->scale); |
---|
167 | 170 | error: |
---|
168 | 171 | close(fd); |
---|
169 | 172 | return ret; |
---|
.. | .. |
---|
271 | 274 | } |
---|
272 | 275 | |
---|
273 | 276 | /* Delete an alias entry. */ |
---|
274 | | -static void perf_pmu_free_alias(struct perf_pmu_alias *newalias) |
---|
| 277 | +void perf_pmu_free_alias(struct perf_pmu_alias *newalias) |
---|
275 | 278 | { |
---|
276 | 279 | zfree(&newalias->name); |
---|
277 | 280 | zfree(&newalias->desc); |
---|
.. | .. |
---|
307 | 310 | char *long_desc, char *topic, |
---|
308 | 311 | char *unit, char *perpkg, |
---|
309 | 312 | char *metric_expr, |
---|
310 | | - char *metric_name) |
---|
| 313 | + char *metric_name, |
---|
| 314 | + char *deprecated) |
---|
311 | 315 | { |
---|
312 | 316 | struct parse_events_term *term; |
---|
313 | 317 | struct perf_pmu_alias *alias; |
---|
.. | .. |
---|
324 | 328 | alias->unit[0] = '\0'; |
---|
325 | 329 | alias->per_pkg = false; |
---|
326 | 330 | alias->snapshot = false; |
---|
| 331 | + alias->deprecated = false; |
---|
327 | 332 | |
---|
328 | 333 | ret = parse_events_terms(&alias->terms, val); |
---|
329 | 334 | if (ret) { |
---|
.. | .. |
---|
371 | 376 | desc ? strdup(desc) : NULL; |
---|
372 | 377 | alias->topic = topic ? strdup(topic) : NULL; |
---|
373 | 378 | if (unit) { |
---|
374 | | - if (convert_scale(unit, &unit, &alias->scale) < 0) |
---|
| 379 | + if (perf_pmu__convert_scale(unit, &unit, &alias->scale) < 0) |
---|
375 | 380 | return -1; |
---|
376 | 381 | snprintf(alias->unit, sizeof(alias->unit), "%s", unit); |
---|
377 | 382 | } |
---|
378 | 383 | alias->per_pkg = perpkg && sscanf(perpkg, "%d", &num) == 1 && num == 1; |
---|
379 | 384 | alias->str = strdup(newval); |
---|
| 385 | + |
---|
| 386 | + if (deprecated) |
---|
| 387 | + alias->deprecated = true; |
---|
380 | 388 | |
---|
381 | 389 | if (!perf_pmu_merge_alias(alias, list)) |
---|
382 | 390 | list_add_tail(&alias->list, list); |
---|
.. | .. |
---|
396 | 404 | buf[ret] = 0; |
---|
397 | 405 | |
---|
398 | 406 | /* Remove trailing newline from sysfs file */ |
---|
399 | | - rtrim(buf); |
---|
| 407 | + strim(buf); |
---|
400 | 408 | |
---|
401 | 409 | return __perf_pmu__new_alias(list, dir, name, NULL, buf, NULL, NULL, NULL, |
---|
402 | | - NULL, NULL, NULL); |
---|
| 410 | + NULL, NULL, NULL, NULL); |
---|
403 | 411 | } |
---|
404 | 412 | |
---|
405 | 413 | static inline bool pmu_alias_info_file(char *name) |
---|
.. | .. |
---|
469 | 477 | */ |
---|
470 | 478 | static int pmu_aliases(const char *name, struct list_head *head) |
---|
471 | 479 | { |
---|
472 | | - struct stat st; |
---|
473 | 480 | char path[PATH_MAX]; |
---|
474 | 481 | const char *sysfs = sysfs__mountpoint(); |
---|
475 | 482 | |
---|
.. | .. |
---|
479 | 486 | snprintf(path, PATH_MAX, |
---|
480 | 487 | "%s/bus/event_source/devices/%s/events", sysfs, name); |
---|
481 | 488 | |
---|
482 | | - if (stat(path, &st) < 0) |
---|
483 | | - return 0; /* no error if 'events' does not exist */ |
---|
| 489 | + if (!file_available(path)) |
---|
| 490 | + return 0; |
---|
484 | 491 | |
---|
485 | 492 | if (pmu_aliases_parse(path, head)) |
---|
486 | 493 | return -1; |
---|
.. | .. |
---|
519 | 526 | */ |
---|
520 | 527 | static int pmu_type(const char *name, __u32 *type) |
---|
521 | 528 | { |
---|
522 | | - struct stat st; |
---|
523 | 529 | char path[PATH_MAX]; |
---|
524 | 530 | FILE *file; |
---|
525 | 531 | int ret = 0; |
---|
.. | .. |
---|
531 | 537 | snprintf(path, PATH_MAX, |
---|
532 | 538 | "%s" EVENT_SOURCE_DEVICE_PATH "%s/type", sysfs, name); |
---|
533 | 539 | |
---|
534 | | - if (stat(path, &st) < 0) |
---|
| 540 | + if (access(path, R_OK) < 0) |
---|
535 | 541 | return -1; |
---|
536 | 542 | |
---|
537 | 543 | file = fopen(path, "r"); |
---|
.. | .. |
---|
573 | 579 | closedir(dir); |
---|
574 | 580 | } |
---|
575 | 581 | |
---|
576 | | -static struct cpu_map *__pmu_cpumask(const char *path) |
---|
| 582 | +static struct perf_cpu_map *__pmu_cpumask(const char *path) |
---|
577 | 583 | { |
---|
578 | 584 | FILE *file; |
---|
579 | | - struct cpu_map *cpus; |
---|
| 585 | + struct perf_cpu_map *cpus; |
---|
580 | 586 | |
---|
581 | 587 | file = fopen(path, "r"); |
---|
582 | 588 | if (!file) |
---|
583 | 589 | return NULL; |
---|
584 | 590 | |
---|
585 | | - cpus = cpu_map__read(file); |
---|
| 591 | + cpus = perf_cpu_map__read(file); |
---|
586 | 592 | fclose(file); |
---|
587 | 593 | return cpus; |
---|
588 | 594 | } |
---|
.. | .. |
---|
594 | 600 | #define CPUS_TEMPLATE_UNCORE "%s/bus/event_source/devices/%s/cpumask" |
---|
595 | 601 | #define CPUS_TEMPLATE_CPU "%s/bus/event_source/devices/%s/cpus" |
---|
596 | 602 | |
---|
597 | | -static struct cpu_map *pmu_cpumask(const char *name) |
---|
| 603 | +static struct perf_cpu_map *pmu_cpumask(const char *name) |
---|
598 | 604 | { |
---|
599 | 605 | char path[PATH_MAX]; |
---|
600 | | - struct cpu_map *cpus; |
---|
| 606 | + struct perf_cpu_map *cpus; |
---|
601 | 607 | const char *sysfs = sysfs__mountpoint(); |
---|
602 | 608 | const char *templates[] = { |
---|
603 | 609 | CPUS_TEMPLATE_UNCORE, |
---|
.. | .. |
---|
622 | 628 | static bool pmu_is_uncore(const char *name) |
---|
623 | 629 | { |
---|
624 | 630 | char path[PATH_MAX]; |
---|
625 | | - struct cpu_map *cpus; |
---|
626 | | - const char *sysfs = sysfs__mountpoint(); |
---|
| 631 | + const char *sysfs; |
---|
627 | 632 | |
---|
| 633 | + sysfs = sysfs__mountpoint(); |
---|
628 | 634 | snprintf(path, PATH_MAX, CPUS_TEMPLATE_UNCORE, sysfs, name); |
---|
629 | | - cpus = __pmu_cpumask(path); |
---|
630 | | - cpu_map__put(cpus); |
---|
631 | | - |
---|
632 | | - return !!cpus; |
---|
| 635 | + return file_available(path); |
---|
633 | 636 | } |
---|
634 | 637 | |
---|
635 | 638 | /* |
---|
.. | .. |
---|
639 | 642 | */ |
---|
640 | 643 | static int is_arm_pmu_core(const char *name) |
---|
641 | 644 | { |
---|
642 | | - struct stat st; |
---|
643 | 645 | char path[PATH_MAX]; |
---|
644 | 646 | const char *sysfs = sysfs__mountpoint(); |
---|
645 | 647 | |
---|
.. | .. |
---|
649 | 651 | /* Look for cpu sysfs (specific to arm) */ |
---|
650 | 652 | scnprintf(path, PATH_MAX, "%s/bus/event_source/devices/%s/cpus", |
---|
651 | 653 | sysfs, name); |
---|
652 | | - if (stat(path, &st) == 0) |
---|
653 | | - return 1; |
---|
654 | | - |
---|
655 | | - return 0; |
---|
656 | | -} |
---|
657 | | - |
---|
658 | | -/* |
---|
659 | | - * Return the CPU id as a raw string. |
---|
660 | | - * |
---|
661 | | - * Each architecture should provide a more precise id string that |
---|
662 | | - * can be use to match the architecture's "mapfile". |
---|
663 | | - */ |
---|
664 | | -char * __weak get_cpuid_str(struct perf_pmu *pmu __maybe_unused) |
---|
665 | | -{ |
---|
666 | | - return NULL; |
---|
667 | | -} |
---|
668 | | - |
---|
669 | | -/* Return zero when the cpuid from the mapfile.csv matches the |
---|
670 | | - * cpuid string generated on this platform. |
---|
671 | | - * Otherwise return non-zero. |
---|
672 | | - */ |
---|
673 | | -int strcmp_cpuid_str(const char *mapcpuid, const char *cpuid) |
---|
674 | | -{ |
---|
675 | | - regex_t re; |
---|
676 | | - regmatch_t pmatch[1]; |
---|
677 | | - int match; |
---|
678 | | - |
---|
679 | | - if (regcomp(&re, mapcpuid, REG_EXTENDED) != 0) { |
---|
680 | | - /* Warn unable to generate match particular string. */ |
---|
681 | | - pr_info("Invalid regular expression %s\n", mapcpuid); |
---|
682 | | - return 1; |
---|
683 | | - } |
---|
684 | | - |
---|
685 | | - match = !regexec(&re, cpuid, 1, pmatch, 0); |
---|
686 | | - regfree(&re); |
---|
687 | | - if (match) { |
---|
688 | | - size_t match_len = (pmatch[0].rm_eo - pmatch[0].rm_so); |
---|
689 | | - |
---|
690 | | - /* Verify the entire string matched. */ |
---|
691 | | - if (match_len == strlen(cpuid)) |
---|
692 | | - return 0; |
---|
693 | | - } |
---|
694 | | - return 1; |
---|
| 654 | + return file_available(path); |
---|
695 | 655 | } |
---|
696 | 656 | |
---|
697 | 657 | static char *perf_pmu__getcpuid(struct perf_pmu *pmu) |
---|
.. | .. |
---|
741 | 701 | return map; |
---|
742 | 702 | } |
---|
743 | 703 | |
---|
| 704 | +bool pmu_uncore_alias_match(const char *pmu_name, const char *name) |
---|
| 705 | +{ |
---|
| 706 | + char *tmp = NULL, *tok, *str; |
---|
| 707 | + bool res; |
---|
| 708 | + |
---|
| 709 | + str = strdup(pmu_name); |
---|
| 710 | + if (!str) |
---|
| 711 | + return false; |
---|
| 712 | + |
---|
| 713 | + /* |
---|
| 714 | + * uncore alias may be from different PMU with common prefix |
---|
| 715 | + */ |
---|
| 716 | + tok = strtok_r(str, ",", &tmp); |
---|
| 717 | + if (strncmp(pmu_name, tok, strlen(tok))) { |
---|
| 718 | + res = false; |
---|
| 719 | + goto out; |
---|
| 720 | + } |
---|
| 721 | + |
---|
| 722 | + /* |
---|
| 723 | + * Match more complex aliases where the alias name is a comma-delimited |
---|
| 724 | + * list of tokens, orderly contained in the matching PMU name. |
---|
| 725 | + * |
---|
| 726 | + * Example: For alias "socket,pmuname" and PMU "socketX_pmunameY", we |
---|
| 727 | + * match "socket" in "socketX_pmunameY" and then "pmuname" in |
---|
| 728 | + * "pmunameY". |
---|
| 729 | + */ |
---|
| 730 | + for (; tok; name += strlen(tok), tok = strtok_r(NULL, ",", &tmp)) { |
---|
| 731 | + name = strstr(name, tok); |
---|
| 732 | + if (!name) { |
---|
| 733 | + res = false; |
---|
| 734 | + goto out; |
---|
| 735 | + } |
---|
| 736 | + } |
---|
| 737 | + |
---|
| 738 | + res = true; |
---|
| 739 | +out: |
---|
| 740 | + free(str); |
---|
| 741 | + return res; |
---|
| 742 | +} |
---|
| 743 | + |
---|
744 | 744 | /* |
---|
745 | 745 | * From the pmu_events_map, find the table of PMU events that corresponds |
---|
746 | 746 | * to the current running CPU. Then, add all PMU events from that table |
---|
747 | 747 | * as aliases. |
---|
748 | 748 | */ |
---|
749 | | -static void pmu_add_cpu_aliases(struct list_head *head, struct perf_pmu *pmu) |
---|
| 749 | +void pmu_add_cpu_aliases_map(struct list_head *head, struct perf_pmu *pmu, |
---|
| 750 | + struct pmu_events_map *map) |
---|
750 | 751 | { |
---|
751 | 752 | int i; |
---|
752 | | - struct pmu_events_map *map; |
---|
753 | 753 | const char *name = pmu->name; |
---|
754 | | - |
---|
755 | | - map = perf_pmu__find_map(pmu); |
---|
756 | | - if (!map) |
---|
757 | | - return; |
---|
758 | | - |
---|
759 | 754 | /* |
---|
760 | 755 | * Found a matching PMU events table. Create aliases |
---|
761 | 756 | */ |
---|
.. | .. |
---|
771 | 766 | break; |
---|
772 | 767 | } |
---|
773 | 768 | |
---|
774 | | - /* |
---|
775 | | - * uncore alias may be from different PMU |
---|
776 | | - * with common prefix |
---|
777 | | - */ |
---|
778 | 769 | if (pmu_is_uncore(name) && |
---|
779 | | - !strncmp(pname, name, strlen(pname))) |
---|
| 770 | + pmu_uncore_alias_match(pname, name)) |
---|
780 | 771 | goto new_alias; |
---|
781 | 772 | |
---|
782 | 773 | if (strcmp(pname, name)) |
---|
.. | .. |
---|
789 | 780 | (char *)pe->long_desc, (char *)pe->topic, |
---|
790 | 781 | (char *)pe->unit, (char *)pe->perpkg, |
---|
791 | 782 | (char *)pe->metric_expr, |
---|
792 | | - (char *)pe->metric_name); |
---|
| 783 | + (char *)pe->metric_name, |
---|
| 784 | + (char *)pe->deprecated); |
---|
793 | 785 | } |
---|
| 786 | +} |
---|
| 787 | + |
---|
| 788 | +static void pmu_add_cpu_aliases(struct list_head *head, struct perf_pmu *pmu) |
---|
| 789 | +{ |
---|
| 790 | + struct pmu_events_map *map; |
---|
| 791 | + |
---|
| 792 | + map = perf_pmu__find_map(pmu); |
---|
| 793 | + if (!map) |
---|
| 794 | + return; |
---|
| 795 | + |
---|
| 796 | + pmu_add_cpu_aliases_map(head, pmu, map); |
---|
794 | 797 | } |
---|
795 | 798 | |
---|
796 | 799 | struct perf_event_attr * __weak |
---|
797 | 800 | perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused) |
---|
798 | 801 | { |
---|
799 | 802 | return NULL; |
---|
| 803 | +} |
---|
| 804 | + |
---|
| 805 | +static int pmu_max_precise(const char *name) |
---|
| 806 | +{ |
---|
| 807 | + char path[PATH_MAX]; |
---|
| 808 | + int max_precise = -1; |
---|
| 809 | + |
---|
| 810 | + scnprintf(path, PATH_MAX, |
---|
| 811 | + "bus/event_source/devices/%s/caps/max_precise", |
---|
| 812 | + name); |
---|
| 813 | + |
---|
| 814 | + sysfs__read_int(path, &max_precise); |
---|
| 815 | + return max_precise; |
---|
800 | 816 | } |
---|
801 | 817 | |
---|
802 | 818 | static struct perf_pmu *pmu_lookup(const char *name) |
---|
.. | .. |
---|
831 | 847 | pmu->name = strdup(name); |
---|
832 | 848 | pmu->type = type; |
---|
833 | 849 | pmu->is_uncore = pmu_is_uncore(name); |
---|
| 850 | + pmu->max_precise = pmu_max_precise(name); |
---|
834 | 851 | pmu_add_cpu_aliases(&aliases, pmu); |
---|
835 | 852 | |
---|
836 | 853 | INIT_LIST_HEAD(&pmu->format); |
---|
837 | 854 | INIT_LIST_HEAD(&pmu->aliases); |
---|
| 855 | + INIT_LIST_HEAD(&pmu->caps); |
---|
838 | 856 | list_splice(&format, &pmu->format); |
---|
839 | 857 | list_splice(&aliases, &pmu->aliases); |
---|
840 | 858 | list_add_tail(&pmu->list, &pmus); |
---|
.. | .. |
---|
844 | 862 | return pmu; |
---|
845 | 863 | } |
---|
846 | 864 | |
---|
| 865 | +void perf_pmu__warn_invalid_formats(struct perf_pmu *pmu) |
---|
| 866 | +{ |
---|
| 867 | + struct perf_pmu_format *format; |
---|
| 868 | + |
---|
| 869 | + /* fake pmu doesn't have format list */ |
---|
| 870 | + if (pmu == &perf_pmu__fake) |
---|
| 871 | + return; |
---|
| 872 | + |
---|
| 873 | + list_for_each_entry(format, &pmu->format, list) |
---|
| 874 | + if (format->value >= PERF_PMU_FORMAT_VALUE_CONFIG_END) { |
---|
| 875 | + pr_warning("WARNING: '%s' format '%s' requires 'perf_event_attr::config%d'" |
---|
| 876 | + "which is not supported by this version of perf!\n", |
---|
| 877 | + pmu->name, format->name, format->value); |
---|
| 878 | + return; |
---|
| 879 | + } |
---|
| 880 | +} |
---|
| 881 | + |
---|
847 | 882 | static struct perf_pmu *pmu_find(const char *name) |
---|
848 | 883 | { |
---|
849 | 884 | struct perf_pmu *pmu; |
---|
850 | 885 | |
---|
851 | 886 | list_for_each_entry(pmu, &pmus, list) |
---|
852 | 887 | if (!strcmp(pmu->name, name)) |
---|
| 888 | + return pmu; |
---|
| 889 | + |
---|
| 890 | + return NULL; |
---|
| 891 | +} |
---|
| 892 | + |
---|
| 893 | +struct perf_pmu *perf_pmu__find_by_type(unsigned int type) |
---|
| 894 | +{ |
---|
| 895 | + struct perf_pmu *pmu; |
---|
| 896 | + |
---|
| 897 | + list_for_each_entry(pmu, &pmus, list) |
---|
| 898 | + if (pmu->type == type) |
---|
853 | 899 | return pmu; |
---|
854 | 900 | |
---|
855 | 901 | return NULL; |
---|
.. | .. |
---|
868 | 914 | list_for_each_entry_continue(pmu, &pmus, list) |
---|
869 | 915 | return pmu; |
---|
870 | 916 | return NULL; |
---|
| 917 | +} |
---|
| 918 | + |
---|
| 919 | +struct perf_pmu *evsel__find_pmu(struct evsel *evsel) |
---|
| 920 | +{ |
---|
| 921 | + struct perf_pmu *pmu = NULL; |
---|
| 922 | + |
---|
| 923 | + while ((pmu = perf_pmu__scan(pmu)) != NULL) { |
---|
| 924 | + if (pmu->type == evsel->core.attr.type) |
---|
| 925 | + break; |
---|
| 926 | + } |
---|
| 927 | + |
---|
| 928 | + return pmu; |
---|
| 929 | +} |
---|
| 930 | + |
---|
| 931 | +bool evsel__is_aux_event(struct evsel *evsel) |
---|
| 932 | +{ |
---|
| 933 | + struct perf_pmu *pmu = evsel__find_pmu(evsel); |
---|
| 934 | + |
---|
| 935 | + return pmu && pmu->auxtrace; |
---|
871 | 936 | } |
---|
872 | 937 | |
---|
873 | 938 | struct perf_pmu *perf_pmu__find(const char *name) |
---|
.. | .. |
---|
911 | 976 | bits |= 1ULL << fbit; |
---|
912 | 977 | |
---|
913 | 978 | return bits; |
---|
| 979 | +} |
---|
| 980 | + |
---|
| 981 | +int perf_pmu__format_type(struct list_head *formats, const char *name) |
---|
| 982 | +{ |
---|
| 983 | + struct perf_pmu_format *format = pmu_find_format(formats, name); |
---|
| 984 | + |
---|
| 985 | + if (!format) |
---|
| 986 | + return -1; |
---|
| 987 | + |
---|
| 988 | + return format->value; |
---|
914 | 989 | } |
---|
915 | 990 | |
---|
916 | 991 | /* |
---|
.. | .. |
---|
960 | 1035 | struct parse_events_term *t; |
---|
961 | 1036 | |
---|
962 | 1037 | list_for_each_entry(t, head_terms, list) { |
---|
963 | | - if (t->type_val == PARSE_EVENTS__TERM_TYPE_NUM) { |
---|
964 | | - if (!strcmp(t->config, term->config)) { |
---|
965 | | - t->used = true; |
---|
966 | | - *value = t->val.num; |
---|
967 | | - return 0; |
---|
968 | | - } |
---|
| 1038 | + if (t->type_val == PARSE_EVENTS__TERM_TYPE_NUM && |
---|
| 1039 | + t->config && !strcmp(t->config, term->config)) { |
---|
| 1040 | + t->used = true; |
---|
| 1041 | + *value = t->val.num; |
---|
| 1042 | + return 0; |
---|
969 | 1043 | } |
---|
970 | 1044 | } |
---|
971 | 1045 | |
---|
.. | .. |
---|
1001 | 1075 | * Setup one of config[12] attr members based on the |
---|
1002 | 1076 | * user input data - term parameter. |
---|
1003 | 1077 | */ |
---|
1004 | | -static int pmu_config_term(struct list_head *formats, |
---|
| 1078 | +static int pmu_config_term(const char *pmu_name, |
---|
| 1079 | + struct list_head *formats, |
---|
1005 | 1080 | struct perf_event_attr *attr, |
---|
1006 | 1081 | struct parse_events_term *term, |
---|
1007 | 1082 | struct list_head *head_terms, |
---|
.. | .. |
---|
1027 | 1102 | |
---|
1028 | 1103 | format = pmu_find_format(formats, term->config); |
---|
1029 | 1104 | if (!format) { |
---|
1030 | | - if (verbose > 0) |
---|
1031 | | - printf("Invalid event/parameter '%s'\n", term->config); |
---|
1032 | | - if (err) { |
---|
1033 | | - char *pmu_term = pmu_formats_string(formats); |
---|
| 1105 | + char *pmu_term = pmu_formats_string(formats); |
---|
| 1106 | + char *unknown_term; |
---|
| 1107 | + char *help_msg; |
---|
1034 | 1108 | |
---|
1035 | | - err->idx = term->err_term; |
---|
1036 | | - err->str = strdup("unknown term"); |
---|
1037 | | - err->help = parse_events_formats_error_string(pmu_term); |
---|
1038 | | - free(pmu_term); |
---|
| 1109 | + if (asprintf(&unknown_term, |
---|
| 1110 | + "unknown term '%s' for pmu '%s'", |
---|
| 1111 | + term->config, pmu_name) < 0) |
---|
| 1112 | + unknown_term = NULL; |
---|
| 1113 | + help_msg = parse_events_formats_error_string(pmu_term); |
---|
| 1114 | + if (err) { |
---|
| 1115 | + parse_events__handle_error(err, term->err_term, |
---|
| 1116 | + unknown_term, |
---|
| 1117 | + help_msg); |
---|
| 1118 | + } else { |
---|
| 1119 | + pr_debug("%s (%s)\n", unknown_term, help_msg); |
---|
| 1120 | + free(unknown_term); |
---|
1039 | 1121 | } |
---|
| 1122 | + free(pmu_term); |
---|
1040 | 1123 | return -EINVAL; |
---|
1041 | 1124 | } |
---|
1042 | 1125 | |
---|
.. | .. |
---|
1062 | 1145 | if (term->no_value && |
---|
1063 | 1146 | bitmap_weight(format->bits, PERF_PMU_FORMAT_BITS) > 1) { |
---|
1064 | 1147 | if (err) { |
---|
1065 | | - err->idx = term->err_val; |
---|
1066 | | - err->str = strdup("no value assigned for term"); |
---|
| 1148 | + parse_events__handle_error(err, term->err_val, |
---|
| 1149 | + strdup("no value assigned for term"), |
---|
| 1150 | + NULL); |
---|
1067 | 1151 | } |
---|
1068 | 1152 | return -EINVAL; |
---|
1069 | 1153 | } |
---|
.. | .. |
---|
1076 | 1160 | term->config, term->val.str); |
---|
1077 | 1161 | } |
---|
1078 | 1162 | if (err) { |
---|
1079 | | - err->idx = term->err_val; |
---|
1080 | | - err->str = strdup("expected numeric value"); |
---|
| 1163 | + parse_events__handle_error(err, term->err_val, |
---|
| 1164 | + strdup("expected numeric value"), |
---|
| 1165 | + NULL); |
---|
1081 | 1166 | } |
---|
1082 | 1167 | return -EINVAL; |
---|
1083 | 1168 | } |
---|
.. | .. |
---|
1090 | 1175 | max_val = pmu_format_max_value(format->bits); |
---|
1091 | 1176 | if (val > max_val) { |
---|
1092 | 1177 | if (err) { |
---|
1093 | | - err->idx = term->err_val; |
---|
1094 | | - if (asprintf(&err->str, |
---|
1095 | | - "value too big for format, maximum is %llu", |
---|
1096 | | - (unsigned long long)max_val) < 0) |
---|
1097 | | - err->str = strdup("value too big for format"); |
---|
| 1178 | + char *err_str; |
---|
| 1179 | + |
---|
| 1180 | + parse_events__handle_error(err, term->err_val, |
---|
| 1181 | + asprintf(&err_str, |
---|
| 1182 | + "value too big for format, maximum is %llu", |
---|
| 1183 | + (unsigned long long)max_val) < 0 |
---|
| 1184 | + ? strdup("value too big for format") |
---|
| 1185 | + : err_str, |
---|
| 1186 | + NULL); |
---|
1098 | 1187 | return -EINVAL; |
---|
1099 | 1188 | } |
---|
1100 | 1189 | /* |
---|
.. | .. |
---|
1107 | 1196 | return 0; |
---|
1108 | 1197 | } |
---|
1109 | 1198 | |
---|
1110 | | -int perf_pmu__config_terms(struct list_head *formats, |
---|
| 1199 | +int perf_pmu__config_terms(const char *pmu_name, struct list_head *formats, |
---|
1111 | 1200 | struct perf_event_attr *attr, |
---|
1112 | 1201 | struct list_head *head_terms, |
---|
1113 | 1202 | bool zero, struct parse_events_error *err) |
---|
.. | .. |
---|
1115 | 1204 | struct parse_events_term *term; |
---|
1116 | 1205 | |
---|
1117 | 1206 | list_for_each_entry(term, head_terms, list) { |
---|
1118 | | - if (pmu_config_term(formats, attr, term, head_terms, |
---|
| 1207 | + if (pmu_config_term(pmu_name, formats, attr, term, head_terms, |
---|
1119 | 1208 | zero, err)) |
---|
1120 | 1209 | return -EINVAL; |
---|
1121 | 1210 | } |
---|
.. | .. |
---|
1135 | 1224 | bool zero = !!pmu->default_config; |
---|
1136 | 1225 | |
---|
1137 | 1226 | attr->type = pmu->type; |
---|
1138 | | - return perf_pmu__config_terms(&pmu->format, attr, head_terms, |
---|
1139 | | - zero, err); |
---|
| 1227 | + return perf_pmu__config_terms(pmu->name, &pmu->format, attr, |
---|
| 1228 | + head_terms, zero, err); |
---|
1140 | 1229 | } |
---|
1141 | 1230 | |
---|
1142 | 1231 | static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu, |
---|
.. | .. |
---|
1235 | 1324 | info->metric_expr = alias->metric_expr; |
---|
1236 | 1325 | info->metric_name = alias->metric_name; |
---|
1237 | 1326 | |
---|
1238 | | - list_del(&term->list); |
---|
1239 | | - free(term); |
---|
| 1327 | + list_del_init(&term->list); |
---|
| 1328 | + parse_events_term__delete(term); |
---|
1240 | 1329 | } |
---|
1241 | 1330 | |
---|
1242 | 1331 | /* |
---|
.. | .. |
---|
1341 | 1430 | char *pmu; |
---|
1342 | 1431 | char *metric_expr; |
---|
1343 | 1432 | char *metric_name; |
---|
| 1433 | + int is_cpu; |
---|
1344 | 1434 | }; |
---|
1345 | 1435 | |
---|
1346 | 1436 | static int cmp_sevent(const void *a, const void *b) |
---|
.. | .. |
---|
1357 | 1447 | if (n) |
---|
1358 | 1448 | return n; |
---|
1359 | 1449 | } |
---|
| 1450 | + |
---|
| 1451 | + /* Order CPU core events to be first */ |
---|
| 1452 | + if (as->is_cpu != bs->is_cpu) |
---|
| 1453 | + return bs->is_cpu - as->is_cpu; |
---|
| 1454 | + |
---|
1360 | 1455 | return strcmp(as->name, bs->name); |
---|
1361 | 1456 | } |
---|
1362 | 1457 | |
---|
.. | .. |
---|
1377 | 1472 | break; |
---|
1378 | 1473 | s += wlen; |
---|
1379 | 1474 | column += n; |
---|
1380 | | - s = ltrim(s); |
---|
| 1475 | + s = skip_spaces(s); |
---|
1381 | 1476 | } |
---|
1382 | 1477 | } |
---|
1383 | 1478 | |
---|
| 1479 | +bool is_pmu_core(const char *name) |
---|
| 1480 | +{ |
---|
| 1481 | + return !strcmp(name, "cpu") || is_arm_pmu_core(name); |
---|
| 1482 | +} |
---|
| 1483 | + |
---|
1384 | 1484 | void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag, |
---|
1385 | | - bool long_desc, bool details_flag) |
---|
| 1485 | + bool long_desc, bool details_flag, bool deprecated) |
---|
1386 | 1486 | { |
---|
1387 | 1487 | struct perf_pmu *pmu; |
---|
1388 | 1488 | struct perf_pmu_alias *alias; |
---|
.. | .. |
---|
1411 | 1511 | list_for_each_entry(alias, &pmu->aliases, list) { |
---|
1412 | 1512 | char *name = alias->desc ? alias->name : |
---|
1413 | 1513 | format_alias(buf, sizeof(buf), pmu, alias); |
---|
1414 | | - bool is_cpu = !strcmp(pmu->name, "cpu"); |
---|
| 1514 | + bool is_cpu = is_pmu_core(pmu->name); |
---|
| 1515 | + |
---|
| 1516 | + if (alias->deprecated && !deprecated) |
---|
| 1517 | + continue; |
---|
1415 | 1518 | |
---|
1416 | 1519 | if (event_glob != NULL && |
---|
1417 | 1520 | !(strglobmatch_nocase(name, event_glob) || |
---|
.. | .. |
---|
1440 | 1543 | aliases[j].pmu = pmu->name; |
---|
1441 | 1544 | aliases[j].metric_expr = alias->metric_expr; |
---|
1442 | 1545 | aliases[j].metric_name = alias->metric_name; |
---|
| 1546 | + aliases[j].is_cpu = is_cpu; |
---|
1443 | 1547 | j++; |
---|
1444 | 1548 | } |
---|
1445 | 1549 | if (pmu->selectable && |
---|
.. | .. |
---|
1518 | 1622 | |
---|
1519 | 1623 | static FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name) |
---|
1520 | 1624 | { |
---|
1521 | | - struct stat st; |
---|
1522 | 1625 | char path[PATH_MAX]; |
---|
1523 | 1626 | const char *sysfs; |
---|
1524 | 1627 | |
---|
.. | .. |
---|
1528 | 1631 | |
---|
1529 | 1632 | snprintf(path, PATH_MAX, |
---|
1530 | 1633 | "%s" EVENT_SOURCE_DEVICE_PATH "%s/%s", sysfs, pmu->name, name); |
---|
1531 | | - |
---|
1532 | | - if (stat(path, &st) < 0) |
---|
| 1634 | + if (!file_available(path)) |
---|
1533 | 1635 | return NULL; |
---|
1534 | | - |
---|
1535 | 1636 | return fopen(path, "r"); |
---|
1536 | 1637 | } |
---|
1537 | 1638 | |
---|
.. | .. |
---|
1551 | 1652 | va_end(args); |
---|
1552 | 1653 | return ret; |
---|
1553 | 1654 | } |
---|
| 1655 | + |
---|
| 1656 | +static int perf_pmu__new_caps(struct list_head *list, char *name, char *value) |
---|
| 1657 | +{ |
---|
| 1658 | + struct perf_pmu_caps *caps = zalloc(sizeof(*caps)); |
---|
| 1659 | + |
---|
| 1660 | + if (!caps) |
---|
| 1661 | + return -ENOMEM; |
---|
| 1662 | + |
---|
| 1663 | + caps->name = strdup(name); |
---|
| 1664 | + if (!caps->name) |
---|
| 1665 | + goto free_caps; |
---|
| 1666 | + caps->value = strndup(value, strlen(value) - 1); |
---|
| 1667 | + if (!caps->value) |
---|
| 1668 | + goto free_name; |
---|
| 1669 | + list_add_tail(&caps->list, list); |
---|
| 1670 | + return 0; |
---|
| 1671 | + |
---|
| 1672 | +free_name: |
---|
| 1673 | + zfree(caps->name); |
---|
| 1674 | +free_caps: |
---|
| 1675 | + free(caps); |
---|
| 1676 | + |
---|
| 1677 | + return -ENOMEM; |
---|
| 1678 | +} |
---|
| 1679 | + |
---|
| 1680 | +/* |
---|
| 1681 | + * Reading/parsing the given pmu capabilities, which should be located at: |
---|
| 1682 | + * /sys/bus/event_source/devices/<dev>/caps as sysfs group attributes. |
---|
| 1683 | + * Return the number of capabilities |
---|
| 1684 | + */ |
---|
| 1685 | +int perf_pmu__caps_parse(struct perf_pmu *pmu) |
---|
| 1686 | +{ |
---|
| 1687 | + struct stat st; |
---|
| 1688 | + char caps_path[PATH_MAX]; |
---|
| 1689 | + const char *sysfs = sysfs__mountpoint(); |
---|
| 1690 | + DIR *caps_dir; |
---|
| 1691 | + struct dirent *evt_ent; |
---|
| 1692 | + int nr_caps = 0; |
---|
| 1693 | + |
---|
| 1694 | + if (!sysfs) |
---|
| 1695 | + return -1; |
---|
| 1696 | + |
---|
| 1697 | + snprintf(caps_path, PATH_MAX, |
---|
| 1698 | + "%s" EVENT_SOURCE_DEVICE_PATH "%s/caps", sysfs, pmu->name); |
---|
| 1699 | + |
---|
| 1700 | + if (stat(caps_path, &st) < 0) |
---|
| 1701 | + return 0; /* no error if caps does not exist */ |
---|
| 1702 | + |
---|
| 1703 | + caps_dir = opendir(caps_path); |
---|
| 1704 | + if (!caps_dir) |
---|
| 1705 | + return -EINVAL; |
---|
| 1706 | + |
---|
| 1707 | + while ((evt_ent = readdir(caps_dir)) != NULL) { |
---|
| 1708 | + char path[PATH_MAX + NAME_MAX + 1]; |
---|
| 1709 | + char *name = evt_ent->d_name; |
---|
| 1710 | + char value[128]; |
---|
| 1711 | + FILE *file; |
---|
| 1712 | + |
---|
| 1713 | + if (!strcmp(name, ".") || !strcmp(name, "..")) |
---|
| 1714 | + continue; |
---|
| 1715 | + |
---|
| 1716 | + snprintf(path, sizeof(path), "%s/%s", caps_path, name); |
---|
| 1717 | + |
---|
| 1718 | + file = fopen(path, "r"); |
---|
| 1719 | + if (!file) |
---|
| 1720 | + continue; |
---|
| 1721 | + |
---|
| 1722 | + if (!fgets(value, sizeof(value), file) || |
---|
| 1723 | + (perf_pmu__new_caps(&pmu->caps, name, value) < 0)) { |
---|
| 1724 | + fclose(file); |
---|
| 1725 | + continue; |
---|
| 1726 | + } |
---|
| 1727 | + |
---|
| 1728 | + nr_caps++; |
---|
| 1729 | + fclose(file); |
---|
| 1730 | + } |
---|
| 1731 | + |
---|
| 1732 | + closedir(caps_dir); |
---|
| 1733 | + |
---|
| 1734 | + return nr_caps; |
---|
| 1735 | +} |
---|
| 1736 | + |
---|
| 1737 | +void perf_pmu__warn_invalid_config(struct perf_pmu *pmu, __u64 config, |
---|
| 1738 | + char *name) |
---|
| 1739 | +{ |
---|
| 1740 | + struct perf_pmu_format *format; |
---|
| 1741 | + __u64 masks = 0, bits; |
---|
| 1742 | + char buf[100]; |
---|
| 1743 | + unsigned int i; |
---|
| 1744 | + |
---|
| 1745 | + list_for_each_entry(format, &pmu->format, list) { |
---|
| 1746 | + if (format->value != PERF_PMU_FORMAT_VALUE_CONFIG) |
---|
| 1747 | + continue; |
---|
| 1748 | + |
---|
| 1749 | + for_each_set_bit(i, format->bits, PERF_PMU_FORMAT_BITS) |
---|
| 1750 | + masks |= 1ULL << i; |
---|
| 1751 | + } |
---|
| 1752 | + |
---|
| 1753 | + /* |
---|
| 1754 | + * Kernel doesn't export any valid format bits. |
---|
| 1755 | + */ |
---|
| 1756 | + if (masks == 0) |
---|
| 1757 | + return; |
---|
| 1758 | + |
---|
| 1759 | + bits = config & ~masks; |
---|
| 1760 | + if (bits == 0) |
---|
| 1761 | + return; |
---|
| 1762 | + |
---|
| 1763 | + bitmap_scnprintf((unsigned long *)&bits, sizeof(bits) * 8, buf, sizeof(buf)); |
---|
| 1764 | + |
---|
| 1765 | + pr_warning("WARNING: event '%s' not valid (bits %s of config " |
---|
| 1766 | + "'%llx' not supported by kernel)!\n", |
---|
| 1767 | + name ?: "N/A", buf, config); |
---|
| 1768 | +} |
---|