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 "pmu-hybrid.h" 15 #include "print-events.h" 16 #include "expr.h" 17 #include "rblist.h" 18 #include <string.h> 19 #include <errno.h> 20 #include "strlist.h" 21 #include <assert.h> 22 #include <linux/ctype.h> 23 #include <linux/list_sort.h> 24 #include <linux/string.h> 25 #include <linux/zalloc.h> 26 #include <perf/cpumap.h> 27 #include <subcmd/parse-options.h> 28 #include <api/fs/fs.h> 29 #include "util.h" 30 #include <asm/bug.h> 31 #include "cgroup.h" 32 #include "util/hashmap.h" 33 34 struct metric_event *metricgroup__lookup(struct rblist *metric_events, 35 struct evsel *evsel, 36 bool create) 37 { 38 struct rb_node *nd; 39 struct metric_event me = { 40 .evsel = evsel 41 }; 42 43 if (!metric_events) 44 return NULL; 45 46 nd = rblist__find(metric_events, &me); 47 if (nd) 48 return container_of(nd, struct metric_event, nd); 49 if (create) { 50 rblist__add_node(metric_events, &me); 51 nd = rblist__find(metric_events, &me); 52 if (nd) 53 return container_of(nd, struct metric_event, nd); 54 } 55 return NULL; 56 } 57 58 static int metric_event_cmp(struct rb_node *rb_node, const void *entry) 59 { 60 struct metric_event *a = container_of(rb_node, 61 struct metric_event, 62 nd); 63 const struct metric_event *b = entry; 64 65 if (a->evsel == b->evsel) 66 return 0; 67 if ((char *)a->evsel < (char *)b->evsel) 68 return -1; 69 return +1; 70 } 71 72 static struct rb_node *metric_event_new(struct rblist *rblist __maybe_unused, 73 const void *entry) 74 { 75 struct metric_event *me = malloc(sizeof(struct metric_event)); 76 77 if (!me) 78 return NULL; 79 memcpy(me, entry, sizeof(struct metric_event)); 80 me->evsel = ((struct metric_event *)entry)->evsel; 81 INIT_LIST_HEAD(&me->head); 82 return &me->nd; 83 } 84 85 static void metric_event_delete(struct rblist *rblist __maybe_unused, 86 struct rb_node *rb_node) 87 { 88 struct metric_event *me = container_of(rb_node, struct metric_event, nd); 89 struct metric_expr *expr, *tmp; 90 91 list_for_each_entry_safe(expr, tmp, &me->head, nd) { 92 free((char *)expr->metric_name); 93 free(expr->metric_refs); 94 free(expr->metric_events); 95 free(expr); 96 } 97 98 free(me); 99 } 100 101 static void metricgroup__rblist_init(struct rblist *metric_events) 102 { 103 rblist__init(metric_events); 104 metric_events->node_cmp = metric_event_cmp; 105 metric_events->node_new = metric_event_new; 106 metric_events->node_delete = metric_event_delete; 107 } 108 109 void metricgroup__rblist_exit(struct rblist *metric_events) 110 { 111 rblist__exit(metric_events); 112 } 113 114 /** 115 * The metric under construction. The data held here will be placed in a 116 * metric_expr. 117 */ 118 struct metric { 119 struct list_head nd; 120 /** 121 * The expression parse context importantly holding the IDs contained 122 * within the expression. 123 */ 124 struct expr_parse_ctx *pctx; 125 /** The name of the metric such as "IPC". */ 126 const char *metric_name; 127 /** Modifier on the metric such as "u" or NULL for none. */ 128 const char *modifier; 129 /** The expression to parse, for example, "instructions/cycles". */ 130 const char *metric_expr; 131 /** 132 * The "ScaleUnit" that scales and adds a unit to the metric during 133 * output. 134 */ 135 const char *metric_unit; 136 /** Optional null terminated array of referenced metrics. */ 137 struct metric_ref *metric_refs; 138 /** 139 * Is there a constraint on the group of events? In which case the 140 * events won't be grouped. 141 */ 142 bool has_constraint; 143 /** 144 * Parsed events for the metric. Optional as events may be taken from a 145 * different metric whose group contains all the IDs necessary for this 146 * one. 147 */ 148 struct evlist *evlist; 149 }; 150 151 static void metricgroup___watchdog_constraint_hint(const char *name, bool foot) 152 { 153 static bool violate_nmi_constraint; 154 155 if (!foot) { 156 pr_warning("Splitting metric group %s into standalone metrics.\n", name); 157 violate_nmi_constraint = true; 158 return; 159 } 160 161 if (!violate_nmi_constraint) 162 return; 163 164 pr_warning("Try disabling the NMI watchdog to comply NO_NMI_WATCHDOG metric constraint:\n" 165 " echo 0 > /proc/sys/kernel/nmi_watchdog\n" 166 " perf stat ...\n" 167 " echo 1 > /proc/sys/kernel/nmi_watchdog\n"); 168 } 169 170 static bool metricgroup__has_constraint(const struct pmu_metric *pm) 171 { 172 if (!pm->metric_constraint) 173 return false; 174 175 if (!strcmp(pm->metric_constraint, "NO_NMI_WATCHDOG") && 176 sysctl__nmi_watchdog_enabled()) { 177 metricgroup___watchdog_constraint_hint(pm->metric_name, false); 178 return true; 179 } 180 181 return false; 182 } 183 184 static void metric__free(struct metric *m) 185 { 186 if (!m) 187 return; 188 189 free(m->metric_refs); 190 expr__ctx_free(m->pctx); 191 free((char *)m->modifier); 192 evlist__delete(m->evlist); 193 free(m); 194 } 195 196 static struct metric *metric__new(const struct pmu_metric *pm, 197 const char *modifier, 198 bool metric_no_group, 199 int runtime, 200 const char *user_requested_cpu_list, 201 bool system_wide) 202 { 203 struct metric *m; 204 205 m = zalloc(sizeof(*m)); 206 if (!m) 207 return NULL; 208 209 m->pctx = expr__ctx_new(); 210 if (!m->pctx) 211 goto out_err; 212 213 m->metric_name = pm->metric_name; 214 m->modifier = NULL; 215 if (modifier) { 216 m->modifier = strdup(modifier); 217 if (!m->modifier) 218 goto out_err; 219 } 220 m->metric_expr = pm->metric_expr; 221 m->metric_unit = pm->unit; 222 m->pctx->sctx.user_requested_cpu_list = NULL; 223 if (user_requested_cpu_list) { 224 m->pctx->sctx.user_requested_cpu_list = strdup(user_requested_cpu_list); 225 if (!m->pctx->sctx.user_requested_cpu_list) 226 goto out_err; 227 } 228 m->pctx->sctx.runtime = runtime; 229 m->pctx->sctx.system_wide = system_wide; 230 m->has_constraint = metric_no_group || metricgroup__has_constraint(pm); 231 m->metric_refs = NULL; 232 m->evlist = NULL; 233 234 return m; 235 out_err: 236 metric__free(m); 237 return NULL; 238 } 239 240 static bool contains_metric_id(struct evsel **metric_events, int num_events, 241 const char *metric_id) 242 { 243 int i; 244 245 for (i = 0; i < num_events; i++) { 246 if (!strcmp(evsel__metric_id(metric_events[i]), metric_id)) 247 return true; 248 } 249 return false; 250 } 251 252 /** 253 * setup_metric_events - Find a group of events in metric_evlist that correspond 254 * to the IDs from a parsed metric expression. 255 * @ids: the metric IDs to match. 256 * @metric_evlist: the list of perf events. 257 * @out_metric_events: holds the created metric events array. 258 */ 259 static int setup_metric_events(struct hashmap *ids, 260 struct evlist *metric_evlist, 261 struct evsel ***out_metric_events) 262 { 263 struct evsel **metric_events; 264 const char *metric_id; 265 struct evsel *ev; 266 size_t ids_size, matched_events, i; 267 268 *out_metric_events = NULL; 269 ids_size = hashmap__size(ids); 270 271 metric_events = calloc(sizeof(void *), ids_size + 1); 272 if (!metric_events) 273 return -ENOMEM; 274 275 matched_events = 0; 276 evlist__for_each_entry(metric_evlist, ev) { 277 struct expr_id_data *val_ptr; 278 279 /* 280 * Check for duplicate events with the same name. For 281 * example, uncore_imc/cas_count_read/ will turn into 6 282 * events per socket on skylakex. Only the first such 283 * event is placed in metric_events. 284 */ 285 metric_id = evsel__metric_id(ev); 286 if (contains_metric_id(metric_events, matched_events, metric_id)) 287 continue; 288 /* 289 * Does this event belong to the parse context? For 290 * combined or shared groups, this metric may not care 291 * about this event. 292 */ 293 if (hashmap__find(ids, metric_id, &val_ptr)) { 294 metric_events[matched_events++] = ev; 295 296 if (matched_events >= ids_size) 297 break; 298 } 299 } 300 if (matched_events < ids_size) { 301 free(metric_events); 302 return -EINVAL; 303 } 304 for (i = 0; i < ids_size; i++) { 305 ev = metric_events[i]; 306 ev->collect_stat = true; 307 308 /* 309 * The metric leader points to the identically named 310 * event in metric_events. 311 */ 312 ev->metric_leader = ev; 313 /* 314 * Mark two events with identical names in the same 315 * group (or globally) as being in use as uncore events 316 * may be duplicated for each pmu. Set the metric leader 317 * of such events to be the event that appears in 318 * metric_events. 319 */ 320 metric_id = evsel__metric_id(ev); 321 evlist__for_each_entry_continue(metric_evlist, ev) { 322 if (!strcmp(evsel__metric_id(ev), metric_id)) 323 ev->metric_leader = metric_events[i]; 324 } 325 } 326 *out_metric_events = metric_events; 327 return 0; 328 } 329 330 static bool match_metric(const char *n, const char *list) 331 { 332 int len; 333 char *m; 334 335 if (!list) 336 return false; 337 if (!strcmp(list, "all")) 338 return true; 339 if (!n) 340 return !strcasecmp(list, "No_group"); 341 len = strlen(list); 342 m = strcasestr(n, list); 343 if (!m) 344 return false; 345 if ((m == n || m[-1] == ';' || m[-1] == ' ') && 346 (m[len] == 0 || m[len] == ';')) 347 return true; 348 return false; 349 } 350 351 static bool match_pm_metric(const struct pmu_metric *pm, const char *metric) 352 { 353 return match_metric(pm->metric_group, metric) || 354 match_metric(pm->metric_name, metric); 355 } 356 357 /** struct mep - RB-tree node for building printing information. */ 358 struct mep { 359 /** nd - RB-tree element. */ 360 struct rb_node nd; 361 /** @metric_group: Owned metric group name, separated others with ';'. */ 362 char *metric_group; 363 const char *metric_name; 364 const char *metric_desc; 365 const char *metric_long_desc; 366 const char *metric_expr; 367 const char *metric_unit; 368 }; 369 370 static int mep_cmp(struct rb_node *rb_node, const void *entry) 371 { 372 struct mep *a = container_of(rb_node, struct mep, nd); 373 struct mep *b = (struct mep *)entry; 374 int ret; 375 376 ret = strcmp(a->metric_group, b->metric_group); 377 if (ret) 378 return ret; 379 380 return strcmp(a->metric_name, b->metric_name); 381 } 382 383 static struct rb_node *mep_new(struct rblist *rl __maybe_unused, const void *entry) 384 { 385 struct mep *me = malloc(sizeof(struct mep)); 386 387 if (!me) 388 return NULL; 389 390 memcpy(me, entry, sizeof(struct mep)); 391 return &me->nd; 392 } 393 394 static void mep_delete(struct rblist *rl __maybe_unused, 395 struct rb_node *nd) 396 { 397 struct mep *me = container_of(nd, struct mep, nd); 398 399 zfree(&me->metric_group); 400 free(me); 401 } 402 403 static struct mep *mep_lookup(struct rblist *groups, const char *metric_group, 404 const char *metric_name) 405 { 406 struct rb_node *nd; 407 struct mep me = { 408 .metric_group = strdup(metric_group), 409 .metric_name = metric_name, 410 }; 411 nd = rblist__find(groups, &me); 412 if (nd) { 413 free(me.metric_group); 414 return container_of(nd, struct mep, nd); 415 } 416 rblist__add_node(groups, &me); 417 nd = rblist__find(groups, &me); 418 if (nd) 419 return container_of(nd, struct mep, nd); 420 return NULL; 421 } 422 423 static int metricgroup__add_to_mep_groups(const struct pmu_metric *pm, 424 struct rblist *groups) 425 { 426 const char *g; 427 char *omg, *mg; 428 429 mg = strdup(pm->metric_group ?: "No_group"); 430 if (!mg) 431 return -ENOMEM; 432 omg = mg; 433 while ((g = strsep(&mg, ";")) != NULL) { 434 struct mep *me; 435 436 g = skip_spaces(g); 437 if (strlen(g)) 438 me = mep_lookup(groups, g, pm->metric_name); 439 else 440 me = mep_lookup(groups, "No_group", pm->metric_name); 441 442 if (me) { 443 me->metric_desc = pm->desc; 444 me->metric_long_desc = pm->long_desc; 445 me->metric_expr = pm->metric_expr; 446 me->metric_unit = pm->unit; 447 } 448 } 449 free(omg); 450 451 return 0; 452 } 453 454 struct metricgroup_iter_data { 455 pmu_metric_iter_fn fn; 456 void *data; 457 }; 458 459 static int metricgroup__sys_event_iter(const struct pmu_metric *pm, 460 const struct pmu_metrics_table *table, 461 void *data) 462 { 463 struct metricgroup_iter_data *d = data; 464 struct perf_pmu *pmu = NULL; 465 466 if (!pm->metric_expr || !pm->compat) 467 return 0; 468 469 while ((pmu = perf_pmu__scan(pmu))) { 470 471 if (!pmu->id || strcmp(pmu->id, pm->compat)) 472 continue; 473 474 return d->fn(pm, table, d->data); 475 } 476 return 0; 477 } 478 479 static int metricgroup__add_to_mep_groups_callback(const struct pmu_metric *pm, 480 const struct pmu_metrics_table *table __maybe_unused, 481 void *vdata) 482 { 483 struct rblist *groups = vdata; 484 485 return metricgroup__add_to_mep_groups(pm, groups); 486 } 487 488 void metricgroup__print(const struct print_callbacks *print_cb, void *print_state) 489 { 490 struct rblist groups; 491 const struct pmu_metrics_table *table; 492 struct rb_node *node, *next; 493 494 rblist__init(&groups); 495 groups.node_new = mep_new; 496 groups.node_cmp = mep_cmp; 497 groups.node_delete = mep_delete; 498 table = pmu_metrics_table__find(); 499 if (table) { 500 pmu_metrics_table_for_each_metric(table, 501 metricgroup__add_to_mep_groups_callback, 502 &groups); 503 } 504 { 505 struct metricgroup_iter_data data = { 506 .fn = metricgroup__add_to_mep_groups_callback, 507 .data = &groups, 508 }; 509 pmu_for_each_sys_metric(metricgroup__sys_event_iter, &data); 510 } 511 512 for (node = rb_first_cached(&groups.entries); node; node = next) { 513 struct mep *me = container_of(node, struct mep, nd); 514 515 print_cb->print_metric(print_state, 516 me->metric_group, 517 me->metric_name, 518 me->metric_desc, 519 me->metric_long_desc, 520 me->metric_expr, 521 me->metric_unit); 522 next = rb_next(node); 523 rblist__remove_node(&groups, node); 524 } 525 } 526 527 static const char *code_characters = ",-=@"; 528 529 static int encode_metric_id(struct strbuf *sb, const char *x) 530 { 531 char *c; 532 int ret = 0; 533 534 for (; *x; x++) { 535 c = strchr(code_characters, *x); 536 if (c) { 537 ret = strbuf_addch(sb, '!'); 538 if (ret) 539 break; 540 541 ret = strbuf_addch(sb, '0' + (c - code_characters)); 542 if (ret) 543 break; 544 } else { 545 ret = strbuf_addch(sb, *x); 546 if (ret) 547 break; 548 } 549 } 550 return ret; 551 } 552 553 static int decode_metric_id(struct strbuf *sb, const char *x) 554 { 555 const char *orig = x; 556 size_t i; 557 char c; 558 int ret; 559 560 for (; *x; x++) { 561 c = *x; 562 if (*x == '!') { 563 x++; 564 i = *x - '0'; 565 if (i > strlen(code_characters)) { 566 pr_err("Bad metric-id encoding in: '%s'", orig); 567 return -1; 568 } 569 c = code_characters[i]; 570 } 571 ret = strbuf_addch(sb, c); 572 if (ret) 573 return ret; 574 } 575 return 0; 576 } 577 578 static int decode_all_metric_ids(struct evlist *perf_evlist, const char *modifier) 579 { 580 struct evsel *ev; 581 struct strbuf sb = STRBUF_INIT; 582 char *cur; 583 int ret = 0; 584 585 evlist__for_each_entry(perf_evlist, ev) { 586 if (!ev->metric_id) 587 continue; 588 589 ret = strbuf_setlen(&sb, 0); 590 if (ret) 591 break; 592 593 ret = decode_metric_id(&sb, ev->metric_id); 594 if (ret) 595 break; 596 597 free((char *)ev->metric_id); 598 ev->metric_id = strdup(sb.buf); 599 if (!ev->metric_id) { 600 ret = -ENOMEM; 601 break; 602 } 603 /* 604 * If the name is just the parsed event, use the metric-id to 605 * give a more friendly display version. 606 */ 607 if (strstr(ev->name, "metric-id=")) { 608 bool has_slash = false; 609 610 free(ev->name); 611 for (cur = strchr(sb.buf, '@') ; cur; cur = strchr(++cur, '@')) { 612 *cur = '/'; 613 has_slash = true; 614 } 615 616 if (modifier) { 617 if (!has_slash && !strchr(sb.buf, ':')) { 618 ret = strbuf_addch(&sb, ':'); 619 if (ret) 620 break; 621 } 622 ret = strbuf_addstr(&sb, modifier); 623 if (ret) 624 break; 625 } 626 ev->name = strdup(sb.buf); 627 if (!ev->name) { 628 ret = -ENOMEM; 629 break; 630 } 631 } 632 } 633 strbuf_release(&sb); 634 return ret; 635 } 636 637 static int metricgroup__build_event_string(struct strbuf *events, 638 const struct expr_parse_ctx *ctx, 639 const char *modifier, 640 bool has_constraint) 641 { 642 struct hashmap_entry *cur; 643 size_t bkt; 644 bool no_group = true, has_tool_events = false; 645 bool tool_events[PERF_TOOL_MAX] = {false}; 646 int ret = 0; 647 648 #define RETURN_IF_NON_ZERO(x) do { if (x) return x; } while (0) 649 650 hashmap__for_each_entry(ctx->ids, cur, bkt) { 651 const char *sep, *rsep, *id = cur->pkey; 652 enum perf_tool_event ev; 653 654 pr_debug("found event %s\n", id); 655 656 /* Always move tool events outside of the group. */ 657 ev = perf_tool_event__from_str(id); 658 if (ev != PERF_TOOL_NONE) { 659 has_tool_events = true; 660 tool_events[ev] = true; 661 continue; 662 } 663 /* Separate events with commas and open the group if necessary. */ 664 if (no_group) { 665 if (!has_constraint) { 666 ret = strbuf_addch(events, '{'); 667 RETURN_IF_NON_ZERO(ret); 668 } 669 670 no_group = false; 671 } else { 672 ret = strbuf_addch(events, ','); 673 RETURN_IF_NON_ZERO(ret); 674 } 675 /* 676 * Encode the ID as an event string. Add a qualifier for 677 * metric_id that is the original name except with characters 678 * that parse-events can't parse replaced. For example, 679 * 'msr@tsc@' gets added as msr/tsc,metric-id=msr!3tsc!3/ 680 */ 681 sep = strchr(id, '@'); 682 if (sep != NULL) { 683 ret = strbuf_add(events, id, sep - id); 684 RETURN_IF_NON_ZERO(ret); 685 ret = strbuf_addch(events, '/'); 686 RETURN_IF_NON_ZERO(ret); 687 rsep = strrchr(sep, '@'); 688 ret = strbuf_add(events, sep + 1, rsep - sep - 1); 689 RETURN_IF_NON_ZERO(ret); 690 ret = strbuf_addstr(events, ",metric-id="); 691 RETURN_IF_NON_ZERO(ret); 692 sep = rsep; 693 } else { 694 sep = strchr(id, ':'); 695 if (sep != NULL) { 696 ret = strbuf_add(events, id, sep - id); 697 RETURN_IF_NON_ZERO(ret); 698 } else { 699 ret = strbuf_addstr(events, id); 700 RETURN_IF_NON_ZERO(ret); 701 } 702 ret = strbuf_addstr(events, "/metric-id="); 703 RETURN_IF_NON_ZERO(ret); 704 } 705 ret = encode_metric_id(events, id); 706 RETURN_IF_NON_ZERO(ret); 707 ret = strbuf_addstr(events, "/"); 708 RETURN_IF_NON_ZERO(ret); 709 710 if (sep != NULL) { 711 ret = strbuf_addstr(events, sep + 1); 712 RETURN_IF_NON_ZERO(ret); 713 } 714 if (modifier) { 715 ret = strbuf_addstr(events, modifier); 716 RETURN_IF_NON_ZERO(ret); 717 } 718 } 719 if (!no_group && !has_constraint) { 720 ret = strbuf_addf(events, "}:W"); 721 RETURN_IF_NON_ZERO(ret); 722 } 723 if (has_tool_events) { 724 int i; 725 726 perf_tool_event__for_each_event(i) { 727 if (tool_events[i]) { 728 if (!no_group) { 729 ret = strbuf_addch(events, ','); 730 RETURN_IF_NON_ZERO(ret); 731 } 732 no_group = false; 733 ret = strbuf_addstr(events, perf_tool_event__to_str(i)); 734 RETURN_IF_NON_ZERO(ret); 735 } 736 } 737 } 738 739 return ret; 740 #undef RETURN_IF_NON_ZERO 741 } 742 743 int __weak arch_get_runtimeparam(const struct pmu_metric *pm __maybe_unused) 744 { 745 return 1; 746 } 747 748 /* 749 * A singly linked list on the stack of the names of metrics being 750 * processed. Used to identify recursion. 751 */ 752 struct visited_metric { 753 const char *name; 754 const struct visited_metric *parent; 755 }; 756 757 struct metricgroup_add_iter_data { 758 struct list_head *metric_list; 759 const char *metric_name; 760 const char *modifier; 761 int *ret; 762 bool *has_match; 763 bool metric_no_group; 764 const char *user_requested_cpu_list; 765 bool system_wide; 766 struct metric *root_metric; 767 const struct visited_metric *visited; 768 const struct pmu_metrics_table *table; 769 }; 770 771 static bool metricgroup__find_metric(const char *metric, 772 const struct pmu_metrics_table *table, 773 struct pmu_metric *pm); 774 775 static int add_metric(struct list_head *metric_list, 776 const struct pmu_metric *pm, 777 const char *modifier, 778 bool metric_no_group, 779 const char *user_requested_cpu_list, 780 bool system_wide, 781 struct metric *root_metric, 782 const struct visited_metric *visited, 783 const struct pmu_metrics_table *table); 784 785 /** 786 * resolve_metric - Locate metrics within the root metric and recursively add 787 * references to them. 788 * @metric_list: The list the metric is added to. 789 * @modifier: if non-null event modifiers like "u". 790 * @metric_no_group: Should events written to events be grouped "{}" or 791 * global. Grouping is the default but due to multiplexing the 792 * user may override. 793 * @user_requested_cpu_list: Command line specified CPUs to record on. 794 * @system_wide: Are events for all processes recorded. 795 * @root_metric: Metrics may reference other metrics to form a tree. In this 796 * case the root_metric holds all the IDs and a list of referenced 797 * metrics. When adding a root this argument is NULL. 798 * @visited: A singly linked list of metric names being added that is used to 799 * detect recursion. 800 * @table: The table that is searched for metrics, most commonly the table for the 801 * architecture perf is running upon. 802 */ 803 static int resolve_metric(struct list_head *metric_list, 804 const char *modifier, 805 bool metric_no_group, 806 const char *user_requested_cpu_list, 807 bool system_wide, 808 struct metric *root_metric, 809 const struct visited_metric *visited, 810 const struct pmu_metrics_table *table) 811 { 812 struct hashmap_entry *cur; 813 size_t bkt; 814 struct to_resolve { 815 /* The metric to resolve. */ 816 struct pmu_metric pm; 817 /* 818 * The key in the IDs map, this may differ from in case, 819 * etc. from pm->metric_name. 820 */ 821 const char *key; 822 } *pending = NULL; 823 int i, ret = 0, pending_cnt = 0; 824 825 /* 826 * Iterate all the parsed IDs and if there's a matching metric and it to 827 * the pending array. 828 */ 829 hashmap__for_each_entry(root_metric->pctx->ids, cur, bkt) { 830 struct pmu_metric pm; 831 832 if (metricgroup__find_metric(cur->pkey, table, &pm)) { 833 pending = realloc(pending, 834 (pending_cnt + 1) * sizeof(struct to_resolve)); 835 if (!pending) 836 return -ENOMEM; 837 838 memcpy(&pending[pending_cnt].pm, &pm, sizeof(pm)); 839 pending[pending_cnt].key = cur->pkey; 840 pending_cnt++; 841 } 842 } 843 844 /* Remove the metric IDs from the context. */ 845 for (i = 0; i < pending_cnt; i++) 846 expr__del_id(root_metric->pctx, pending[i].key); 847 848 /* 849 * Recursively add all the metrics, IDs are added to the root metric's 850 * context. 851 */ 852 for (i = 0; i < pending_cnt; i++) { 853 ret = add_metric(metric_list, &pending[i].pm, modifier, metric_no_group, 854 user_requested_cpu_list, system_wide, root_metric, visited, 855 table); 856 if (ret) 857 break; 858 } 859 860 free(pending); 861 return ret; 862 } 863 864 /** 865 * __add_metric - Add a metric to metric_list. 866 * @metric_list: The list the metric is added to. 867 * @pm: The pmu_metric containing the metric to be added. 868 * @modifier: if non-null event modifiers like "u". 869 * @metric_no_group: Should events written to events be grouped "{}" or 870 * global. Grouping is the default but due to multiplexing the 871 * user may override. 872 * @runtime: A special argument for the parser only known at runtime. 873 * @user_requested_cpu_list: Command line specified CPUs to record on. 874 * @system_wide: Are events for all processes recorded. 875 * @root_metric: Metrics may reference other metrics to form a tree. In this 876 * case the root_metric holds all the IDs and a list of referenced 877 * metrics. When adding a root this argument is NULL. 878 * @visited: A singly linked list of metric names being added that is used to 879 * detect recursion. 880 * @table: The table that is searched for metrics, most commonly the table for the 881 * architecture perf is running upon. 882 */ 883 static int __add_metric(struct list_head *metric_list, 884 const struct pmu_metric *pm, 885 const char *modifier, 886 bool metric_no_group, 887 int runtime, 888 const char *user_requested_cpu_list, 889 bool system_wide, 890 struct metric *root_metric, 891 const struct visited_metric *visited, 892 const struct pmu_metrics_table *table) 893 { 894 const struct visited_metric *vm; 895 int ret; 896 bool is_root = !root_metric; 897 struct visited_metric visited_node = { 898 .name = pm->metric_name, 899 .parent = visited, 900 }; 901 902 for (vm = visited; vm; vm = vm->parent) { 903 if (!strcmp(pm->metric_name, vm->name)) { 904 pr_err("failed: recursion detected for %s\n", pm->metric_name); 905 return -1; 906 } 907 } 908 909 if (is_root) { 910 /* 911 * This metric is the root of a tree and may reference other 912 * metrics that are added recursively. 913 */ 914 root_metric = metric__new(pm, modifier, metric_no_group, runtime, 915 user_requested_cpu_list, system_wide); 916 if (!root_metric) 917 return -ENOMEM; 918 919 } else { 920 int cnt = 0; 921 922 /* 923 * This metric was referenced in a metric higher in the 924 * tree. Check if the same metric is already resolved in the 925 * metric_refs list. 926 */ 927 if (root_metric->metric_refs) { 928 for (; root_metric->metric_refs[cnt].metric_name; cnt++) { 929 if (!strcmp(pm->metric_name, 930 root_metric->metric_refs[cnt].metric_name)) 931 return 0; 932 } 933 } 934 935 /* Create reference. Need space for the entry and the terminator. */ 936 root_metric->metric_refs = realloc(root_metric->metric_refs, 937 (cnt + 2) * sizeof(struct metric_ref)); 938 if (!root_metric->metric_refs) 939 return -ENOMEM; 940 941 /* 942 * Intentionally passing just const char pointers, 943 * from 'pe' object, so they never go away. We don't 944 * need to change them, so there's no need to create 945 * our own copy. 946 */ 947 root_metric->metric_refs[cnt].metric_name = pm->metric_name; 948 root_metric->metric_refs[cnt].metric_expr = pm->metric_expr; 949 950 /* Null terminate array. */ 951 root_metric->metric_refs[cnt+1].metric_name = NULL; 952 root_metric->metric_refs[cnt+1].metric_expr = NULL; 953 } 954 955 /* 956 * For both the parent and referenced metrics, we parse 957 * all the metric's IDs and add it to the root context. 958 */ 959 if (expr__find_ids(pm->metric_expr, NULL, root_metric->pctx) < 0) { 960 /* Broken metric. */ 961 ret = -EINVAL; 962 } else { 963 /* Resolve referenced metrics. */ 964 ret = resolve_metric(metric_list, modifier, metric_no_group, 965 user_requested_cpu_list, system_wide, 966 root_metric, &visited_node, table); 967 } 968 969 if (ret) { 970 if (is_root) 971 metric__free(root_metric); 972 973 } else if (is_root) 974 list_add(&root_metric->nd, metric_list); 975 976 return ret; 977 } 978 979 struct metricgroup__find_metric_data { 980 const char *metric; 981 struct pmu_metric *pm; 982 }; 983 984 static int metricgroup__find_metric_callback(const struct pmu_metric *pm, 985 const struct pmu_metrics_table *table __maybe_unused, 986 void *vdata) 987 { 988 struct metricgroup__find_metric_data *data = vdata; 989 990 if (!match_metric(pm->metric_name, data->metric)) 991 return 0; 992 993 memcpy(data->pm, pm, sizeof(*pm)); 994 return 1; 995 } 996 997 static bool metricgroup__find_metric(const char *metric, 998 const struct pmu_metrics_table *table, 999 struct pmu_metric *pm) 1000 { 1001 struct metricgroup__find_metric_data data = { 1002 .metric = metric, 1003 .pm = pm, 1004 }; 1005 1006 return pmu_metrics_table_for_each_metric(table, metricgroup__find_metric_callback, &data) 1007 ? true : false; 1008 } 1009 1010 static int add_metric(struct list_head *metric_list, 1011 const struct pmu_metric *pm, 1012 const char *modifier, 1013 bool metric_no_group, 1014 const char *user_requested_cpu_list, 1015 bool system_wide, 1016 struct metric *root_metric, 1017 const struct visited_metric *visited, 1018 const struct pmu_metrics_table *table) 1019 { 1020 int ret = 0; 1021 1022 pr_debug("metric expr %s for %s\n", pm->metric_expr, pm->metric_name); 1023 1024 if (!strstr(pm->metric_expr, "?")) { 1025 ret = __add_metric(metric_list, pm, modifier, metric_no_group, 0, 1026 user_requested_cpu_list, system_wide, root_metric, 1027 visited, table); 1028 } else { 1029 int j, count; 1030 1031 count = arch_get_runtimeparam(pm); 1032 1033 /* This loop is added to create multiple 1034 * events depend on count value and add 1035 * those events to metric_list. 1036 */ 1037 1038 for (j = 0; j < count && !ret; j++) 1039 ret = __add_metric(metric_list, pm, modifier, metric_no_group, j, 1040 user_requested_cpu_list, system_wide, 1041 root_metric, visited, table); 1042 } 1043 1044 return ret; 1045 } 1046 1047 static int metricgroup__add_metric_sys_event_iter(const struct pmu_metric *pm, 1048 const struct pmu_metrics_table *table __maybe_unused, 1049 void *data) 1050 { 1051 struct metricgroup_add_iter_data *d = data; 1052 int ret; 1053 1054 if (!match_pm_metric(pm, d->metric_name)) 1055 return 0; 1056 1057 ret = add_metric(d->metric_list, pm, d->modifier, d->metric_no_group, 1058 d->user_requested_cpu_list, d->system_wide, 1059 d->root_metric, d->visited, d->table); 1060 if (ret) 1061 goto out; 1062 1063 *(d->has_match) = true; 1064 1065 out: 1066 *(d->ret) = ret; 1067 return ret; 1068 } 1069 1070 /** 1071 * metric_list_cmp - list_sort comparator that sorts metrics with more events to 1072 * the front. tool events are excluded from the count. 1073 */ 1074 static int metric_list_cmp(void *priv __maybe_unused, const struct list_head *l, 1075 const struct list_head *r) 1076 { 1077 const struct metric *left = container_of(l, struct metric, nd); 1078 const struct metric *right = container_of(r, struct metric, nd); 1079 struct expr_id_data *data; 1080 int i, left_count, right_count; 1081 1082 left_count = hashmap__size(left->pctx->ids); 1083 perf_tool_event__for_each_event(i) { 1084 if (!expr__get_id(left->pctx, perf_tool_event__to_str(i), &data)) 1085 left_count--; 1086 } 1087 1088 right_count = hashmap__size(right->pctx->ids); 1089 perf_tool_event__for_each_event(i) { 1090 if (!expr__get_id(right->pctx, perf_tool_event__to_str(i), &data)) 1091 right_count--; 1092 } 1093 1094 return right_count - left_count; 1095 } 1096 1097 struct metricgroup__add_metric_data { 1098 struct list_head *list; 1099 const char *metric_name; 1100 const char *modifier; 1101 const char *user_requested_cpu_list; 1102 bool metric_no_group; 1103 bool system_wide; 1104 bool has_match; 1105 }; 1106 1107 static int metricgroup__add_metric_callback(const struct pmu_metric *pm, 1108 const struct pmu_metrics_table *table, 1109 void *vdata) 1110 { 1111 struct metricgroup__add_metric_data *data = vdata; 1112 int ret = 0; 1113 1114 if (pm->metric_expr && 1115 (match_metric(pm->metric_group, data->metric_name) || 1116 match_metric(pm->metric_name, data->metric_name))) { 1117 1118 data->has_match = true; 1119 ret = add_metric(data->list, pm, data->modifier, data->metric_no_group, 1120 data->user_requested_cpu_list, data->system_wide, 1121 /*root_metric=*/NULL, /*visited_metrics=*/NULL, table); 1122 } 1123 return ret; 1124 } 1125 1126 /** 1127 * metricgroup__add_metric - Find and add a metric, or a metric group. 1128 * @metric_name: The name of the metric or metric group. For example, "IPC" 1129 * could be the name of a metric and "TopDownL1" the name of a 1130 * metric group. 1131 * @modifier: if non-null event modifiers like "u". 1132 * @metric_no_group: Should events written to events be grouped "{}" or 1133 * global. Grouping is the default but due to multiplexing the 1134 * user may override. 1135 * @user_requested_cpu_list: Command line specified CPUs to record on. 1136 * @system_wide: Are events for all processes recorded. 1137 * @metric_list: The list that the metric or metric group are added to. 1138 * @table: The table that is searched for metrics, most commonly the table for the 1139 * architecture perf is running upon. 1140 */ 1141 static int metricgroup__add_metric(const char *metric_name, const char *modifier, 1142 bool metric_no_group, 1143 const char *user_requested_cpu_list, 1144 bool system_wide, 1145 struct list_head *metric_list, 1146 const struct pmu_metrics_table *table) 1147 { 1148 LIST_HEAD(list); 1149 int ret; 1150 bool has_match = false; 1151 1152 { 1153 struct metricgroup__add_metric_data data = { 1154 .list = &list, 1155 .metric_name = metric_name, 1156 .modifier = modifier, 1157 .metric_no_group = metric_no_group, 1158 .user_requested_cpu_list = user_requested_cpu_list, 1159 .system_wide = system_wide, 1160 .has_match = false, 1161 }; 1162 /* 1163 * Iterate over all metrics seeing if metric matches either the 1164 * name or group. When it does add the metric to the list. 1165 */ 1166 ret = pmu_metrics_table_for_each_metric(table, metricgroup__add_metric_callback, 1167 &data); 1168 if (ret) 1169 goto out; 1170 1171 has_match = data.has_match; 1172 } 1173 { 1174 struct metricgroup_iter_data data = { 1175 .fn = metricgroup__add_metric_sys_event_iter, 1176 .data = (void *) &(struct metricgroup_add_iter_data) { 1177 .metric_list = &list, 1178 .metric_name = metric_name, 1179 .modifier = modifier, 1180 .metric_no_group = metric_no_group, 1181 .user_requested_cpu_list = user_requested_cpu_list, 1182 .system_wide = system_wide, 1183 .has_match = &has_match, 1184 .ret = &ret, 1185 .table = table, 1186 }, 1187 }; 1188 1189 pmu_for_each_sys_metric(metricgroup__sys_event_iter, &data); 1190 } 1191 /* End of pmu events. */ 1192 if (!has_match) 1193 ret = -EINVAL; 1194 1195 out: 1196 /* 1197 * add to metric_list so that they can be released 1198 * even if it's failed 1199 */ 1200 list_splice(&list, metric_list); 1201 return ret; 1202 } 1203 1204 /** 1205 * metricgroup__add_metric_list - Find and add metrics, or metric groups, 1206 * specified in a list. 1207 * @list: the list of metrics or metric groups. For example, "IPC,CPI,TopDownL1" 1208 * would match the IPC and CPI metrics, and TopDownL1 would match all 1209 * the metrics in the TopDownL1 group. 1210 * @metric_no_group: Should events written to events be grouped "{}" or 1211 * global. Grouping is the default but due to multiplexing the 1212 * user may override. 1213 * @user_requested_cpu_list: Command line specified CPUs to record on. 1214 * @system_wide: Are events for all processes recorded. 1215 * @metric_list: The list that metrics are added to. 1216 * @table: The table that is searched for metrics, most commonly the table for the 1217 * architecture perf is running upon. 1218 */ 1219 static int metricgroup__add_metric_list(const char *list, bool metric_no_group, 1220 const char *user_requested_cpu_list, 1221 bool system_wide, struct list_head *metric_list, 1222 const struct pmu_metrics_table *table) 1223 { 1224 char *list_itr, *list_copy, *metric_name, *modifier; 1225 int ret, count = 0; 1226 1227 list_copy = strdup(list); 1228 if (!list_copy) 1229 return -ENOMEM; 1230 list_itr = list_copy; 1231 1232 while ((metric_name = strsep(&list_itr, ",")) != NULL) { 1233 modifier = strchr(metric_name, ':'); 1234 if (modifier) 1235 *modifier++ = '\0'; 1236 1237 ret = metricgroup__add_metric(metric_name, modifier, 1238 metric_no_group, user_requested_cpu_list, 1239 system_wide, metric_list, table); 1240 if (ret == -EINVAL) 1241 pr_err("Cannot find metric or group `%s'\n", metric_name); 1242 1243 if (ret) 1244 break; 1245 1246 count++; 1247 } 1248 free(list_copy); 1249 1250 if (!ret) { 1251 /* 1252 * Warn about nmi_watchdog if any parsed metrics had the 1253 * NO_NMI_WATCHDOG constraint. 1254 */ 1255 metricgroup___watchdog_constraint_hint(NULL, true); 1256 /* No metrics. */ 1257 if (count == 0) 1258 return -EINVAL; 1259 } 1260 return ret; 1261 } 1262 1263 static void metricgroup__free_metrics(struct list_head *metric_list) 1264 { 1265 struct metric *m, *tmp; 1266 1267 list_for_each_entry_safe (m, tmp, metric_list, nd) { 1268 list_del_init(&m->nd); 1269 metric__free(m); 1270 } 1271 } 1272 1273 /** 1274 * find_tool_events - Search for the pressence of tool events in metric_list. 1275 * @metric_list: List to take metrics from. 1276 * @tool_events: Array of false values, indices corresponding to tool events set 1277 * to true if tool event is found. 1278 */ 1279 static void find_tool_events(const struct list_head *metric_list, 1280 bool tool_events[PERF_TOOL_MAX]) 1281 { 1282 struct metric *m; 1283 1284 list_for_each_entry(m, metric_list, nd) { 1285 int i; 1286 1287 perf_tool_event__for_each_event(i) { 1288 struct expr_id_data *data; 1289 1290 if (!tool_events[i] && 1291 !expr__get_id(m->pctx, perf_tool_event__to_str(i), &data)) 1292 tool_events[i] = true; 1293 } 1294 } 1295 } 1296 1297 /** 1298 * build_combined_expr_ctx - Make an expr_parse_ctx with all has_constraint 1299 * metric IDs, as the IDs are held in a set, 1300 * duplicates will be removed. 1301 * @metric_list: List to take metrics from. 1302 * @combined: Out argument for result. 1303 */ 1304 static int build_combined_expr_ctx(const struct list_head *metric_list, 1305 struct expr_parse_ctx **combined) 1306 { 1307 struct hashmap_entry *cur; 1308 size_t bkt; 1309 struct metric *m; 1310 char *dup; 1311 int ret; 1312 1313 *combined = expr__ctx_new(); 1314 if (!*combined) 1315 return -ENOMEM; 1316 1317 list_for_each_entry(m, metric_list, nd) { 1318 if (m->has_constraint && !m->modifier) { 1319 hashmap__for_each_entry(m->pctx->ids, cur, bkt) { 1320 dup = strdup(cur->pkey); 1321 if (!dup) { 1322 ret = -ENOMEM; 1323 goto err_out; 1324 } 1325 ret = expr__add_id(*combined, dup); 1326 if (ret) 1327 goto err_out; 1328 } 1329 } 1330 } 1331 return 0; 1332 err_out: 1333 expr__ctx_free(*combined); 1334 *combined = NULL; 1335 return ret; 1336 } 1337 1338 /** 1339 * parse_ids - Build the event string for the ids and parse them creating an 1340 * evlist. The encoded metric_ids are decoded. 1341 * @metric_no_merge: is metric sharing explicitly disabled. 1342 * @fake_pmu: used when testing metrics not supported by the current CPU. 1343 * @ids: the event identifiers parsed from a metric. 1344 * @modifier: any modifiers added to the events. 1345 * @has_constraint: false if events should be placed in a weak group. 1346 * @tool_events: entries set true if the tool event of index could be present in 1347 * the overall list of metrics. 1348 * @out_evlist: the created list of events. 1349 */ 1350 static int parse_ids(bool metric_no_merge, struct perf_pmu *fake_pmu, 1351 struct expr_parse_ctx *ids, const char *modifier, 1352 bool has_constraint, const bool tool_events[PERF_TOOL_MAX], 1353 struct evlist **out_evlist) 1354 { 1355 struct parse_events_error parse_error; 1356 struct evlist *parsed_evlist; 1357 struct strbuf events = STRBUF_INIT; 1358 int ret; 1359 1360 *out_evlist = NULL; 1361 if (!metric_no_merge || hashmap__size(ids->ids) == 0) { 1362 bool added_event = false; 1363 int i; 1364 /* 1365 * We may fail to share events between metrics because a tool 1366 * event isn't present in one metric. For example, a ratio of 1367 * cache misses doesn't need duration_time but the same events 1368 * may be used for a misses per second. Events without sharing 1369 * implies multiplexing, that is best avoided, so place 1370 * all tool events in every group. 1371 * 1372 * Also, there may be no ids/events in the expression parsing 1373 * context because of constant evaluation, e.g.: 1374 * event1 if #smt_on else 0 1375 * Add a tool event to avoid a parse error on an empty string. 1376 */ 1377 perf_tool_event__for_each_event(i) { 1378 if (tool_events[i]) { 1379 char *tmp = strdup(perf_tool_event__to_str(i)); 1380 1381 if (!tmp) 1382 return -ENOMEM; 1383 ids__insert(ids->ids, tmp); 1384 added_event = true; 1385 } 1386 } 1387 if (!added_event && hashmap__size(ids->ids) == 0) { 1388 char *tmp = strdup("duration_time"); 1389 1390 if (!tmp) 1391 return -ENOMEM; 1392 ids__insert(ids->ids, tmp); 1393 } 1394 } 1395 ret = metricgroup__build_event_string(&events, ids, modifier, 1396 has_constraint); 1397 if (ret) 1398 return ret; 1399 1400 parsed_evlist = evlist__new(); 1401 if (!parsed_evlist) { 1402 ret = -ENOMEM; 1403 goto err_out; 1404 } 1405 pr_debug("Parsing metric events '%s'\n", events.buf); 1406 parse_events_error__init(&parse_error); 1407 ret = __parse_events(parsed_evlist, events.buf, &parse_error, fake_pmu); 1408 if (ret) { 1409 parse_events_error__print(&parse_error, events.buf); 1410 goto err_out; 1411 } 1412 ret = decode_all_metric_ids(parsed_evlist, modifier); 1413 if (ret) 1414 goto err_out; 1415 1416 *out_evlist = parsed_evlist; 1417 parsed_evlist = NULL; 1418 err_out: 1419 parse_events_error__exit(&parse_error); 1420 evlist__delete(parsed_evlist); 1421 strbuf_release(&events); 1422 return ret; 1423 } 1424 1425 static int parse_groups(struct evlist *perf_evlist, const char *str, 1426 bool metric_no_group, 1427 bool metric_no_merge, 1428 const char *user_requested_cpu_list, 1429 bool system_wide, 1430 struct perf_pmu *fake_pmu, 1431 struct rblist *metric_events_list, 1432 const struct pmu_metrics_table *table) 1433 { 1434 struct evlist *combined_evlist = NULL; 1435 LIST_HEAD(metric_list); 1436 struct metric *m; 1437 bool tool_events[PERF_TOOL_MAX] = {false}; 1438 int ret; 1439 1440 if (metric_events_list->nr_entries == 0) 1441 metricgroup__rblist_init(metric_events_list); 1442 ret = metricgroup__add_metric_list(str, metric_no_group, 1443 user_requested_cpu_list, 1444 system_wide, &metric_list, table); 1445 if (ret) 1446 goto out; 1447 1448 /* Sort metrics from largest to smallest. */ 1449 list_sort(NULL, &metric_list, metric_list_cmp); 1450 1451 if (!metric_no_merge) { 1452 struct expr_parse_ctx *combined = NULL; 1453 1454 find_tool_events(&metric_list, tool_events); 1455 1456 ret = build_combined_expr_ctx(&metric_list, &combined); 1457 1458 if (!ret && combined && hashmap__size(combined->ids)) { 1459 ret = parse_ids(metric_no_merge, fake_pmu, combined, 1460 /*modifier=*/NULL, 1461 /*has_constraint=*/true, 1462 tool_events, 1463 &combined_evlist); 1464 } 1465 if (combined) 1466 expr__ctx_free(combined); 1467 1468 if (ret) 1469 goto out; 1470 } 1471 1472 list_for_each_entry(m, &metric_list, nd) { 1473 struct metric_event *me; 1474 struct evsel **metric_events; 1475 struct evlist *metric_evlist = NULL; 1476 struct metric *n; 1477 struct metric_expr *expr; 1478 1479 if (combined_evlist && m->has_constraint) { 1480 metric_evlist = combined_evlist; 1481 } else if (!metric_no_merge) { 1482 /* 1483 * See if the IDs for this metric are a subset of an 1484 * earlier metric. 1485 */ 1486 list_for_each_entry(n, &metric_list, nd) { 1487 if (m == n) 1488 break; 1489 1490 if (n->evlist == NULL) 1491 continue; 1492 1493 if ((!m->modifier && n->modifier) || 1494 (m->modifier && !n->modifier) || 1495 (m->modifier && n->modifier && 1496 strcmp(m->modifier, n->modifier))) 1497 continue; 1498 1499 if (expr__subset_of_ids(n->pctx, m->pctx)) { 1500 pr_debug("Events in '%s' fully contained within '%s'\n", 1501 m->metric_name, n->metric_name); 1502 metric_evlist = n->evlist; 1503 break; 1504 } 1505 1506 } 1507 } 1508 if (!metric_evlist) { 1509 ret = parse_ids(metric_no_merge, fake_pmu, m->pctx, m->modifier, 1510 m->has_constraint, tool_events, &m->evlist); 1511 if (ret) 1512 goto out; 1513 1514 metric_evlist = m->evlist; 1515 } 1516 ret = setup_metric_events(m->pctx->ids, metric_evlist, &metric_events); 1517 if (ret) { 1518 pr_debug("Cannot resolve IDs for %s: %s\n", 1519 m->metric_name, m->metric_expr); 1520 goto out; 1521 } 1522 1523 me = metricgroup__lookup(metric_events_list, metric_events[0], true); 1524 1525 expr = malloc(sizeof(struct metric_expr)); 1526 if (!expr) { 1527 ret = -ENOMEM; 1528 free(metric_events); 1529 goto out; 1530 } 1531 1532 expr->metric_refs = m->metric_refs; 1533 m->metric_refs = NULL; 1534 expr->metric_expr = m->metric_expr; 1535 if (m->modifier) { 1536 char *tmp; 1537 1538 if (asprintf(&tmp, "%s:%s", m->metric_name, m->modifier) < 0) 1539 expr->metric_name = NULL; 1540 else 1541 expr->metric_name = tmp; 1542 } else 1543 expr->metric_name = strdup(m->metric_name); 1544 1545 if (!expr->metric_name) { 1546 ret = -ENOMEM; 1547 free(metric_events); 1548 goto out; 1549 } 1550 expr->metric_unit = m->metric_unit; 1551 expr->metric_events = metric_events; 1552 expr->runtime = m->pctx->sctx.runtime; 1553 list_add(&expr->nd, &me->head); 1554 } 1555 1556 1557 if (combined_evlist) { 1558 evlist__splice_list_tail(perf_evlist, &combined_evlist->core.entries); 1559 evlist__delete(combined_evlist); 1560 } 1561 1562 list_for_each_entry(m, &metric_list, nd) { 1563 if (m->evlist) 1564 evlist__splice_list_tail(perf_evlist, &m->evlist->core.entries); 1565 } 1566 1567 out: 1568 metricgroup__free_metrics(&metric_list); 1569 return ret; 1570 } 1571 1572 int metricgroup__parse_groups(struct evlist *perf_evlist, 1573 const char *str, 1574 bool metric_no_group, 1575 bool metric_no_merge, 1576 const char *user_requested_cpu_list, 1577 bool system_wide, 1578 struct rblist *metric_events) 1579 { 1580 const struct pmu_metrics_table *table = pmu_metrics_table__find(); 1581 1582 if (!table) 1583 return -EINVAL; 1584 1585 return parse_groups(perf_evlist, str, metric_no_group, metric_no_merge, 1586 user_requested_cpu_list, system_wide, 1587 /*fake_pmu=*/NULL, metric_events, table); 1588 } 1589 1590 int metricgroup__parse_groups_test(struct evlist *evlist, 1591 const struct pmu_metrics_table *table, 1592 const char *str, 1593 bool metric_no_group, 1594 bool metric_no_merge, 1595 struct rblist *metric_events) 1596 { 1597 return parse_groups(evlist, str, metric_no_group, metric_no_merge, 1598 /*user_requested_cpu_list=*/NULL, 1599 /*system_wide=*/false, 1600 &perf_pmu__fake, metric_events, table); 1601 } 1602 1603 static int metricgroup__has_metric_callback(const struct pmu_metric *pm, 1604 const struct pmu_metrics_table *table __maybe_unused, 1605 void *vdata) 1606 { 1607 const char *metric = vdata; 1608 1609 if (!pm->metric_expr) 1610 return 0; 1611 1612 if (match_metric(pm->metric_name, metric)) 1613 return 1; 1614 1615 return 0; 1616 } 1617 1618 bool metricgroup__has_metric(const char *metric) 1619 { 1620 const struct pmu_metrics_table *table = pmu_metrics_table__find(); 1621 1622 if (!table) 1623 return false; 1624 1625 return pmu_metrics_table_for_each_metric(table, metricgroup__has_metric_callback, 1626 (void *)metric) ? true : false; 1627 } 1628 1629 int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp, 1630 struct rblist *new_metric_events, 1631 struct rblist *old_metric_events) 1632 { 1633 unsigned int i; 1634 1635 for (i = 0; i < rblist__nr_entries(old_metric_events); i++) { 1636 struct rb_node *nd; 1637 struct metric_event *old_me, *new_me; 1638 struct metric_expr *old_expr, *new_expr; 1639 struct evsel *evsel; 1640 size_t alloc_size; 1641 int idx, nr; 1642 1643 nd = rblist__entry(old_metric_events, i); 1644 old_me = container_of(nd, struct metric_event, nd); 1645 1646 evsel = evlist__find_evsel(evlist, old_me->evsel->core.idx); 1647 if (!evsel) 1648 return -EINVAL; 1649 new_me = metricgroup__lookup(new_metric_events, evsel, true); 1650 if (!new_me) 1651 return -ENOMEM; 1652 1653 pr_debug("copying metric event for cgroup '%s': %s (idx=%d)\n", 1654 cgrp ? cgrp->name : "root", evsel->name, evsel->core.idx); 1655 1656 list_for_each_entry(old_expr, &old_me->head, nd) { 1657 new_expr = malloc(sizeof(*new_expr)); 1658 if (!new_expr) 1659 return -ENOMEM; 1660 1661 new_expr->metric_expr = old_expr->metric_expr; 1662 new_expr->metric_name = strdup(old_expr->metric_name); 1663 if (!new_expr->metric_name) 1664 return -ENOMEM; 1665 1666 new_expr->metric_unit = old_expr->metric_unit; 1667 new_expr->runtime = old_expr->runtime; 1668 1669 if (old_expr->metric_refs) { 1670 /* calculate number of metric_events */ 1671 for (nr = 0; old_expr->metric_refs[nr].metric_name; nr++) 1672 continue; 1673 alloc_size = sizeof(*new_expr->metric_refs); 1674 new_expr->metric_refs = calloc(nr + 1, alloc_size); 1675 if (!new_expr->metric_refs) { 1676 free(new_expr); 1677 return -ENOMEM; 1678 } 1679 1680 memcpy(new_expr->metric_refs, old_expr->metric_refs, 1681 nr * alloc_size); 1682 } else { 1683 new_expr->metric_refs = NULL; 1684 } 1685 1686 /* calculate number of metric_events */ 1687 for (nr = 0; old_expr->metric_events[nr]; nr++) 1688 continue; 1689 alloc_size = sizeof(*new_expr->metric_events); 1690 new_expr->metric_events = calloc(nr + 1, alloc_size); 1691 if (!new_expr->metric_events) { 1692 free(new_expr->metric_refs); 1693 free(new_expr); 1694 return -ENOMEM; 1695 } 1696 1697 /* copy evsel in the same position */ 1698 for (idx = 0; idx < nr; idx++) { 1699 evsel = old_expr->metric_events[idx]; 1700 evsel = evlist__find_evsel(evlist, evsel->core.idx); 1701 if (evsel == NULL) { 1702 free(new_expr->metric_events); 1703 free(new_expr->metric_refs); 1704 free(new_expr); 1705 return -EINVAL; 1706 } 1707 new_expr->metric_events[idx] = evsel; 1708 } 1709 1710 list_add(&new_expr->nd, &new_me->head); 1711 } 1712 } 1713 return 0; 1714 } 1715