| .. | .. |
|---|
| 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 | +} |
|---|