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