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