162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Benchmark scanning sysfs files for PMU information. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2023 Google LLC. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <stdio.h> 862306a36Sopenharmony_ci#include "bench.h" 962306a36Sopenharmony_ci#include "util/debug.h" 1062306a36Sopenharmony_ci#include "util/pmu.h" 1162306a36Sopenharmony_ci#include "util/pmus.h" 1262306a36Sopenharmony_ci#include "util/stat.h" 1362306a36Sopenharmony_ci#include <linux/atomic.h> 1462306a36Sopenharmony_ci#include <linux/err.h> 1562306a36Sopenharmony_ci#include <linux/time64.h> 1662306a36Sopenharmony_ci#include <subcmd/parse-options.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic unsigned int iterations = 100; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistruct pmu_scan_result { 2162306a36Sopenharmony_ci char *name; 2262306a36Sopenharmony_ci int nr_aliases; 2362306a36Sopenharmony_ci int nr_formats; 2462306a36Sopenharmony_ci int nr_caps; 2562306a36Sopenharmony_ci bool is_core; 2662306a36Sopenharmony_ci}; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic const struct option options[] = { 2962306a36Sopenharmony_ci OPT_UINTEGER('i', "iterations", &iterations, 3062306a36Sopenharmony_ci "Number of iterations used to compute average"), 3162306a36Sopenharmony_ci OPT_END() 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic const char *const bench_usage[] = { 3562306a36Sopenharmony_ci "perf bench internals pmu-scan <options>", 3662306a36Sopenharmony_ci NULL 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic int nr_pmus; 4062306a36Sopenharmony_cistatic struct pmu_scan_result *results; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic int save_result(void) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci struct perf_pmu *pmu = NULL; 4562306a36Sopenharmony_ci struct list_head *list; 4662306a36Sopenharmony_ci struct pmu_scan_result *r; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci while ((pmu = perf_pmus__scan(pmu)) != NULL) { 4962306a36Sopenharmony_ci r = realloc(results, (nr_pmus + 1) * sizeof(*r)); 5062306a36Sopenharmony_ci if (r == NULL) 5162306a36Sopenharmony_ci return -ENOMEM; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci results = r; 5462306a36Sopenharmony_ci r = results + nr_pmus; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci r->name = strdup(pmu->name); 5762306a36Sopenharmony_ci r->is_core = pmu->is_core; 5862306a36Sopenharmony_ci r->nr_caps = pmu->nr_caps; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci r->nr_aliases = perf_pmu__num_events(pmu); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci r->nr_formats = 0; 6362306a36Sopenharmony_ci list_for_each(list, &pmu->format) 6462306a36Sopenharmony_ci r->nr_formats++; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci pr_debug("pmu[%d] name=%s, nr_caps=%d, nr_aliases=%d, nr_formats=%d\n", 6762306a36Sopenharmony_ci nr_pmus, r->name, r->nr_caps, r->nr_aliases, r->nr_formats); 6862306a36Sopenharmony_ci nr_pmus++; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci perf_pmus__destroy(); 7262306a36Sopenharmony_ci return 0; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int check_result(bool core_only) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct pmu_scan_result *r; 7862306a36Sopenharmony_ci struct perf_pmu *pmu; 7962306a36Sopenharmony_ci struct list_head *list; 8062306a36Sopenharmony_ci int nr; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci for (int i = 0; i < nr_pmus; i++) { 8362306a36Sopenharmony_ci r = &results[i]; 8462306a36Sopenharmony_ci if (core_only && !r->is_core) 8562306a36Sopenharmony_ci continue; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci pmu = perf_pmus__find(r->name); 8862306a36Sopenharmony_ci if (pmu == NULL) { 8962306a36Sopenharmony_ci pr_err("Cannot find PMU %s\n", r->name); 9062306a36Sopenharmony_ci return -1; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (pmu->nr_caps != (u32)r->nr_caps) { 9462306a36Sopenharmony_ci pr_err("Unmatched number of event caps in %s: expect %d vs got %d\n", 9562306a36Sopenharmony_ci pmu->name, r->nr_caps, pmu->nr_caps); 9662306a36Sopenharmony_ci return -1; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci nr = perf_pmu__num_events(pmu); 10062306a36Sopenharmony_ci if (nr != r->nr_aliases) { 10162306a36Sopenharmony_ci pr_err("Unmatched number of event aliases in %s: expect %d vs got %d\n", 10262306a36Sopenharmony_ci pmu->name, r->nr_aliases, nr); 10362306a36Sopenharmony_ci return -1; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci nr = 0; 10762306a36Sopenharmony_ci list_for_each(list, &pmu->format) 10862306a36Sopenharmony_ci nr++; 10962306a36Sopenharmony_ci if (nr != r->nr_formats) { 11062306a36Sopenharmony_ci pr_err("Unmatched number of event formats in %s: expect %d vs got %d\n", 11162306a36Sopenharmony_ci pmu->name, r->nr_formats, nr); 11262306a36Sopenharmony_ci return -1; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci return 0; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic void delete_result(void) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci for (int i = 0; i < nr_pmus; i++) 12162306a36Sopenharmony_ci free(results[i].name); 12262306a36Sopenharmony_ci free(results); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci results = NULL; 12562306a36Sopenharmony_ci nr_pmus = 0; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic int run_pmu_scan(void) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct stats stats; 13162306a36Sopenharmony_ci struct timeval start, end, diff; 13262306a36Sopenharmony_ci double time_average, time_stddev; 13362306a36Sopenharmony_ci u64 runtime_us; 13462306a36Sopenharmony_ci int ret; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci init_stats(&stats); 13762306a36Sopenharmony_ci pr_info("Computing performance of sysfs PMU event scan for %u times\n", 13862306a36Sopenharmony_ci iterations); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (save_result() < 0) { 14162306a36Sopenharmony_ci pr_err("Failed to initialize PMU scan result\n"); 14262306a36Sopenharmony_ci return -1; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci for (int j = 0; j < 2; j++) { 14662306a36Sopenharmony_ci bool core_only = (j == 0); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci for (unsigned int i = 0; i < iterations; i++) { 14962306a36Sopenharmony_ci gettimeofday(&start, NULL); 15062306a36Sopenharmony_ci if (core_only) 15162306a36Sopenharmony_ci perf_pmus__scan_core(NULL); 15262306a36Sopenharmony_ci else 15362306a36Sopenharmony_ci perf_pmus__scan(NULL); 15462306a36Sopenharmony_ci gettimeofday(&end, NULL); 15562306a36Sopenharmony_ci timersub(&end, &start, &diff); 15662306a36Sopenharmony_ci runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec; 15762306a36Sopenharmony_ci update_stats(&stats, runtime_us); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci ret = check_result(core_only); 16062306a36Sopenharmony_ci perf_pmus__destroy(); 16162306a36Sopenharmony_ci if (ret < 0) 16262306a36Sopenharmony_ci break; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci time_average = avg_stats(&stats); 16562306a36Sopenharmony_ci time_stddev = stddev_stats(&stats); 16662306a36Sopenharmony_ci pr_info(" Average%s PMU scanning took: %.3f usec (+- %.3f usec)\n", 16762306a36Sopenharmony_ci core_only ? " core" : "", time_average, time_stddev); 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci delete_result(); 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ciint bench_pmu_scan(int argc, const char **argv) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci int err = 0; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci argc = parse_options(argc, argv, options, bench_usage, 0); 17862306a36Sopenharmony_ci if (argc) { 17962306a36Sopenharmony_ci usage_with_options(bench_usage, options); 18062306a36Sopenharmony_ci exit(EXIT_FAILURE); 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci err = run_pmu_scan(); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return err; 18662306a36Sopenharmony_ci} 187