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