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 int idnum; 89 const char **ids; 90 const char *metric_name; 91 const char *metric_expr; 92 const char *metric_unit; 93 int runtime; 94 }; 95 96 static struct evsel *find_evsel_group(struct evlist *perf_evlist, 97 const char **ids, 98 int idnum, 99 struct evsel **metric_events, 100 bool *evlist_used) 101 { 102 struct evsel *ev; 103 int i = 0, j = 0; 104 bool leader_found; 105 106 evlist__for_each_entry (perf_evlist, ev) { 107 if (evlist_used[j++]) 108 continue; 109 if (!strcmp(ev->name, ids[i])) { 110 if (!metric_events[i]) 111 metric_events[i] = ev; 112 i++; 113 if (i == idnum) 114 break; 115 } else { 116 /* Discard the whole match and start again */ 117 i = 0; 118 memset(metric_events, 0, 119 sizeof(struct evsel *) * idnum); 120 121 if (!strcmp(ev->name, ids[i])) { 122 if (!metric_events[i]) 123 metric_events[i] = ev; 124 i++; 125 if (i == idnum) 126 break; 127 } 128 } 129 } 130 131 if (i != idnum) { 132 /* Not whole match */ 133 return NULL; 134 } 135 136 metric_events[idnum] = NULL; 137 138 for (i = 0; i < idnum; i++) { 139 leader_found = false; 140 evlist__for_each_entry(perf_evlist, ev) { 141 if (!leader_found && (ev == metric_events[i])) 142 leader_found = true; 143 144 if (leader_found && 145 !strcmp(ev->name, metric_events[i]->name)) { 146 ev->metric_leader = metric_events[i]; 147 } 148 j++; 149 } 150 ev = metric_events[i]; 151 evlist_used[ev->idx] = true; 152 } 153 154 return metric_events[0]; 155 } 156 157 static int metricgroup__setup_events(struct list_head *groups, 158 struct evlist *perf_evlist, 159 struct rblist *metric_events_list) 160 { 161 struct metric_event *me; 162 struct metric_expr *expr; 163 int i = 0; 164 int ret = 0; 165 struct egroup *eg; 166 struct evsel *evsel; 167 bool *evlist_used; 168 169 evlist_used = calloc(perf_evlist->core.nr_entries, sizeof(bool)); 170 if (!evlist_used) { 171 ret = -ENOMEM; 172 return ret; 173 } 174 175 list_for_each_entry (eg, groups, nd) { 176 struct evsel **metric_events; 177 178 metric_events = calloc(sizeof(void *), eg->idnum + 1); 179 if (!metric_events) { 180 ret = -ENOMEM; 181 break; 182 } 183 evsel = find_evsel_group(perf_evlist, eg->ids, eg->idnum, 184 metric_events, evlist_used); 185 if (!evsel) { 186 pr_debug("Cannot resolve %s: %s\n", 187 eg->metric_name, eg->metric_expr); 188 continue; 189 } 190 for (i = 0; i < eg->idnum; i++) 191 metric_events[i]->collect_stat = true; 192 me = metricgroup__lookup(metric_events_list, evsel, true); 193 if (!me) { 194 ret = -ENOMEM; 195 break; 196 } 197 expr = malloc(sizeof(struct metric_expr)); 198 if (!expr) { 199 ret = -ENOMEM; 200 break; 201 } 202 expr->metric_expr = eg->metric_expr; 203 expr->metric_name = eg->metric_name; 204 expr->metric_unit = eg->metric_unit; 205 expr->metric_events = metric_events; 206 expr->runtime = eg->runtime; 207 list_add(&expr->nd, &me->head); 208 } 209 210 free(evlist_used); 211 212 return ret; 213 } 214 215 static bool match_metric(const char *n, const char *list) 216 { 217 int len; 218 char *m; 219 220 if (!list) 221 return false; 222 if (!strcmp(list, "all")) 223 return true; 224 if (!n) 225 return !strcasecmp(list, "No_group"); 226 len = strlen(list); 227 m = strcasestr(n, list); 228 if (!m) 229 return false; 230 if ((m == n || m[-1] == ';' || m[-1] == ' ') && 231 (m[len] == 0 || m[len] == ';')) 232 return true; 233 return false; 234 } 235 236 struct mep { 237 struct rb_node nd; 238 const char *name; 239 struct strlist *metrics; 240 }; 241 242 static int mep_cmp(struct rb_node *rb_node, const void *entry) 243 { 244 struct mep *a = container_of(rb_node, struct mep, nd); 245 struct mep *b = (struct mep *)entry; 246 247 return strcmp(a->name, b->name); 248 } 249 250 static struct rb_node *mep_new(struct rblist *rl __maybe_unused, 251 const void *entry) 252 { 253 struct mep *me = malloc(sizeof(struct mep)); 254 255 if (!me) 256 return NULL; 257 memcpy(me, entry, sizeof(struct mep)); 258 me->name = strdup(me->name); 259 if (!me->name) 260 goto out_me; 261 me->metrics = strlist__new(NULL, NULL); 262 if (!me->metrics) 263 goto out_name; 264 return &me->nd; 265 out_name: 266 zfree(&me->name); 267 out_me: 268 free(me); 269 return NULL; 270 } 271 272 static struct mep *mep_lookup(struct rblist *groups, const char *name) 273 { 274 struct rb_node *nd; 275 struct mep me = { 276 .name = name 277 }; 278 nd = rblist__find(groups, &me); 279 if (nd) 280 return container_of(nd, struct mep, nd); 281 rblist__add_node(groups, &me); 282 nd = rblist__find(groups, &me); 283 if (nd) 284 return container_of(nd, struct mep, nd); 285 return NULL; 286 } 287 288 static void mep_delete(struct rblist *rl __maybe_unused, 289 struct rb_node *nd) 290 { 291 struct mep *me = container_of(nd, struct mep, nd); 292 293 strlist__delete(me->metrics); 294 zfree(&me->name); 295 free(me); 296 } 297 298 static void metricgroup__print_strlist(struct strlist *metrics, bool raw) 299 { 300 struct str_node *sn; 301 int n = 0; 302 303 strlist__for_each_entry (sn, metrics) { 304 if (raw) 305 printf("%s%s", n > 0 ? " " : "", sn->s); 306 else 307 printf(" %s\n", sn->s); 308 n++; 309 } 310 if (raw) 311 putchar('\n'); 312 } 313 314 void metricgroup__print(bool metrics, bool metricgroups, char *filter, 315 bool raw, bool details) 316 { 317 struct pmu_events_map *map = perf_pmu__find_map(NULL); 318 struct pmu_event *pe; 319 int i; 320 struct rblist groups; 321 struct rb_node *node, *next; 322 struct strlist *metriclist = NULL; 323 324 if (!map) 325 return; 326 327 if (!metricgroups) { 328 metriclist = strlist__new(NULL, NULL); 329 if (!metriclist) 330 return; 331 } 332 333 rblist__init(&groups); 334 groups.node_new = mep_new; 335 groups.node_cmp = mep_cmp; 336 groups.node_delete = mep_delete; 337 for (i = 0; ; i++) { 338 const char *g; 339 pe = &map->table[i]; 340 341 if (!pe->name && !pe->metric_group && !pe->metric_name) 342 break; 343 if (!pe->metric_expr) 344 continue; 345 g = pe->metric_group; 346 if (!g && pe->metric_name) { 347 if (pe->name) 348 continue; 349 g = "No_group"; 350 } 351 if (g) { 352 char *omg; 353 char *mg = strdup(g); 354 355 if (!mg) 356 return; 357 omg = mg; 358 while ((g = strsep(&mg, ";")) != NULL) { 359 struct mep *me; 360 char *s; 361 362 g = skip_spaces(g); 363 if (*g == 0) 364 g = "No_group"; 365 if (filter && !strstr(g, filter)) 366 continue; 367 if (raw) 368 s = (char *)pe->metric_name; 369 else { 370 if (asprintf(&s, "%s\n%*s%s]", 371 pe->metric_name, 8, "[", pe->desc) < 0) 372 return; 373 374 if (details) { 375 if (asprintf(&s, "%s\n%*s%s]", 376 s, 8, "[", pe->metric_expr) < 0) 377 return; 378 } 379 } 380 381 if (!s) 382 continue; 383 384 if (!metricgroups) { 385 strlist__add(metriclist, s); 386 } else { 387 me = mep_lookup(&groups, g); 388 if (!me) 389 continue; 390 strlist__add(me->metrics, s); 391 } 392 } 393 free(omg); 394 } 395 } 396 397 if (metricgroups && !raw) 398 printf("\nMetric Groups:\n\n"); 399 else if (metrics && !raw) 400 printf("\nMetrics:\n\n"); 401 402 for (node = rb_first_cached(&groups.entries); node; node = next) { 403 struct mep *me = container_of(node, struct mep, nd); 404 405 if (metricgroups) 406 printf("%s%s%s", me->name, metrics && !raw ? ":" : "", raw ? " " : "\n"); 407 if (metrics) 408 metricgroup__print_strlist(me->metrics, raw); 409 next = rb_next(node); 410 rblist__remove_node(&groups, node); 411 } 412 if (!metricgroups) 413 metricgroup__print_strlist(metriclist, raw); 414 strlist__delete(metriclist); 415 } 416 417 static void metricgroup__add_metric_weak_group(struct strbuf *events, 418 const char **ids, 419 int idnum) 420 { 421 bool no_group = false; 422 int i; 423 424 for (i = 0; i < idnum; i++) { 425 pr_debug("found event %s\n", ids[i]); 426 /* 427 * Duration time maps to a software event and can make 428 * groups not count. Always use it outside a 429 * group. 430 */ 431 if (!strcmp(ids[i], "duration_time")) { 432 if (i > 0) 433 strbuf_addf(events, "}:W,"); 434 strbuf_addf(events, "duration_time"); 435 no_group = true; 436 continue; 437 } 438 strbuf_addf(events, "%s%s", 439 i == 0 || no_group ? "{" : ",", 440 ids[i]); 441 no_group = false; 442 } 443 if (!no_group) 444 strbuf_addf(events, "}:W"); 445 } 446 447 static void metricgroup__add_metric_non_group(struct strbuf *events, 448 const char **ids, 449 int idnum) 450 { 451 int i; 452 453 for (i = 0; i < idnum; i++) 454 strbuf_addf(events, ",%s", ids[i]); 455 } 456 457 static void metricgroup___watchdog_constraint_hint(const char *name, bool foot) 458 { 459 static bool violate_nmi_constraint; 460 461 if (!foot) { 462 pr_warning("Splitting metric group %s into standalone metrics.\n", name); 463 violate_nmi_constraint = true; 464 return; 465 } 466 467 if (!violate_nmi_constraint) 468 return; 469 470 pr_warning("Try disabling the NMI watchdog to comply NO_NMI_WATCHDOG metric constraint:\n" 471 " echo 0 > /proc/sys/kernel/nmi_watchdog\n" 472 " perf stat ...\n" 473 " echo 1 > /proc/sys/kernel/nmi_watchdog\n"); 474 } 475 476 static bool metricgroup__has_constraint(struct pmu_event *pe) 477 { 478 if (!pe->metric_constraint) 479 return false; 480 481 if (!strcmp(pe->metric_constraint, "NO_NMI_WATCHDOG") && 482 sysctl__nmi_watchdog_enabled()) { 483 metricgroup___watchdog_constraint_hint(pe->metric_name, false); 484 return true; 485 } 486 487 return false; 488 } 489 490 int __weak arch_get_runtimeparam(void) 491 { 492 return 1; 493 } 494 495 static int __metricgroup__add_metric(struct strbuf *events, 496 struct list_head *group_list, struct pmu_event *pe, int runtime) 497 { 498 499 const char **ids; 500 int idnum; 501 struct egroup *eg; 502 503 if (expr__find_other(pe->metric_expr, NULL, &ids, &idnum, runtime) < 0) 504 return -EINVAL; 505 506 if (events->len > 0) 507 strbuf_addf(events, ","); 508 509 if (metricgroup__has_constraint(pe)) 510 metricgroup__add_metric_non_group(events, ids, idnum); 511 else 512 metricgroup__add_metric_weak_group(events, ids, idnum); 513 514 eg = malloc(sizeof(*eg)); 515 if (!eg) 516 return -ENOMEM; 517 518 eg->ids = ids; 519 eg->idnum = idnum; 520 eg->metric_name = pe->metric_name; 521 eg->metric_expr = pe->metric_expr; 522 eg->metric_unit = pe->unit; 523 eg->runtime = runtime; 524 list_add_tail(&eg->nd, group_list); 525 526 return 0; 527 } 528 529 static int metricgroup__add_metric(const char *metric, struct strbuf *events, 530 struct list_head *group_list) 531 { 532 struct pmu_events_map *map = perf_pmu__find_map(NULL); 533 struct pmu_event *pe; 534 int i, ret = -EINVAL; 535 536 if (!map) 537 return 0; 538 539 for (i = 0; ; i++) { 540 pe = &map->table[i]; 541 542 if (!pe->name && !pe->metric_group && !pe->metric_name) 543 break; 544 if (!pe->metric_expr) 545 continue; 546 if (match_metric(pe->metric_group, metric) || 547 match_metric(pe->metric_name, metric)) { 548 549 pr_debug("metric expr %s for %s\n", pe->metric_expr, pe->metric_name); 550 551 if (!strstr(pe->metric_expr, "?")) { 552 ret = __metricgroup__add_metric(events, group_list, pe, 1); 553 } else { 554 int j, count; 555 556 count = arch_get_runtimeparam(); 557 558 /* This loop is added to create multiple 559 * events depend on count value and add 560 * those events to group_list. 561 */ 562 563 for (j = 0; j < count; j++) 564 ret = __metricgroup__add_metric(events, group_list, pe, j); 565 } 566 if (ret == -ENOMEM) 567 break; 568 } 569 } 570 return ret; 571 } 572 573 static int metricgroup__add_metric_list(const char *list, struct strbuf *events, 574 struct list_head *group_list) 575 { 576 char *llist, *nlist, *p; 577 int ret = -EINVAL; 578 579 nlist = strdup(list); 580 if (!nlist) 581 return -ENOMEM; 582 llist = nlist; 583 584 strbuf_init(events, 100); 585 strbuf_addf(events, "%s", ""); 586 587 while ((p = strsep(&llist, ",")) != NULL) { 588 ret = metricgroup__add_metric(p, events, group_list); 589 if (ret == -EINVAL) { 590 fprintf(stderr, "Cannot find metric or group `%s'\n", 591 p); 592 break; 593 } 594 } 595 free(nlist); 596 597 if (!ret) 598 metricgroup___watchdog_constraint_hint(NULL, true); 599 600 return ret; 601 } 602 603 static void metricgroup__free_egroups(struct list_head *group_list) 604 { 605 struct egroup *eg, *egtmp; 606 int i; 607 608 list_for_each_entry_safe (eg, egtmp, group_list, nd) { 609 for (i = 0; i < eg->idnum; i++) 610 zfree(&eg->ids[i]); 611 zfree(&eg->ids); 612 list_del_init(&eg->nd); 613 free(eg); 614 } 615 } 616 617 int metricgroup__parse_groups(const struct option *opt, 618 const char *str, 619 struct rblist *metric_events) 620 { 621 struct parse_events_error parse_error; 622 struct evlist *perf_evlist = *(struct evlist **)opt->value; 623 struct strbuf extra_events; 624 LIST_HEAD(group_list); 625 int ret; 626 627 if (metric_events->nr_entries == 0) 628 metricgroup__rblist_init(metric_events); 629 ret = metricgroup__add_metric_list(str, &extra_events, &group_list); 630 if (ret) 631 return ret; 632 pr_debug("adding %s\n", extra_events.buf); 633 bzero(&parse_error, sizeof(parse_error)); 634 ret = parse_events(perf_evlist, extra_events.buf, &parse_error); 635 if (ret) { 636 parse_events_print_error(&parse_error, extra_events.buf); 637 goto out; 638 } 639 strbuf_release(&extra_events); 640 ret = metricgroup__setup_events(&group_list, perf_evlist, 641 metric_events); 642 out: 643 metricgroup__free_egroups(&group_list); 644 return ret; 645 } 646 647 bool metricgroup__has_metric(const char *metric) 648 { 649 struct pmu_events_map *map = perf_pmu__find_map(NULL); 650 struct pmu_event *pe; 651 int i; 652 653 if (!map) 654 return false; 655 656 for (i = 0; ; i++) { 657 pe = &map->table[i]; 658 659 if (!pe->name && !pe->metric_group && !pe->metric_name) 660 break; 661 if (!pe->metric_expr) 662 continue; 663 if (match_metric(pe->metric_name, metric)) 664 return true; 665 } 666 return false; 667 } 668