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