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