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