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 <stdio.h> 8 #include "bench.h" 9 #include "util/debug.h" 10 #include "util/pmu.h" 11 #include "util/pmus.h" 12 #include "util/stat.h" 13 #include <linux/atomic.h> 14 #include <linux/err.h> 15 #include <linux/time64.h> 16 #include <subcmd/parse-options.h> 17 18 static unsigned int iterations = 100; 19 20 struct pmu_scan_result { 21 char *name; 22 int nr_aliases; 23 int nr_formats; 24 int nr_caps; 25 }; 26 27 static const struct option options[] = { 28 OPT_UINTEGER('i', "iterations", &iterations, 29 "Number of iterations used to compute average"), 30 OPT_END() 31 }; 32 33 static const char *const bench_usage[] = { 34 "perf bench internals pmu-scan <options>", 35 NULL 36 }; 37 38 static int nr_pmus; 39 static struct pmu_scan_result *results; 40 41 static int save_result(void) 42 { 43 struct perf_pmu *pmu; 44 struct list_head *list; 45 struct pmu_scan_result *r; 46 47 perf_pmu__scan(NULL); 48 49 perf_pmus__for_each_pmu(pmu) { 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->nr_caps = pmu->nr_caps; 59 60 r->nr_aliases = 0; 61 list_for_each(list, &pmu->aliases) 62 r->nr_aliases++; 63 64 r->nr_formats = 0; 65 list_for_each(list, &pmu->format) 66 r->nr_formats++; 67 68 pr_debug("pmu[%d] name=%s, nr_caps=%d, nr_aliases=%d, nr_formats=%d\n", 69 nr_pmus, r->name, r->nr_caps, r->nr_aliases, r->nr_formats); 70 nr_pmus++; 71 } 72 73 perf_pmu__destroy(); 74 return 0; 75 } 76 77 static int check_result(void) 78 { 79 struct pmu_scan_result *r; 80 struct perf_pmu *pmu; 81 struct list_head *list; 82 int nr; 83 84 for (int i = 0; i < nr_pmus; i++) { 85 r = &results[i]; 86 pmu = perf_pmu__find(r->name); 87 if (pmu == NULL) { 88 pr_err("Cannot find PMU %s\n", r->name); 89 return -1; 90 } 91 92 if (pmu->nr_caps != (u32)r->nr_caps) { 93 pr_err("Unmatched number of event caps in %s: expect %d vs got %d\n", 94 pmu->name, r->nr_caps, pmu->nr_caps); 95 return -1; 96 } 97 98 nr = 0; 99 list_for_each(list, &pmu->aliases) 100 nr++; 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 unsigned int i; 136 int ret; 137 138 init_stats(&stats); 139 pr_info("Computing performance of sysfs PMU event scan for %u times\n", 140 iterations); 141 142 if (save_result() < 0) { 143 pr_err("Failed to initialize PMU scan result\n"); 144 return -1; 145 } 146 147 for (i = 0; i < iterations; i++) { 148 gettimeofday(&start, NULL); 149 perf_pmu__scan(NULL); 150 gettimeofday(&end, NULL); 151 152 timersub(&end, &start, &diff); 153 runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec; 154 update_stats(&stats, runtime_us); 155 156 ret = check_result(); 157 perf_pmu__destroy(); 158 if (ret < 0) 159 break; 160 } 161 162 time_average = avg_stats(&stats); 163 time_stddev = stddev_stats(&stats); 164 pr_info(" Average PMU scanning took: %.3f usec (+- %.3f usec)\n", 165 time_average, time_stddev); 166 167 delete_result(); 168 return 0; 169 } 170 171 int bench_pmu_scan(int argc, const char **argv) 172 { 173 int err = 0; 174 175 argc = parse_options(argc, argv, options, bench_usage, 0); 176 if (argc) { 177 usage_with_options(bench_usage, options); 178 exit(EXIT_FAILURE); 179 } 180 181 err = run_pmu_scan(); 182 183 return err; 184 } 185