12025cf9eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2b18f3e36SAndi Kleen /* 3b18f3e36SAndi Kleen * Copyright (c) 2017, Intel Corporation. 4b18f3e36SAndi Kleen */ 5b18f3e36SAndi Kleen 6b18f3e36SAndi Kleen /* Manage metrics and groups of metrics from JSON files */ 7b18f3e36SAndi Kleen 8b18f3e36SAndi Kleen #include "metricgroup.h" 9b4209025SArnaldo Carvalho de Melo #include "debug.h" 10b18f3e36SAndi Kleen #include "evlist.h" 110b8026e8SArnaldo Carvalho de Melo #include "evsel.h" 12b18f3e36SAndi Kleen #include "strbuf.h" 13b18f3e36SAndi Kleen #include "pmu.h" 14b18f3e36SAndi Kleen #include "expr.h" 15b18f3e36SAndi Kleen #include "rblist.h" 16b18f3e36SAndi Kleen #include <string.h> 17b18f3e36SAndi Kleen #include <errno.h> 18b18f3e36SAndi Kleen #include "pmu-events/pmu-events.h" 19b18f3e36SAndi Kleen #include "strlist.h" 20b18f3e36SAndi Kleen #include <assert.h> 21bd9860bfSArnaldo Carvalho de Melo #include <linux/ctype.h> 22b4209025SArnaldo Carvalho de Melo #include <linux/string.h> 23d8f9da24SArnaldo Carvalho de Melo #include <linux/zalloc.h> 240b8026e8SArnaldo Carvalho de Melo #include <subcmd/parse-options.h> 25ab483d8bSKan Liang #include <api/fs/fs.h> 26ab483d8bSKan Liang #include "util.h" 27b18f3e36SAndi Kleen 28b18f3e36SAndi Kleen struct metric_event *metricgroup__lookup(struct rblist *metric_events, 2932dcd021SJiri Olsa struct evsel *evsel, 30b18f3e36SAndi Kleen bool create) 31b18f3e36SAndi Kleen { 32b18f3e36SAndi Kleen struct rb_node *nd; 33b18f3e36SAndi Kleen struct metric_event me = { 34b18f3e36SAndi Kleen .evsel = evsel 35b18f3e36SAndi Kleen }; 364bd1bef8SAndi Kleen 374bd1bef8SAndi Kleen if (!metric_events) 384bd1bef8SAndi Kleen return NULL; 394bd1bef8SAndi Kleen 40b18f3e36SAndi Kleen nd = rblist__find(metric_events, &me); 41b18f3e36SAndi Kleen if (nd) 42b18f3e36SAndi Kleen return container_of(nd, struct metric_event, nd); 43b18f3e36SAndi Kleen if (create) { 44b18f3e36SAndi Kleen rblist__add_node(metric_events, &me); 45b18f3e36SAndi Kleen nd = rblist__find(metric_events, &me); 46b18f3e36SAndi Kleen if (nd) 47b18f3e36SAndi Kleen return container_of(nd, struct metric_event, nd); 48b18f3e36SAndi Kleen } 49b18f3e36SAndi Kleen return NULL; 50b18f3e36SAndi Kleen } 51b18f3e36SAndi Kleen 52b18f3e36SAndi Kleen static int metric_event_cmp(struct rb_node *rb_node, const void *entry) 53b18f3e36SAndi Kleen { 54b18f3e36SAndi Kleen struct metric_event *a = container_of(rb_node, 55b18f3e36SAndi Kleen struct metric_event, 56b18f3e36SAndi Kleen nd); 57b18f3e36SAndi Kleen const struct metric_event *b = entry; 58b18f3e36SAndi Kleen 59b18f3e36SAndi Kleen if (a->evsel == b->evsel) 60b18f3e36SAndi Kleen return 0; 61b18f3e36SAndi Kleen if ((char *)a->evsel < (char *)b->evsel) 62b18f3e36SAndi Kleen return -1; 63b18f3e36SAndi Kleen return +1; 64b18f3e36SAndi Kleen } 65b18f3e36SAndi Kleen 66b18f3e36SAndi Kleen static struct rb_node *metric_event_new(struct rblist *rblist __maybe_unused, 67b18f3e36SAndi Kleen const void *entry) 68b18f3e36SAndi Kleen { 69b18f3e36SAndi Kleen struct metric_event *me = malloc(sizeof(struct metric_event)); 70b18f3e36SAndi Kleen 71b18f3e36SAndi Kleen if (!me) 72b18f3e36SAndi Kleen return NULL; 73b18f3e36SAndi Kleen memcpy(me, entry, sizeof(struct metric_event)); 74b18f3e36SAndi Kleen me->evsel = ((struct metric_event *)entry)->evsel; 75b18f3e36SAndi Kleen INIT_LIST_HEAD(&me->head); 76b18f3e36SAndi Kleen return &me->nd; 77b18f3e36SAndi Kleen } 78b18f3e36SAndi Kleen 799afe5658SJiri Olsa static void metric_event_delete(struct rblist *rblist __maybe_unused, 809afe5658SJiri Olsa struct rb_node *rb_node) 819afe5658SJiri Olsa { 829afe5658SJiri Olsa struct metric_event *me = container_of(rb_node, struct metric_event, nd); 839afe5658SJiri Olsa struct metric_expr *expr, *tmp; 849afe5658SJiri Olsa 859afe5658SJiri Olsa list_for_each_entry_safe(expr, tmp, &me->head, nd) { 86*4ea28967SJiri Olsa free(expr->metric_refs); 879afe5658SJiri Olsa free(expr); 889afe5658SJiri Olsa } 899afe5658SJiri Olsa 909afe5658SJiri Olsa free(me); 919afe5658SJiri Olsa } 929afe5658SJiri Olsa 93b18f3e36SAndi Kleen static void metricgroup__rblist_init(struct rblist *metric_events) 94b18f3e36SAndi Kleen { 95b18f3e36SAndi Kleen rblist__init(metric_events); 96b18f3e36SAndi Kleen metric_events->node_cmp = metric_event_cmp; 97b18f3e36SAndi Kleen metric_events->node_new = metric_event_new; 989afe5658SJiri Olsa metric_events->node_delete = metric_event_delete; 999afe5658SJiri Olsa } 1009afe5658SJiri Olsa 1019afe5658SJiri Olsa void metricgroup__rblist_exit(struct rblist *metric_events) 1029afe5658SJiri Olsa { 1039afe5658SJiri Olsa rblist__exit(metric_events); 104b18f3e36SAndi Kleen } 105b18f3e36SAndi Kleen 10683de0b7dSJiri Olsa /* 10783de0b7dSJiri Olsa * A node in the list of referenced metrics. metric_expr 10883de0b7dSJiri Olsa * is held as a convenience to avoid a search through the 10983de0b7dSJiri Olsa * metric list. 11083de0b7dSJiri Olsa */ 11183de0b7dSJiri Olsa struct metric_ref_node { 11283de0b7dSJiri Olsa const char *metric_name; 11383de0b7dSJiri Olsa const char *metric_expr; 11483de0b7dSJiri Olsa struct list_head list; 11583de0b7dSJiri Olsa }; 11683de0b7dSJiri Olsa 117b18f3e36SAndi Kleen struct egroup { 118b18f3e36SAndi Kleen struct list_head nd; 119ded80bdaSIan Rogers struct expr_parse_ctx pctx; 120b18f3e36SAndi Kleen const char *metric_name; 121b18f3e36SAndi Kleen const char *metric_expr; 122287f2649SJin Yao const char *metric_unit; 12383de0b7dSJiri Olsa struct list_head metric_refs; 12483de0b7dSJiri Olsa int metric_refs_cnt; 1251e1a873dSKajol Jain int runtime; 1267f9eca51SIan Rogers bool has_constraint; 127b18f3e36SAndi Kleen }; 128b18f3e36SAndi Kleen 1292440689dSIan Rogers /** 1302440689dSIan Rogers * Find a group of events in perf_evlist that correpond to those from a parsed 13105530a79SIan Rogers * metric expression. Note, as find_evsel_group is called in the same order as 13205530a79SIan Rogers * perf_evlist was constructed, metric_no_merge doesn't need to test for 13305530a79SIan Rogers * underfilling a group. 1342440689dSIan Rogers * @perf_evlist: a list of events something like: {metric1 leader, metric1 1352440689dSIan Rogers * sibling, metric1 sibling}:W,duration_time,{metric2 leader, metric2 sibling, 1362440689dSIan Rogers * metric2 sibling}:W,duration_time 1372440689dSIan Rogers * @pctx: the parse context for the metric expression. 13805530a79SIan Rogers * @metric_no_merge: don't attempt to share events for the metric with other 13905530a79SIan Rogers * metrics. 1402440689dSIan Rogers * @has_constraint: is there a contraint on the group of events? In which case 1412440689dSIan Rogers * the events won't be grouped. 1422440689dSIan Rogers * @metric_events: out argument, null terminated array of evsel's associated 1432440689dSIan Rogers * with the metric. 1442440689dSIan Rogers * @evlist_used: in/out argument, bitmap tracking which evlist events are used. 1452440689dSIan Rogers * @return the first metric event or NULL on failure. 1462440689dSIan Rogers */ 14763503dbaSJiri Olsa static struct evsel *find_evsel_group(struct evlist *perf_evlist, 148ded80bdaSIan Rogers struct expr_parse_ctx *pctx, 14905530a79SIan Rogers bool metric_no_merge, 1502440689dSIan Rogers bool has_constraint, 15158fc90fdSKajol Jain struct evsel **metric_events, 15245db55f2SIan Rogers unsigned long *evlist_used) 153b18f3e36SAndi Kleen { 1542440689dSIan Rogers struct evsel *ev, *current_leader = NULL; 155070b3b5aSJiri Olsa struct expr_id_data *val_ptr; 1562440689dSIan Rogers int i = 0, matched_events = 0, events_to_match; 1572440689dSIan Rogers const int idnum = (int)hashmap__size(&pctx->ids); 1582440689dSIan Rogers 1592440689dSIan Rogers /* duration_time is grouped separately. */ 1602440689dSIan Rogers if (!has_constraint && 1612440689dSIan Rogers hashmap__find(&pctx->ids, "duration_time", (void **)&val_ptr)) 1622440689dSIan Rogers events_to_match = idnum - 1; 1632440689dSIan Rogers else 1642440689dSIan Rogers events_to_match = idnum; 165b18f3e36SAndi Kleen 166b18f3e36SAndi Kleen evlist__for_each_entry (perf_evlist, ev) { 1672440689dSIan Rogers /* 1682440689dSIan Rogers * Events with a constraint aren't grouped and match the first 1692440689dSIan Rogers * events available. 1702440689dSIan Rogers */ 1712440689dSIan Rogers if (has_constraint && ev->weak_group) 17258fc90fdSKajol Jain continue; 17305530a79SIan Rogers /* Ignore event if already used and merging is disabled. */ 17405530a79SIan Rogers if (metric_no_merge && test_bit(ev->idx, evlist_used)) 17505530a79SIan Rogers continue; 1762440689dSIan Rogers if (!has_constraint && ev->leader != current_leader) { 1772440689dSIan Rogers /* 1782440689dSIan Rogers * Start of a new group, discard the whole match and 1792440689dSIan Rogers * start again. 1802440689dSIan Rogers */ 1812440689dSIan Rogers matched_events = 0; 182f01642e4SJin Yao memset(metric_events, 0, 183f01642e4SJin Yao sizeof(struct evsel *) * idnum); 1842440689dSIan Rogers current_leader = ev->leader; 1852440689dSIan Rogers } 18605530a79SIan Rogers if (hashmap__find(&pctx->ids, ev->name, (void **)&val_ptr)) { 18705530a79SIan Rogers if (has_constraint) { 18805530a79SIan Rogers /* 18905530a79SIan Rogers * Events aren't grouped, ensure the same event 19005530a79SIan Rogers * isn't matched from two groups. 19105530a79SIan Rogers */ 19205530a79SIan Rogers for (i = 0; i < matched_events; i++) { 19305530a79SIan Rogers if (!strcmp(ev->name, 19405530a79SIan Rogers metric_events[i]->name)) { 19505530a79SIan Rogers break; 19605530a79SIan Rogers } 19705530a79SIan Rogers } 19805530a79SIan Rogers if (i != matched_events) 19905530a79SIan Rogers continue; 20005530a79SIan Rogers } 2012440689dSIan Rogers metric_events[matched_events++] = ev; 20205530a79SIan Rogers } 2032440689dSIan Rogers if (matched_events == events_to_match) 2042440689dSIan Rogers break; 2052440689dSIan Rogers } 2062440689dSIan Rogers 2072440689dSIan Rogers if (events_to_match != idnum) { 2082440689dSIan Rogers /* Add the first duration_time. */ 2092440689dSIan Rogers evlist__for_each_entry(perf_evlist, ev) { 2102440689dSIan Rogers if (!strcmp(ev->name, "duration_time")) { 2112440689dSIan Rogers metric_events[matched_events++] = ev; 2122440689dSIan Rogers break; 2132440689dSIan Rogers } 214b18f3e36SAndi Kleen } 215b18f3e36SAndi Kleen } 216f01642e4SJin Yao 2172440689dSIan Rogers if (matched_events != idnum) { 218f01642e4SJin Yao /* Not whole match */ 219b18f3e36SAndi Kleen return NULL; 220b18f3e36SAndi Kleen } 221b18f3e36SAndi Kleen 222f01642e4SJin Yao metric_events[idnum] = NULL; 223f01642e4SJin Yao 224f01642e4SJin Yao for (i = 0; i < idnum; i++) { 22558fc90fdSKajol Jain ev = metric_events[i]; 2262440689dSIan Rogers ev->metric_leader = ev; 22745db55f2SIan Rogers set_bit(ev->idx, evlist_used); 228f01642e4SJin Yao } 229f01642e4SJin Yao 230f01642e4SJin Yao return metric_events[0]; 231f01642e4SJin Yao } 232f01642e4SJin Yao 233b18f3e36SAndi Kleen static int metricgroup__setup_events(struct list_head *groups, 23405530a79SIan Rogers bool metric_no_merge, 23563503dbaSJiri Olsa struct evlist *perf_evlist, 236b18f3e36SAndi Kleen struct rblist *metric_events_list) 237b18f3e36SAndi Kleen { 238b18f3e36SAndi Kleen struct metric_event *me; 239b18f3e36SAndi Kleen struct metric_expr *expr; 240b18f3e36SAndi Kleen int i = 0; 241b18f3e36SAndi Kleen int ret = 0; 242b18f3e36SAndi Kleen struct egroup *eg; 2432440689dSIan Rogers struct evsel *evsel, *tmp; 24445db55f2SIan Rogers unsigned long *evlist_used; 24558fc90fdSKajol Jain 24645db55f2SIan Rogers evlist_used = bitmap_alloc(perf_evlist->core.nr_entries); 24745db55f2SIan Rogers if (!evlist_used) 24845db55f2SIan Rogers return -ENOMEM; 249b18f3e36SAndi Kleen 250b18f3e36SAndi Kleen list_for_each_entry (eg, groups, nd) { 25132dcd021SJiri Olsa struct evsel **metric_events; 252*4ea28967SJiri Olsa struct metric_ref *metric_refs = NULL; 253b18f3e36SAndi Kleen 254ded80bdaSIan Rogers metric_events = calloc(sizeof(void *), 255ded80bdaSIan Rogers hashmap__size(&eg->pctx.ids) + 1); 256b18f3e36SAndi Kleen if (!metric_events) { 257b18f3e36SAndi Kleen ret = -ENOMEM; 258b18f3e36SAndi Kleen break; 259b18f3e36SAndi Kleen } 2602440689dSIan Rogers evsel = find_evsel_group(perf_evlist, &eg->pctx, 26105530a79SIan Rogers metric_no_merge, 2622440689dSIan Rogers eg->has_constraint, metric_events, 263ded80bdaSIan Rogers evlist_used); 264b18f3e36SAndi Kleen if (!evsel) { 265b18f3e36SAndi Kleen pr_debug("Cannot resolve %s: %s\n", 266b18f3e36SAndi Kleen eg->metric_name, eg->metric_expr); 267a159e2feSIan Rogers free(metric_events); 268b18f3e36SAndi Kleen continue; 269b18f3e36SAndi Kleen } 270ded80bdaSIan Rogers for (i = 0; metric_events[i]; i++) 271b18f3e36SAndi Kleen metric_events[i]->collect_stat = true; 272b18f3e36SAndi Kleen me = metricgroup__lookup(metric_events_list, evsel, true); 273b18f3e36SAndi Kleen if (!me) { 274b18f3e36SAndi Kleen ret = -ENOMEM; 275a159e2feSIan Rogers free(metric_events); 276b18f3e36SAndi Kleen break; 277b18f3e36SAndi Kleen } 278b18f3e36SAndi Kleen expr = malloc(sizeof(struct metric_expr)); 279b18f3e36SAndi Kleen if (!expr) { 280b18f3e36SAndi Kleen ret = -ENOMEM; 281a159e2feSIan Rogers free(metric_events); 282b18f3e36SAndi Kleen break; 283b18f3e36SAndi Kleen } 284*4ea28967SJiri Olsa 285*4ea28967SJiri Olsa /* 286*4ea28967SJiri Olsa * Collect and store collected nested expressions 287*4ea28967SJiri Olsa * for metric processing. 288*4ea28967SJiri Olsa */ 289*4ea28967SJiri Olsa if (eg->metric_refs_cnt) { 290*4ea28967SJiri Olsa struct metric_ref_node *ref; 291*4ea28967SJiri Olsa 292*4ea28967SJiri Olsa metric_refs = zalloc(sizeof(struct metric_ref) * (eg->metric_refs_cnt + 1)); 293*4ea28967SJiri Olsa if (!metric_refs) { 294*4ea28967SJiri Olsa ret = -ENOMEM; 295*4ea28967SJiri Olsa free(metric_events); 296*4ea28967SJiri Olsa break; 297*4ea28967SJiri Olsa } 298*4ea28967SJiri Olsa 299*4ea28967SJiri Olsa i = 0; 300*4ea28967SJiri Olsa list_for_each_entry(ref, &eg->metric_refs, list) { 301*4ea28967SJiri Olsa /* 302*4ea28967SJiri Olsa * Intentionally passing just const char pointers, 303*4ea28967SJiri Olsa * originally from 'struct pmu_event' object. 304*4ea28967SJiri Olsa * We don't need to change them, so there's no 305*4ea28967SJiri Olsa * need to create our own copy. 306*4ea28967SJiri Olsa */ 307*4ea28967SJiri Olsa metric_refs[i].metric_name = ref->metric_name; 308*4ea28967SJiri Olsa metric_refs[i].metric_expr = ref->metric_expr; 309*4ea28967SJiri Olsa i++; 310*4ea28967SJiri Olsa } 311*4ea28967SJiri Olsa }; 312*4ea28967SJiri Olsa 313*4ea28967SJiri Olsa expr->metric_refs = metric_refs; 314b18f3e36SAndi Kleen expr->metric_expr = eg->metric_expr; 315b18f3e36SAndi Kleen expr->metric_name = eg->metric_name; 316287f2649SJin Yao expr->metric_unit = eg->metric_unit; 317b18f3e36SAndi Kleen expr->metric_events = metric_events; 3181e1a873dSKajol Jain expr->runtime = eg->runtime; 319b18f3e36SAndi Kleen list_add(&expr->nd, &me->head); 320b18f3e36SAndi Kleen } 32158fc90fdSKajol Jain 3222440689dSIan Rogers evlist__for_each_entry_safe(perf_evlist, tmp, evsel) { 3232440689dSIan Rogers if (!test_bit(evsel->idx, evlist_used)) { 3242440689dSIan Rogers evlist__remove(perf_evlist, evsel); 3252440689dSIan Rogers evsel__delete(evsel); 3262440689dSIan Rogers } 3272440689dSIan Rogers } 32845db55f2SIan Rogers bitmap_free(evlist_used); 32958fc90fdSKajol Jain 330b18f3e36SAndi Kleen return ret; 331b18f3e36SAndi Kleen } 332b18f3e36SAndi Kleen 333b18f3e36SAndi Kleen static bool match_metric(const char *n, const char *list) 334b18f3e36SAndi Kleen { 335b18f3e36SAndi Kleen int len; 336b18f3e36SAndi Kleen char *m; 337b18f3e36SAndi Kleen 338b18f3e36SAndi Kleen if (!list) 339b18f3e36SAndi Kleen return false; 340b18f3e36SAndi Kleen if (!strcmp(list, "all")) 341b18f3e36SAndi Kleen return true; 342b18f3e36SAndi Kleen if (!n) 343b18f3e36SAndi Kleen return !strcasecmp(list, "No_group"); 344b18f3e36SAndi Kleen len = strlen(list); 345b18f3e36SAndi Kleen m = strcasestr(n, list); 346b18f3e36SAndi Kleen if (!m) 347b18f3e36SAndi Kleen return false; 348b18f3e36SAndi Kleen if ((m == n || m[-1] == ';' || m[-1] == ' ') && 349b18f3e36SAndi Kleen (m[len] == 0 || m[len] == ';')) 350b18f3e36SAndi Kleen return true; 351b18f3e36SAndi Kleen return false; 352b18f3e36SAndi Kleen } 353b18f3e36SAndi Kleen 35471b0acceSAndi Kleen struct mep { 35571b0acceSAndi Kleen struct rb_node nd; 35671b0acceSAndi Kleen const char *name; 35771b0acceSAndi Kleen struct strlist *metrics; 35871b0acceSAndi Kleen }; 35971b0acceSAndi Kleen 36071b0acceSAndi Kleen static int mep_cmp(struct rb_node *rb_node, const void *entry) 36171b0acceSAndi Kleen { 36271b0acceSAndi Kleen struct mep *a = container_of(rb_node, struct mep, nd); 36371b0acceSAndi Kleen struct mep *b = (struct mep *)entry; 36471b0acceSAndi Kleen 36571b0acceSAndi Kleen return strcmp(a->name, b->name); 36671b0acceSAndi Kleen } 36771b0acceSAndi Kleen 36871b0acceSAndi Kleen static struct rb_node *mep_new(struct rblist *rl __maybe_unused, 36971b0acceSAndi Kleen const void *entry) 37071b0acceSAndi Kleen { 37171b0acceSAndi Kleen struct mep *me = malloc(sizeof(struct mep)); 37271b0acceSAndi Kleen 37371b0acceSAndi Kleen if (!me) 37471b0acceSAndi Kleen return NULL; 37571b0acceSAndi Kleen memcpy(me, entry, sizeof(struct mep)); 37671b0acceSAndi Kleen me->name = strdup(me->name); 37771b0acceSAndi Kleen if (!me->name) 37871b0acceSAndi Kleen goto out_me; 37971b0acceSAndi Kleen me->metrics = strlist__new(NULL, NULL); 38071b0acceSAndi Kleen if (!me->metrics) 38171b0acceSAndi Kleen goto out_name; 38271b0acceSAndi Kleen return &me->nd; 38371b0acceSAndi Kleen out_name: 384d8f9da24SArnaldo Carvalho de Melo zfree(&me->name); 38571b0acceSAndi Kleen out_me: 38671b0acceSAndi Kleen free(me); 38771b0acceSAndi Kleen return NULL; 38871b0acceSAndi Kleen } 38971b0acceSAndi Kleen 39071b0acceSAndi Kleen static struct mep *mep_lookup(struct rblist *groups, const char *name) 39171b0acceSAndi Kleen { 39271b0acceSAndi Kleen struct rb_node *nd; 39371b0acceSAndi Kleen struct mep me = { 39471b0acceSAndi Kleen .name = name 39571b0acceSAndi Kleen }; 39671b0acceSAndi Kleen nd = rblist__find(groups, &me); 39771b0acceSAndi Kleen if (nd) 39871b0acceSAndi Kleen return container_of(nd, struct mep, nd); 39971b0acceSAndi Kleen rblist__add_node(groups, &me); 40071b0acceSAndi Kleen nd = rblist__find(groups, &me); 40171b0acceSAndi Kleen if (nd) 40271b0acceSAndi Kleen return container_of(nd, struct mep, nd); 40371b0acceSAndi Kleen return NULL; 40471b0acceSAndi Kleen } 40571b0acceSAndi Kleen 40671b0acceSAndi Kleen static void mep_delete(struct rblist *rl __maybe_unused, 40771b0acceSAndi Kleen struct rb_node *nd) 40871b0acceSAndi Kleen { 40971b0acceSAndi Kleen struct mep *me = container_of(nd, struct mep, nd); 41071b0acceSAndi Kleen 41171b0acceSAndi Kleen strlist__delete(me->metrics); 412d8f9da24SArnaldo Carvalho de Melo zfree(&me->name); 41371b0acceSAndi Kleen free(me); 41471b0acceSAndi Kleen } 41571b0acceSAndi Kleen 41671b0acceSAndi Kleen static void metricgroup__print_strlist(struct strlist *metrics, bool raw) 41771b0acceSAndi Kleen { 41871b0acceSAndi Kleen struct str_node *sn; 41971b0acceSAndi Kleen int n = 0; 42071b0acceSAndi Kleen 42171b0acceSAndi Kleen strlist__for_each_entry (sn, metrics) { 42271b0acceSAndi Kleen if (raw) 42371b0acceSAndi Kleen printf("%s%s", n > 0 ? " " : "", sn->s); 42471b0acceSAndi Kleen else 42571b0acceSAndi Kleen printf(" %s\n", sn->s); 42671b0acceSAndi Kleen n++; 42771b0acceSAndi Kleen } 42871b0acceSAndi Kleen if (raw) 42971b0acceSAndi Kleen putchar('\n'); 43071b0acceSAndi Kleen } 43171b0acceSAndi Kleen 43271b0acceSAndi Kleen void metricgroup__print(bool metrics, bool metricgroups, char *filter, 43333bbc571SJiri Olsa bool raw, bool details) 43471b0acceSAndi Kleen { 43554e32dc0SGanapatrao Kulkarni struct pmu_events_map *map = perf_pmu__find_map(NULL); 43671b0acceSAndi Kleen struct pmu_event *pe; 43771b0acceSAndi Kleen int i; 43871b0acceSAndi Kleen struct rblist groups; 43971b0acceSAndi Kleen struct rb_node *node, *next; 44071b0acceSAndi Kleen struct strlist *metriclist = NULL; 44171b0acceSAndi Kleen 44271b0acceSAndi Kleen if (!map) 44371b0acceSAndi Kleen return; 44471b0acceSAndi Kleen 44571b0acceSAndi Kleen if (!metricgroups) { 44671b0acceSAndi Kleen metriclist = strlist__new(NULL, NULL); 44771b0acceSAndi Kleen if (!metriclist) 44871b0acceSAndi Kleen return; 44971b0acceSAndi Kleen } 45071b0acceSAndi Kleen 45171b0acceSAndi Kleen rblist__init(&groups); 45271b0acceSAndi Kleen groups.node_new = mep_new; 45371b0acceSAndi Kleen groups.node_cmp = mep_cmp; 45471b0acceSAndi Kleen groups.node_delete = mep_delete; 45571b0acceSAndi Kleen for (i = 0; ; i++) { 45671b0acceSAndi Kleen const char *g; 45771b0acceSAndi Kleen pe = &map->table[i]; 45871b0acceSAndi Kleen 45971b0acceSAndi Kleen if (!pe->name && !pe->metric_group && !pe->metric_name) 46071b0acceSAndi Kleen break; 46171b0acceSAndi Kleen if (!pe->metric_expr) 46271b0acceSAndi Kleen continue; 46371b0acceSAndi Kleen g = pe->metric_group; 46471b0acceSAndi Kleen if (!g && pe->metric_name) { 46571b0acceSAndi Kleen if (pe->name) 46671b0acceSAndi Kleen continue; 46771b0acceSAndi Kleen g = "No_group"; 46871b0acceSAndi Kleen } 46971b0acceSAndi Kleen if (g) { 47071b0acceSAndi Kleen char *omg; 47171b0acceSAndi Kleen char *mg = strdup(g); 47271b0acceSAndi Kleen 47371b0acceSAndi Kleen if (!mg) 47471b0acceSAndi Kleen return; 47571b0acceSAndi Kleen omg = mg; 47671b0acceSAndi Kleen while ((g = strsep(&mg, ";")) != NULL) { 47771b0acceSAndi Kleen struct mep *me; 47871b0acceSAndi Kleen char *s; 47971b0acceSAndi Kleen 48080e9073fSArnaldo Carvalho de Melo g = skip_spaces(g); 48171b0acceSAndi Kleen if (*g == 0) 48271b0acceSAndi Kleen g = "No_group"; 48371b0acceSAndi Kleen if (filter && !strstr(g, filter)) 48471b0acceSAndi Kleen continue; 48571b0acceSAndi Kleen if (raw) 48671b0acceSAndi Kleen s = (char *)pe->metric_name; 48771b0acceSAndi Kleen else { 48895f04328SMichael Petlan if (asprintf(&s, "%s\n%*s%s]", 48995f04328SMichael Petlan pe->metric_name, 8, "[", pe->desc) < 0) 49071b0acceSAndi Kleen return; 49133bbc571SJiri Olsa 49233bbc571SJiri Olsa if (details) { 49333bbc571SJiri Olsa if (asprintf(&s, "%s\n%*s%s]", 49433bbc571SJiri Olsa s, 8, "[", pe->metric_expr) < 0) 49533bbc571SJiri Olsa return; 49633bbc571SJiri Olsa } 49771b0acceSAndi Kleen } 49871b0acceSAndi Kleen 49971b0acceSAndi Kleen if (!s) 50071b0acceSAndi Kleen continue; 50171b0acceSAndi Kleen 50271b0acceSAndi Kleen if (!metricgroups) { 50371b0acceSAndi Kleen strlist__add(metriclist, s); 50471b0acceSAndi Kleen } else { 50571b0acceSAndi Kleen me = mep_lookup(&groups, g); 50671b0acceSAndi Kleen if (!me) 50771b0acceSAndi Kleen continue; 50871b0acceSAndi Kleen strlist__add(me->metrics, s); 50971b0acceSAndi Kleen } 51071b0acceSAndi Kleen } 51171b0acceSAndi Kleen free(omg); 51271b0acceSAndi Kleen } 51371b0acceSAndi Kleen } 51471b0acceSAndi Kleen 51571b0acceSAndi Kleen if (metricgroups && !raw) 51671b0acceSAndi Kleen printf("\nMetric Groups:\n\n"); 51771b0acceSAndi Kleen else if (metrics && !raw) 51871b0acceSAndi Kleen printf("\nMetrics:\n\n"); 51971b0acceSAndi Kleen 520ca227029SDavidlohr Bueso for (node = rb_first_cached(&groups.entries); node; node = next) { 52171b0acceSAndi Kleen struct mep *me = container_of(node, struct mep, nd); 52271b0acceSAndi Kleen 52371b0acceSAndi Kleen if (metricgroups) 5249c344d15SAndi Kleen printf("%s%s%s", me->name, metrics && !raw ? ":" : "", raw ? " " : "\n"); 52571b0acceSAndi Kleen if (metrics) 52671b0acceSAndi Kleen metricgroup__print_strlist(me->metrics, raw); 52771b0acceSAndi Kleen next = rb_next(node); 52871b0acceSAndi Kleen rblist__remove_node(&groups, node); 52971b0acceSAndi Kleen } 53071b0acceSAndi Kleen if (!metricgroups) 53171b0acceSAndi Kleen metricgroup__print_strlist(metriclist, raw); 53271b0acceSAndi Kleen strlist__delete(metriclist); 53371b0acceSAndi Kleen } 53471b0acceSAndi Kleen 535f742634aSKan Liang static void metricgroup__add_metric_weak_group(struct strbuf *events, 536ded80bdaSIan Rogers struct expr_parse_ctx *ctx) 537f742634aSKan Liang { 538ded80bdaSIan Rogers struct hashmap_entry *cur; 5394e21c13aSIan Rogers size_t bkt; 5404e21c13aSIan Rogers bool no_group = true, has_duration = false; 541f742634aSKan Liang 542ded80bdaSIan Rogers hashmap__for_each_entry((&ctx->ids), cur, bkt) { 543ded80bdaSIan Rogers pr_debug("found event %s\n", (const char *)cur->key); 544f742634aSKan Liang /* 545f742634aSKan Liang * Duration time maps to a software event and can make 546f742634aSKan Liang * groups not count. Always use it outside a 547f742634aSKan Liang * group. 548f742634aSKan Liang */ 549ded80bdaSIan Rogers if (!strcmp(cur->key, "duration_time")) { 5504e21c13aSIan Rogers has_duration = true; 551f742634aSKan Liang continue; 552f742634aSKan Liang } 553f742634aSKan Liang strbuf_addf(events, "%s%s", 5544e21c13aSIan Rogers no_group ? "{" : ",", 555ded80bdaSIan Rogers (const char *)cur->key); 556f742634aSKan Liang no_group = false; 557f742634aSKan Liang } 5584e21c13aSIan Rogers if (!no_group) { 559f742634aSKan Liang strbuf_addf(events, "}:W"); 5604e21c13aSIan Rogers if (has_duration) 5614e21c13aSIan Rogers strbuf_addf(events, ",duration_time"); 5624e21c13aSIan Rogers } else if (has_duration) 5634e21c13aSIan Rogers strbuf_addf(events, "duration_time"); 564f742634aSKan Liang } 565f742634aSKan Liang 566ab483d8bSKan Liang static void metricgroup__add_metric_non_group(struct strbuf *events, 567ded80bdaSIan Rogers struct expr_parse_ctx *ctx) 568ab483d8bSKan Liang { 569ded80bdaSIan Rogers struct hashmap_entry *cur; 570ded80bdaSIan Rogers size_t bkt; 571e2ce1059SIan Rogers bool first = true; 572ab483d8bSKan Liang 573e2ce1059SIan Rogers hashmap__for_each_entry((&ctx->ids), cur, bkt) { 574e2ce1059SIan Rogers if (!first) 575e2ce1059SIan Rogers strbuf_addf(events, ","); 576e2ce1059SIan Rogers strbuf_addf(events, "%s", (const char *)cur->key); 577e2ce1059SIan Rogers first = false; 578e2ce1059SIan Rogers } 579ab483d8bSKan Liang } 580ab483d8bSKan Liang 581ab483d8bSKan Liang static void metricgroup___watchdog_constraint_hint(const char *name, bool foot) 582ab483d8bSKan Liang { 583ab483d8bSKan Liang static bool violate_nmi_constraint; 584ab483d8bSKan Liang 585ab483d8bSKan Liang if (!foot) { 586ab483d8bSKan Liang pr_warning("Splitting metric group %s into standalone metrics.\n", name); 587ab483d8bSKan Liang violate_nmi_constraint = true; 588ab483d8bSKan Liang return; 589ab483d8bSKan Liang } 590ab483d8bSKan Liang 591ab483d8bSKan Liang if (!violate_nmi_constraint) 592ab483d8bSKan Liang return; 593ab483d8bSKan Liang 594ab483d8bSKan Liang pr_warning("Try disabling the NMI watchdog to comply NO_NMI_WATCHDOG metric constraint:\n" 595ab483d8bSKan Liang " echo 0 > /proc/sys/kernel/nmi_watchdog\n" 596ab483d8bSKan Liang " perf stat ...\n" 597ab483d8bSKan Liang " echo 1 > /proc/sys/kernel/nmi_watchdog\n"); 598ab483d8bSKan Liang } 599ab483d8bSKan Liang 600ab483d8bSKan Liang static bool metricgroup__has_constraint(struct pmu_event *pe) 601ab483d8bSKan Liang { 602ab483d8bSKan Liang if (!pe->metric_constraint) 603ab483d8bSKan Liang return false; 604ab483d8bSKan Liang 605ab483d8bSKan Liang if (!strcmp(pe->metric_constraint, "NO_NMI_WATCHDOG") && 606ab483d8bSKan Liang sysctl__nmi_watchdog_enabled()) { 607ab483d8bSKan Liang metricgroup___watchdog_constraint_hint(pe->metric_name, false); 608ab483d8bSKan Liang return true; 609ab483d8bSKan Liang } 610ab483d8bSKan Liang 611ab483d8bSKan Liang return false; 612ab483d8bSKan Liang } 613ab483d8bSKan Liang 6141e1a873dSKajol Jain int __weak arch_get_runtimeparam(void) 6151e1a873dSKajol Jain { 6161e1a873dSKajol Jain return 1; 6171e1a873dSKajol Jain } 6181e1a873dSKajol Jain 619e7e1baddSJiri Olsa static int __add_metric(struct list_head *group_list, 62005530a79SIan Rogers struct pmu_event *pe, 62105530a79SIan Rogers bool metric_no_group, 62283de0b7dSJiri Olsa int runtime, 62383de0b7dSJiri Olsa struct egroup **egp) 62447352abaSKajol Jain { 62583de0b7dSJiri Olsa struct metric_ref_node *ref; 62647352abaSKajol Jain struct egroup *eg; 62747352abaSKajol Jain 62883de0b7dSJiri Olsa if (*egp == NULL) { 62983de0b7dSJiri Olsa /* 63083de0b7dSJiri Olsa * We got in here for the parent group, 63183de0b7dSJiri Olsa * allocate it and put it on the list. 63283de0b7dSJiri Olsa */ 63347352abaSKajol Jain eg = malloc(sizeof(*eg)); 63447352abaSKajol Jain if (!eg) 63547352abaSKajol Jain return -ENOMEM; 63647352abaSKajol Jain 637ded80bdaSIan Rogers expr__ctx_init(&eg->pctx); 63847352abaSKajol Jain eg->metric_name = pe->metric_name; 63947352abaSKajol Jain eg->metric_expr = pe->metric_expr; 64047352abaSKajol Jain eg->metric_unit = pe->unit; 6411e1a873dSKajol Jain eg->runtime = runtime; 64205530a79SIan Rogers eg->has_constraint = metric_no_group || metricgroup__has_constraint(pe); 64383de0b7dSJiri Olsa INIT_LIST_HEAD(&eg->metric_refs); 64483de0b7dSJiri Olsa eg->metric_refs_cnt = 0; 64583de0b7dSJiri Olsa *egp = eg; 64683de0b7dSJiri Olsa } else { 64783de0b7dSJiri Olsa /* 64883de0b7dSJiri Olsa * We got here for the referenced metric, via the 64983de0b7dSJiri Olsa * recursive metricgroup__add_metric call, add 65083de0b7dSJiri Olsa * it to the parent group. 65183de0b7dSJiri Olsa */ 65283de0b7dSJiri Olsa eg = *egp; 653ded80bdaSIan Rogers 65483de0b7dSJiri Olsa ref = malloc(sizeof(*ref)); 65583de0b7dSJiri Olsa if (!ref) 65683de0b7dSJiri Olsa return -ENOMEM; 65783de0b7dSJiri Olsa 65883de0b7dSJiri Olsa /* 65983de0b7dSJiri Olsa * Intentionally passing just const char pointers, 66083de0b7dSJiri Olsa * from 'pe' object, so they never go away. We don't 66183de0b7dSJiri Olsa * need to change them, so there's no need to create 66283de0b7dSJiri Olsa * our own copy. 66383de0b7dSJiri Olsa */ 66483de0b7dSJiri Olsa ref->metric_name = pe->metric_name; 66583de0b7dSJiri Olsa ref->metric_expr = pe->metric_expr; 66683de0b7dSJiri Olsa 66783de0b7dSJiri Olsa list_add(&ref->list, &eg->metric_refs); 66883de0b7dSJiri Olsa eg->metric_refs_cnt++; 66983de0b7dSJiri Olsa } 67083de0b7dSJiri Olsa 67183de0b7dSJiri Olsa /* 67283de0b7dSJiri Olsa * For both the parent and referenced metrics, we parse 67383de0b7dSJiri Olsa * all the metric's IDs and add it to the parent context. 67483de0b7dSJiri Olsa */ 675ded80bdaSIan Rogers if (expr__find_other(pe->metric_expr, NULL, &eg->pctx, runtime) < 0) { 676ded80bdaSIan Rogers expr__ctx_clear(&eg->pctx); 677ded80bdaSIan Rogers free(eg); 678ded80bdaSIan Rogers return -EINVAL; 679ded80bdaSIan Rogers } 680ded80bdaSIan Rogers 68183de0b7dSJiri Olsa /* 68283de0b7dSJiri Olsa * We add new group only in the 'parent' call, 68383de0b7dSJiri Olsa * so bail out for referenced metric case. 68483de0b7dSJiri Olsa */ 68583de0b7dSJiri Olsa if (eg->metric_refs_cnt) 68683de0b7dSJiri Olsa return 0; 68783de0b7dSJiri Olsa 6886bf2102bSIan Rogers if (list_empty(group_list)) 6896bf2102bSIan Rogers list_add(&eg->nd, group_list); 6906bf2102bSIan Rogers else { 6916bf2102bSIan Rogers struct list_head *pos; 6926bf2102bSIan Rogers 6936bf2102bSIan Rogers /* Place the largest groups at the front. */ 6946bf2102bSIan Rogers list_for_each_prev(pos, group_list) { 6956bf2102bSIan Rogers struct egroup *old = list_entry(pos, struct egroup, nd); 6966bf2102bSIan Rogers 6976bf2102bSIan Rogers if (hashmap__size(&eg->pctx.ids) <= 6986bf2102bSIan Rogers hashmap__size(&old->pctx.ids)) 6996bf2102bSIan Rogers break; 7006bf2102bSIan Rogers } 7016bf2102bSIan Rogers list_add(&eg->nd, pos); 7026bf2102bSIan Rogers } 70347352abaSKajol Jain 70447352abaSKajol Jain return 0; 70547352abaSKajol Jain } 70647352abaSKajol Jain 707ce391940SJiri Olsa #define map_for_each_event(__pe, __idx, __map) \ 708ce391940SJiri Olsa for (__idx = 0, __pe = &__map->table[__idx]; \ 709ce391940SJiri Olsa __pe->name || __pe->metric_group || __pe->metric_name; \ 710ce391940SJiri Olsa __pe = &__map->table[++__idx]) 711ce391940SJiri Olsa 712ce391940SJiri Olsa #define map_for_each_metric(__pe, __idx, __map, __metric) \ 713ce391940SJiri Olsa map_for_each_event(__pe, __idx, __map) \ 714ce391940SJiri Olsa if (__pe->metric_expr && \ 715ce391940SJiri Olsa (match_metric(__pe->metric_group, __metric) || \ 716ce391940SJiri Olsa match_metric(__pe->metric_name, __metric))) 717ce391940SJiri Olsa 71883de0b7dSJiri Olsa static struct pmu_event *find_metric(const char *metric, struct pmu_events_map *map) 71983de0b7dSJiri Olsa { 72083de0b7dSJiri Olsa struct pmu_event *pe; 72183de0b7dSJiri Olsa int i; 72283de0b7dSJiri Olsa 72383de0b7dSJiri Olsa map_for_each_event(pe, i, map) { 72483de0b7dSJiri Olsa if (match_metric(pe->metric_name, metric)) 72583de0b7dSJiri Olsa return pe; 72683de0b7dSJiri Olsa } 72783de0b7dSJiri Olsa 72883de0b7dSJiri Olsa return NULL; 72983de0b7dSJiri Olsa } 73083de0b7dSJiri Olsa 731a29c164aSJiri Olsa static int add_metric(struct list_head *group_list, 732a29c164aSJiri Olsa struct pmu_event *pe, 73383de0b7dSJiri Olsa bool metric_no_group, 73483de0b7dSJiri Olsa struct egroup **egp); 73583de0b7dSJiri Olsa 73683de0b7dSJiri Olsa static int __resolve_metric(struct egroup *eg, 73783de0b7dSJiri Olsa bool metric_no_group, 73883de0b7dSJiri Olsa struct list_head *group_list, 73983de0b7dSJiri Olsa struct pmu_events_map *map) 740a29c164aSJiri Olsa { 74183de0b7dSJiri Olsa struct hashmap_entry *cur; 74283de0b7dSJiri Olsa size_t bkt; 74383de0b7dSJiri Olsa bool all; 74483de0b7dSJiri Olsa int ret; 74583de0b7dSJiri Olsa 74683de0b7dSJiri Olsa /* 74783de0b7dSJiri Olsa * Iterate all the parsed IDs and if there's metric, 74883de0b7dSJiri Olsa * add it to the context. 74983de0b7dSJiri Olsa */ 75083de0b7dSJiri Olsa do { 75183de0b7dSJiri Olsa all = true; 75283de0b7dSJiri Olsa hashmap__for_each_entry((&eg->pctx.ids), cur, bkt) { 75383de0b7dSJiri Olsa struct pmu_event *pe; 75483de0b7dSJiri Olsa 75583de0b7dSJiri Olsa pe = find_metric(cur->key, map); 75683de0b7dSJiri Olsa if (!pe) 75783de0b7dSJiri Olsa continue; 75883de0b7dSJiri Olsa 75983de0b7dSJiri Olsa all = false; 76083de0b7dSJiri Olsa /* The metric key itself needs to go out.. */ 76183de0b7dSJiri Olsa expr__del_id(&eg->pctx, cur->key); 76283de0b7dSJiri Olsa 76383de0b7dSJiri Olsa /* ... and it gets resolved to the parent context. */ 76483de0b7dSJiri Olsa ret = add_metric(group_list, pe, metric_no_group, &eg); 76583de0b7dSJiri Olsa if (ret) 76683de0b7dSJiri Olsa return ret; 76783de0b7dSJiri Olsa 76883de0b7dSJiri Olsa /* 76983de0b7dSJiri Olsa * We added new metric to hashmap, so we need 77083de0b7dSJiri Olsa * to break the iteration and start over. 77183de0b7dSJiri Olsa */ 77283de0b7dSJiri Olsa break; 77383de0b7dSJiri Olsa } 77483de0b7dSJiri Olsa } while (!all); 77583de0b7dSJiri Olsa 77683de0b7dSJiri Olsa return 0; 77783de0b7dSJiri Olsa } 77883de0b7dSJiri Olsa 77983de0b7dSJiri Olsa static int resolve_metric(bool metric_no_group, 78083de0b7dSJiri Olsa struct list_head *metric_list, 78183de0b7dSJiri Olsa struct pmu_events_map *map) 78283de0b7dSJiri Olsa { 78383de0b7dSJiri Olsa struct egroup *eg; 78483de0b7dSJiri Olsa int err; 78583de0b7dSJiri Olsa 78683de0b7dSJiri Olsa list_for_each_entry(eg, metric_list, nd) { 78783de0b7dSJiri Olsa err = __resolve_metric(eg, metric_no_group, metric_list, map); 78883de0b7dSJiri Olsa if (err) 78983de0b7dSJiri Olsa return err; 79083de0b7dSJiri Olsa } 79183de0b7dSJiri Olsa return 0; 79283de0b7dSJiri Olsa } 79383de0b7dSJiri Olsa 79483de0b7dSJiri Olsa static int add_metric(struct list_head *group_list, 79583de0b7dSJiri Olsa struct pmu_event *pe, 79683de0b7dSJiri Olsa bool metric_no_group, 79783de0b7dSJiri Olsa struct egroup **egp) 79883de0b7dSJiri Olsa { 79983de0b7dSJiri Olsa struct egroup *orig = *egp; 800a29c164aSJiri Olsa int ret = 0; 801a29c164aSJiri Olsa 802a29c164aSJiri Olsa pr_debug("metric expr %s for %s\n", pe->metric_expr, pe->metric_name); 803a29c164aSJiri Olsa 804a29c164aSJiri Olsa if (!strstr(pe->metric_expr, "?")) { 80583de0b7dSJiri Olsa ret = __add_metric(group_list, pe, metric_no_group, 1, egp); 806a29c164aSJiri Olsa } else { 807a29c164aSJiri Olsa int j, count; 808a29c164aSJiri Olsa 809a29c164aSJiri Olsa count = arch_get_runtimeparam(); 810a29c164aSJiri Olsa 811a29c164aSJiri Olsa /* This loop is added to create multiple 812a29c164aSJiri Olsa * events depend on count value and add 813a29c164aSJiri Olsa * those events to group_list. 814a29c164aSJiri Olsa */ 815a29c164aSJiri Olsa 81683de0b7dSJiri Olsa for (j = 0; j < count && !ret; j++, *egp = orig) 81783de0b7dSJiri Olsa ret = __add_metric(group_list, pe, metric_no_group, j, egp); 818a29c164aSJiri Olsa } 819a29c164aSJiri Olsa 820a29c164aSJiri Olsa return ret; 821a29c164aSJiri Olsa } 822a29c164aSJiri Olsa 82305530a79SIan Rogers static int metricgroup__add_metric(const char *metric, bool metric_no_group, 82405530a79SIan Rogers struct strbuf *events, 8251381396bSJiri Olsa struct list_head *group_list, 8261381396bSJiri Olsa struct pmu_events_map *map) 827b18f3e36SAndi Kleen { 828b18f3e36SAndi Kleen struct pmu_event *pe; 8297f9eca51SIan Rogers struct egroup *eg; 83090810399SIan Rogers int i, ret; 83190810399SIan Rogers bool has_match = false; 832b18f3e36SAndi Kleen 833ce391940SJiri Olsa map_for_each_metric(pe, i, map, metric) { 834ce391940SJiri Olsa has_match = true; 83583de0b7dSJiri Olsa eg = NULL; 836b18f3e36SAndi Kleen 83783de0b7dSJiri Olsa ret = add_metric(group_list, pe, metric_no_group, &eg); 83883de0b7dSJiri Olsa if (ret) 83983de0b7dSJiri Olsa return ret; 84083de0b7dSJiri Olsa 84183de0b7dSJiri Olsa /* 84283de0b7dSJiri Olsa * Process any possible referenced metrics 84383de0b7dSJiri Olsa * included in the expression. 84483de0b7dSJiri Olsa */ 84583de0b7dSJiri Olsa ret = resolve_metric(metric_no_group, 84683de0b7dSJiri Olsa group_list, map); 84790810399SIan Rogers if (ret) 84890810399SIan Rogers return ret; 84990810399SIan Rogers } 850ce391940SJiri Olsa 851ce391940SJiri Olsa /* End of pmu events. */ 852ce391940SJiri Olsa if (!has_match) 853ce391940SJiri Olsa return -EINVAL; 854ce391940SJiri Olsa 8557f9eca51SIan Rogers list_for_each_entry(eg, group_list, nd) { 8567f9eca51SIan Rogers if (events->len > 0) 8577f9eca51SIan Rogers strbuf_addf(events, ","); 8587f9eca51SIan Rogers 8597f9eca51SIan Rogers if (eg->has_constraint) { 8607f9eca51SIan Rogers metricgroup__add_metric_non_group(events, 8617f9eca51SIan Rogers &eg->pctx); 8627f9eca51SIan Rogers } else { 8637f9eca51SIan Rogers metricgroup__add_metric_weak_group(events, 8647f9eca51SIan Rogers &eg->pctx); 8657f9eca51SIan Rogers } 8667f9eca51SIan Rogers } 86790810399SIan Rogers return 0; 86890810399SIan Rogers } 869b18f3e36SAndi Kleen 87005530a79SIan Rogers static int metricgroup__add_metric_list(const char *list, bool metric_no_group, 87105530a79SIan Rogers struct strbuf *events, 8721381396bSJiri Olsa struct list_head *group_list, 8731381396bSJiri Olsa struct pmu_events_map *map) 874b18f3e36SAndi Kleen { 875b18f3e36SAndi Kleen char *llist, *nlist, *p; 876b18f3e36SAndi Kleen int ret = -EINVAL; 877b18f3e36SAndi Kleen 878b18f3e36SAndi Kleen nlist = strdup(list); 879b18f3e36SAndi Kleen if (!nlist) 880b18f3e36SAndi Kleen return -ENOMEM; 881b18f3e36SAndi Kleen llist = nlist; 882411bc316SAndi Kleen 883411bc316SAndi Kleen strbuf_init(events, 100); 884411bc316SAndi Kleen strbuf_addf(events, "%s", ""); 885411bc316SAndi Kleen 886b18f3e36SAndi Kleen while ((p = strsep(&llist, ",")) != NULL) { 88705530a79SIan Rogers ret = metricgroup__add_metric(p, metric_no_group, events, 8881381396bSJiri Olsa group_list, map); 889b18f3e36SAndi Kleen if (ret == -EINVAL) { 890b18f3e36SAndi Kleen fprintf(stderr, "Cannot find metric or group `%s'\n", 891b18f3e36SAndi Kleen p); 892b18f3e36SAndi Kleen break; 893b18f3e36SAndi Kleen } 894b18f3e36SAndi Kleen } 895b18f3e36SAndi Kleen free(nlist); 896ab483d8bSKan Liang 897ab483d8bSKan Liang if (!ret) 898ab483d8bSKan Liang metricgroup___watchdog_constraint_hint(NULL, true); 899ab483d8bSKan Liang 900b18f3e36SAndi Kleen return ret; 901b18f3e36SAndi Kleen } 902b18f3e36SAndi Kleen 90383de0b7dSJiri Olsa static void egroup__free_refs(struct egroup *egroup) 90483de0b7dSJiri Olsa { 90583de0b7dSJiri Olsa struct metric_ref_node *ref, *tmp; 90683de0b7dSJiri Olsa 90783de0b7dSJiri Olsa list_for_each_entry_safe(ref, tmp, &egroup->metric_refs, list) { 90883de0b7dSJiri Olsa list_del(&ref->list); 90983de0b7dSJiri Olsa free(ref); 91083de0b7dSJiri Olsa } 91183de0b7dSJiri Olsa } 91283de0b7dSJiri Olsa 913b18f3e36SAndi Kleen static void metricgroup__free_egroups(struct list_head *group_list) 914b18f3e36SAndi Kleen { 915b18f3e36SAndi Kleen struct egroup *eg, *egtmp; 916b18f3e36SAndi Kleen 917b18f3e36SAndi Kleen list_for_each_entry_safe (eg, egtmp, group_list, nd) { 91883de0b7dSJiri Olsa egroup__free_refs(eg); 919ded80bdaSIan Rogers expr__ctx_clear(&eg->pctx); 920acc7bfb3SArnaldo Carvalho de Melo list_del_init(&eg->nd); 921b18f3e36SAndi Kleen free(eg); 922b18f3e36SAndi Kleen } 923b18f3e36SAndi Kleen } 924b18f3e36SAndi Kleen 9258b4468a2SJiri Olsa static int parse_groups(struct evlist *perf_evlist, const char *str, 92605530a79SIan Rogers bool metric_no_group, 92705530a79SIan Rogers bool metric_no_merge, 92868173bdaSJiri Olsa struct perf_pmu *fake_pmu, 9291381396bSJiri Olsa struct rblist *metric_events, 9301381396bSJiri Olsa struct pmu_events_map *map) 931b18f3e36SAndi Kleen { 932b18f3e36SAndi Kleen struct parse_events_error parse_error; 933b18f3e36SAndi Kleen struct strbuf extra_events; 934b18f3e36SAndi Kleen LIST_HEAD(group_list); 935b18f3e36SAndi Kleen int ret; 936b18f3e36SAndi Kleen 937b18f3e36SAndi Kleen if (metric_events->nr_entries == 0) 938b18f3e36SAndi Kleen metricgroup__rblist_init(metric_events); 93905530a79SIan Rogers ret = metricgroup__add_metric_list(str, metric_no_group, 9401381396bSJiri Olsa &extra_events, &group_list, map); 941b18f3e36SAndi Kleen if (ret) 942b18f3e36SAndi Kleen return ret; 943b18f3e36SAndi Kleen pr_debug("adding %s\n", extra_events.buf); 944a910e466SIan Rogers bzero(&parse_error, sizeof(parse_error)); 94568173bdaSJiri Olsa ret = __parse_events(perf_evlist, extra_events.buf, &parse_error, fake_pmu); 946b18f3e36SAndi Kleen if (ret) { 947333b5665SAndi Kleen parse_events_print_error(&parse_error, extra_events.buf); 948b18f3e36SAndi Kleen goto out; 949b18f3e36SAndi Kleen } 950b18f3e36SAndi Kleen strbuf_release(&extra_events); 95105530a79SIan Rogers ret = metricgroup__setup_events(&group_list, metric_no_merge, 95205530a79SIan Rogers perf_evlist, metric_events); 953b18f3e36SAndi Kleen out: 954b18f3e36SAndi Kleen metricgroup__free_egroups(&group_list); 955b18f3e36SAndi Kleen return ret; 956b18f3e36SAndi Kleen } 957742d92ffSThomas Richter 9588b4468a2SJiri Olsa int metricgroup__parse_groups(const struct option *opt, 9598b4468a2SJiri Olsa const char *str, 9608b4468a2SJiri Olsa bool metric_no_group, 9618b4468a2SJiri Olsa bool metric_no_merge, 9628b4468a2SJiri Olsa struct rblist *metric_events) 9638b4468a2SJiri Olsa { 9648b4468a2SJiri Olsa struct evlist *perf_evlist = *(struct evlist **)opt->value; 9651381396bSJiri Olsa struct pmu_events_map *map = perf_pmu__find_map(NULL); 9661381396bSJiri Olsa 9671381396bSJiri Olsa if (!map) 9681381396bSJiri Olsa return 0; 9698b4468a2SJiri Olsa 9708b4468a2SJiri Olsa return parse_groups(perf_evlist, str, metric_no_group, 9711381396bSJiri Olsa metric_no_merge, NULL, metric_events, map); 9728b4468a2SJiri Olsa } 9738b4468a2SJiri Olsa 974f78ac00aSJiri Olsa int metricgroup__parse_groups_test(struct evlist *evlist, 975f78ac00aSJiri Olsa struct pmu_events_map *map, 976f78ac00aSJiri Olsa const char *str, 977f78ac00aSJiri Olsa bool metric_no_group, 978f78ac00aSJiri Olsa bool metric_no_merge, 979f78ac00aSJiri Olsa struct rblist *metric_events) 980f78ac00aSJiri Olsa { 981f78ac00aSJiri Olsa return parse_groups(evlist, str, metric_no_group, 982f78ac00aSJiri Olsa metric_no_merge, &perf_pmu__fake, metric_events, map); 983f78ac00aSJiri Olsa } 984f78ac00aSJiri Olsa 985742d92ffSThomas Richter bool metricgroup__has_metric(const char *metric) 986742d92ffSThomas Richter { 987742d92ffSThomas Richter struct pmu_events_map *map = perf_pmu__find_map(NULL); 988742d92ffSThomas Richter struct pmu_event *pe; 989742d92ffSThomas Richter int i; 990742d92ffSThomas Richter 991742d92ffSThomas Richter if (!map) 992742d92ffSThomas Richter return false; 993742d92ffSThomas Richter 994742d92ffSThomas Richter for (i = 0; ; i++) { 995742d92ffSThomas Richter pe = &map->table[i]; 996742d92ffSThomas Richter 997742d92ffSThomas Richter if (!pe->name && !pe->metric_group && !pe->metric_name) 998742d92ffSThomas Richter break; 999742d92ffSThomas Richter if (!pe->metric_expr) 1000742d92ffSThomas Richter continue; 1001742d92ffSThomas Richter if (match_metric(pe->metric_name, metric)) 1002742d92ffSThomas Richter return true; 1003742d92ffSThomas Richter } 1004742d92ffSThomas Richter return false; 1005742d92ffSThomas Richter } 1006