1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2017, Intel Corporation. 4 */ 5 6 /* Manage metrics and groups of metrics from JSON files */ 7 8 #include "metricgroup.h" 9 #include "debug.h" 10 #include "evlist.h" 11 #include "evsel.h" 12 #include "strbuf.h" 13 #include "pmu.h" 14 #include "expr.h" 15 #include "rblist.h" 16 #include <string.h> 17 #include <errno.h> 18 #include "pmu-events/pmu-events.h" 19 #include "strlist.h" 20 #include <assert.h> 21 #include <linux/ctype.h> 22 #include <linux/string.h> 23 #include <linux/zalloc.h> 24 #include <subcmd/parse-options.h> 25 #include <api/fs/fs.h> 26 #include "util.h" 27 28 struct metric_event *metricgroup__lookup(struct rblist *metric_events, 29 struct evsel *evsel, 30 bool create) 31 { 32 struct rb_node *nd; 33 struct metric_event me = { 34 .evsel = evsel 35 }; 36 37 if (!metric_events) 38 return NULL; 39 40 nd = rblist__find(metric_events, &me); 41 if (nd) 42 return container_of(nd, struct metric_event, nd); 43 if (create) { 44 rblist__add_node(metric_events, &me); 45 nd = rblist__find(metric_events, &me); 46 if (nd) 47 return container_of(nd, struct metric_event, nd); 48 } 49 return NULL; 50 } 51 52 static int metric_event_cmp(struct rb_node *rb_node, const void *entry) 53 { 54 struct metric_event *a = container_of(rb_node, 55 struct metric_event, 56 nd); 57 const struct metric_event *b = entry; 58 59 if (a->evsel == b->evsel) 60 return 0; 61 if ((char *)a->evsel < (char *)b->evsel) 62 return -1; 63 return +1; 64 } 65 66 static struct rb_node *metric_event_new(struct rblist *rblist __maybe_unused, 67 const void *entry) 68 { 69 struct metric_event *me = malloc(sizeof(struct metric_event)); 70 71 if (!me) 72 return NULL; 73 memcpy(me, entry, sizeof(struct metric_event)); 74 me->evsel = ((struct metric_event *)entry)->evsel; 75 INIT_LIST_HEAD(&me->head); 76 return &me->nd; 77 } 78 79 static void metricgroup__rblist_init(struct rblist *metric_events) 80 { 81 rblist__init(metric_events); 82 metric_events->node_cmp = metric_event_cmp; 83 metric_events->node_new = metric_event_new; 84 } 85 86 struct egroup { 87 struct list_head nd; 88 struct expr_parse_ctx pctx; 89 const char *metric_name; 90 const char *metric_expr; 91 const char *metric_unit; 92 int runtime; 93 bool has_constraint; 94 }; 95 96 /** 97 * Find a group of events in perf_evlist that correpond to those from a parsed 98 * metric expression. Note, as find_evsel_group is called in the same order as 99 * perf_evlist was constructed, metric_no_merge doesn't need to test for 100 * underfilling a group. 101 * @perf_evlist: a list of events something like: {metric1 leader, metric1 102 * sibling, metric1 sibling}:W,duration_time,{metric2 leader, metric2 sibling, 103 * metric2 sibling}:W,duration_time 104 * @pctx: the parse context for the metric expression. 105 * @metric_no_merge: don't attempt to share events for the metric with other 106 * metrics. 107 * @has_constraint: is there a contraint on the group of events? In which case 108 * the events won't be grouped. 109 * @metric_events: out argument, null terminated array of evsel's associated 110 * with the metric. 111 * @evlist_used: in/out argument, bitmap tracking which evlist events are used. 112 * @return the first metric event or NULL on failure. 113 */ 114 static struct evsel *find_evsel_group(struct evlist *perf_evlist, 115 struct expr_parse_ctx *pctx, 116 bool metric_no_merge, 117 bool has_constraint, 118 struct evsel **metric_events, 119 unsigned long *evlist_used) 120 { 121 struct evsel *ev, *current_leader = NULL; 122 double *val_ptr; 123 int i = 0, matched_events = 0, events_to_match; 124 const int idnum = (int)hashmap__size(&pctx->ids); 125 126 /* duration_time is grouped separately. */ 127 if (!has_constraint && 128 hashmap__find(&pctx->ids, "duration_time", (void **)&val_ptr)) 129 events_to_match = idnum - 1; 130 else 131 events_to_match = idnum; 132 133 evlist__for_each_entry (perf_evlist, ev) { 134 /* 135 * Events with a constraint aren't grouped and match the first 136 * events available. 137 */ 138 if (has_constraint && ev->weak_group) 139 continue; 140 /* Ignore event if already used and merging is disabled. */ 141 if (metric_no_merge && test_bit(ev->idx, evlist_used)) 142 continue; 143 if (!has_constraint && ev->leader != current_leader) { 144 /* 145 * Start of a new group, discard the whole match and 146 * start again. 147 */ 148 matched_events = 0; 149 memset(metric_events, 0, 150 sizeof(struct evsel *) * idnum); 151 current_leader = ev->leader; 152 } 153 if (hashmap__find(&pctx->ids, ev->name, (void **)&val_ptr)) { 154 if (has_constraint) { 155 /* 156 * Events aren't grouped, ensure the same event 157 * isn't matched from two groups. 158 */ 159 for (i = 0; i < matched_events; i++) { 160 if (!strcmp(ev->name, 161 metric_events[i]->name)) { 162 break; 163 } 164 } 165 if (i != matched_events) 166 continue; 167 } 168 metric_events[matched_events++] = ev; 169 } 170 if (matched_events == events_to_match) 171 break; 172 } 173 174 if (events_to_match != idnum) { 175 /* Add the first duration_time. */ 176 evlist__for_each_entry(perf_evlist, ev) { 177 if (!strcmp(ev->name, "duration_time")) { 178 metric_events[matched_events++] = ev; 179 break; 180 } 181 } 182 } 183 184 if (matched_events != idnum) { 185 /* Not whole match */ 186 return NULL; 187 } 188 189 metric_events[idnum] = NULL; 190 191 for (i = 0; i < idnum; i++) { 192 ev = metric_events[i]; 193 ev->metric_leader = ev; 194 set_bit(ev->idx, evlist_used); 195 } 196 197 return metric_events[0]; 198 } 199 200 static int metricgroup__setup_events(struct list_head *groups, 201 bool metric_no_merge, 202 struct evlist *perf_evlist, 203 struct rblist *metric_events_list) 204 { 205 struct metric_event *me; 206 struct metric_expr *expr; 207 int i = 0; 208 int ret = 0; 209 struct egroup *eg; 210 struct evsel *evsel, *tmp; 211 unsigned long *evlist_used; 212 213 evlist_used = bitmap_alloc(perf_evlist->core.nr_entries); 214 if (!evlist_used) 215 return -ENOMEM; 216 217 list_for_each_entry (eg, groups, nd) { 218 struct evsel **metric_events; 219 220 metric_events = calloc(sizeof(void *), 221 hashmap__size(&eg->pctx.ids) + 1); 222 if (!metric_events) { 223 ret = -ENOMEM; 224 break; 225 } 226 evsel = find_evsel_group(perf_evlist, &eg->pctx, 227 metric_no_merge, 228 eg->has_constraint, metric_events, 229 evlist_used); 230 if (!evsel) { 231 pr_debug("Cannot resolve %s: %s\n", 232 eg->metric_name, eg->metric_expr); 233 free(metric_events); 234 continue; 235 } 236 for (i = 0; metric_events[i]; i++) 237 metric_events[i]->collect_stat = true; 238 me = metricgroup__lookup(metric_events_list, evsel, true); 239 if (!me) { 240 ret = -ENOMEM; 241 free(metric_events); 242 break; 243 } 244 expr = malloc(sizeof(struct metric_expr)); 245 if (!expr) { 246 ret = -ENOMEM; 247 free(metric_events); 248 break; 249 } 250 expr->metric_expr = eg->metric_expr; 251 expr->metric_name = eg->metric_name; 252 expr->metric_unit = eg->metric_unit; 253 expr->metric_events = metric_events; 254 expr->runtime = eg->runtime; 255 list_add(&expr->nd, &me->head); 256 } 257 258 evlist__for_each_entry_safe(perf_evlist, tmp, evsel) { 259 if (!test_bit(evsel->idx, evlist_used)) { 260 evlist__remove(perf_evlist, evsel); 261 evsel__delete(evsel); 262 } 263 } 264 bitmap_free(evlist_used); 265 266 return ret; 267 } 268 269 static bool match_metric(const char *n, const char *list) 270 { 271 int len; 272 char *m; 273 274 if (!list) 275 return false; 276 if (!strcmp(list, "all")) 277 return true; 278 if (!n) 279 return !strcasecmp(list, "No_group"); 280 len = strlen(list); 281 m = strcasestr(n, list); 282 if (!m) 283 return false; 284 if ((m == n || m[-1] == ';' || m[-1] == ' ') && 285 (m[len] == 0 || m[len] == ';')) 286 return true; 287 return false; 288 } 289 290 struct mep { 291 struct rb_node nd; 292 const char *name; 293 struct strlist *metrics; 294 }; 295 296 static int mep_cmp(struct rb_node *rb_node, const void *entry) 297 { 298 struct mep *a = container_of(rb_node, struct mep, nd); 299 struct mep *b = (struct mep *)entry; 300 301 return strcmp(a->name, b->name); 302 } 303 304 static struct rb_node *mep_new(struct rblist *rl __maybe_unused, 305 const void *entry) 306 { 307 struct mep *me = malloc(sizeof(struct mep)); 308 309 if (!me) 310 return NULL; 311 memcpy(me, entry, sizeof(struct mep)); 312 me->name = strdup(me->name); 313 if (!me->name) 314 goto out_me; 315 me->metrics = strlist__new(NULL, NULL); 316 if (!me->metrics) 317 goto out_name; 318 return &me->nd; 319 out_name: 320 zfree(&me->name); 321 out_me: 322 free(me); 323 return NULL; 324 } 325 326 static struct mep *mep_lookup(struct rblist *groups, const char *name) 327 { 328 struct rb_node *nd; 329 struct mep me = { 330 .name = name 331 }; 332 nd = rblist__find(groups, &me); 333 if (nd) 334 return container_of(nd, struct mep, nd); 335 rblist__add_node(groups, &me); 336 nd = rblist__find(groups, &me); 337 if (nd) 338 return container_of(nd, struct mep, nd); 339 return NULL; 340 } 341 342 static void mep_delete(struct rblist *rl __maybe_unused, 343 struct rb_node *nd) 344 { 345 struct mep *me = container_of(nd, struct mep, nd); 346 347 strlist__delete(me->metrics); 348 zfree(&me->name); 349 free(me); 350 } 351 352 static void metricgroup__print_strlist(struct strlist *metrics, bool raw) 353 { 354 struct str_node *sn; 355 int n = 0; 356 357 strlist__for_each_entry (sn, metrics) { 358 if (raw) 359 printf("%s%s", n > 0 ? " " : "", sn->s); 360 else 361 printf(" %s\n", sn->s); 362 n++; 363 } 364 if (raw) 365 putchar('\n'); 366 } 367 368 void metricgroup__print(bool metrics, bool metricgroups, char *filter, 369 bool raw, bool details) 370 { 371 struct pmu_events_map *map = perf_pmu__find_map(NULL); 372 struct pmu_event *pe; 373 int i; 374 struct rblist groups; 375 struct rb_node *node, *next; 376 struct strlist *metriclist = NULL; 377 378 if (!map) 379 return; 380 381 if (!metricgroups) { 382 metriclist = strlist__new(NULL, NULL); 383 if (!metriclist) 384 return; 385 } 386 387 rblist__init(&groups); 388 groups.node_new = mep_new; 389 groups.node_cmp = mep_cmp; 390 groups.node_delete = mep_delete; 391 for (i = 0; ; i++) { 392 const char *g; 393 pe = &map->table[i]; 394 395 if (!pe->name && !pe->metric_group && !pe->metric_name) 396 break; 397 if (!pe->metric_expr) 398 continue; 399 g = pe->metric_group; 400 if (!g && pe->metric_name) { 401 if (pe->name) 402 continue; 403 g = "No_group"; 404 } 405 if (g) { 406 char *omg; 407 char *mg = strdup(g); 408 409 if (!mg) 410 return; 411 omg = mg; 412 while ((g = strsep(&mg, ";")) != NULL) { 413 struct mep *me; 414 char *s; 415 416 g = skip_spaces(g); 417 if (*g == 0) 418 g = "No_group"; 419 if (filter && !strstr(g, filter)) 420 continue; 421 if (raw) 422 s = (char *)pe->metric_name; 423 else { 424 if (asprintf(&s, "%s\n%*s%s]", 425 pe->metric_name, 8, "[", pe->desc) < 0) 426 return; 427 428 if (details) { 429 if (asprintf(&s, "%s\n%*s%s]", 430 s, 8, "[", pe->metric_expr) < 0) 431 return; 432 } 433 } 434 435 if (!s) 436 continue; 437 438 if (!metricgroups) { 439 strlist__add(metriclist, s); 440 } else { 441 me = mep_lookup(&groups, g); 442 if (!me) 443 continue; 444 strlist__add(me->metrics, s); 445 } 446 } 447 free(omg); 448 } 449 } 450 451 if (metricgroups && !raw) 452 printf("\nMetric Groups:\n\n"); 453 else if (metrics && !raw) 454 printf("\nMetrics:\n\n"); 455 456 for (node = rb_first_cached(&groups.entries); node; node = next) { 457 struct mep *me = container_of(node, struct mep, nd); 458 459 if (metricgroups) 460 printf("%s%s%s", me->name, metrics && !raw ? ":" : "", raw ? " " : "\n"); 461 if (metrics) 462 metricgroup__print_strlist(me->metrics, raw); 463 next = rb_next(node); 464 rblist__remove_node(&groups, node); 465 } 466 if (!metricgroups) 467 metricgroup__print_strlist(metriclist, raw); 468 strlist__delete(metriclist); 469 } 470 471 static void metricgroup__add_metric_weak_group(struct strbuf *events, 472 struct expr_parse_ctx *ctx) 473 { 474 struct hashmap_entry *cur; 475 size_t bkt; 476 bool no_group = true, has_duration = false; 477 478 hashmap__for_each_entry((&ctx->ids), cur, bkt) { 479 pr_debug("found event %s\n", (const char *)cur->key); 480 /* 481 * Duration time maps to a software event and can make 482 * groups not count. Always use it outside a 483 * group. 484 */ 485 if (!strcmp(cur->key, "duration_time")) { 486 has_duration = true; 487 continue; 488 } 489 strbuf_addf(events, "%s%s", 490 no_group ? "{" : ",", 491 (const char *)cur->key); 492 no_group = false; 493 } 494 if (!no_group) { 495 strbuf_addf(events, "}:W"); 496 if (has_duration) 497 strbuf_addf(events, ",duration_time"); 498 } else if (has_duration) 499 strbuf_addf(events, "duration_time"); 500 } 501 502 static void metricgroup__add_metric_non_group(struct strbuf *events, 503 struct expr_parse_ctx *ctx) 504 { 505 struct hashmap_entry *cur; 506 size_t bkt; 507 bool first = true; 508 509 hashmap__for_each_entry((&ctx->ids), cur, bkt) { 510 if (!first) 511 strbuf_addf(events, ","); 512 strbuf_addf(events, "%s", (const char *)cur->key); 513 first = false; 514 } 515 } 516 517 static void metricgroup___watchdog_constraint_hint(const char *name, bool foot) 518 { 519 static bool violate_nmi_constraint; 520 521 if (!foot) { 522 pr_warning("Splitting metric group %s into standalone metrics.\n", name); 523 violate_nmi_constraint = true; 524 return; 525 } 526 527 if (!violate_nmi_constraint) 528 return; 529 530 pr_warning("Try disabling the NMI watchdog to comply NO_NMI_WATCHDOG metric constraint:\n" 531 " echo 0 > /proc/sys/kernel/nmi_watchdog\n" 532 " perf stat ...\n" 533 " echo 1 > /proc/sys/kernel/nmi_watchdog\n"); 534 } 535 536 static bool metricgroup__has_constraint(struct pmu_event *pe) 537 { 538 if (!pe->metric_constraint) 539 return false; 540 541 if (!strcmp(pe->metric_constraint, "NO_NMI_WATCHDOG") && 542 sysctl__nmi_watchdog_enabled()) { 543 metricgroup___watchdog_constraint_hint(pe->metric_name, false); 544 return true; 545 } 546 547 return false; 548 } 549 550 int __weak arch_get_runtimeparam(void) 551 { 552 return 1; 553 } 554 555 static int __metricgroup__add_metric(struct list_head *group_list, 556 struct pmu_event *pe, 557 bool metric_no_group, 558 int runtime) 559 { 560 struct egroup *eg; 561 562 eg = malloc(sizeof(*eg)); 563 if (!eg) 564 return -ENOMEM; 565 566 expr__ctx_init(&eg->pctx); 567 eg->metric_name = pe->metric_name; 568 eg->metric_expr = pe->metric_expr; 569 eg->metric_unit = pe->unit; 570 eg->runtime = runtime; 571 eg->has_constraint = metric_no_group || metricgroup__has_constraint(pe); 572 573 if (expr__find_other(pe->metric_expr, NULL, &eg->pctx, runtime) < 0) { 574 expr__ctx_clear(&eg->pctx); 575 free(eg); 576 return -EINVAL; 577 } 578 579 if (list_empty(group_list)) 580 list_add(&eg->nd, group_list); 581 else { 582 struct list_head *pos; 583 584 /* Place the largest groups at the front. */ 585 list_for_each_prev(pos, group_list) { 586 struct egroup *old = list_entry(pos, struct egroup, nd); 587 588 if (hashmap__size(&eg->pctx.ids) <= 589 hashmap__size(&old->pctx.ids)) 590 break; 591 } 592 list_add(&eg->nd, pos); 593 } 594 595 return 0; 596 } 597 598 static int metricgroup__add_metric(const char *metric, bool metric_no_group, 599 struct strbuf *events, 600 struct list_head *group_list) 601 { 602 struct pmu_events_map *map = perf_pmu__find_map(NULL); 603 struct pmu_event *pe; 604 struct egroup *eg; 605 int i, ret; 606 bool has_match = false; 607 608 if (!map) 609 return 0; 610 611 for (i = 0; ; i++) { 612 pe = &map->table[i]; 613 614 if (!pe->name && !pe->metric_group && !pe->metric_name) { 615 /* End of pmu events. */ 616 if (!has_match) 617 return -EINVAL; 618 break; 619 } 620 if (!pe->metric_expr) 621 continue; 622 if (match_metric(pe->metric_group, metric) || 623 match_metric(pe->metric_name, metric)) { 624 has_match = true; 625 pr_debug("metric expr %s for %s\n", pe->metric_expr, pe->metric_name); 626 627 if (!strstr(pe->metric_expr, "?")) { 628 ret = __metricgroup__add_metric(group_list, 629 pe, 630 metric_no_group, 631 1); 632 if (ret) 633 return ret; 634 } else { 635 int j, count; 636 637 count = arch_get_runtimeparam(); 638 639 /* This loop is added to create multiple 640 * events depend on count value and add 641 * those events to group_list. 642 */ 643 644 for (j = 0; j < count; j++) { 645 ret = __metricgroup__add_metric( 646 group_list, pe, 647 metric_no_group, j); 648 if (ret) 649 return ret; 650 } 651 } 652 } 653 } 654 list_for_each_entry(eg, group_list, nd) { 655 if (events->len > 0) 656 strbuf_addf(events, ","); 657 658 if (eg->has_constraint) { 659 metricgroup__add_metric_non_group(events, 660 &eg->pctx); 661 } else { 662 metricgroup__add_metric_weak_group(events, 663 &eg->pctx); 664 } 665 } 666 return 0; 667 } 668 669 static int metricgroup__add_metric_list(const char *list, bool metric_no_group, 670 struct strbuf *events, 671 struct list_head *group_list) 672 { 673 char *llist, *nlist, *p; 674 int ret = -EINVAL; 675 676 nlist = strdup(list); 677 if (!nlist) 678 return -ENOMEM; 679 llist = nlist; 680 681 strbuf_init(events, 100); 682 strbuf_addf(events, "%s", ""); 683 684 while ((p = strsep(&llist, ",")) != NULL) { 685 ret = metricgroup__add_metric(p, metric_no_group, events, 686 group_list); 687 if (ret == -EINVAL) { 688 fprintf(stderr, "Cannot find metric or group `%s'\n", 689 p); 690 break; 691 } 692 } 693 free(nlist); 694 695 if (!ret) 696 metricgroup___watchdog_constraint_hint(NULL, true); 697 698 return ret; 699 } 700 701 static void metricgroup__free_egroups(struct list_head *group_list) 702 { 703 struct egroup *eg, *egtmp; 704 705 list_for_each_entry_safe (eg, egtmp, group_list, nd) { 706 expr__ctx_clear(&eg->pctx); 707 list_del_init(&eg->nd); 708 free(eg); 709 } 710 } 711 712 int metricgroup__parse_groups(const struct option *opt, 713 const char *str, 714 bool metric_no_group, 715 bool metric_no_merge, 716 struct rblist *metric_events) 717 { 718 struct parse_events_error parse_error; 719 struct evlist *perf_evlist = *(struct evlist **)opt->value; 720 struct strbuf extra_events; 721 LIST_HEAD(group_list); 722 int ret; 723 724 if (metric_events->nr_entries == 0) 725 metricgroup__rblist_init(metric_events); 726 ret = metricgroup__add_metric_list(str, metric_no_group, 727 &extra_events, &group_list); 728 if (ret) 729 return ret; 730 pr_debug("adding %s\n", extra_events.buf); 731 bzero(&parse_error, sizeof(parse_error)); 732 ret = parse_events(perf_evlist, extra_events.buf, &parse_error); 733 if (ret) { 734 parse_events_print_error(&parse_error, extra_events.buf); 735 goto out; 736 } 737 strbuf_release(&extra_events); 738 ret = metricgroup__setup_events(&group_list, metric_no_merge, 739 perf_evlist, metric_events); 740 out: 741 metricgroup__free_egroups(&group_list); 742 return ret; 743 } 744 745 bool metricgroup__has_metric(const char *metric) 746 { 747 struct pmu_events_map *map = perf_pmu__find_map(NULL); 748 struct pmu_event *pe; 749 int i; 750 751 if (!map) 752 return false; 753 754 for (i = 0; ; i++) { 755 pe = &map->table[i]; 756 757 if (!pe->name && !pe->metric_group && !pe->metric_name) 758 break; 759 if (!pe->metric_expr) 760 continue; 761 if (match_metric(pe->metric_name, metric)) 762 return true; 763 } 764 return false; 765 } 766