parse-metric.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  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. static struct pmu_event pme_test[] = {
  15. {
  16. .metric_expr = "inst_retired.any / cpu_clk_unhalted.thread",
  17. .metric_name = "IPC",
  18. .metric_group = "group1",
  19. },
  20. {
  21. .metric_expr = "idq_uops_not_delivered.core / (4 * (( ( cpu_clk_unhalted.thread / 2 ) * "
  22. "( 1 + cpu_clk_unhalted.one_thread_active / cpu_clk_unhalted.ref_xclk ) )))",
  23. .metric_name = "Frontend_Bound_SMT",
  24. },
  25. {
  26. .metric_expr = "l1d\\-loads\\-misses / inst_retired.any",
  27. .metric_name = "dcache_miss_cpi",
  28. },
  29. {
  30. .metric_expr = "l1i\\-loads\\-misses / inst_retired.any",
  31. .metric_name = "icache_miss_cycles",
  32. },
  33. {
  34. .metric_expr = "(dcache_miss_cpi + icache_miss_cycles)",
  35. .metric_name = "cache_miss_cycles",
  36. .metric_group = "group1",
  37. },
  38. {
  39. .metric_expr = "l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hit",
  40. .metric_name = "DCache_L2_All_Hits",
  41. },
  42. {
  43. .metric_expr = "max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) + "
  44. "l2_rqsts.pf_miss + l2_rqsts.rfo_miss",
  45. .metric_name = "DCache_L2_All_Miss",
  46. },
  47. {
  48. .metric_expr = "dcache_l2_all_hits + dcache_l2_all_miss",
  49. .metric_name = "DCache_L2_All",
  50. },
  51. {
  52. .metric_expr = "d_ratio(dcache_l2_all_hits, dcache_l2_all)",
  53. .metric_name = "DCache_L2_Hits",
  54. },
  55. {
  56. .metric_expr = "d_ratio(dcache_l2_all_miss, dcache_l2_all)",
  57. .metric_name = "DCache_L2_Misses",
  58. },
  59. {
  60. .metric_expr = "ipc + m2",
  61. .metric_name = "M1",
  62. },
  63. {
  64. .metric_expr = "ipc + m1",
  65. .metric_name = "M2",
  66. },
  67. {
  68. .metric_expr = "1/m3",
  69. .metric_name = "M3",
  70. },
  71. {
  72. .name = NULL,
  73. }
  74. };
  75. static struct pmu_events_map map = {
  76. .cpuid = "test",
  77. .version = "1",
  78. .type = "core",
  79. .table = pme_test,
  80. };
  81. struct value {
  82. const char *event;
  83. u64 val;
  84. };
  85. static u64 find_value(const char *name, struct value *values)
  86. {
  87. struct value *v = values;
  88. while (v->event) {
  89. if (!strcmp(name, v->event))
  90. return v->val;
  91. v++;
  92. };
  93. return 0;
  94. }
  95. static void load_runtime_stat(struct runtime_stat *st, struct evlist *evlist,
  96. struct value *vals)
  97. {
  98. struct evsel *evsel;
  99. u64 count;
  100. evlist__for_each_entry(evlist, evsel) {
  101. count = find_value(evsel->name, vals);
  102. perf_stat__update_shadow_stats(evsel, count, 0, st);
  103. }
  104. }
  105. static double compute_single(struct rblist *metric_events, struct evlist *evlist,
  106. struct runtime_stat *st, const char *name)
  107. {
  108. struct metric_expr *mexp;
  109. struct metric_event *me;
  110. struct evsel *evsel;
  111. evlist__for_each_entry(evlist, evsel) {
  112. me = metricgroup__lookup(metric_events, evsel, false);
  113. if (me != NULL) {
  114. list_for_each_entry (mexp, &me->head, nd) {
  115. if (strcmp(mexp->metric_name, name))
  116. continue;
  117. return test_generic_metric(mexp, 0, st);
  118. }
  119. }
  120. }
  121. return 0.;
  122. }
  123. static int __compute_metric(const char *name, struct value *vals,
  124. const char *name1, double *ratio1,
  125. const char *name2, double *ratio2)
  126. {
  127. struct rblist metric_events = {
  128. .nr_entries = 0,
  129. };
  130. struct perf_cpu_map *cpus;
  131. struct runtime_stat st;
  132. struct evlist *evlist;
  133. int err;
  134. /*
  135. * We need to prepare evlist for stat mode running on CPU 0
  136. * because that's where all the stats are going to be created.
  137. */
  138. evlist = evlist__new();
  139. if (!evlist)
  140. return -ENOMEM;
  141. cpus = perf_cpu_map__new("0");
  142. if (!cpus) {
  143. evlist__delete(evlist);
  144. return -ENOMEM;
  145. }
  146. perf_evlist__set_maps(&evlist->core, cpus, NULL);
  147. runtime_stat__init(&st);
  148. /* Parse the metric into metric_events list. */
  149. err = metricgroup__parse_groups_test(evlist, &map, name,
  150. false, false,
  151. &metric_events);
  152. if (err)
  153. goto out;
  154. err = perf_evlist__alloc_stats(evlist, false);
  155. if (err)
  156. goto out;
  157. /* Load the runtime stats with given numbers for events. */
  158. load_runtime_stat(&st, evlist, vals);
  159. /* And execute the metric */
  160. if (name1 && ratio1)
  161. *ratio1 = compute_single(&metric_events, evlist, &st, name1);
  162. if (name2 && ratio2)
  163. *ratio2 = compute_single(&metric_events, evlist, &st, name2);
  164. out:
  165. /* ... clenup. */
  166. metricgroup__rblist_exit(&metric_events);
  167. runtime_stat__exit(&st);
  168. perf_evlist__free_stats(evlist);
  169. perf_cpu_map__put(cpus);
  170. evlist__delete(evlist);
  171. return err;
  172. }
  173. static int compute_metric(const char *name, struct value *vals, double *ratio)
  174. {
  175. return __compute_metric(name, vals, name, ratio, NULL, NULL);
  176. }
  177. static int compute_metric_group(const char *name, struct value *vals,
  178. const char *name1, double *ratio1,
  179. const char *name2, double *ratio2)
  180. {
  181. return __compute_metric(name, vals, name1, ratio1, name2, ratio2);
  182. }
  183. static int test_ipc(void)
  184. {
  185. double ratio;
  186. struct value vals[] = {
  187. { .event = "inst_retired.any", .val = 300 },
  188. { .event = "cpu_clk_unhalted.thread", .val = 200 },
  189. { .event = NULL, },
  190. };
  191. TEST_ASSERT_VAL("failed to compute metric",
  192. compute_metric("IPC", vals, &ratio) == 0);
  193. TEST_ASSERT_VAL("IPC failed, wrong ratio",
  194. ratio == 1.5);
  195. return 0;
  196. }
  197. static int test_frontend(void)
  198. {
  199. double ratio;
  200. struct value vals[] = {
  201. { .event = "idq_uops_not_delivered.core", .val = 300 },
  202. { .event = "cpu_clk_unhalted.thread", .val = 200 },
  203. { .event = "cpu_clk_unhalted.one_thread_active", .val = 400 },
  204. { .event = "cpu_clk_unhalted.ref_xclk", .val = 600 },
  205. { .event = NULL, },
  206. };
  207. TEST_ASSERT_VAL("failed to compute metric",
  208. compute_metric("Frontend_Bound_SMT", vals, &ratio) == 0);
  209. TEST_ASSERT_VAL("Frontend_Bound_SMT failed, wrong ratio",
  210. ratio == 0.45);
  211. return 0;
  212. }
  213. static int test_cache_miss_cycles(void)
  214. {
  215. double ratio;
  216. struct value vals[] = {
  217. { .event = "l1d-loads-misses", .val = 300 },
  218. { .event = "l1i-loads-misses", .val = 200 },
  219. { .event = "inst_retired.any", .val = 400 },
  220. { .event = NULL, },
  221. };
  222. TEST_ASSERT_VAL("failed to compute metric",
  223. compute_metric("cache_miss_cycles", vals, &ratio) == 0);
  224. TEST_ASSERT_VAL("cache_miss_cycles failed, wrong ratio",
  225. ratio == 1.25);
  226. return 0;
  227. }
  228. /*
  229. * DCache_L2_All_Hits = l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hi
  230. * DCache_L2_All_Miss = max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) +
  231. * l2_rqsts.pf_miss + l2_rqsts.rfo_miss
  232. * DCache_L2_All = dcache_l2_all_hits + dcache_l2_all_miss
  233. * DCache_L2_Hits = d_ratio(dcache_l2_all_hits, dcache_l2_all)
  234. * DCache_L2_Misses = d_ratio(dcache_l2_all_miss, dcache_l2_all)
  235. *
  236. * l2_rqsts.demand_data_rd_hit = 100
  237. * l2_rqsts.pf_hit = 200
  238. * l2_rqsts.rfo_hi = 300
  239. * l2_rqsts.all_demand_data_rd = 400
  240. * l2_rqsts.pf_miss = 500
  241. * l2_rqsts.rfo_miss = 600
  242. *
  243. * DCache_L2_All_Hits = 600
  244. * DCache_L2_All_Miss = MAX(400 - 100, 0) + 500 + 600 = 1400
  245. * DCache_L2_All = 600 + 1400 = 2000
  246. * DCache_L2_Hits = 600 / 2000 = 0.3
  247. * DCache_L2_Misses = 1400 / 2000 = 0.7
  248. */
  249. static int test_dcache_l2(void)
  250. {
  251. double ratio;
  252. struct value vals[] = {
  253. { .event = "l2_rqsts.demand_data_rd_hit", .val = 100 },
  254. { .event = "l2_rqsts.pf_hit", .val = 200 },
  255. { .event = "l2_rqsts.rfo_hit", .val = 300 },
  256. { .event = "l2_rqsts.all_demand_data_rd", .val = 400 },
  257. { .event = "l2_rqsts.pf_miss", .val = 500 },
  258. { .event = "l2_rqsts.rfo_miss", .val = 600 },
  259. { .event = NULL, },
  260. };
  261. TEST_ASSERT_VAL("failed to compute metric",
  262. compute_metric("DCache_L2_Hits", vals, &ratio) == 0);
  263. TEST_ASSERT_VAL("DCache_L2_Hits failed, wrong ratio",
  264. ratio == 0.3);
  265. TEST_ASSERT_VAL("failed to compute metric",
  266. compute_metric("DCache_L2_Misses", vals, &ratio) == 0);
  267. TEST_ASSERT_VAL("DCache_L2_Misses failed, wrong ratio",
  268. ratio == 0.7);
  269. return 0;
  270. }
  271. static int test_recursion_fail(void)
  272. {
  273. double ratio;
  274. struct value vals[] = {
  275. { .event = "inst_retired.any", .val = 300 },
  276. { .event = "cpu_clk_unhalted.thread", .val = 200 },
  277. { .event = NULL, },
  278. };
  279. TEST_ASSERT_VAL("failed to find recursion",
  280. compute_metric("M1", vals, &ratio) == -1);
  281. TEST_ASSERT_VAL("failed to find recursion",
  282. compute_metric("M3", vals, &ratio) == -1);
  283. return 0;
  284. }
  285. static int test_metric_group(void)
  286. {
  287. double ratio1, ratio2;
  288. struct value vals[] = {
  289. { .event = "cpu_clk_unhalted.thread", .val = 200 },
  290. { .event = "l1d-loads-misses", .val = 300 },
  291. { .event = "l1i-loads-misses", .val = 200 },
  292. { .event = "inst_retired.any", .val = 400 },
  293. { .event = NULL, },
  294. };
  295. TEST_ASSERT_VAL("failed to find recursion",
  296. compute_metric_group("group1", vals,
  297. "IPC", &ratio1,
  298. "cache_miss_cycles", &ratio2) == 0);
  299. TEST_ASSERT_VAL("group IPC failed, wrong ratio",
  300. ratio1 == 2.0);
  301. TEST_ASSERT_VAL("group cache_miss_cycles failed, wrong ratio",
  302. ratio2 == 1.25);
  303. return 0;
  304. }
  305. int test__parse_metric(struct test *test __maybe_unused, int subtest __maybe_unused)
  306. {
  307. TEST_ASSERT_VAL("IPC failed", test_ipc() == 0);
  308. TEST_ASSERT_VAL("frontend failed", test_frontend() == 0);
  309. TEST_ASSERT_VAL("cache_miss_cycles failed", test_cache_miss_cycles() == 0);
  310. TEST_ASSERT_VAL("DCache_L2 failed", test_dcache_l2() == 0);
  311. TEST_ASSERT_VAL("recursion fail failed", test_recursion_fail() == 0);
  312. TEST_ASSERT_VAL("test metric group", test_metric_group() == 0);
  313. return 0;
  314. }