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