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