Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf stat: Refactor retry/skip/fatal error handling

For the sake of Intel topdown events commit 9eac5612da1c9102 ("perf
stat: Don't skip failing group events") changed 'perf stat' error
handling making it so that more errors were fatal and didn't report
"<not supported>" events. The change outside of topdown events was
unintentional.

The notion of "fatal" error handling was introduced in commit
e0e6a6ca3ac211cc ("perf stat: Factor out open error handling") and
refined in commits like commit cb5ef60067c11cc8 ("perf stat: Error out
unsupported group leader immediately") to be an approach for avoiding
later assertion failures in the code base.

This change fixes those issues and removes the notion of a fatal error
on an event. If all events fail to open then a fatal error occurs with
the previous fatal error message. This seems to best match the notion of
supported events and allowing some errors not to stop 'perf stat', while
allowing the truly fatal no event case to terminate the tool early.

The evsel->errored flag is only used in the stat code but always just
meaning !evsel->supported although there is a comment about it being
sticky. Force all evsels to be supported in evsel__init and then clear
this when evsel__open fails. When an event is tried the supported is
set to true again. This simplifies the notion of whether an evsel is
broken.

In the get_group_fd code, fail to get a group fd when the evsel isn't
supported. If the leader isn't supported then it is also expected that
there is no group_fd as the leader will have been skipped. Therefore
change the BUG_ON test to be on supported rather than skippable. This
corrects the assertion errors that were the reason for the previous
fatal error handling.

Fixes: 9eac5612da1c9102 ("perf stat: Don't skip failing group events")
Reviewed-by: James Clark <james.clark@linaro.org>
Signed-off-by: Ian Rogers <irogers@google.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Chun-Tse Shao <ctshao@google.com>
Cc: Dapeng Mi <dapeng1.mi@linux.intel.com>
Cc: Howard Chu <howardchu95@gmail.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Falcon <thomas.falcon@intel.com>
Link: https://lore.kernel.org/r/20251002220727.1889799-2-irogers@google.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Ian Rogers and committed by
Arnaldo Carvalho de Melo
2cc7aa99 6026ab65

+77 -91
-2
tools/perf/builtin-record.c
··· 1408 1408 ui__error("%s\n", msg); 1409 1409 goto out; 1410 1410 } 1411 - 1412 - pos->supported = true; 1413 1411 } 1414 1412 1415 1413 if (symbol_conf.kptr_restrict && !evlist__exclude_kernel(evlist)) {
+50 -71
tools/perf/builtin-stat.c
··· 610 610 enum counter_recovery { 611 611 COUNTER_SKIP, 612 612 COUNTER_RETRY, 613 - COUNTER_FATAL, 614 613 }; 615 614 616 615 static enum counter_recovery stat_handle_error(struct evsel *counter, int err) 617 616 { 618 617 char msg[BUFSIZ]; 619 618 620 - if (counter->skippable) { 621 - if (verbose > 0) { 622 - ui__warning("skipping event %s that kernel failed to open .\n", 623 - evsel__name(counter)); 624 - } 625 - counter->supported = false; 626 - counter->errored = true; 627 - return COUNTER_SKIP; 628 - } 619 + assert(!counter->supported); 629 620 630 621 /* 631 622 * PPC returns ENXIO for HW counters until 2.6.37 ··· 627 636 ui__warning("%s event is not supported by the kernel.\n", 628 637 evsel__name(counter)); 629 638 } 630 - counter->supported = false; 631 - /* 632 - * errored is a sticky flag that means one of the counter's 633 - * cpu event had a problem and needs to be reexamined. 634 - */ 635 - counter->errored = true; 636 - } else if (evsel__fallback(counter, &target, err, msg, sizeof(msg))) { 639 + return COUNTER_SKIP; 640 + } 641 + if (evsel__fallback(counter, &target, err, msg, sizeof(msg))) { 637 642 if (verbose > 0) 638 643 ui__warning("%s\n", msg); 644 + counter->supported = true; 639 645 return COUNTER_RETRY; 640 - } else if (target__has_per_thread(&target) && err != EOPNOTSUPP && 641 - evsel_list->core.threads && 642 - evsel_list->core.threads->err_thread != -1) { 646 + } 647 + if (target__has_per_thread(&target) && err != EOPNOTSUPP && 648 + evsel_list->core.threads && evsel_list->core.threads->err_thread != -1) { 643 649 /* 644 650 * For global --per-thread case, skip current 645 651 * error thread. ··· 644 656 if (!thread_map__remove(evsel_list->core.threads, 645 657 evsel_list->core.threads->err_thread)) { 646 658 evsel_list->core.threads->err_thread = -1; 659 + counter->supported = true; 647 660 return COUNTER_RETRY; 648 661 } 649 - } else if (err == EOPNOTSUPP) { 650 - if (verbose > 0) { 651 - ui__warning("%s event is not supported by the kernel.\n", 652 - evsel__name(counter)); 653 - } 654 - counter->supported = false; 655 - counter->errored = true; 656 662 } 657 - 658 - evsel__open_strerror(counter, &target, err, msg, sizeof(msg)); 659 - ui__error("%s\n", msg); 660 - 661 - if (child_pid != -1) 662 - kill(child_pid, SIGTERM); 663 - 664 - return COUNTER_FATAL; 663 + if (verbose > 0) { 664 + ui__warning(err == EOPNOTSUPP 665 + ? "%s event is not supported by the kernel.\n" 666 + : "skipping event %s that kernel failed to open.\n", 667 + evsel__name(counter)); 668 + } 669 + return COUNTER_SKIP; 665 670 } 666 671 667 672 static int create_perf_stat_counter(struct evsel *evsel, ··· 727 746 bool is_pipe = STAT_RECORD ? perf_stat.data.is_pipe : false; 728 747 struct evlist_cpu_iterator evlist_cpu_itr; 729 748 struct affinity saved_affinity, *affinity = NULL; 730 - int err; 731 - bool second_pass = false; 749 + int err, open_err = 0; 750 + bool second_pass = false, has_supported_counters; 732 751 733 752 if (forks) { 734 753 if (evlist__prepare_workload(evsel_list, &target, argv, is_pipe, workload_exec_failed_signal) < 0) { ··· 768 787 if (target.use_bpf) 769 788 break; 770 789 771 - if (counter->reset_group || counter->errored) 790 + if (counter->reset_group || !counter->supported) 772 791 continue; 773 792 if (evsel__is_bperf(counter)) 774 793 continue; 775 - try_again: 776 - if (create_perf_stat_counter(counter, &stat_config, 777 - evlist_cpu_itr.cpu_map_idx) < 0) { 778 794 795 + while (true) { 796 + if (create_perf_stat_counter(counter, &stat_config, 797 + evlist_cpu_itr.cpu_map_idx) == 0) 798 + break; 799 + 800 + open_err = errno; 779 801 /* 780 802 * Weak group failed. We cannot just undo this here 781 803 * because earlier CPUs might be in group mode, and the kernel ··· 786 802 * it to later. 787 803 * Don't close here because we're in the wrong affinity. 788 804 */ 789 - if ((errno == EINVAL || errno == EBADF) && 805 + if ((open_err == EINVAL || open_err == EBADF) && 790 806 evsel__leader(counter) != counter && 791 807 counter->weak_group) { 792 808 evlist__reset_weak_group(evsel_list, counter, false); 793 809 assert(counter->reset_group); 810 + counter->supported = true; 794 811 second_pass = true; 795 - continue; 796 - } 797 - 798 - switch (stat_handle_error(counter, errno)) { 799 - case COUNTER_FATAL: 800 - err = -1; 801 - goto err_out; 802 - case COUNTER_RETRY: 803 - goto try_again; 804 - case COUNTER_SKIP: 805 - continue; 806 - default: 807 812 break; 808 813 } 809 814 815 + if (stat_handle_error(counter, open_err) != COUNTER_RETRY) 816 + break; 810 817 } 811 - counter->supported = true; 812 818 } 813 819 814 820 if (second_pass) { ··· 811 837 evlist__for_each_cpu(evlist_cpu_itr, evsel_list, affinity) { 812 838 counter = evlist_cpu_itr.evsel; 813 839 814 - if (!counter->reset_group && !counter->errored) 840 + if (!counter->reset_group && counter->supported) 815 841 continue; 816 842 817 843 perf_evsel__close_cpu(&counter->core, evlist_cpu_itr.cpu_map_idx); ··· 822 848 823 849 if (!counter->reset_group) 824 850 continue; 825 - try_again_reset: 826 - pr_debug2("reopening weak %s\n", evsel__name(counter)); 827 - if (create_perf_stat_counter(counter, &stat_config, 828 - evlist_cpu_itr.cpu_map_idx) < 0) { 829 851 830 - switch (stat_handle_error(counter, errno)) { 831 - case COUNTER_FATAL: 832 - err = -1; 833 - goto err_out; 834 - case COUNTER_RETRY: 835 - goto try_again_reset; 836 - case COUNTER_SKIP: 837 - continue; 838 - default: 852 + while (true) { 853 + pr_debug2("reopening weak %s\n", evsel__name(counter)); 854 + if (create_perf_stat_counter(counter, &stat_config, 855 + evlist_cpu_itr.cpu_map_idx) == 0) 839 856 break; 840 - } 857 + 858 + open_err = errno; 859 + if (stat_handle_error(counter, open_err) != COUNTER_RETRY) 860 + break; 841 861 } 842 - counter->supported = true; 843 862 } 844 863 } 845 864 affinity__cleanup(affinity); 846 865 affinity = NULL; 847 866 867 + has_supported_counters = false; 848 868 evlist__for_each_entry(evsel_list, counter) { 849 869 if (!counter->supported) { 850 870 perf_evsel__free_fd(&counter->core); 851 871 continue; 852 872 } 873 + has_supported_counters = true; 853 874 854 875 l = strlen(counter->unit); 855 876 if (l > stat_config.unit_width) ··· 855 886 err = -1; 856 887 goto err_out; 857 888 } 889 + } 890 + if (!has_supported_counters) { 891 + evsel__open_strerror(evlist__first(evsel_list), &target, open_err, 892 + msg, sizeof(msg)); 893 + ui__error("No supported events found.\n%s\n", msg); 894 + 895 + if (child_pid != -1) 896 + kill(child_pid, SIGTERM); 897 + err = -1; 898 + goto err_out; 858 899 } 859 900 860 901 if (evlist__apply_filters(evsel_list, &counter, &target)) {
+27 -17
tools/perf/util/evsel.c
··· 407 407 evsel->collect_stat = false; 408 408 evsel->group_pmu_name = NULL; 409 409 evsel->skippable = false; 410 + evsel->supported = true; 410 411 evsel->alternate_hw_config = PERF_COUNT_HW_MAX; 411 412 evsel->script_output_type = -1; // FIXME: OUTPUT_TYPE_UNSET, see builtin-script.c 412 413 } ··· 1942 1941 struct evsel *leader = evsel__leader(evsel); 1943 1942 int fd; 1944 1943 1945 - if (evsel__is_group_leader(evsel)) 1944 + if (!evsel->supported || evsel__is_group_leader(evsel)) 1946 1945 return -1; 1947 1946 1948 1947 /* ··· 1956 1955 return -1; 1957 1956 1958 1957 fd = FD(leader, cpu_map_idx, thread); 1959 - BUG_ON(fd == -1 && !leader->skippable); 1958 + BUG_ON(fd == -1 && leader->supported); 1960 1959 1961 1960 /* 1962 1961 * When the leader has been skipped, return -2 to distinguish from no ··· 2574 2573 enum rlimit_action set_rlimit = NO_CHANGE; 2575 2574 struct perf_cpu cpu; 2576 2575 2577 - if (evsel__is_retire_lat(evsel)) 2578 - return evsel__tpebs_open(evsel); 2576 + if (evsel__is_retire_lat(evsel)) { 2577 + err = evsel__tpebs_open(evsel); 2578 + goto out; 2579 + } 2579 2580 2580 2581 err = __evsel__prepare_open(evsel, cpus, threads); 2581 2582 if (err) 2582 - return err; 2583 + goto out; 2583 2584 2584 2585 if (cpus == NULL) 2585 2586 cpus = empty_cpu_map; ··· 2601 2598 display_attr(&evsel->core.attr); 2602 2599 2603 2600 if (evsel__is_tool(evsel)) { 2604 - return evsel__tool_pmu_open(evsel, threads, 2605 - start_cpu_map_idx, 2606 - end_cpu_map_idx); 2607 - } 2608 - if (evsel__is_hwmon(evsel)) { 2609 - return evsel__hwmon_pmu_open(evsel, threads, 2610 - start_cpu_map_idx, 2611 - end_cpu_map_idx); 2612 - } 2613 - if (evsel__is_drm(evsel)) { 2614 - return evsel__drm_pmu_open(evsel, threads, 2601 + err = evsel__tool_pmu_open(evsel, threads, 2615 2602 start_cpu_map_idx, 2616 2603 end_cpu_map_idx); 2604 + goto out; 2605 + } 2606 + if (evsel__is_hwmon(evsel)) { 2607 + err = evsel__hwmon_pmu_open(evsel, threads, 2608 + start_cpu_map_idx, 2609 + end_cpu_map_idx); 2610 + goto out; 2611 + } 2612 + if (evsel__is_drm(evsel)) { 2613 + err = evsel__drm_pmu_open(evsel, threads, 2614 + start_cpu_map_idx, 2615 + end_cpu_map_idx); 2616 + goto out; 2617 2617 } 2618 2618 2619 2619 for (idx = start_cpu_map_idx; idx < end_cpu_map_idx; idx++) { ··· 2695 2689 } 2696 2690 } 2697 2691 2698 - return 0; 2692 + err = 0; 2693 + goto out; 2699 2694 2700 2695 try_fallback: 2701 2696 if (evsel__ignore_missing_thread(evsel, perf_cpu_map__nr(cpus), ··· 2735 2728 thread = nthreads; 2736 2729 } while (--idx >= 0); 2737 2730 errno = old_errno; 2731 + out: 2732 + if (err) 2733 + evsel->supported = false; 2738 2734 return err; 2739 2735 } 2740 2736
-1
tools/perf/util/evsel.h
··· 121 121 bool forced_leader; 122 122 bool cmdline_group_boundary; 123 123 bool reset_group; 124 - bool errored; 125 124 bool needs_auxtrace_mmap; 126 125 bool default_metricgroup; /* A member of the Default metricgroup */ 127 126 bool needs_uniquify;