pmu-events.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  1. // SPDX-License-Identifier: GPL-2.0
  2. #include "math.h"
  3. #include "parse-events.h"
  4. #include "pmu.h"
  5. #include "tests.h"
  6. #include <errno.h>
  7. #include <stdio.h>
  8. #include <linux/kernel.h>
  9. #include <linux/zalloc.h>
  10. #include "debug.h"
  11. #include "../pmu-events/pmu-events.h"
  12. #include "util/evlist.h"
  13. #include "util/expr.h"
  14. #include "util/parse-events.h"
  15. struct perf_pmu_test_event {
  16. struct pmu_event event;
  17. /* extra events for aliases */
  18. const char *alias_str;
  19. /*
  20. * Note: For when PublicDescription does not exist in the JSON, we
  21. * will have no long_desc in pmu_event.long_desc, but long_desc may
  22. * be set in the alias.
  23. */
  24. const char *alias_long_desc;
  25. };
  26. static struct perf_pmu_test_event test_cpu_events[] = {
  27. {
  28. .event = {
  29. .name = "bp_l1_btb_correct",
  30. .event = "event=0x8a",
  31. .desc = "L1 BTB Correction",
  32. .topic = "branch",
  33. },
  34. .alias_str = "event=0x8a",
  35. .alias_long_desc = "L1 BTB Correction",
  36. },
  37. {
  38. .event = {
  39. .name = "bp_l2_btb_correct",
  40. .event = "event=0x8b",
  41. .desc = "L2 BTB Correction",
  42. .topic = "branch",
  43. },
  44. .alias_str = "event=0x8b",
  45. .alias_long_desc = "L2 BTB Correction",
  46. },
  47. {
  48. .event = {
  49. .name = "segment_reg_loads.any",
  50. .event = "umask=0x80,period=200000,event=0x6",
  51. .desc = "Number of segment register loads",
  52. .topic = "other",
  53. },
  54. .alias_str = "umask=0x80,(null)=0x30d40,event=0x6",
  55. .alias_long_desc = "Number of segment register loads",
  56. },
  57. {
  58. .event = {
  59. .name = "dispatch_blocked.any",
  60. .event = "umask=0x20,period=200000,event=0x9",
  61. .desc = "Memory cluster signals to block micro-op dispatch for any reason",
  62. .topic = "other",
  63. },
  64. .alias_str = "umask=0x20,(null)=0x30d40,event=0x9",
  65. .alias_long_desc = "Memory cluster signals to block micro-op dispatch for any reason",
  66. },
  67. {
  68. .event = {
  69. .name = "eist_trans",
  70. .event = "umask=0x0,period=200000,event=0x3a",
  71. .desc = "Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions",
  72. .topic = "other",
  73. },
  74. .alias_str = "umask=0,(null)=0x30d40,event=0x3a",
  75. .alias_long_desc = "Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions",
  76. },
  77. { /* sentinel */
  78. .event = {
  79. .name = NULL,
  80. },
  81. },
  82. };
  83. static struct perf_pmu_test_event test_uncore_events[] = {
  84. {
  85. .event = {
  86. .name = "uncore_hisi_ddrc.flux_wcmd",
  87. .event = "event=0x2",
  88. .desc = "DDRC write commands. Unit: hisi_sccl,ddrc ",
  89. .topic = "uncore",
  90. .long_desc = "DDRC write commands",
  91. .pmu = "hisi_sccl,ddrc",
  92. },
  93. .alias_str = "event=0x2",
  94. .alias_long_desc = "DDRC write commands",
  95. },
  96. {
  97. .event = {
  98. .name = "unc_cbo_xsnp_response.miss_eviction",
  99. .event = "umask=0x81,event=0x22",
  100. .desc = "Unit: uncore_cbox A cross-core snoop resulted from L3 Eviction which misses in some processor core",
  101. .topic = "uncore",
  102. .long_desc = "A cross-core snoop resulted from L3 Eviction which misses in some processor core",
  103. .pmu = "uncore_cbox",
  104. },
  105. .alias_str = "umask=0x81,event=0x22",
  106. .alias_long_desc = "A cross-core snoop resulted from L3 Eviction which misses in some processor core",
  107. },
  108. { /* sentinel */
  109. .event = {
  110. .name = NULL,
  111. },
  112. }
  113. };
  114. const int total_test_events_size = ARRAY_SIZE(test_uncore_events);
  115. static bool is_same(const char *reference, const char *test)
  116. {
  117. if (!reference && !test)
  118. return true;
  119. if (reference && !test)
  120. return false;
  121. if (!reference && test)
  122. return false;
  123. return !strcmp(reference, test);
  124. }
  125. static struct pmu_events_map *__test_pmu_get_events_map(void)
  126. {
  127. struct pmu_events_map *map;
  128. for (map = &pmu_events_map[0]; map->cpuid; map++) {
  129. if (!strcmp(map->cpuid, "testcpu"))
  130. return map;
  131. }
  132. pr_err("could not find test events map\n");
  133. return NULL;
  134. }
  135. /* Verify generated events from pmu-events.c is as expected */
  136. static int test_pmu_event_table(void)
  137. {
  138. struct pmu_events_map *map = __test_pmu_get_events_map();
  139. struct pmu_event *table;
  140. int map_events = 0, expected_events;
  141. /* ignore 2x sentinels */
  142. expected_events = ARRAY_SIZE(test_cpu_events) +
  143. ARRAY_SIZE(test_uncore_events) - 2;
  144. if (!map)
  145. return -1;
  146. for (table = map->table; table->name; table++) {
  147. struct perf_pmu_test_event *test;
  148. struct pmu_event *te;
  149. bool found = false;
  150. if (table->pmu)
  151. test = &test_uncore_events[0];
  152. else
  153. test = &test_cpu_events[0];
  154. te = &test->event;
  155. for (; te->name; test++, te = &test->event) {
  156. if (strcmp(table->name, te->name))
  157. continue;
  158. found = true;
  159. map_events++;
  160. if (!is_same(table->desc, te->desc)) {
  161. pr_debug2("testing event table %s: mismatched desc, %s vs %s\n",
  162. table->name, table->desc, te->desc);
  163. return -1;
  164. }
  165. if (!is_same(table->topic, te->topic)) {
  166. pr_debug2("testing event table %s: mismatched topic, %s vs %s\n",
  167. table->name, table->topic,
  168. te->topic);
  169. return -1;
  170. }
  171. if (!is_same(table->long_desc, te->long_desc)) {
  172. pr_debug2("testing event table %s: mismatched long_desc, %s vs %s\n",
  173. table->name, table->long_desc,
  174. te->long_desc);
  175. return -1;
  176. }
  177. if (!is_same(table->unit, te->unit)) {
  178. pr_debug2("testing event table %s: mismatched unit, %s vs %s\n",
  179. table->name, table->unit,
  180. te->unit);
  181. return -1;
  182. }
  183. if (!is_same(table->perpkg, te->perpkg)) {
  184. pr_debug2("testing event table %s: mismatched perpkg, %s vs %s\n",
  185. table->name, table->perpkg,
  186. te->perpkg);
  187. return -1;
  188. }
  189. if (!is_same(table->metric_expr, te->metric_expr)) {
  190. pr_debug2("testing event table %s: mismatched metric_expr, %s vs %s\n",
  191. table->name, table->metric_expr,
  192. te->metric_expr);
  193. return -1;
  194. }
  195. if (!is_same(table->metric_name, te->metric_name)) {
  196. pr_debug2("testing event table %s: mismatched metric_name, %s vs %s\n",
  197. table->name, table->metric_name,
  198. te->metric_name);
  199. return -1;
  200. }
  201. if (!is_same(table->deprecated, te->deprecated)) {
  202. pr_debug2("testing event table %s: mismatched deprecated, %s vs %s\n",
  203. table->name, table->deprecated,
  204. te->deprecated);
  205. return -1;
  206. }
  207. pr_debug("testing event table %s: pass\n", table->name);
  208. }
  209. if (!found) {
  210. pr_err("testing event table: could not find event %s\n",
  211. table->name);
  212. return -1;
  213. }
  214. }
  215. if (map_events != expected_events) {
  216. pr_err("testing event table: found %d, but expected %d\n",
  217. map_events, expected_events);
  218. return -1;
  219. }
  220. return 0;
  221. }
  222. static struct perf_pmu_alias *find_alias(const char *test_event, struct list_head *aliases)
  223. {
  224. struct perf_pmu_alias *alias;
  225. list_for_each_entry(alias, aliases, list)
  226. if (!strcmp(test_event, alias->name))
  227. return alias;
  228. return NULL;
  229. }
  230. /* Verify aliases are as expected */
  231. static int __test__pmu_event_aliases(char *pmu_name, int *count)
  232. {
  233. struct perf_pmu_test_event *test;
  234. struct pmu_event *te;
  235. struct perf_pmu *pmu;
  236. LIST_HEAD(aliases);
  237. int res = 0;
  238. bool use_uncore_table;
  239. struct pmu_events_map *map = __test_pmu_get_events_map();
  240. struct perf_pmu_alias *a, *tmp;
  241. if (!map)
  242. return -1;
  243. if (is_pmu_core(pmu_name)) {
  244. test = &test_cpu_events[0];
  245. use_uncore_table = false;
  246. } else {
  247. test = &test_uncore_events[0];
  248. use_uncore_table = true;
  249. }
  250. pmu = zalloc(sizeof(*pmu));
  251. if (!pmu)
  252. return -1;
  253. pmu->name = pmu_name;
  254. pmu_add_cpu_aliases_map(&aliases, pmu, map);
  255. for (te = &test->event; te->name; test++, te = &test->event) {
  256. struct perf_pmu_alias *alias = find_alias(te->name, &aliases);
  257. if (!alias) {
  258. bool uncore_match = pmu_uncore_alias_match(pmu_name,
  259. te->pmu);
  260. if (use_uncore_table && !uncore_match) {
  261. pr_debug3("testing aliases PMU %s: skip matching alias %s\n",
  262. pmu_name, te->name);
  263. continue;
  264. }
  265. pr_debug2("testing aliases PMU %s: no alias, alias_table->name=%s\n",
  266. pmu_name, te->name);
  267. res = -1;
  268. break;
  269. }
  270. if (!is_same(alias->desc, te->desc)) {
  271. pr_debug2("testing aliases PMU %s: mismatched desc, %s vs %s\n",
  272. pmu_name, alias->desc, te->desc);
  273. res = -1;
  274. break;
  275. }
  276. if (!is_same(alias->long_desc, test->alias_long_desc)) {
  277. pr_debug2("testing aliases PMU %s: mismatched long_desc, %s vs %s\n",
  278. pmu_name, alias->long_desc,
  279. test->alias_long_desc);
  280. res = -1;
  281. break;
  282. }
  283. if (!is_same(alias->str, test->alias_str)) {
  284. pr_debug2("testing aliases PMU %s: mismatched str, %s vs %s\n",
  285. pmu_name, alias->str, test->alias_str);
  286. res = -1;
  287. break;
  288. }
  289. if (!is_same(alias->topic, te->topic)) {
  290. pr_debug2("testing aliases PMU %s: mismatched topic, %s vs %s\n",
  291. pmu_name, alias->topic, te->topic);
  292. res = -1;
  293. break;
  294. }
  295. (*count)++;
  296. pr_debug2("testing aliases PMU %s: matched event %s\n",
  297. pmu_name, alias->name);
  298. }
  299. list_for_each_entry_safe(a, tmp, &aliases, list) {
  300. list_del(&a->list);
  301. perf_pmu_free_alias(a);
  302. }
  303. free(pmu);
  304. return res;
  305. }
  306. static int test_aliases(void)
  307. {
  308. struct perf_pmu *pmu = NULL;
  309. while ((pmu = perf_pmu__scan(pmu)) != NULL) {
  310. int count = 0;
  311. if (list_empty(&pmu->format)) {
  312. pr_debug2("skipping testing PMU %s\n", pmu->name);
  313. continue;
  314. }
  315. if (__test__pmu_event_aliases(pmu->name, &count)) {
  316. pr_debug("testing PMU %s aliases: failed\n", pmu->name);
  317. return -1;
  318. }
  319. if (count == 0)
  320. pr_debug3("testing PMU %s aliases: no events to match\n",
  321. pmu->name);
  322. else
  323. pr_debug("testing PMU %s aliases: pass\n", pmu->name);
  324. }
  325. return 0;
  326. }
  327. static bool is_number(const char *str)
  328. {
  329. char *end_ptr;
  330. double v;
  331. errno = 0;
  332. v = strtod(str, &end_ptr);
  333. (void)v; // We're not interested in this value, only if it is valid
  334. return errno == 0 && end_ptr != str;
  335. }
  336. static int check_parse_id(const char *id, struct parse_events_error *error,
  337. struct perf_pmu *fake_pmu)
  338. {
  339. struct evlist *evlist;
  340. int ret;
  341. /* Numbers are always valid. */
  342. if (is_number(id))
  343. return 0;
  344. evlist = evlist__new();
  345. if (!evlist)
  346. return -ENOMEM;
  347. ret = __parse_events(evlist, id, error, fake_pmu);
  348. evlist__delete(evlist);
  349. return ret;
  350. }
  351. static int check_parse_cpu(const char *id, bool same_cpu, struct pmu_event *pe)
  352. {
  353. struct parse_events_error error = { .idx = 0, };
  354. int ret = check_parse_id(id, &error, NULL);
  355. if (ret && same_cpu) {
  356. pr_warning("Parse event failed metric '%s' id '%s' expr '%s'\n",
  357. pe->metric_name, id, pe->metric_expr);
  358. pr_warning("Error string '%s' help '%s'\n", error.str,
  359. error.help);
  360. } else if (ret) {
  361. pr_debug3("Parse event failed, but for an event that may not be supported by this CPU.\nid '%s' metric '%s' expr '%s'\n",
  362. id, pe->metric_name, pe->metric_expr);
  363. ret = 0;
  364. }
  365. free(error.str);
  366. free(error.help);
  367. free(error.first_str);
  368. free(error.first_help);
  369. return ret;
  370. }
  371. static int check_parse_fake(const char *id)
  372. {
  373. struct parse_events_error error = { .idx = 0, };
  374. int ret = check_parse_id(id, &error, &perf_pmu__fake);
  375. free(error.str);
  376. free(error.help);
  377. free(error.first_str);
  378. free(error.first_help);
  379. return ret;
  380. }
  381. static void expr_failure(const char *msg,
  382. const struct pmu_events_map *map,
  383. const struct pmu_event *pe)
  384. {
  385. pr_debug("%s for map %s %s %s\n",
  386. msg, map->cpuid, map->version, map->type);
  387. pr_debug("On metric %s\n", pe->metric_name);
  388. pr_debug("On expression %s\n", pe->metric_expr);
  389. }
  390. static int test_parsing(void)
  391. {
  392. struct pmu_events_map *cpus_map = perf_pmu__find_map(NULL);
  393. struct pmu_events_map *map;
  394. struct pmu_event *pe;
  395. int i, j, k;
  396. int ret = 0;
  397. struct expr_parse_ctx ctx;
  398. double result;
  399. i = 0;
  400. for (;;) {
  401. map = &pmu_events_map[i++];
  402. if (!map->table)
  403. break;
  404. j = 0;
  405. for (;;) {
  406. struct hashmap_entry *cur;
  407. size_t bkt;
  408. pe = &map->table[j++];
  409. if (!pe->name && !pe->metric_group && !pe->metric_name)
  410. break;
  411. if (!pe->metric_expr)
  412. continue;
  413. expr__ctx_init(&ctx);
  414. if (expr__find_other(pe->metric_expr, NULL, &ctx, 0)
  415. < 0) {
  416. expr_failure("Parse other failed", map, pe);
  417. ret++;
  418. continue;
  419. }
  420. /*
  421. * Add all ids with a made up value. The value may
  422. * trigger divide by zero when subtracted and so try to
  423. * make them unique.
  424. */
  425. k = 1;
  426. hashmap__for_each_entry((&ctx.ids), cur, bkt)
  427. expr__add_id_val(&ctx, strdup(cur->key), k++);
  428. hashmap__for_each_entry((&ctx.ids), cur, bkt) {
  429. if (check_parse_cpu(cur->key, map == cpus_map,
  430. pe))
  431. ret++;
  432. }
  433. if (expr__parse(&result, &ctx, pe->metric_expr, 0)) {
  434. expr_failure("Parse failed", map, pe);
  435. ret++;
  436. }
  437. expr__ctx_clear(&ctx);
  438. }
  439. }
  440. /* TODO: fail when not ok */
  441. return ret == 0 ? TEST_OK : TEST_SKIP;
  442. }
  443. struct test_metric {
  444. const char *str;
  445. };
  446. static struct test_metric metrics[] = {
  447. { "(unc_p_power_state_occupancy.cores_c0 / unc_p_clockticks) * 100." },
  448. { "imx8_ddr0@read\\-cycles@ * 4 * 4", },
  449. { "imx8_ddr0@axid\\-read\\,axi_mask\\=0xffff\\,axi_id\\=0x0000@ * 4", },
  450. { "(cstate_pkg@c2\\-residency@ / msr@tsc@) * 100", },
  451. { "(imx8_ddr0@read\\-cycles@ + imx8_ddr0@write\\-cycles@)", },
  452. };
  453. static int metric_parse_fake(const char *str)
  454. {
  455. struct expr_parse_ctx ctx;
  456. struct hashmap_entry *cur;
  457. double result;
  458. int ret = -1;
  459. size_t bkt;
  460. int i;
  461. pr_debug("parsing '%s'\n", str);
  462. expr__ctx_init(&ctx);
  463. if (expr__find_other(str, NULL, &ctx, 0) < 0) {
  464. pr_err("expr__find_other failed\n");
  465. return -1;
  466. }
  467. /*
  468. * Add all ids with a made up value. The value may
  469. * trigger divide by zero when subtracted and so try to
  470. * make them unique.
  471. */
  472. i = 1;
  473. hashmap__for_each_entry((&ctx.ids), cur, bkt)
  474. expr__add_id_val(&ctx, strdup(cur->key), i++);
  475. hashmap__for_each_entry((&ctx.ids), cur, bkt) {
  476. if (check_parse_fake(cur->key)) {
  477. pr_err("check_parse_fake failed\n");
  478. goto out;
  479. }
  480. }
  481. if (expr__parse(&result, &ctx, str, 0))
  482. pr_err("expr__parse failed\n");
  483. else
  484. ret = 0;
  485. out:
  486. expr__ctx_clear(&ctx);
  487. return ret;
  488. }
  489. /*
  490. * Parse all the metrics for current architecture,
  491. * or all defined cpus via the 'fake_pmu'
  492. * in parse_events.
  493. */
  494. static int test_parsing_fake(void)
  495. {
  496. struct pmu_events_map *map;
  497. struct pmu_event *pe;
  498. unsigned int i, j;
  499. int err = 0;
  500. for (i = 0; i < ARRAY_SIZE(metrics); i++) {
  501. err = metric_parse_fake(metrics[i].str);
  502. if (err)
  503. return err;
  504. }
  505. i = 0;
  506. for (;;) {
  507. map = &pmu_events_map[i++];
  508. if (!map->table)
  509. break;
  510. j = 0;
  511. for (;;) {
  512. pe = &map->table[j++];
  513. if (!pe->name && !pe->metric_group && !pe->metric_name)
  514. break;
  515. if (!pe->metric_expr)
  516. continue;
  517. err = metric_parse_fake(pe->metric_expr);
  518. if (err)
  519. return err;
  520. }
  521. }
  522. return 0;
  523. }
  524. static const struct {
  525. int (*func)(void);
  526. const char *desc;
  527. } pmu_events_testcase_table[] = {
  528. {
  529. .func = test_pmu_event_table,
  530. .desc = "PMU event table sanity",
  531. },
  532. {
  533. .func = test_aliases,
  534. .desc = "PMU event map aliases",
  535. },
  536. {
  537. .func = test_parsing,
  538. .desc = "Parsing of PMU event table metrics",
  539. },
  540. {
  541. .func = test_parsing_fake,
  542. .desc = "Parsing of PMU event table metrics with fake PMUs",
  543. },
  544. };
  545. const char *test__pmu_events_subtest_get_desc(int subtest)
  546. {
  547. if (subtest < 0 ||
  548. subtest >= (int)ARRAY_SIZE(pmu_events_testcase_table))
  549. return NULL;
  550. return pmu_events_testcase_table[subtest].desc;
  551. }
  552. const char *test__pmu_events_subtest_skip_reason(int subtest)
  553. {
  554. if (subtest < 0 ||
  555. subtest >= (int)ARRAY_SIZE(pmu_events_testcase_table))
  556. return NULL;
  557. if (pmu_events_testcase_table[subtest].func != test_parsing)
  558. return NULL;
  559. return "some metrics failed";
  560. }
  561. int test__pmu_events_subtest_get_nr(void)
  562. {
  563. return (int)ARRAY_SIZE(pmu_events_testcase_table);
  564. }
  565. int test__pmu_events(struct test *test __maybe_unused, int subtest)
  566. {
  567. if (subtest < 0 ||
  568. subtest >= (int)ARRAY_SIZE(pmu_events_testcase_table))
  569. return TEST_FAIL;
  570. return pmu_events_testcase_table[subtest].func();
  571. }