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 #include "metricgroup.h" 16 17 struct perf_pmu_test_event { 18 /* used for matching against events from generated pmu-events.c */ 19 struct pmu_event event; 20 21 /* used for matching against event aliases */ 22 /* extra events for aliases */ 23 const char *alias_str; 24 25 /* 26 * Note: For when PublicDescription does not exist in the JSON, we 27 * will have no long_desc in pmu_event.long_desc, but long_desc may 28 * be set in the alias. 29 */ 30 const char *alias_long_desc; 31 32 /* PMU which we should match against */ 33 const char *matching_pmu; 34 }; 35 36 struct perf_pmu_test_pmu { 37 struct perf_pmu pmu; 38 struct perf_pmu_test_event const *aliases[10]; 39 }; 40 41 static const struct perf_pmu_test_event bp_l1_btb_correct = { 42 .event = { 43 .name = "bp_l1_btb_correct", 44 .event = "event=0x8a", 45 .desc = "L1 BTB Correction", 46 .topic = "branch", 47 }, 48 .alias_str = "event=0x8a", 49 .alias_long_desc = "L1 BTB Correction", 50 }; 51 52 static const struct perf_pmu_test_event bp_l2_btb_correct = { 53 .event = { 54 .name = "bp_l2_btb_correct", 55 .event = "event=0x8b", 56 .desc = "L2 BTB Correction", 57 .topic = "branch", 58 }, 59 .alias_str = "event=0x8b", 60 .alias_long_desc = "L2 BTB Correction", 61 }; 62 63 static const struct perf_pmu_test_event segment_reg_loads_any = { 64 .event = { 65 .name = "segment_reg_loads.any", 66 .event = "umask=0x80,period=200000,event=0x6", 67 .desc = "Number of segment register loads", 68 .topic = "other", 69 }, 70 .alias_str = "umask=0x80,period=0x30d40,event=0x6", 71 .alias_long_desc = "Number of segment register loads", 72 }; 73 74 static const struct perf_pmu_test_event dispatch_blocked_any = { 75 .event = { 76 .name = "dispatch_blocked.any", 77 .event = "umask=0x20,period=200000,event=0x9", 78 .desc = "Memory cluster signals to block micro-op dispatch for any reason", 79 .topic = "other", 80 }, 81 .alias_str = "umask=0x20,period=0x30d40,event=0x9", 82 .alias_long_desc = "Memory cluster signals to block micro-op dispatch for any reason", 83 }; 84 85 static const struct perf_pmu_test_event eist_trans = { 86 .event = { 87 .name = "eist_trans", 88 .event = "umask=0x0,period=200000,event=0x3a", 89 .desc = "Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions", 90 .topic = "other", 91 }, 92 .alias_str = "umask=0,period=0x30d40,event=0x3a", 93 .alias_long_desc = "Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions", 94 }; 95 96 static const struct perf_pmu_test_event l3_cache_rd = { 97 .event = { 98 .name = "l3_cache_rd", 99 .event = "event=0x40", 100 .desc = "L3 cache access, read", 101 .long_desc = "Attributable Level 3 cache access, read", 102 .topic = "cache", 103 }, 104 .alias_str = "event=0x40", 105 .alias_long_desc = "Attributable Level 3 cache access, read", 106 }; 107 108 static const struct perf_pmu_test_event *core_events[] = { 109 &bp_l1_btb_correct, 110 &bp_l2_btb_correct, 111 &segment_reg_loads_any, 112 &dispatch_blocked_any, 113 &eist_trans, 114 &l3_cache_rd, 115 NULL 116 }; 117 118 static const struct perf_pmu_test_event uncore_hisi_ddrc_flux_wcmd = { 119 .event = { 120 .name = "uncore_hisi_ddrc.flux_wcmd", 121 .event = "event=0x2", 122 .desc = "DDRC write commands. Unit: hisi_sccl,ddrc ", 123 .topic = "uncore", 124 .long_desc = "DDRC write commands", 125 .pmu = "hisi_sccl,ddrc", 126 }, 127 .alias_str = "event=0x2", 128 .alias_long_desc = "DDRC write commands", 129 .matching_pmu = "hisi_sccl1_ddrc2", 130 }; 131 132 static const struct perf_pmu_test_event unc_cbo_xsnp_response_miss_eviction = { 133 .event = { 134 .name = "unc_cbo_xsnp_response.miss_eviction", 135 .event = "umask=0x81,event=0x22", 136 .desc = "Unit: uncore_cbox A cross-core snoop resulted from L3 Eviction which misses in some processor core", 137 .topic = "uncore", 138 .long_desc = "A cross-core snoop resulted from L3 Eviction which misses in some processor core", 139 .pmu = "uncore_cbox", 140 }, 141 .alias_str = "umask=0x81,event=0x22", 142 .alias_long_desc = "A cross-core snoop resulted from L3 Eviction which misses in some processor core", 143 .matching_pmu = "uncore_cbox_0", 144 }; 145 146 static const struct perf_pmu_test_event uncore_hisi_l3c_rd_hit_cpipe = { 147 .event = { 148 .name = "uncore_hisi_l3c.rd_hit_cpipe", 149 .event = "event=0x7", 150 .desc = "Total read hits. Unit: hisi_sccl,l3c ", 151 .topic = "uncore", 152 .long_desc = "Total read hits", 153 .pmu = "hisi_sccl,l3c", 154 }, 155 .alias_str = "event=0x7", 156 .alias_long_desc = "Total read hits", 157 .matching_pmu = "hisi_sccl3_l3c7", 158 }; 159 160 static const struct perf_pmu_test_event uncore_imc_free_running_cache_miss = { 161 .event = { 162 .name = "uncore_imc_free_running.cache_miss", 163 .event = "event=0x12", 164 .desc = "Total cache misses. Unit: uncore_imc_free_running ", 165 .topic = "uncore", 166 .long_desc = "Total cache misses", 167 .pmu = "uncore_imc_free_running", 168 }, 169 .alias_str = "event=0x12", 170 .alias_long_desc = "Total cache misses", 171 .matching_pmu = "uncore_imc_free_running_0", 172 }; 173 174 static const struct perf_pmu_test_event uncore_imc_cache_hits = { 175 .event = { 176 .name = "uncore_imc.cache_hits", 177 .event = "event=0x34", 178 .desc = "Total cache hits. Unit: uncore_imc ", 179 .topic = "uncore", 180 .long_desc = "Total cache hits", 181 .pmu = "uncore_imc", 182 }, 183 .alias_str = "event=0x34", 184 .alias_long_desc = "Total cache hits", 185 .matching_pmu = "uncore_imc_0", 186 }; 187 188 static const struct perf_pmu_test_event *uncore_events[] = { 189 &uncore_hisi_ddrc_flux_wcmd, 190 &unc_cbo_xsnp_response_miss_eviction, 191 &uncore_hisi_l3c_rd_hit_cpipe, 192 &uncore_imc_free_running_cache_miss, 193 &uncore_imc_cache_hits, 194 NULL 195 }; 196 197 static const struct perf_pmu_test_event sys_ddr_pmu_write_cycles = { 198 .event = { 199 .name = "sys_ddr_pmu.write_cycles", 200 .event = "event=0x2b", 201 .desc = "ddr write-cycles event. Unit: uncore_sys_ddr_pmu ", 202 .topic = "uncore", 203 .pmu = "uncore_sys_ddr_pmu", 204 .compat = "v8", 205 }, 206 .alias_str = "event=0x2b", 207 .alias_long_desc = "ddr write-cycles event. Unit: uncore_sys_ddr_pmu ", 208 .matching_pmu = "uncore_sys_ddr_pmu", 209 }; 210 211 static const struct perf_pmu_test_event sys_ccn_pmu_read_cycles = { 212 .event = { 213 .name = "sys_ccn_pmu.read_cycles", 214 .event = "config=0x2c", 215 .desc = "ccn read-cycles event. Unit: uncore_sys_ccn_pmu ", 216 .topic = "uncore", 217 .pmu = "uncore_sys_ccn_pmu", 218 .compat = "0x01", 219 }, 220 .alias_str = "config=0x2c", 221 .alias_long_desc = "ccn read-cycles event. Unit: uncore_sys_ccn_pmu ", 222 .matching_pmu = "uncore_sys_ccn_pmu", 223 }; 224 225 static const struct perf_pmu_test_event *sys_events[] = { 226 &sys_ddr_pmu_write_cycles, 227 &sys_ccn_pmu_read_cycles, 228 NULL 229 }; 230 231 static bool is_same(const char *reference, const char *test) 232 { 233 if (!reference && !test) 234 return true; 235 236 if (reference && !test) 237 return false; 238 239 if (!reference && test) 240 return false; 241 242 return !strcmp(reference, test); 243 } 244 245 static const struct pmu_events_map *__test_pmu_get_events_map(void) 246 { 247 const struct pmu_events_map *map; 248 249 for (map = &pmu_events_map[0]; map->cpuid; map++) { 250 if (!strcmp(map->cpuid, "testcpu")) 251 return map; 252 } 253 254 pr_err("could not find test events map\n"); 255 256 return NULL; 257 } 258 259 static const struct pmu_event *__test_pmu_get_sys_events_table(void) 260 { 261 const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; 262 263 for ( ; tables->name; tables++) { 264 if (!strcmp("pme_test_soc_sys", tables->name)) 265 return tables->table; 266 } 267 268 return NULL; 269 } 270 271 static int compare_pmu_events(const struct pmu_event *e1, const struct pmu_event *e2) 272 { 273 if (!is_same(e1->name, e2->name)) { 274 pr_debug2("testing event e1 %s: mismatched name string, %s vs %s\n", 275 e1->name, e1->name, e2->name); 276 return -1; 277 } 278 279 if (!is_same(e1->compat, e2->compat)) { 280 pr_debug2("testing event e1 %s: mismatched compat string, %s vs %s\n", 281 e1->name, e1->compat, e2->compat); 282 return -1; 283 } 284 285 if (!is_same(e1->event, e2->event)) { 286 pr_debug2("testing event e1 %s: mismatched event, %s vs %s\n", 287 e1->name, e1->event, e2->event); 288 return -1; 289 } 290 291 if (!is_same(e1->desc, e2->desc)) { 292 pr_debug2("testing event e1 %s: mismatched desc, %s vs %s\n", 293 e1->name, e1->desc, e2->desc); 294 return -1; 295 } 296 297 if (!is_same(e1->topic, e2->topic)) { 298 pr_debug2("testing event e1 %s: mismatched topic, %s vs %s\n", 299 e1->name, e1->topic, e2->topic); 300 return -1; 301 } 302 303 if (!is_same(e1->long_desc, e2->long_desc)) { 304 pr_debug2("testing event e1 %s: mismatched long_desc, %s vs %s\n", 305 e1->name, e1->long_desc, e2->long_desc); 306 return -1; 307 } 308 309 if (!is_same(e1->pmu, e2->pmu)) { 310 pr_debug2("testing event e1 %s: mismatched pmu string, %s vs %s\n", 311 e1->name, e1->pmu, e2->pmu); 312 return -1; 313 } 314 315 if (!is_same(e1->unit, e2->unit)) { 316 pr_debug2("testing event e1 %s: mismatched unit, %s vs %s\n", 317 e1->name, e1->unit, e2->unit); 318 return -1; 319 } 320 321 if (!is_same(e1->perpkg, e2->perpkg)) { 322 pr_debug2("testing event e1 %s: mismatched perpkg, %s vs %s\n", 323 e1->name, e1->perpkg, e2->perpkg); 324 return -1; 325 } 326 327 if (!is_same(e1->aggr_mode, e2->aggr_mode)) { 328 pr_debug2("testing event e1 %s: mismatched aggr_mode, %s vs %s\n", 329 e1->name, e1->aggr_mode, e2->aggr_mode); 330 return -1; 331 } 332 333 if (!is_same(e1->metric_expr, e2->metric_expr)) { 334 pr_debug2("testing event e1 %s: mismatched metric_expr, %s vs %s\n", 335 e1->name, e1->metric_expr, e2->metric_expr); 336 return -1; 337 } 338 339 if (!is_same(e1->metric_name, e2->metric_name)) { 340 pr_debug2("testing event e1 %s: mismatched metric_name, %s vs %s\n", 341 e1->name, e1->metric_name, e2->metric_name); 342 return -1; 343 } 344 345 if (!is_same(e1->metric_group, e2->metric_group)) { 346 pr_debug2("testing event e1 %s: mismatched metric_group, %s vs %s\n", 347 e1->name, e1->metric_group, e2->metric_group); 348 return -1; 349 } 350 351 if (!is_same(e1->deprecated, e2->deprecated)) { 352 pr_debug2("testing event e1 %s: mismatched deprecated, %s vs %s\n", 353 e1->name, e1->deprecated, e2->deprecated); 354 return -1; 355 } 356 357 if (!is_same(e1->metric_constraint, e2->metric_constraint)) { 358 pr_debug2("testing event e1 %s: mismatched metric_constant, %s vs %s\n", 359 e1->name, e1->metric_constraint, e2->metric_constraint); 360 return -1; 361 } 362 363 return 0; 364 } 365 366 static int compare_alias_to_test_event(struct perf_pmu_alias *alias, 367 struct perf_pmu_test_event const *test_event, 368 char const *pmu_name) 369 { 370 struct pmu_event const *event = &test_event->event; 371 372 /* An alias was found, ensure everything is in order */ 373 if (!is_same(alias->name, event->name)) { 374 pr_debug("testing aliases PMU %s: mismatched name, %s vs %s\n", 375 pmu_name, alias->name, event->name); 376 return -1; 377 } 378 379 if (!is_same(alias->desc, event->desc)) { 380 pr_debug("testing aliases PMU %s: mismatched desc, %s vs %s\n", 381 pmu_name, alias->desc, event->desc); 382 return -1; 383 } 384 385 if (!is_same(alias->long_desc, test_event->alias_long_desc)) { 386 pr_debug("testing aliases PMU %s: mismatched long_desc, %s vs %s\n", 387 pmu_name, alias->long_desc, 388 test_event->alias_long_desc); 389 return -1; 390 } 391 392 if (!is_same(alias->topic, event->topic)) { 393 pr_debug("testing aliases PMU %s: mismatched topic, %s vs %s\n", 394 pmu_name, alias->topic, event->topic); 395 return -1; 396 } 397 398 if (!is_same(alias->str, test_event->alias_str)) { 399 pr_debug("testing aliases PMU %s: mismatched str, %s vs %s\n", 400 pmu_name, alias->str, test_event->alias_str); 401 return -1; 402 } 403 404 if (!is_same(alias->long_desc, test_event->alias_long_desc)) { 405 pr_debug("testing aliases PMU %s: mismatched long desc, %s vs %s\n", 406 pmu_name, alias->str, test_event->alias_long_desc); 407 return -1; 408 } 409 410 411 if (!is_same(alias->pmu_name, test_event->event.pmu)) { 412 pr_debug("testing aliases PMU %s: mismatched pmu_name, %s vs %s\n", 413 pmu_name, alias->pmu_name, test_event->event.pmu); 414 return -1; 415 } 416 417 return 0; 418 } 419 420 /* Verify generated events from pmu-events.c are as expected */ 421 static int test__pmu_event_table(struct test_suite *test __maybe_unused, 422 int subtest __maybe_unused) 423 { 424 const struct pmu_event *sys_event_tables = __test_pmu_get_sys_events_table(); 425 const struct pmu_events_map *map = __test_pmu_get_events_map(); 426 const struct pmu_event *table; 427 int map_events = 0, expected_events; 428 429 /* ignore 3x sentinels */ 430 expected_events = ARRAY_SIZE(core_events) + 431 ARRAY_SIZE(uncore_events) + 432 ARRAY_SIZE(sys_events) - 3; 433 434 if (!map || !sys_event_tables) 435 return -1; 436 437 for (table = map->table; table->name; table++) { 438 struct perf_pmu_test_event const **test_event_table; 439 bool found = false; 440 441 if (table->pmu) 442 test_event_table = &uncore_events[0]; 443 else 444 test_event_table = &core_events[0]; 445 446 for (; *test_event_table; test_event_table++) { 447 struct perf_pmu_test_event const *test_event = *test_event_table; 448 struct pmu_event const *event = &test_event->event; 449 450 if (strcmp(table->name, event->name)) 451 continue; 452 found = true; 453 map_events++; 454 455 if (compare_pmu_events(table, event)) 456 return -1; 457 458 pr_debug("testing event table %s: pass\n", table->name); 459 } 460 461 if (!found) { 462 pr_err("testing event table: could not find event %s\n", 463 table->name); 464 return -1; 465 } 466 } 467 468 for (table = sys_event_tables; table->name; table++) { 469 struct perf_pmu_test_event const **test_event_table; 470 bool found = false; 471 472 test_event_table = &sys_events[0]; 473 474 for (; *test_event_table; test_event_table++) { 475 struct perf_pmu_test_event const *test_event = *test_event_table; 476 struct pmu_event const *event = &test_event->event; 477 478 if (strcmp(table->name, event->name)) 479 continue; 480 found = true; 481 map_events++; 482 483 if (compare_pmu_events(table, event)) 484 return -1; 485 486 pr_debug("testing sys event table %s: pass\n", table->name); 487 } 488 if (!found) { 489 pr_debug("testing event table: could not find event %s\n", 490 table->name); 491 return -1; 492 } 493 } 494 495 if (map_events != expected_events) { 496 pr_err("testing event table: found %d, but expected %d\n", 497 map_events, expected_events); 498 return -1; 499 } 500 501 return 0; 502 } 503 504 static struct perf_pmu_alias *find_alias(const char *test_event, struct list_head *aliases) 505 { 506 struct perf_pmu_alias *alias; 507 508 list_for_each_entry(alias, aliases, list) 509 if (!strcmp(test_event, alias->name)) 510 return alias; 511 512 return NULL; 513 } 514 515 /* Verify aliases are as expected */ 516 static int __test_core_pmu_event_aliases(char *pmu_name, int *count) 517 { 518 struct perf_pmu_test_event const **test_event_table; 519 struct perf_pmu *pmu; 520 LIST_HEAD(aliases); 521 int res = 0; 522 const struct pmu_events_map *map = __test_pmu_get_events_map(); 523 struct perf_pmu_alias *a, *tmp; 524 525 if (!map) 526 return -1; 527 528 test_event_table = &core_events[0]; 529 530 pmu = zalloc(sizeof(*pmu)); 531 if (!pmu) 532 return -1; 533 534 pmu->name = pmu_name; 535 536 pmu_add_cpu_aliases_map(&aliases, pmu, map); 537 538 for (; *test_event_table; test_event_table++) { 539 struct perf_pmu_test_event const *test_event = *test_event_table; 540 struct pmu_event const *event = &test_event->event; 541 struct perf_pmu_alias *alias = find_alias(event->name, &aliases); 542 543 if (!alias) { 544 pr_debug("testing aliases core PMU %s: no alias, alias_table->name=%s\n", 545 pmu_name, event->name); 546 res = -1; 547 break; 548 } 549 550 if (compare_alias_to_test_event(alias, test_event, pmu_name)) { 551 res = -1; 552 break; 553 } 554 555 (*count)++; 556 pr_debug2("testing aliases core PMU %s: matched event %s\n", 557 pmu_name, alias->name); 558 } 559 560 list_for_each_entry_safe(a, tmp, &aliases, list) { 561 list_del(&a->list); 562 perf_pmu_free_alias(a); 563 } 564 free(pmu); 565 return res; 566 } 567 568 static int __test_uncore_pmu_event_aliases(struct perf_pmu_test_pmu *test_pmu) 569 { 570 int alias_count = 0, to_match_count = 0, matched_count = 0; 571 struct perf_pmu_test_event const **table; 572 struct perf_pmu *pmu = &test_pmu->pmu; 573 const char *pmu_name = pmu->name; 574 struct perf_pmu_alias *a, *tmp, *alias; 575 const struct pmu_events_map *map; 576 LIST_HEAD(aliases); 577 int res = 0; 578 579 map = __test_pmu_get_events_map(); 580 if (!map) 581 return -1; 582 pmu_add_cpu_aliases_map(&aliases, pmu, map); 583 pmu_add_sys_aliases(&aliases, pmu); 584 585 /* Count how many aliases we generated */ 586 list_for_each_entry(alias, &aliases, list) 587 alias_count++; 588 589 /* Count how many aliases we expect from the known table */ 590 for (table = &test_pmu->aliases[0]; *table; table++) 591 to_match_count++; 592 593 if (alias_count != to_match_count) { 594 pr_debug("testing aliases uncore PMU %s: mismatch expected aliases (%d) vs found (%d)\n", 595 pmu_name, to_match_count, alias_count); 596 res = -1; 597 goto out; 598 } 599 600 list_for_each_entry(alias, &aliases, list) { 601 bool matched = false; 602 603 for (table = &test_pmu->aliases[0]; *table; table++) { 604 struct perf_pmu_test_event const *test_event = *table; 605 struct pmu_event const *event = &test_event->event; 606 607 if (!strcmp(event->name, alias->name)) { 608 if (compare_alias_to_test_event(alias, 609 test_event, 610 pmu_name)) { 611 continue; 612 } 613 matched = true; 614 matched_count++; 615 } 616 } 617 618 if (matched == false) { 619 pr_debug("testing aliases uncore PMU %s: could not match alias %s\n", 620 pmu_name, alias->name); 621 res = -1; 622 goto out; 623 } 624 } 625 626 if (alias_count != matched_count) { 627 pr_debug("testing aliases uncore PMU %s: mismatch found aliases (%d) vs matched (%d)\n", 628 pmu_name, matched_count, alias_count); 629 res = -1; 630 } 631 632 out: 633 list_for_each_entry_safe(a, tmp, &aliases, list) { 634 list_del(&a->list); 635 perf_pmu_free_alias(a); 636 } 637 return res; 638 } 639 640 static struct perf_pmu_test_pmu test_pmus[] = { 641 { 642 .pmu = { 643 .name = (char *)"hisi_sccl1_ddrc2", 644 .is_uncore = 1, 645 }, 646 .aliases = { 647 &uncore_hisi_ddrc_flux_wcmd, 648 }, 649 }, 650 { 651 .pmu = { 652 .name = (char *)"uncore_cbox_0", 653 .is_uncore = 1, 654 }, 655 .aliases = { 656 &unc_cbo_xsnp_response_miss_eviction, 657 }, 658 }, 659 { 660 .pmu = { 661 .name = (char *)"hisi_sccl3_l3c7", 662 .is_uncore = 1, 663 }, 664 .aliases = { 665 &uncore_hisi_l3c_rd_hit_cpipe, 666 }, 667 }, 668 { 669 .pmu = { 670 .name = (char *)"uncore_imc_free_running_0", 671 .is_uncore = 1, 672 }, 673 .aliases = { 674 &uncore_imc_free_running_cache_miss, 675 }, 676 }, 677 { 678 .pmu = { 679 .name = (char *)"uncore_imc_0", 680 .is_uncore = 1, 681 }, 682 .aliases = { 683 &uncore_imc_cache_hits, 684 }, 685 }, 686 { 687 .pmu = { 688 .name = (char *)"uncore_sys_ddr_pmu0", 689 .is_uncore = 1, 690 .id = (char *)"v8", 691 }, 692 .aliases = { 693 &sys_ddr_pmu_write_cycles, 694 }, 695 }, 696 { 697 .pmu = { 698 .name = (char *)"uncore_sys_ccn_pmu4", 699 .is_uncore = 1, 700 .id = (char *)"0x01", 701 }, 702 .aliases = { 703 &sys_ccn_pmu_read_cycles, 704 }, 705 }, 706 }; 707 708 /* Test that aliases generated are as expected */ 709 static int test__aliases(struct test_suite *test __maybe_unused, 710 int subtest __maybe_unused) 711 { 712 struct perf_pmu *pmu = NULL; 713 unsigned long i; 714 715 while ((pmu = perf_pmu__scan(pmu)) != NULL) { 716 int count = 0; 717 718 if (!is_pmu_core(pmu->name)) 719 continue; 720 721 if (list_empty(&pmu->format)) { 722 pr_debug2("skipping testing core PMU %s\n", pmu->name); 723 continue; 724 } 725 726 if (__test_core_pmu_event_aliases(pmu->name, &count)) { 727 pr_debug("testing core PMU %s aliases: failed\n", pmu->name); 728 return -1; 729 } 730 731 if (count == 0) { 732 pr_debug("testing core PMU %s aliases: no events to match\n", 733 pmu->name); 734 return -1; 735 } 736 737 pr_debug("testing core PMU %s aliases: pass\n", pmu->name); 738 } 739 740 for (i = 0; i < ARRAY_SIZE(test_pmus); i++) { 741 int res = __test_uncore_pmu_event_aliases(&test_pmus[i]); 742 743 if (res) 744 return res; 745 } 746 747 return 0; 748 } 749 750 static bool is_number(const char *str) 751 { 752 char *end_ptr; 753 double v; 754 755 errno = 0; 756 v = strtod(str, &end_ptr); 757 (void)v; // We're not interested in this value, only if it is valid 758 return errno == 0 && end_ptr != str; 759 } 760 761 static int check_parse_id(const char *id, struct parse_events_error *error, 762 struct perf_pmu *fake_pmu) 763 { 764 struct evlist *evlist; 765 int ret; 766 char *dup, *cur; 767 768 /* Numbers are always valid. */ 769 if (is_number(id)) 770 return 0; 771 772 evlist = evlist__new(); 773 if (!evlist) 774 return -ENOMEM; 775 776 dup = strdup(id); 777 if (!dup) 778 return -ENOMEM; 779 780 for (cur = strchr(dup, '@') ; cur; cur = strchr(++cur, '@')) 781 *cur = '/'; 782 783 ret = __parse_events(evlist, dup, error, fake_pmu); 784 free(dup); 785 786 evlist__delete(evlist); 787 return ret; 788 } 789 790 static int check_parse_cpu(const char *id, bool same_cpu, const struct pmu_event *pe) 791 { 792 struct parse_events_error error; 793 int ret; 794 795 parse_events_error__init(&error); 796 ret = check_parse_id(id, &error, NULL); 797 if (ret && same_cpu) { 798 pr_warning("Parse event failed metric '%s' id '%s' expr '%s'\n", 799 pe->metric_name, id, pe->metric_expr); 800 pr_warning("Error string '%s' help '%s'\n", error.str, 801 error.help); 802 } else if (ret) { 803 pr_debug3("Parse event failed, but for an event that may not be supported by this CPU.\nid '%s' metric '%s' expr '%s'\n", 804 id, pe->metric_name, pe->metric_expr); 805 ret = 0; 806 } 807 parse_events_error__exit(&error); 808 return ret; 809 } 810 811 static int check_parse_fake(const char *id) 812 { 813 struct parse_events_error error; 814 int ret; 815 816 parse_events_error__init(&error); 817 ret = check_parse_id(id, &error, &perf_pmu__fake); 818 parse_events_error__exit(&error); 819 return ret; 820 } 821 822 static void expr_failure(const char *msg, 823 const struct pmu_events_map *map, 824 const struct pmu_event *pe) 825 { 826 pr_debug("%s for map %s %s %s\n", 827 msg, map->cpuid, map->version, map->type); 828 pr_debug("On metric %s\n", pe->metric_name); 829 pr_debug("On expression %s\n", pe->metric_expr); 830 } 831 832 struct metric { 833 struct list_head list; 834 struct metric_ref metric_ref; 835 }; 836 837 static int resolve_metric_simple(struct expr_parse_ctx *pctx, 838 struct list_head *compound_list, 839 const struct pmu_events_map *map, 840 const char *metric_name) 841 { 842 struct hashmap_entry *cur, *cur_tmp; 843 struct metric *metric, *tmp; 844 size_t bkt; 845 bool all; 846 int rc; 847 848 do { 849 all = true; 850 hashmap__for_each_entry_safe(pctx->ids, cur, cur_tmp, bkt) { 851 struct metric_ref *ref; 852 const struct pmu_event *pe; 853 854 pe = metricgroup__find_metric(cur->key, map); 855 if (!pe) 856 continue; 857 858 if (!strcmp(metric_name, (char *)cur->key)) { 859 pr_warning("Recursion detected for metric %s\n", metric_name); 860 rc = -1; 861 goto out_err; 862 } 863 864 all = false; 865 866 /* The metric key itself needs to go out.. */ 867 expr__del_id(pctx, cur->key); 868 869 metric = malloc(sizeof(*metric)); 870 if (!metric) { 871 rc = -ENOMEM; 872 goto out_err; 873 } 874 875 ref = &metric->metric_ref; 876 ref->metric_name = pe->metric_name; 877 ref->metric_expr = pe->metric_expr; 878 list_add_tail(&metric->list, compound_list); 879 880 rc = expr__find_ids(pe->metric_expr, NULL, pctx); 881 if (rc) 882 goto out_err; 883 break; /* The hashmap has been modified, so restart */ 884 } 885 } while (!all); 886 887 return 0; 888 889 out_err: 890 list_for_each_entry_safe(metric, tmp, compound_list, list) 891 free(metric); 892 893 return rc; 894 895 } 896 897 static int test__parsing(struct test_suite *test __maybe_unused, 898 int subtest __maybe_unused) 899 { 900 const struct pmu_events_map *cpus_map = pmu_events_map__find(); 901 const struct pmu_events_map *map; 902 const struct pmu_event *pe; 903 int i, j, k; 904 int ret = 0; 905 struct expr_parse_ctx *ctx; 906 double result; 907 908 ctx = expr__ctx_new(); 909 if (!ctx) { 910 pr_debug("expr__ctx_new failed"); 911 return TEST_FAIL; 912 } 913 i = 0; 914 for (;;) { 915 map = &pmu_events_map[i++]; 916 if (!map->table) 917 break; 918 j = 0; 919 for (;;) { 920 struct metric *metric, *tmp; 921 struct hashmap_entry *cur; 922 LIST_HEAD(compound_list); 923 size_t bkt; 924 925 pe = &map->table[j++]; 926 if (!pe->name && !pe->metric_group && !pe->metric_name) 927 break; 928 if (!pe->metric_expr) 929 continue; 930 expr__ctx_clear(ctx); 931 if (expr__find_ids(pe->metric_expr, NULL, ctx) < 0) { 932 expr_failure("Parse find ids failed", map, pe); 933 ret++; 934 continue; 935 } 936 937 if (resolve_metric_simple(ctx, &compound_list, map, 938 pe->metric_name)) { 939 expr_failure("Could not resolve metrics", map, pe); 940 ret++; 941 goto exit; /* Don't tolerate errors due to severity */ 942 } 943 944 /* 945 * Add all ids with a made up value. The value may 946 * trigger divide by zero when subtracted and so try to 947 * make them unique. 948 */ 949 k = 1; 950 hashmap__for_each_entry(ctx->ids, cur, bkt) 951 expr__add_id_val(ctx, strdup(cur->key), k++); 952 953 hashmap__for_each_entry(ctx->ids, cur, bkt) { 954 if (check_parse_cpu(cur->key, map == cpus_map, 955 pe)) 956 ret++; 957 } 958 959 list_for_each_entry_safe(metric, tmp, &compound_list, list) { 960 expr__add_ref(ctx, &metric->metric_ref); 961 free(metric); 962 } 963 964 if (expr__parse(&result, ctx, pe->metric_expr)) { 965 expr_failure("Parse failed", map, pe); 966 ret++; 967 } 968 } 969 } 970 expr__ctx_free(ctx); 971 /* TODO: fail when not ok */ 972 exit: 973 return ret == 0 ? TEST_OK : TEST_SKIP; 974 } 975 976 struct test_metric { 977 const char *str; 978 }; 979 980 static struct test_metric metrics[] = { 981 { "(unc_p_power_state_occupancy.cores_c0 / unc_p_clockticks) * 100." }, 982 { "imx8_ddr0@read\\-cycles@ * 4 * 4", }, 983 { "imx8_ddr0@axid\\-read\\,axi_mask\\=0xffff\\,axi_id\\=0x0000@ * 4", }, 984 { "(cstate_pkg@c2\\-residency@ / msr@tsc@) * 100", }, 985 { "(imx8_ddr0@read\\-cycles@ + imx8_ddr0@write\\-cycles@)", }, 986 }; 987 988 static int metric_parse_fake(const char *str) 989 { 990 struct expr_parse_ctx *ctx; 991 struct hashmap_entry *cur; 992 double result; 993 int ret = -1; 994 size_t bkt; 995 int i; 996 997 pr_debug("parsing '%s'\n", str); 998 999 ctx = expr__ctx_new(); 1000 if (!ctx) { 1001 pr_debug("expr__ctx_new failed"); 1002 return TEST_FAIL; 1003 } 1004 if (expr__find_ids(str, NULL, ctx) < 0) { 1005 pr_err("expr__find_ids failed\n"); 1006 return -1; 1007 } 1008 1009 /* 1010 * Add all ids with a made up value. The value may 1011 * trigger divide by zero when subtracted and so try to 1012 * make them unique. 1013 */ 1014 i = 1; 1015 hashmap__for_each_entry(ctx->ids, cur, bkt) 1016 expr__add_id_val(ctx, strdup(cur->key), i++); 1017 1018 hashmap__for_each_entry(ctx->ids, cur, bkt) { 1019 if (check_parse_fake(cur->key)) { 1020 pr_err("check_parse_fake failed\n"); 1021 goto out; 1022 } 1023 } 1024 1025 if (expr__parse(&result, ctx, str)) 1026 pr_err("expr__parse failed\n"); 1027 else 1028 ret = 0; 1029 1030 out: 1031 expr__ctx_free(ctx); 1032 return ret; 1033 } 1034 1035 /* 1036 * Parse all the metrics for current architecture, 1037 * or all defined cpus via the 'fake_pmu' 1038 * in parse_events. 1039 */ 1040 static int test__parsing_fake(struct test_suite *test __maybe_unused, 1041 int subtest __maybe_unused) 1042 { 1043 const struct pmu_events_map *map; 1044 const struct pmu_event *pe; 1045 unsigned int i, j; 1046 int err = 0; 1047 1048 for (i = 0; i < ARRAY_SIZE(metrics); i++) { 1049 err = metric_parse_fake(metrics[i].str); 1050 if (err) 1051 return err; 1052 } 1053 1054 i = 0; 1055 for (;;) { 1056 map = &pmu_events_map[i++]; 1057 if (!map->table) 1058 break; 1059 j = 0; 1060 for (;;) { 1061 pe = &map->table[j++]; 1062 if (!pe->name && !pe->metric_group && !pe->metric_name) 1063 break; 1064 if (!pe->metric_expr) 1065 continue; 1066 err = metric_parse_fake(pe->metric_expr); 1067 if (err) 1068 return err; 1069 } 1070 } 1071 1072 return 0; 1073 } 1074 1075 static struct test_case pmu_events_tests[] = { 1076 TEST_CASE("PMU event table sanity", pmu_event_table), 1077 TEST_CASE("PMU event map aliases", aliases), 1078 TEST_CASE_REASON("Parsing of PMU event table metrics", parsing, 1079 "some metrics failed"), 1080 TEST_CASE("Parsing of PMU event table metrics with fake PMUs", parsing_fake), 1081 { .name = NULL, } 1082 }; 1083 1084 struct test_suite suite__pmu_events = { 1085 .desc = "PMU events", 1086 .test_cases = pmu_events_tests, 1087 }; 1088