1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Benchmark scanning sysfs files for PMU information. 4 * 5 * Copyright 2023 Google LLC. 6 */ 7 #include <errno.h> 8 #include <stdio.h> 9 #include "bench.h" 10 #include "util/debug.h" 11 #include "util/pmu.h" 12 #include "util/pmus.h" 13 #include "util/stat.h" 14 #include <linux/atomic.h> 15 #include <linux/err.h> 16 #include <linux/time64.h> 17 #include <subcmd/parse-options.h> 18 19 static unsigned int iterations = 100; 20 21 struct pmu_scan_result { 22 char *name; 23 int nr_aliases; 24 int nr_formats; 25 int nr_caps; 26 bool is_core; 27 }; 28 29 static const struct option options[] = { 30 OPT_UINTEGER('i', "iterations", &iterations, 31 "Number of iterations used to compute average"), 32 OPT_END() 33 }; 34 35 static const char *const bench_usage[] = { 36 "perf bench internals pmu-scan <options>", 37 NULL 38 }; 39 40 static int nr_pmus; 41 static struct pmu_scan_result *results; 42 43 static int save_result(void) 44 { 45 struct perf_pmu *pmu = NULL; 46 struct list_head *list; 47 struct pmu_scan_result *r; 48 49 while ((pmu = perf_pmus__scan(pmu)) != NULL) { 50 r = realloc(results, (nr_pmus + 1) * sizeof(*r)); 51 if (r == NULL) 52 return -ENOMEM; 53 54 results = r; 55 r = results + nr_pmus; 56 57 r->name = strdup(pmu->name); 58 r->is_core = pmu->is_core; 59 r->nr_caps = pmu->nr_caps; 60 61 r->nr_aliases = perf_pmu__num_events(pmu); 62 63 r->nr_formats = 0; 64 list_for_each(list, &pmu->format) 65 r->nr_formats++; 66 67 pr_debug("pmu[%d] name=%s, nr_caps=%d, nr_aliases=%d, nr_formats=%d\n", 68 nr_pmus, r->name, r->nr_caps, r->nr_aliases, r->nr_formats); 69 nr_pmus++; 70 } 71 72 perf_pmus__destroy(); 73 return 0; 74 } 75 76 static int check_result(bool core_only) 77 { 78 struct pmu_scan_result *r; 79 struct perf_pmu *pmu; 80 struct list_head *list; 81 int nr; 82 83 for (int i = 0; i < nr_pmus; i++) { 84 r = &results[i]; 85 if (core_only && !r->is_core) 86 continue; 87 88 pmu = perf_pmus__find(r->name); 89 if (pmu == NULL) { 90 pr_err("Cannot find PMU %s\n", r->name); 91 return -1; 92 } 93 94 if (pmu->nr_caps != (u32)r->nr_caps) { 95 pr_err("Unmatched number of event caps in %s: expect %d vs got %d\n", 96 pmu->name, r->nr_caps, pmu->nr_caps); 97 return -1; 98 } 99 100 nr = perf_pmu__num_events(pmu); 101 if (nr != r->nr_aliases) { 102 pr_err("Unmatched number of event aliases in %s: expect %d vs got %d\n", 103 pmu->name, r->nr_aliases, nr); 104 return -1; 105 } 106 107 nr = 0; 108 list_for_each(list, &pmu->format) 109 nr++; 110 if (nr != r->nr_formats) { 111 pr_err("Unmatched number of event formats in %s: expect %d vs got %d\n", 112 pmu->name, r->nr_formats, nr); 113 return -1; 114 } 115 } 116 return 0; 117 } 118 119 static void delete_result(void) 120 { 121 for (int i = 0; i < nr_pmus; i++) 122 free(results[i].name); 123 free(results); 124 125 results = NULL; 126 nr_pmus = 0; 127 } 128 129 static int run_pmu_scan(void) 130 { 131 struct stats stats; 132 struct timeval start, end, diff; 133 double time_average, time_stddev; 134 u64 runtime_us; 135 int ret; 136 137 init_stats(&stats); 138 pr_info("Computing performance of sysfs PMU event scan for %u times\n", 139 iterations); 140 141 if (save_result() < 0) { 142 pr_err("Failed to initialize PMU scan result\n"); 143 return -1; 144 } 145 146 for (int j = 0; j < 2; j++) { 147 bool core_only = (j == 0); 148 149 for (unsigned int i = 0; i < iterations; i++) { 150 gettimeofday(&start, NULL); 151 if (core_only) 152 perf_pmus__scan_core(NULL); 153 else 154 perf_pmus__scan(NULL); 155 gettimeofday(&end, NULL); 156 timersub(&end, &start, &diff); 157 runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec; 158 update_stats(&stats, runtime_us); 159 160 ret = check_result(core_only); 161 perf_pmus__destroy(); 162 if (ret < 0) 163 break; 164 } 165 time_average = avg_stats(&stats); 166 time_stddev = stddev_stats(&stats); 167 pr_info(" Average%s PMU scanning took: %.3f usec (+- %.3f usec)\n", 168 core_only ? " core" : "", time_average, time_stddev); 169 } 170 delete_result(); 171 return 0; 172 } 173 174 int bench_pmu_scan(int argc, const char **argv) 175 { 176 int err = 0; 177 178 argc = parse_options(argc, argv, options, bench_usage, 0); 179 if (argc) { 180 usage_with_options(bench_usage, options); 181 exit(EXIT_FAILURE); 182 } 183 184 err = run_pmu_scan(); 185 186 return err; 187 } 188