parse-metric.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. // SPDX-License-Identifier: GPL-2.0
  2. #include <linux/compiler.h>
  3. #include <string.h>
  4. #include <perf/cpumap.h>
  5. #include <perf/evlist.h>
  6. #include "metricgroup.h"
  7. #include "tests.h"
  8. #include "pmu-events/pmu-events.h"
  9. #include "evlist.h"
  10. #include "rblist.h"
  11. #include "debug.h"
  12. #include "expr.h"
  13. #include "stat.h"
  14. #include "pmus.h"
  15. struct value {
  16. const char *event;
  17. u64 val;
  18. };
  19. static u64 find_value(const char *name, struct value *values)
  20. {
  21. struct value *v = values;
  22. while (v->event) {
  23. if (!strcmp(name, v->event))
  24. return v->val;
  25. v++;
  26. }
  27. return 0;
  28. }
  29. static void load_runtime_stat(struct evlist *evlist, struct value *vals)
  30. {
  31. struct evsel *evsel;
  32. u64 count;
  33. evlist__alloc_aggr_stats(evlist, 1);
  34. evlist__for_each_entry(evlist, evsel) {
  35. count = find_value(evsel->name, vals);
  36. evsel->supported = true;
  37. evsel->stats->aggr->counts.val = count;
  38. if (evsel__name_is(evsel, "duration_time"))
  39. update_stats(&walltime_nsecs_stats, count);
  40. }
  41. }
  42. static double compute_single(struct rblist *metric_events, struct evlist *evlist,
  43. const char *name)
  44. {
  45. struct metric_expr *mexp;
  46. struct metric_event *me;
  47. struct evsel *evsel;
  48. evlist__for_each_entry(evlist, evsel) {
  49. me = metricgroup__lookup(metric_events, evsel, false);
  50. if (me != NULL) {
  51. list_for_each_entry (mexp, &me->head, nd) {
  52. if (strcmp(mexp->metric_name, name))
  53. continue;
  54. return test_generic_metric(mexp, 0);
  55. }
  56. }
  57. }
  58. return 0.;
  59. }
  60. static int __compute_metric(const char *name, struct value *vals,
  61. const char *name1, double *ratio1,
  62. const char *name2, double *ratio2)
  63. {
  64. struct rblist metric_events = {
  65. .nr_entries = 0,
  66. };
  67. const struct pmu_metrics_table *pme_test;
  68. struct perf_cpu_map *cpus;
  69. struct evlist *evlist;
  70. int err;
  71. /*
  72. * We need to prepare evlist for stat mode running on CPU 0
  73. * because that's where all the stats are going to be created.
  74. */
  75. evlist = evlist__new();
  76. if (!evlist)
  77. return -ENOMEM;
  78. cpus = perf_cpu_map__new("0");
  79. if (!cpus) {
  80. evlist__delete(evlist);
  81. return -ENOMEM;
  82. }
  83. perf_evlist__set_maps(&evlist->core, cpus, NULL);
  84. /* Parse the metric into metric_events list. */
  85. pme_test = find_core_metrics_table("testarch", "testcpu");
  86. err = metricgroup__parse_groups_test(evlist, pme_test, name,
  87. &metric_events);
  88. if (err)
  89. goto out;
  90. err = evlist__alloc_stats(/*config=*/NULL, evlist, /*alloc_raw=*/false);
  91. if (err)
  92. goto out;
  93. /* Load the runtime stats with given numbers for events. */
  94. load_runtime_stat(evlist, vals);
  95. /* And execute the metric */
  96. if (name1 && ratio1)
  97. *ratio1 = compute_single(&metric_events, evlist, name1);
  98. if (name2 && ratio2)
  99. *ratio2 = compute_single(&metric_events, evlist, name2);
  100. out:
  101. /* ... cleanup. */
  102. metricgroup__rblist_exit(&metric_events);
  103. evlist__free_stats(evlist);
  104. perf_cpu_map__put(cpus);
  105. evlist__delete(evlist);
  106. return err;
  107. }
  108. static int compute_metric(const char *name, struct value *vals, double *ratio)
  109. {
  110. return __compute_metric(name, vals, name, ratio, NULL, NULL);
  111. }
  112. static int compute_metric_group(const char *name, struct value *vals,
  113. const char *name1, double *ratio1,
  114. const char *name2, double *ratio2)
  115. {
  116. return __compute_metric(name, vals, name1, ratio1, name2, ratio2);
  117. }
  118. static int test_ipc(void)
  119. {
  120. double ratio;
  121. struct value vals[] = {
  122. { .event = "inst_retired.any", .val = 300 },
  123. { .event = "cpu_clk_unhalted.thread", .val = 200 },
  124. { .event = NULL, },
  125. };
  126. TEST_ASSERT_VAL("failed to compute metric",
  127. compute_metric("IPC", vals, &ratio) == 0);
  128. TEST_ASSERT_VAL("IPC failed, wrong ratio",
  129. ratio == 1.5);
  130. return 0;
  131. }
  132. static int test_frontend(void)
  133. {
  134. double ratio;
  135. struct value vals[] = {
  136. { .event = "idq_uops_not_delivered.core", .val = 300 },
  137. { .event = "cpu_clk_unhalted.thread", .val = 200 },
  138. { .event = "cpu_clk_unhalted.one_thread_active", .val = 400 },
  139. { .event = "cpu_clk_unhalted.ref_xclk", .val = 600 },
  140. { .event = NULL, },
  141. };
  142. TEST_ASSERT_VAL("failed to compute metric",
  143. compute_metric("Frontend_Bound_SMT", vals, &ratio) == 0);
  144. TEST_ASSERT_VAL("Frontend_Bound_SMT failed, wrong ratio",
  145. ratio == 0.45);
  146. return 0;
  147. }
  148. static int test_cache_miss_cycles(void)
  149. {
  150. double ratio;
  151. struct value vals[] = {
  152. { .event = "l1d-loads-misses", .val = 300 },
  153. { .event = "l1i-loads-misses", .val = 200 },
  154. { .event = "inst_retired.any", .val = 400 },
  155. { .event = NULL, },
  156. };
  157. TEST_ASSERT_VAL("failed to compute metric",
  158. compute_metric("cache_miss_cycles", vals, &ratio) == 0);
  159. TEST_ASSERT_VAL("cache_miss_cycles failed, wrong ratio",
  160. ratio == 1.25);
  161. return 0;
  162. }
  163. /*
  164. * DCache_L2_All_Hits = l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hi
  165. * DCache_L2_All_Miss = max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) +
  166. * l2_rqsts.pf_miss + l2_rqsts.rfo_miss
  167. * DCache_L2_All = dcache_l2_all_hits + dcache_l2_all_miss
  168. * DCache_L2_Hits = d_ratio(dcache_l2_all_hits, dcache_l2_all)
  169. * DCache_L2_Misses = d_ratio(dcache_l2_all_miss, dcache_l2_all)
  170. *
  171. * l2_rqsts.demand_data_rd_hit = 100
  172. * l2_rqsts.pf_hit = 200
  173. * l2_rqsts.rfo_hi = 300
  174. * l2_rqsts.all_demand_data_rd = 400
  175. * l2_rqsts.pf_miss = 500
  176. * l2_rqsts.rfo_miss = 600
  177. *
  178. * DCache_L2_All_Hits = 600
  179. * DCache_L2_All_Miss = MAX(400 - 100, 0) + 500 + 600 = 1400
  180. * DCache_L2_All = 600 + 1400 = 2000
  181. * DCache_L2_Hits = 600 / 2000 = 0.3
  182. * DCache_L2_Misses = 1400 / 2000 = 0.7
  183. */
  184. static int test_dcache_l2(void)
  185. {
  186. double ratio;
  187. struct value vals[] = {
  188. { .event = "l2_rqsts.demand_data_rd_hit", .val = 100 },
  189. { .event = "l2_rqsts.pf_hit", .val = 200 },
  190. { .event = "l2_rqsts.rfo_hit", .val = 300 },
  191. { .event = "l2_rqsts.all_demand_data_rd", .val = 400 },
  192. { .event = "l2_rqsts.pf_miss", .val = 500 },
  193. { .event = "l2_rqsts.rfo_miss", .val = 600 },
  194. { .event = NULL, },
  195. };
  196. TEST_ASSERT_VAL("failed to compute metric",
  197. compute_metric("DCache_L2_Hits", vals, &ratio) == 0);
  198. TEST_ASSERT_VAL("DCache_L2_Hits failed, wrong ratio",
  199. ratio == 0.3);
  200. TEST_ASSERT_VAL("failed to compute metric",
  201. compute_metric("DCache_L2_Misses", vals, &ratio) == 0);
  202. TEST_ASSERT_VAL("DCache_L2_Misses failed, wrong ratio",
  203. ratio == 0.7);
  204. return 0;
  205. }
  206. static int test_recursion_fail(void)
  207. {
  208. double ratio;
  209. struct value vals[] = {
  210. { .event = "inst_retired.any", .val = 300 },
  211. { .event = "cpu_clk_unhalted.thread", .val = 200 },
  212. { .event = NULL, },
  213. };
  214. TEST_ASSERT_VAL("failed to find recursion",
  215. compute_metric("M1", vals, &ratio) == -1);
  216. TEST_ASSERT_VAL("failed to find recursion",
  217. compute_metric("M3", vals, &ratio) == -1);
  218. return 0;
  219. }
  220. static int test_memory_bandwidth(void)
  221. {
  222. double ratio;
  223. struct value vals[] = {
  224. { .event = "l1d.replacement", .val = 4000000 },
  225. { .event = "duration_time", .val = 200000000 },
  226. { .event = NULL, },
  227. };
  228. TEST_ASSERT_VAL("failed to compute metric",
  229. compute_metric("L1D_Cache_Fill_BW", vals, &ratio) == 0);
  230. TEST_ASSERT_VAL("L1D_Cache_Fill_BW, wrong ratio",
  231. 1.28 == ratio);
  232. return 0;
  233. }
  234. static int test_metric_group(void)
  235. {
  236. double ratio1, ratio2;
  237. struct value vals[] = {
  238. { .event = "cpu_clk_unhalted.thread", .val = 200 },
  239. { .event = "l1d-loads-misses", .val = 300 },
  240. { .event = "l1i-loads-misses", .val = 200 },
  241. { .event = "inst_retired.any", .val = 400 },
  242. { .event = NULL, },
  243. };
  244. TEST_ASSERT_VAL("failed to find recursion",
  245. compute_metric_group("group1", vals,
  246. "IPC", &ratio1,
  247. "cache_miss_cycles", &ratio2) == 0);
  248. TEST_ASSERT_VAL("group IPC failed, wrong ratio",
  249. ratio1 == 2.0);
  250. TEST_ASSERT_VAL("group cache_miss_cycles failed, wrong ratio",
  251. ratio2 == 1.25);
  252. return 0;
  253. }
  254. static int test__parse_metric(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
  255. {
  256. TEST_ASSERT_VAL("IPC failed", test_ipc() == 0);
  257. TEST_ASSERT_VAL("frontend failed", test_frontend() == 0);
  258. TEST_ASSERT_VAL("DCache_L2 failed", test_dcache_l2() == 0);
  259. TEST_ASSERT_VAL("recursion fail failed", test_recursion_fail() == 0);
  260. TEST_ASSERT_VAL("Memory bandwidth", test_memory_bandwidth() == 0);
  261. TEST_ASSERT_VAL("cache_miss_cycles failed", test_cache_miss_cycles() == 0);
  262. TEST_ASSERT_VAL("test metric group", test_metric_group() == 0);
  263. return 0;
  264. }
  265. DEFINE_SUITE("Parse and process metrics", parse_metric);