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) 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 334 if (!s) 335 continue; 336 337 if (!metricgroups) { 338 strlist__add(metriclist, s); 339 } else { 340 me = mep_lookup(&groups, g); 341 if (!me) 342 continue; 343 strlist__add(me->metrics, s); 344 } 345 } 346 free(omg); 347 } 348 } 349 350 if (metricgroups && !raw) 351 printf("\nMetric Groups:\n\n"); 352 else if (metrics && !raw) 353 printf("\nMetrics:\n\n"); 354 355 for (node = rb_first(&groups.entries); node; node = next) { 356 struct mep *me = container_of(node, struct mep, nd); 357 358 if (metricgroups) 359 printf("%s%s%s", me->name, metrics ? ":" : "", raw ? " " : "\n"); 360 if (metrics) 361 metricgroup__print_strlist(me->metrics, raw); 362 next = rb_next(node); 363 rblist__remove_node(&groups, node); 364 } 365 if (!metricgroups) 366 metricgroup__print_strlist(metriclist, raw); 367 strlist__delete(metriclist); 368 } 369 370 static int metricgroup__add_metric(const char *metric, struct strbuf *events, 371 struct list_head *group_list) 372 { 373 struct pmu_events_map *map = perf_pmu__find_map(NULL); 374 struct pmu_event *pe; 375 int ret = -EINVAL; 376 int i, j; 377 378 if (!map) 379 return 0; 380 381 for (i = 0; ; i++) { 382 pe = &map->table[i]; 383 384 if (!pe->name && !pe->metric_group && !pe->metric_name) 385 break; 386 if (!pe->metric_expr) 387 continue; 388 if (match_metric(pe->metric_group, metric) || 389 match_metric(pe->metric_name, metric)) { 390 const char **ids; 391 int idnum; 392 struct egroup *eg; 393 394 pr_debug("metric expr %s for %s\n", pe->metric_expr, pe->metric_name); 395 396 if (expr__find_other(pe->metric_expr, 397 NULL, &ids, &idnum) < 0) 398 continue; 399 if (events->len > 0) 400 strbuf_addf(events, ","); 401 for (j = 0; j < idnum; j++) { 402 pr_debug("found event %s\n", ids[j]); 403 strbuf_addf(events, "%s%s", 404 j == 0 ? "{" : ",", 405 ids[j]); 406 } 407 strbuf_addf(events, "}:W"); 408 409 eg = malloc(sizeof(struct egroup)); 410 if (!eg) { 411 ret = -ENOMEM; 412 break; 413 } 414 eg->ids = ids; 415 eg->idnum = idnum; 416 eg->metric_name = pe->metric_name; 417 eg->metric_expr = pe->metric_expr; 418 list_add_tail(&eg->nd, group_list); 419 ret = 0; 420 } 421 } 422 return ret; 423 } 424 425 static int metricgroup__add_metric_list(const char *list, struct strbuf *events, 426 struct list_head *group_list) 427 { 428 char *llist, *nlist, *p; 429 int ret = -EINVAL; 430 431 nlist = strdup(list); 432 if (!nlist) 433 return -ENOMEM; 434 llist = nlist; 435 436 strbuf_init(events, 100); 437 strbuf_addf(events, "%s", ""); 438 439 while ((p = strsep(&llist, ",")) != NULL) { 440 ret = metricgroup__add_metric(p, events, group_list); 441 if (ret == -EINVAL) { 442 fprintf(stderr, "Cannot find metric or group `%s'\n", 443 p); 444 break; 445 } 446 } 447 free(nlist); 448 return ret; 449 } 450 451 static void metricgroup__free_egroups(struct list_head *group_list) 452 { 453 struct egroup *eg, *egtmp; 454 int i; 455 456 list_for_each_entry_safe (eg, egtmp, group_list, nd) { 457 for (i = 0; i < eg->idnum; i++) 458 free((char *)eg->ids[i]); 459 free(eg->ids); 460 free(eg); 461 } 462 } 463 464 int metricgroup__parse_groups(const struct option *opt, 465 const char *str, 466 struct rblist *metric_events) 467 { 468 struct parse_events_error parse_error; 469 struct perf_evlist *perf_evlist = *(struct perf_evlist **)opt->value; 470 struct strbuf extra_events; 471 LIST_HEAD(group_list); 472 int ret; 473 474 if (metric_events->nr_entries == 0) 475 metricgroup__rblist_init(metric_events); 476 ret = metricgroup__add_metric_list(str, &extra_events, &group_list); 477 if (ret) 478 return ret; 479 pr_debug("adding %s\n", extra_events.buf); 480 memset(&parse_error, 0, sizeof(struct parse_events_error)); 481 ret = parse_events(perf_evlist, extra_events.buf, &parse_error); 482 if (ret) { 483 parse_events_print_error(&parse_error, extra_events.buf); 484 goto out; 485 } 486 strbuf_release(&extra_events); 487 ret = metricgroup__setup_events(&group_list, perf_evlist, 488 metric_events); 489 out: 490 metricgroup__free_egroups(&group_list); 491 return ret; 492 } 493 494 bool metricgroup__has_metric(const char *metric) 495 { 496 struct pmu_events_map *map = perf_pmu__find_map(NULL); 497 struct pmu_event *pe; 498 int i; 499 500 if (!map) 501 return false; 502 503 for (i = 0; ; i++) { 504 pe = &map->table[i]; 505 506 if (!pe->name && !pe->metric_group && !pe->metric_name) 507 break; 508 if (!pe->metric_expr) 509 continue; 510 if (match_metric(pe->metric_name, metric)) 511 return true; 512 } 513 return false; 514 } 515