pmu-scan.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  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. static unsigned int iterations = 100;
  18. struct pmu_scan_result {
  19. char *name;
  20. int nr_aliases;
  21. int nr_formats;
  22. int nr_caps;
  23. bool is_core;
  24. };
  25. static const struct option options[] = {
  26. OPT_UINTEGER('i', "iterations", &iterations,
  27. "Number of iterations used to compute average"),
  28. OPT_END()
  29. };
  30. static const char *const bench_usage[] = {
  31. "perf bench internals pmu-scan <options>",
  32. NULL
  33. };
  34. static int nr_pmus;
  35. static struct pmu_scan_result *results;
  36. static int save_result(void)
  37. {
  38. struct perf_pmu *pmu = NULL;
  39. struct list_head *list;
  40. struct pmu_scan_result *r;
  41. while ((pmu = perf_pmus__scan(pmu)) != NULL) {
  42. r = realloc(results, (nr_pmus + 1) * sizeof(*r));
  43. if (r == NULL)
  44. return -ENOMEM;
  45. results = r;
  46. r = results + nr_pmus;
  47. r->name = strdup(pmu->name);
  48. r->is_core = pmu->is_core;
  49. r->nr_caps = pmu->nr_caps;
  50. r->nr_aliases = perf_pmu__num_events(pmu);
  51. r->nr_formats = 0;
  52. list_for_each(list, &pmu->format)
  53. r->nr_formats++;
  54. pr_debug("pmu[%d] name=%s, nr_caps=%d, nr_aliases=%d, nr_formats=%d\n",
  55. nr_pmus, r->name, r->nr_caps, r->nr_aliases, r->nr_formats);
  56. nr_pmus++;
  57. }
  58. perf_pmus__destroy();
  59. return 0;
  60. }
  61. static int check_result(bool core_only)
  62. {
  63. struct pmu_scan_result *r;
  64. struct perf_pmu *pmu;
  65. struct list_head *list;
  66. int nr;
  67. for (int i = 0; i < nr_pmus; i++) {
  68. r = &results[i];
  69. if (core_only && !r->is_core)
  70. continue;
  71. pmu = perf_pmus__find(r->name);
  72. if (pmu == NULL) {
  73. pr_err("Cannot find PMU %s\n", r->name);
  74. return -1;
  75. }
  76. if (pmu->nr_caps != (u32)r->nr_caps) {
  77. pr_err("Unmatched number of event caps in %s: expect %d vs got %d\n",
  78. pmu->name, r->nr_caps, pmu->nr_caps);
  79. return -1;
  80. }
  81. nr = perf_pmu__num_events(pmu);
  82. if (nr != r->nr_aliases) {
  83. pr_err("Unmatched number of event aliases in %s: expect %d vs got %d\n",
  84. pmu->name, r->nr_aliases, nr);
  85. return -1;
  86. }
  87. nr = 0;
  88. list_for_each(list, &pmu->format)
  89. nr++;
  90. if (nr != r->nr_formats) {
  91. pr_err("Unmatched number of event formats in %s: expect %d vs got %d\n",
  92. pmu->name, r->nr_formats, nr);
  93. return -1;
  94. }
  95. }
  96. return 0;
  97. }
  98. static void delete_result(void)
  99. {
  100. for (int i = 0; i < nr_pmus; i++)
  101. free(results[i].name);
  102. free(results);
  103. results = NULL;
  104. nr_pmus = 0;
  105. }
  106. static int run_pmu_scan(void)
  107. {
  108. struct stats stats;
  109. struct timeval start, end, diff;
  110. double time_average, time_stddev;
  111. u64 runtime_us;
  112. int ret;
  113. init_stats(&stats);
  114. pr_info("Computing performance of sysfs PMU event scan for %u times\n",
  115. iterations);
  116. if (save_result() < 0) {
  117. pr_err("Failed to initialize PMU scan result\n");
  118. return -1;
  119. }
  120. for (int j = 0; j < 2; j++) {
  121. bool core_only = (j == 0);
  122. for (unsigned int i = 0; i < iterations; i++) {
  123. gettimeofday(&start, NULL);
  124. if (core_only)
  125. perf_pmus__scan_core(NULL);
  126. else
  127. perf_pmus__scan(NULL);
  128. gettimeofday(&end, NULL);
  129. timersub(&end, &start, &diff);
  130. runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
  131. update_stats(&stats, runtime_us);
  132. ret = check_result(core_only);
  133. perf_pmus__destroy();
  134. if (ret < 0)
  135. break;
  136. }
  137. time_average = avg_stats(&stats);
  138. time_stddev = stddev_stats(&stats);
  139. pr_info(" Average%s PMU scanning took: %.3f usec (+- %.3f usec)\n",
  140. core_only ? " core" : "", time_average, time_stddev);
  141. }
  142. delete_result();
  143. return 0;
  144. }
  145. int bench_pmu_scan(int argc, const char **argv)
  146. {
  147. int err = 0;
  148. argc = parse_options(argc, argv, options, bench_usage, 0);
  149. if (argc) {
  150. usage_with_options(bench_usage, options);
  151. exit(EXIT_FAILURE);
  152. }
  153. err = run_pmu_scan();
  154. return err;
  155. }