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