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