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 79b18f3e36SAndi Kleen static void metricgroup__rblist_init(struct rblist *metric_events) 80b18f3e36SAndi Kleen { 81b18f3e36SAndi Kleen rblist__init(metric_events); 82b18f3e36SAndi Kleen metric_events->node_cmp = metric_event_cmp; 83b18f3e36SAndi Kleen metric_events->node_new = metric_event_new; 84b18f3e36SAndi Kleen } 85b18f3e36SAndi Kleen 86b18f3e36SAndi Kleen struct egroup { 87b18f3e36SAndi Kleen struct list_head nd; 88ded80bdaSIan Rogers struct expr_parse_ctx pctx; 89b18f3e36SAndi Kleen const char *metric_name; 90b18f3e36SAndi Kleen const char *metric_expr; 91287f2649SJin Yao const char *metric_unit; 921e1a873dSKajol Jain int runtime; 93b18f3e36SAndi Kleen }; 94b18f3e36SAndi Kleen 9563503dbaSJiri Olsa static struct evsel *find_evsel_group(struct evlist *perf_evlist, 96ded80bdaSIan Rogers struct expr_parse_ctx *pctx, 9758fc90fdSKajol Jain struct evsel **metric_events, 98*45db55f2SIan Rogers unsigned long *evlist_used) 99b18f3e36SAndi Kleen { 100f01642e4SJin Yao struct evsel *ev; 101f01642e4SJin Yao bool leader_found; 102ded80bdaSIan Rogers const size_t idnum = hashmap__size(&pctx->ids); 103ded80bdaSIan Rogers size_t i = 0; 104ded80bdaSIan Rogers int j = 0; 105ded80bdaSIan Rogers double *val_ptr; 106b18f3e36SAndi Kleen 107b18f3e36SAndi Kleen evlist__for_each_entry (perf_evlist, ev) { 108*45db55f2SIan Rogers if (test_bit(j++, evlist_used)) 10958fc90fdSKajol Jain continue; 110ded80bdaSIan Rogers if (hashmap__find(&pctx->ids, ev->name, (void **)&val_ptr)) { 111f01642e4SJin Yao if (!metric_events[i]) 112f01642e4SJin Yao metric_events[i] = ev; 113eb573e74SKajol Jain i++; 114eb573e74SKajol Jain if (i == idnum) 115eb573e74SKajol Jain break; 116b18f3e36SAndi Kleen } else { 117f01642e4SJin Yao /* Discard the whole match and start again */ 118f01642e4SJin Yao i = 0; 119f01642e4SJin Yao memset(metric_events, 0, 120f01642e4SJin Yao sizeof(struct evsel *) * idnum); 121b18f3e36SAndi Kleen } 122b18f3e36SAndi Kleen } 123f01642e4SJin Yao 124eb573e74SKajol Jain if (i != idnum) { 125f01642e4SJin Yao /* Not whole match */ 126b18f3e36SAndi Kleen return NULL; 127b18f3e36SAndi Kleen } 128b18f3e36SAndi Kleen 129f01642e4SJin Yao metric_events[idnum] = NULL; 130f01642e4SJin Yao 131f01642e4SJin Yao for (i = 0; i < idnum; i++) { 132f01642e4SJin Yao leader_found = false; 133f01642e4SJin Yao evlist__for_each_entry(perf_evlist, ev) { 134f01642e4SJin Yao if (!leader_found && (ev == metric_events[i])) 135f01642e4SJin Yao leader_found = true; 136f01642e4SJin Yao 137f01642e4SJin Yao if (leader_found && 138f01642e4SJin Yao !strcmp(ev->name, metric_events[i]->name)) { 139f01642e4SJin Yao ev->metric_leader = metric_events[i]; 140f01642e4SJin Yao } 14158fc90fdSKajol Jain j++; 142f01642e4SJin Yao } 14358fc90fdSKajol Jain ev = metric_events[i]; 144*45db55f2SIan Rogers set_bit(ev->idx, evlist_used); 145f01642e4SJin Yao } 146f01642e4SJin Yao 147f01642e4SJin Yao return metric_events[0]; 148f01642e4SJin Yao } 149f01642e4SJin Yao 150b18f3e36SAndi Kleen static int metricgroup__setup_events(struct list_head *groups, 15163503dbaSJiri Olsa struct evlist *perf_evlist, 152b18f3e36SAndi Kleen struct rblist *metric_events_list) 153b18f3e36SAndi Kleen { 154b18f3e36SAndi Kleen struct metric_event *me; 155b18f3e36SAndi Kleen struct metric_expr *expr; 156b18f3e36SAndi Kleen int i = 0; 157b18f3e36SAndi Kleen int ret = 0; 158b18f3e36SAndi Kleen struct egroup *eg; 15932dcd021SJiri Olsa struct evsel *evsel; 160*45db55f2SIan Rogers unsigned long *evlist_used; 16158fc90fdSKajol Jain 162*45db55f2SIan Rogers evlist_used = bitmap_alloc(perf_evlist->core.nr_entries); 163*45db55f2SIan Rogers if (!evlist_used) 164*45db55f2SIan Rogers return -ENOMEM; 165b18f3e36SAndi Kleen 166b18f3e36SAndi Kleen list_for_each_entry (eg, groups, nd) { 16732dcd021SJiri Olsa struct evsel **metric_events; 168b18f3e36SAndi Kleen 169ded80bdaSIan Rogers metric_events = calloc(sizeof(void *), 170ded80bdaSIan Rogers hashmap__size(&eg->pctx.ids) + 1); 171b18f3e36SAndi Kleen if (!metric_events) { 172b18f3e36SAndi Kleen ret = -ENOMEM; 173b18f3e36SAndi Kleen break; 174b18f3e36SAndi Kleen } 175ded80bdaSIan Rogers evsel = find_evsel_group(perf_evlist, &eg->pctx, metric_events, 176ded80bdaSIan Rogers evlist_used); 177b18f3e36SAndi Kleen if (!evsel) { 178b18f3e36SAndi Kleen pr_debug("Cannot resolve %s: %s\n", 179b18f3e36SAndi Kleen eg->metric_name, eg->metric_expr); 180b18f3e36SAndi Kleen continue; 181b18f3e36SAndi Kleen } 182ded80bdaSIan Rogers for (i = 0; metric_events[i]; i++) 183b18f3e36SAndi Kleen metric_events[i]->collect_stat = true; 184b18f3e36SAndi Kleen me = metricgroup__lookup(metric_events_list, evsel, true); 185b18f3e36SAndi Kleen if (!me) { 186b18f3e36SAndi Kleen ret = -ENOMEM; 187b18f3e36SAndi Kleen break; 188b18f3e36SAndi Kleen } 189b18f3e36SAndi Kleen expr = malloc(sizeof(struct metric_expr)); 190b18f3e36SAndi Kleen if (!expr) { 191b18f3e36SAndi Kleen ret = -ENOMEM; 192b18f3e36SAndi Kleen break; 193b18f3e36SAndi Kleen } 194b18f3e36SAndi Kleen expr->metric_expr = eg->metric_expr; 195b18f3e36SAndi Kleen expr->metric_name = eg->metric_name; 196287f2649SJin Yao expr->metric_unit = eg->metric_unit; 197b18f3e36SAndi Kleen expr->metric_events = metric_events; 1981e1a873dSKajol Jain expr->runtime = eg->runtime; 199b18f3e36SAndi Kleen list_add(&expr->nd, &me->head); 200b18f3e36SAndi Kleen } 20158fc90fdSKajol Jain 202*45db55f2SIan Rogers bitmap_free(evlist_used); 20358fc90fdSKajol Jain 204b18f3e36SAndi Kleen return ret; 205b18f3e36SAndi Kleen } 206b18f3e36SAndi Kleen 207b18f3e36SAndi Kleen static bool match_metric(const char *n, const char *list) 208b18f3e36SAndi Kleen { 209b18f3e36SAndi Kleen int len; 210b18f3e36SAndi Kleen char *m; 211b18f3e36SAndi Kleen 212b18f3e36SAndi Kleen if (!list) 213b18f3e36SAndi Kleen return false; 214b18f3e36SAndi Kleen if (!strcmp(list, "all")) 215b18f3e36SAndi Kleen return true; 216b18f3e36SAndi Kleen if (!n) 217b18f3e36SAndi Kleen return !strcasecmp(list, "No_group"); 218b18f3e36SAndi Kleen len = strlen(list); 219b18f3e36SAndi Kleen m = strcasestr(n, list); 220b18f3e36SAndi Kleen if (!m) 221b18f3e36SAndi Kleen return false; 222b18f3e36SAndi Kleen if ((m == n || m[-1] == ';' || m[-1] == ' ') && 223b18f3e36SAndi Kleen (m[len] == 0 || m[len] == ';')) 224b18f3e36SAndi Kleen return true; 225b18f3e36SAndi Kleen return false; 226b18f3e36SAndi Kleen } 227b18f3e36SAndi Kleen 22871b0acceSAndi Kleen struct mep { 22971b0acceSAndi Kleen struct rb_node nd; 23071b0acceSAndi Kleen const char *name; 23171b0acceSAndi Kleen struct strlist *metrics; 23271b0acceSAndi Kleen }; 23371b0acceSAndi Kleen 23471b0acceSAndi Kleen static int mep_cmp(struct rb_node *rb_node, const void *entry) 23571b0acceSAndi Kleen { 23671b0acceSAndi Kleen struct mep *a = container_of(rb_node, struct mep, nd); 23771b0acceSAndi Kleen struct mep *b = (struct mep *)entry; 23871b0acceSAndi Kleen 23971b0acceSAndi Kleen return strcmp(a->name, b->name); 24071b0acceSAndi Kleen } 24171b0acceSAndi Kleen 24271b0acceSAndi Kleen static struct rb_node *mep_new(struct rblist *rl __maybe_unused, 24371b0acceSAndi Kleen const void *entry) 24471b0acceSAndi Kleen { 24571b0acceSAndi Kleen struct mep *me = malloc(sizeof(struct mep)); 24671b0acceSAndi Kleen 24771b0acceSAndi Kleen if (!me) 24871b0acceSAndi Kleen return NULL; 24971b0acceSAndi Kleen memcpy(me, entry, sizeof(struct mep)); 25071b0acceSAndi Kleen me->name = strdup(me->name); 25171b0acceSAndi Kleen if (!me->name) 25271b0acceSAndi Kleen goto out_me; 25371b0acceSAndi Kleen me->metrics = strlist__new(NULL, NULL); 25471b0acceSAndi Kleen if (!me->metrics) 25571b0acceSAndi Kleen goto out_name; 25671b0acceSAndi Kleen return &me->nd; 25771b0acceSAndi Kleen out_name: 258d8f9da24SArnaldo Carvalho de Melo zfree(&me->name); 25971b0acceSAndi Kleen out_me: 26071b0acceSAndi Kleen free(me); 26171b0acceSAndi Kleen return NULL; 26271b0acceSAndi Kleen } 26371b0acceSAndi Kleen 26471b0acceSAndi Kleen static struct mep *mep_lookup(struct rblist *groups, const char *name) 26571b0acceSAndi Kleen { 26671b0acceSAndi Kleen struct rb_node *nd; 26771b0acceSAndi Kleen struct mep me = { 26871b0acceSAndi Kleen .name = name 26971b0acceSAndi Kleen }; 27071b0acceSAndi Kleen nd = rblist__find(groups, &me); 27171b0acceSAndi Kleen if (nd) 27271b0acceSAndi Kleen return container_of(nd, struct mep, nd); 27371b0acceSAndi Kleen rblist__add_node(groups, &me); 27471b0acceSAndi Kleen nd = rblist__find(groups, &me); 27571b0acceSAndi Kleen if (nd) 27671b0acceSAndi Kleen return container_of(nd, struct mep, nd); 27771b0acceSAndi Kleen return NULL; 27871b0acceSAndi Kleen } 27971b0acceSAndi Kleen 28071b0acceSAndi Kleen static void mep_delete(struct rblist *rl __maybe_unused, 28171b0acceSAndi Kleen struct rb_node *nd) 28271b0acceSAndi Kleen { 28371b0acceSAndi Kleen struct mep *me = container_of(nd, struct mep, nd); 28471b0acceSAndi Kleen 28571b0acceSAndi Kleen strlist__delete(me->metrics); 286d8f9da24SArnaldo Carvalho de Melo zfree(&me->name); 28771b0acceSAndi Kleen free(me); 28871b0acceSAndi Kleen } 28971b0acceSAndi Kleen 29071b0acceSAndi Kleen static void metricgroup__print_strlist(struct strlist *metrics, bool raw) 29171b0acceSAndi Kleen { 29271b0acceSAndi Kleen struct str_node *sn; 29371b0acceSAndi Kleen int n = 0; 29471b0acceSAndi Kleen 29571b0acceSAndi Kleen strlist__for_each_entry (sn, metrics) { 29671b0acceSAndi Kleen if (raw) 29771b0acceSAndi Kleen printf("%s%s", n > 0 ? " " : "", sn->s); 29871b0acceSAndi Kleen else 29971b0acceSAndi Kleen printf(" %s\n", sn->s); 30071b0acceSAndi Kleen n++; 30171b0acceSAndi Kleen } 30271b0acceSAndi Kleen if (raw) 30371b0acceSAndi Kleen putchar('\n'); 30471b0acceSAndi Kleen } 30571b0acceSAndi Kleen 30671b0acceSAndi Kleen void metricgroup__print(bool metrics, bool metricgroups, char *filter, 30733bbc571SJiri Olsa bool raw, bool details) 30871b0acceSAndi Kleen { 30954e32dc0SGanapatrao Kulkarni struct pmu_events_map *map = perf_pmu__find_map(NULL); 31071b0acceSAndi Kleen struct pmu_event *pe; 31171b0acceSAndi Kleen int i; 31271b0acceSAndi Kleen struct rblist groups; 31371b0acceSAndi Kleen struct rb_node *node, *next; 31471b0acceSAndi Kleen struct strlist *metriclist = NULL; 31571b0acceSAndi Kleen 31671b0acceSAndi Kleen if (!map) 31771b0acceSAndi Kleen return; 31871b0acceSAndi Kleen 31971b0acceSAndi Kleen if (!metricgroups) { 32071b0acceSAndi Kleen metriclist = strlist__new(NULL, NULL); 32171b0acceSAndi Kleen if (!metriclist) 32271b0acceSAndi Kleen return; 32371b0acceSAndi Kleen } 32471b0acceSAndi Kleen 32571b0acceSAndi Kleen rblist__init(&groups); 32671b0acceSAndi Kleen groups.node_new = mep_new; 32771b0acceSAndi Kleen groups.node_cmp = mep_cmp; 32871b0acceSAndi Kleen groups.node_delete = mep_delete; 32971b0acceSAndi Kleen for (i = 0; ; i++) { 33071b0acceSAndi Kleen const char *g; 33171b0acceSAndi Kleen pe = &map->table[i]; 33271b0acceSAndi Kleen 33371b0acceSAndi Kleen if (!pe->name && !pe->metric_group && !pe->metric_name) 33471b0acceSAndi Kleen break; 33571b0acceSAndi Kleen if (!pe->metric_expr) 33671b0acceSAndi Kleen continue; 33771b0acceSAndi Kleen g = pe->metric_group; 33871b0acceSAndi Kleen if (!g && pe->metric_name) { 33971b0acceSAndi Kleen if (pe->name) 34071b0acceSAndi Kleen continue; 34171b0acceSAndi Kleen g = "No_group"; 34271b0acceSAndi Kleen } 34371b0acceSAndi Kleen if (g) { 34471b0acceSAndi Kleen char *omg; 34571b0acceSAndi Kleen char *mg = strdup(g); 34671b0acceSAndi Kleen 34771b0acceSAndi Kleen if (!mg) 34871b0acceSAndi Kleen return; 34971b0acceSAndi Kleen omg = mg; 35071b0acceSAndi Kleen while ((g = strsep(&mg, ";")) != NULL) { 35171b0acceSAndi Kleen struct mep *me; 35271b0acceSAndi Kleen char *s; 35371b0acceSAndi Kleen 35480e9073fSArnaldo Carvalho de Melo g = skip_spaces(g); 35571b0acceSAndi Kleen if (*g == 0) 35671b0acceSAndi Kleen g = "No_group"; 35771b0acceSAndi Kleen if (filter && !strstr(g, filter)) 35871b0acceSAndi Kleen continue; 35971b0acceSAndi Kleen if (raw) 36071b0acceSAndi Kleen s = (char *)pe->metric_name; 36171b0acceSAndi Kleen else { 36295f04328SMichael Petlan if (asprintf(&s, "%s\n%*s%s]", 36395f04328SMichael Petlan pe->metric_name, 8, "[", pe->desc) < 0) 36471b0acceSAndi Kleen return; 36533bbc571SJiri Olsa 36633bbc571SJiri Olsa if (details) { 36733bbc571SJiri Olsa if (asprintf(&s, "%s\n%*s%s]", 36833bbc571SJiri Olsa s, 8, "[", pe->metric_expr) < 0) 36933bbc571SJiri Olsa return; 37033bbc571SJiri Olsa } 37171b0acceSAndi Kleen } 37271b0acceSAndi Kleen 37371b0acceSAndi Kleen if (!s) 37471b0acceSAndi Kleen continue; 37571b0acceSAndi Kleen 37671b0acceSAndi Kleen if (!metricgroups) { 37771b0acceSAndi Kleen strlist__add(metriclist, s); 37871b0acceSAndi Kleen } else { 37971b0acceSAndi Kleen me = mep_lookup(&groups, g); 38071b0acceSAndi Kleen if (!me) 38171b0acceSAndi Kleen continue; 38271b0acceSAndi Kleen strlist__add(me->metrics, s); 38371b0acceSAndi Kleen } 38471b0acceSAndi Kleen } 38571b0acceSAndi Kleen free(omg); 38671b0acceSAndi Kleen } 38771b0acceSAndi Kleen } 38871b0acceSAndi Kleen 38971b0acceSAndi Kleen if (metricgroups && !raw) 39071b0acceSAndi Kleen printf("\nMetric Groups:\n\n"); 39171b0acceSAndi Kleen else if (metrics && !raw) 39271b0acceSAndi Kleen printf("\nMetrics:\n\n"); 39371b0acceSAndi Kleen 394ca227029SDavidlohr Bueso for (node = rb_first_cached(&groups.entries); node; node = next) { 39571b0acceSAndi Kleen struct mep *me = container_of(node, struct mep, nd); 39671b0acceSAndi Kleen 39771b0acceSAndi Kleen if (metricgroups) 3989c344d15SAndi Kleen printf("%s%s%s", me->name, metrics && !raw ? ":" : "", raw ? " " : "\n"); 39971b0acceSAndi Kleen if (metrics) 40071b0acceSAndi Kleen metricgroup__print_strlist(me->metrics, raw); 40171b0acceSAndi Kleen next = rb_next(node); 40271b0acceSAndi Kleen rblist__remove_node(&groups, node); 40371b0acceSAndi Kleen } 40471b0acceSAndi Kleen if (!metricgroups) 40571b0acceSAndi Kleen metricgroup__print_strlist(metriclist, raw); 40671b0acceSAndi Kleen strlist__delete(metriclist); 40771b0acceSAndi Kleen } 40871b0acceSAndi Kleen 409f742634aSKan Liang static void metricgroup__add_metric_weak_group(struct strbuf *events, 410ded80bdaSIan Rogers struct expr_parse_ctx *ctx) 411f742634aSKan Liang { 412ded80bdaSIan Rogers struct hashmap_entry *cur; 413ded80bdaSIan Rogers size_t bkt, i = 0; 414f742634aSKan Liang bool no_group = false; 415f742634aSKan Liang 416ded80bdaSIan Rogers hashmap__for_each_entry((&ctx->ids), cur, bkt) { 417ded80bdaSIan Rogers pr_debug("found event %s\n", (const char *)cur->key); 418f742634aSKan Liang /* 419f742634aSKan Liang * Duration time maps to a software event and can make 420f742634aSKan Liang * groups not count. Always use it outside a 421f742634aSKan Liang * group. 422f742634aSKan Liang */ 423ded80bdaSIan Rogers if (!strcmp(cur->key, "duration_time")) { 424f742634aSKan Liang if (i > 0) 425f742634aSKan Liang strbuf_addf(events, "}:W,"); 426f742634aSKan Liang strbuf_addf(events, "duration_time"); 427f742634aSKan Liang no_group = true; 428f742634aSKan Liang continue; 429f742634aSKan Liang } 430f742634aSKan Liang strbuf_addf(events, "%s%s", 431f742634aSKan Liang i == 0 || no_group ? "{" : ",", 432ded80bdaSIan Rogers (const char *)cur->key); 433f742634aSKan Liang no_group = false; 434ded80bdaSIan Rogers i++; 435f742634aSKan Liang } 436f742634aSKan Liang if (!no_group) 437f742634aSKan Liang strbuf_addf(events, "}:W"); 438f742634aSKan Liang } 439f742634aSKan Liang 440ab483d8bSKan Liang static void metricgroup__add_metric_non_group(struct strbuf *events, 441ded80bdaSIan Rogers struct expr_parse_ctx *ctx) 442ab483d8bSKan Liang { 443ded80bdaSIan Rogers struct hashmap_entry *cur; 444ded80bdaSIan Rogers size_t bkt; 445ab483d8bSKan Liang 446ded80bdaSIan Rogers hashmap__for_each_entry((&ctx->ids), cur, bkt) 447ded80bdaSIan Rogers strbuf_addf(events, ",%s", (const char *)cur->key); 448ab483d8bSKan Liang } 449ab483d8bSKan Liang 450ab483d8bSKan Liang static void metricgroup___watchdog_constraint_hint(const char *name, bool foot) 451ab483d8bSKan Liang { 452ab483d8bSKan Liang static bool violate_nmi_constraint; 453ab483d8bSKan Liang 454ab483d8bSKan Liang if (!foot) { 455ab483d8bSKan Liang pr_warning("Splitting metric group %s into standalone metrics.\n", name); 456ab483d8bSKan Liang violate_nmi_constraint = true; 457ab483d8bSKan Liang return; 458ab483d8bSKan Liang } 459ab483d8bSKan Liang 460ab483d8bSKan Liang if (!violate_nmi_constraint) 461ab483d8bSKan Liang return; 462ab483d8bSKan Liang 463ab483d8bSKan Liang pr_warning("Try disabling the NMI watchdog to comply NO_NMI_WATCHDOG metric constraint:\n" 464ab483d8bSKan Liang " echo 0 > /proc/sys/kernel/nmi_watchdog\n" 465ab483d8bSKan Liang " perf stat ...\n" 466ab483d8bSKan Liang " echo 1 > /proc/sys/kernel/nmi_watchdog\n"); 467ab483d8bSKan Liang } 468ab483d8bSKan Liang 469ab483d8bSKan Liang static bool metricgroup__has_constraint(struct pmu_event *pe) 470ab483d8bSKan Liang { 471ab483d8bSKan Liang if (!pe->metric_constraint) 472ab483d8bSKan Liang return false; 473ab483d8bSKan Liang 474ab483d8bSKan Liang if (!strcmp(pe->metric_constraint, "NO_NMI_WATCHDOG") && 475ab483d8bSKan Liang sysctl__nmi_watchdog_enabled()) { 476ab483d8bSKan Liang metricgroup___watchdog_constraint_hint(pe->metric_name, false); 477ab483d8bSKan Liang return true; 478ab483d8bSKan Liang } 479ab483d8bSKan Liang 480ab483d8bSKan Liang return false; 481ab483d8bSKan Liang } 482ab483d8bSKan Liang 4831e1a873dSKajol Jain int __weak arch_get_runtimeparam(void) 4841e1a873dSKajol Jain { 4851e1a873dSKajol Jain return 1; 4861e1a873dSKajol Jain } 4871e1a873dSKajol Jain 48847352abaSKajol Jain static int __metricgroup__add_metric(struct strbuf *events, 4891e1a873dSKajol Jain struct list_head *group_list, struct pmu_event *pe, int runtime) 49047352abaSKajol Jain { 49147352abaSKajol Jain struct egroup *eg; 49247352abaSKajol Jain 49347352abaSKajol Jain eg = malloc(sizeof(*eg)); 49447352abaSKajol Jain if (!eg) 49547352abaSKajol Jain return -ENOMEM; 49647352abaSKajol Jain 497ded80bdaSIan Rogers expr__ctx_init(&eg->pctx); 49847352abaSKajol Jain eg->metric_name = pe->metric_name; 49947352abaSKajol Jain eg->metric_expr = pe->metric_expr; 50047352abaSKajol Jain eg->metric_unit = pe->unit; 5011e1a873dSKajol Jain eg->runtime = runtime; 502ded80bdaSIan Rogers 503ded80bdaSIan Rogers if (expr__find_other(pe->metric_expr, NULL, &eg->pctx, runtime) < 0) { 504ded80bdaSIan Rogers expr__ctx_clear(&eg->pctx); 505ded80bdaSIan Rogers free(eg); 506ded80bdaSIan Rogers return -EINVAL; 507ded80bdaSIan Rogers } 508ded80bdaSIan Rogers 509ded80bdaSIan Rogers if (events->len > 0) 510ded80bdaSIan Rogers strbuf_addf(events, ","); 511ded80bdaSIan Rogers 512ded80bdaSIan Rogers if (metricgroup__has_constraint(pe)) 513ded80bdaSIan Rogers metricgroup__add_metric_non_group(events, &eg->pctx); 514ded80bdaSIan Rogers else 515ded80bdaSIan Rogers metricgroup__add_metric_weak_group(events, &eg->pctx); 516ded80bdaSIan Rogers 51747352abaSKajol Jain list_add_tail(&eg->nd, group_list); 51847352abaSKajol Jain 51947352abaSKajol Jain return 0; 52047352abaSKajol Jain } 52147352abaSKajol Jain 522b18f3e36SAndi Kleen static int metricgroup__add_metric(const char *metric, struct strbuf *events, 523b18f3e36SAndi Kleen struct list_head *group_list) 524b18f3e36SAndi Kleen { 52554e32dc0SGanapatrao Kulkarni struct pmu_events_map *map = perf_pmu__find_map(NULL); 526b18f3e36SAndi Kleen struct pmu_event *pe; 527f742634aSKan Liang int i, ret = -EINVAL; 528b18f3e36SAndi Kleen 529b18f3e36SAndi Kleen if (!map) 530b18f3e36SAndi Kleen return 0; 531b18f3e36SAndi Kleen 532b18f3e36SAndi Kleen for (i = 0; ; i++) { 533b18f3e36SAndi Kleen pe = &map->table[i]; 534b18f3e36SAndi Kleen 535b18f3e36SAndi Kleen if (!pe->name && !pe->metric_group && !pe->metric_name) 536b18f3e36SAndi Kleen break; 537b18f3e36SAndi Kleen if (!pe->metric_expr) 538b18f3e36SAndi Kleen continue; 539b18f3e36SAndi Kleen if (match_metric(pe->metric_group, metric) || 540b18f3e36SAndi Kleen match_metric(pe->metric_name, metric)) { 541b18f3e36SAndi Kleen 542b18f3e36SAndi Kleen pr_debug("metric expr %s for %s\n", pe->metric_expr, pe->metric_name); 543b18f3e36SAndi Kleen 5441e1a873dSKajol Jain if (!strstr(pe->metric_expr, "?")) { 5451e1a873dSKajol Jain ret = __metricgroup__add_metric(events, group_list, pe, 1); 5461e1a873dSKajol Jain } else { 5471e1a873dSKajol Jain int j, count; 5481e1a873dSKajol Jain 5491e1a873dSKajol Jain count = arch_get_runtimeparam(); 5501e1a873dSKajol Jain 5511e1a873dSKajol Jain /* This loop is added to create multiple 5521e1a873dSKajol Jain * events depend on count value and add 5531e1a873dSKajol Jain * those events to group_list. 5541e1a873dSKajol Jain */ 5551e1a873dSKajol Jain 5561e1a873dSKajol Jain for (j = 0; j < count; j++) 5571e1a873dSKajol Jain ret = __metricgroup__add_metric(events, group_list, pe, j); 5581e1a873dSKajol Jain } 55947352abaSKajol Jain if (ret == -ENOMEM) 560b18f3e36SAndi Kleen break; 561b18f3e36SAndi Kleen } 562b18f3e36SAndi Kleen } 563b18f3e36SAndi Kleen return ret; 564b18f3e36SAndi Kleen } 565b18f3e36SAndi Kleen 566b18f3e36SAndi Kleen static int metricgroup__add_metric_list(const char *list, struct strbuf *events, 567b18f3e36SAndi Kleen struct list_head *group_list) 568b18f3e36SAndi Kleen { 569b18f3e36SAndi Kleen char *llist, *nlist, *p; 570b18f3e36SAndi Kleen int ret = -EINVAL; 571b18f3e36SAndi Kleen 572b18f3e36SAndi Kleen nlist = strdup(list); 573b18f3e36SAndi Kleen if (!nlist) 574b18f3e36SAndi Kleen return -ENOMEM; 575b18f3e36SAndi Kleen llist = nlist; 576411bc316SAndi Kleen 577411bc316SAndi Kleen strbuf_init(events, 100); 578411bc316SAndi Kleen strbuf_addf(events, "%s", ""); 579411bc316SAndi Kleen 580b18f3e36SAndi Kleen while ((p = strsep(&llist, ",")) != NULL) { 581b18f3e36SAndi Kleen ret = metricgroup__add_metric(p, events, group_list); 582b18f3e36SAndi Kleen if (ret == -EINVAL) { 583b18f3e36SAndi Kleen fprintf(stderr, "Cannot find metric or group `%s'\n", 584b18f3e36SAndi Kleen p); 585b18f3e36SAndi Kleen break; 586b18f3e36SAndi Kleen } 587b18f3e36SAndi Kleen } 588b18f3e36SAndi Kleen free(nlist); 589ab483d8bSKan Liang 590ab483d8bSKan Liang if (!ret) 591ab483d8bSKan Liang metricgroup___watchdog_constraint_hint(NULL, true); 592ab483d8bSKan Liang 593b18f3e36SAndi Kleen return ret; 594b18f3e36SAndi Kleen } 595b18f3e36SAndi Kleen 596b18f3e36SAndi Kleen static void metricgroup__free_egroups(struct list_head *group_list) 597b18f3e36SAndi Kleen { 598b18f3e36SAndi Kleen struct egroup *eg, *egtmp; 599b18f3e36SAndi Kleen 600b18f3e36SAndi Kleen list_for_each_entry_safe (eg, egtmp, group_list, nd) { 601ded80bdaSIan Rogers expr__ctx_clear(&eg->pctx); 602acc7bfb3SArnaldo Carvalho de Melo list_del_init(&eg->nd); 603b18f3e36SAndi Kleen free(eg); 604b18f3e36SAndi Kleen } 605b18f3e36SAndi Kleen } 606b18f3e36SAndi Kleen 607b18f3e36SAndi Kleen int metricgroup__parse_groups(const struct option *opt, 608b18f3e36SAndi Kleen const char *str, 609b18f3e36SAndi Kleen struct rblist *metric_events) 610b18f3e36SAndi Kleen { 611b18f3e36SAndi Kleen struct parse_events_error parse_error; 61263503dbaSJiri Olsa struct evlist *perf_evlist = *(struct evlist **)opt->value; 613b18f3e36SAndi Kleen struct strbuf extra_events; 614b18f3e36SAndi Kleen LIST_HEAD(group_list); 615b18f3e36SAndi Kleen int ret; 616b18f3e36SAndi Kleen 617b18f3e36SAndi Kleen if (metric_events->nr_entries == 0) 618b18f3e36SAndi Kleen metricgroup__rblist_init(metric_events); 619b18f3e36SAndi Kleen ret = metricgroup__add_metric_list(str, &extra_events, &group_list); 620b18f3e36SAndi Kleen if (ret) 621b18f3e36SAndi Kleen return ret; 622b18f3e36SAndi Kleen pr_debug("adding %s\n", extra_events.buf); 623a910e466SIan Rogers bzero(&parse_error, sizeof(parse_error)); 624b18f3e36SAndi Kleen ret = parse_events(perf_evlist, extra_events.buf, &parse_error); 625b18f3e36SAndi Kleen if (ret) { 626333b5665SAndi Kleen parse_events_print_error(&parse_error, extra_events.buf); 627b18f3e36SAndi Kleen goto out; 628b18f3e36SAndi Kleen } 629b18f3e36SAndi Kleen strbuf_release(&extra_events); 630b18f3e36SAndi Kleen ret = metricgroup__setup_events(&group_list, perf_evlist, 631b18f3e36SAndi Kleen metric_events); 632b18f3e36SAndi Kleen out: 633b18f3e36SAndi Kleen metricgroup__free_egroups(&group_list); 634b18f3e36SAndi Kleen return ret; 635b18f3e36SAndi Kleen } 636742d92ffSThomas Richter 637742d92ffSThomas Richter bool metricgroup__has_metric(const char *metric) 638742d92ffSThomas Richter { 639742d92ffSThomas Richter struct pmu_events_map *map = perf_pmu__find_map(NULL); 640742d92ffSThomas Richter struct pmu_event *pe; 641742d92ffSThomas Richter int i; 642742d92ffSThomas Richter 643742d92ffSThomas Richter if (!map) 644742d92ffSThomas Richter return false; 645742d92ffSThomas Richter 646742d92ffSThomas Richter for (i = 0; ; i++) { 647742d92ffSThomas Richter pe = &map->table[i]; 648742d92ffSThomas Richter 649742d92ffSThomas Richter if (!pe->name && !pe->metric_group && !pe->metric_name) 650742d92ffSThomas Richter break; 651742d92ffSThomas Richter if (!pe->metric_expr) 652742d92ffSThomas Richter continue; 653742d92ffSThomas Richter if (match_metric(pe->metric_name, metric)) 654742d92ffSThomas Richter return true; 655742d92ffSThomas Richter } 656742d92ffSThomas Richter return false; 657742d92ffSThomas Richter } 658