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" 9b18f3e36SAndi Kleen #include "evlist.h" 10b18f3e36SAndi Kleen #include "strbuf.h" 11b18f3e36SAndi Kleen #include "pmu.h" 12b18f3e36SAndi Kleen #include "expr.h" 13b18f3e36SAndi Kleen #include "rblist.h" 14b18f3e36SAndi Kleen #include <string.h> 15b18f3e36SAndi Kleen #include <stdbool.h> 16b18f3e36SAndi Kleen #include <errno.h> 17b18f3e36SAndi Kleen #include "pmu-events/pmu-events.h" 18b18f3e36SAndi Kleen #include "strlist.h" 19b18f3e36SAndi Kleen #include <assert.h> 20bd9860bfSArnaldo Carvalho de Melo #include <linux/ctype.h> 21d8f9da24SArnaldo Carvalho de Melo #include <linux/zalloc.h> 22b18f3e36SAndi Kleen 23b18f3e36SAndi Kleen struct metric_event *metricgroup__lookup(struct rblist *metric_events, 2432dcd021SJiri Olsa struct evsel *evsel, 25b18f3e36SAndi Kleen bool create) 26b18f3e36SAndi Kleen { 27b18f3e36SAndi Kleen struct rb_node *nd; 28b18f3e36SAndi Kleen struct metric_event me = { 29b18f3e36SAndi Kleen .evsel = evsel 30b18f3e36SAndi Kleen }; 314bd1bef8SAndi Kleen 324bd1bef8SAndi Kleen if (!metric_events) 334bd1bef8SAndi Kleen return NULL; 344bd1bef8SAndi Kleen 35b18f3e36SAndi Kleen nd = rblist__find(metric_events, &me); 36b18f3e36SAndi Kleen if (nd) 37b18f3e36SAndi Kleen return container_of(nd, struct metric_event, nd); 38b18f3e36SAndi Kleen if (create) { 39b18f3e36SAndi Kleen rblist__add_node(metric_events, &me); 40b18f3e36SAndi Kleen nd = rblist__find(metric_events, &me); 41b18f3e36SAndi Kleen if (nd) 42b18f3e36SAndi Kleen return container_of(nd, struct metric_event, nd); 43b18f3e36SAndi Kleen } 44b18f3e36SAndi Kleen return NULL; 45b18f3e36SAndi Kleen } 46b18f3e36SAndi Kleen 47b18f3e36SAndi Kleen static int metric_event_cmp(struct rb_node *rb_node, const void *entry) 48b18f3e36SAndi Kleen { 49b18f3e36SAndi Kleen struct metric_event *a = container_of(rb_node, 50b18f3e36SAndi Kleen struct metric_event, 51b18f3e36SAndi Kleen nd); 52b18f3e36SAndi Kleen const struct metric_event *b = entry; 53b18f3e36SAndi Kleen 54b18f3e36SAndi Kleen if (a->evsel == b->evsel) 55b18f3e36SAndi Kleen return 0; 56b18f3e36SAndi Kleen if ((char *)a->evsel < (char *)b->evsel) 57b18f3e36SAndi Kleen return -1; 58b18f3e36SAndi Kleen return +1; 59b18f3e36SAndi Kleen } 60b18f3e36SAndi Kleen 61b18f3e36SAndi Kleen static struct rb_node *metric_event_new(struct rblist *rblist __maybe_unused, 62b18f3e36SAndi Kleen const void *entry) 63b18f3e36SAndi Kleen { 64b18f3e36SAndi Kleen struct metric_event *me = malloc(sizeof(struct metric_event)); 65b18f3e36SAndi Kleen 66b18f3e36SAndi Kleen if (!me) 67b18f3e36SAndi Kleen return NULL; 68b18f3e36SAndi Kleen memcpy(me, entry, sizeof(struct metric_event)); 69b18f3e36SAndi Kleen me->evsel = ((struct metric_event *)entry)->evsel; 70b18f3e36SAndi Kleen INIT_LIST_HEAD(&me->head); 71b18f3e36SAndi Kleen return &me->nd; 72b18f3e36SAndi Kleen } 73b18f3e36SAndi Kleen 74b18f3e36SAndi Kleen static void metricgroup__rblist_init(struct rblist *metric_events) 75b18f3e36SAndi Kleen { 76b18f3e36SAndi Kleen rblist__init(metric_events); 77b18f3e36SAndi Kleen metric_events->node_cmp = metric_event_cmp; 78b18f3e36SAndi Kleen metric_events->node_new = metric_event_new; 79b18f3e36SAndi Kleen } 80b18f3e36SAndi Kleen 81b18f3e36SAndi Kleen struct egroup { 82b18f3e36SAndi Kleen struct list_head nd; 83b18f3e36SAndi Kleen int idnum; 84b18f3e36SAndi Kleen const char **ids; 85b18f3e36SAndi Kleen const char *metric_name; 86b18f3e36SAndi Kleen const char *metric_expr; 87b18f3e36SAndi Kleen }; 88b18f3e36SAndi Kleen 8932dcd021SJiri Olsa static bool record_evsel(int *ind, struct evsel **start, 902f87f33fSAndi Kleen int idnum, 9132dcd021SJiri Olsa struct evsel **metric_events, 9232dcd021SJiri Olsa struct evsel *ev) 932f87f33fSAndi Kleen { 942f87f33fSAndi Kleen metric_events[*ind] = ev; 952f87f33fSAndi Kleen if (*ind == 0) 962f87f33fSAndi Kleen *start = ev; 972f87f33fSAndi Kleen if (++*ind == idnum) { 982f87f33fSAndi Kleen metric_events[*ind] = NULL; 992f87f33fSAndi Kleen return true; 1002f87f33fSAndi Kleen } 1012f87f33fSAndi Kleen return false; 1022f87f33fSAndi Kleen } 1032f87f33fSAndi Kleen 104*63503dbaSJiri Olsa static struct evsel *find_evsel_group(struct evlist *perf_evlist, 105b18f3e36SAndi Kleen const char **ids, 106b18f3e36SAndi Kleen int idnum, 10732dcd021SJiri Olsa struct evsel **metric_events) 108b18f3e36SAndi Kleen { 10932dcd021SJiri Olsa struct evsel *ev, *start = NULL; 110b18f3e36SAndi Kleen int ind = 0; 111b18f3e36SAndi Kleen 112b18f3e36SAndi Kleen evlist__for_each_entry (perf_evlist, ev) { 1132f87f33fSAndi Kleen if (ev->collect_stat) 1142f87f33fSAndi Kleen continue; 115b18f3e36SAndi Kleen if (!strcmp(ev->name, ids[ind])) { 1162f87f33fSAndi Kleen if (record_evsel(&ind, &start, idnum, 1172f87f33fSAndi Kleen metric_events, ev)) 118b18f3e36SAndi Kleen return start; 119b18f3e36SAndi Kleen } else { 1202f87f33fSAndi Kleen /* 1212f87f33fSAndi Kleen * We saw some other event that is not 1222f87f33fSAndi Kleen * in our list of events. Discard 1232f87f33fSAndi Kleen * the whole match and start again. 1242f87f33fSAndi Kleen */ 125b18f3e36SAndi Kleen ind = 0; 126b18f3e36SAndi Kleen start = NULL; 1272f87f33fSAndi Kleen if (!strcmp(ev->name, ids[ind])) { 1282f87f33fSAndi Kleen if (record_evsel(&ind, &start, idnum, 1292f87f33fSAndi Kleen metric_events, ev)) 1302f87f33fSAndi Kleen return start; 1312f87f33fSAndi Kleen } 132b18f3e36SAndi Kleen } 133b18f3e36SAndi Kleen } 134b18f3e36SAndi Kleen /* 135b18f3e36SAndi Kleen * This can happen when an alias expands to multiple 136b18f3e36SAndi Kleen * events, like for uncore events. 137b18f3e36SAndi Kleen * We don't support this case for now. 138b18f3e36SAndi Kleen */ 139b18f3e36SAndi Kleen return NULL; 140b18f3e36SAndi Kleen } 141b18f3e36SAndi Kleen 142b18f3e36SAndi Kleen static int metricgroup__setup_events(struct list_head *groups, 143*63503dbaSJiri Olsa struct evlist *perf_evlist, 144b18f3e36SAndi Kleen struct rblist *metric_events_list) 145b18f3e36SAndi Kleen { 146b18f3e36SAndi Kleen struct metric_event *me; 147b18f3e36SAndi Kleen struct metric_expr *expr; 148b18f3e36SAndi Kleen int i = 0; 149b18f3e36SAndi Kleen int ret = 0; 150b18f3e36SAndi Kleen struct egroup *eg; 15132dcd021SJiri Olsa struct evsel *evsel; 152b18f3e36SAndi Kleen 153b18f3e36SAndi Kleen list_for_each_entry (eg, groups, nd) { 15432dcd021SJiri Olsa struct evsel **metric_events; 155b18f3e36SAndi Kleen 156b18f3e36SAndi Kleen metric_events = calloc(sizeof(void *), eg->idnum + 1); 157b18f3e36SAndi Kleen if (!metric_events) { 158b18f3e36SAndi Kleen ret = -ENOMEM; 159b18f3e36SAndi Kleen break; 160b18f3e36SAndi Kleen } 1612f87f33fSAndi Kleen evsel = find_evsel_group(perf_evlist, eg->ids, eg->idnum, 162b18f3e36SAndi Kleen metric_events); 163b18f3e36SAndi Kleen if (!evsel) { 164b18f3e36SAndi Kleen pr_debug("Cannot resolve %s: %s\n", 165b18f3e36SAndi Kleen eg->metric_name, eg->metric_expr); 166b18f3e36SAndi Kleen continue; 167b18f3e36SAndi Kleen } 168b18f3e36SAndi Kleen for (i = 0; i < eg->idnum; i++) 169b18f3e36SAndi Kleen metric_events[i]->collect_stat = true; 170b18f3e36SAndi Kleen me = metricgroup__lookup(metric_events_list, evsel, true); 171b18f3e36SAndi Kleen if (!me) { 172b18f3e36SAndi Kleen ret = -ENOMEM; 173b18f3e36SAndi Kleen break; 174b18f3e36SAndi Kleen } 175b18f3e36SAndi Kleen expr = malloc(sizeof(struct metric_expr)); 176b18f3e36SAndi Kleen if (!expr) { 177b18f3e36SAndi Kleen ret = -ENOMEM; 178b18f3e36SAndi Kleen break; 179b18f3e36SAndi Kleen } 180b18f3e36SAndi Kleen expr->metric_expr = eg->metric_expr; 181b18f3e36SAndi Kleen expr->metric_name = eg->metric_name; 182b18f3e36SAndi Kleen expr->metric_events = metric_events; 183b18f3e36SAndi Kleen list_add(&expr->nd, &me->head); 184b18f3e36SAndi Kleen } 185b18f3e36SAndi Kleen return ret; 186b18f3e36SAndi Kleen } 187b18f3e36SAndi Kleen 188b18f3e36SAndi Kleen static bool match_metric(const char *n, const char *list) 189b18f3e36SAndi Kleen { 190b18f3e36SAndi Kleen int len; 191b18f3e36SAndi Kleen char *m; 192b18f3e36SAndi Kleen 193b18f3e36SAndi Kleen if (!list) 194b18f3e36SAndi Kleen return false; 195b18f3e36SAndi Kleen if (!strcmp(list, "all")) 196b18f3e36SAndi Kleen return true; 197b18f3e36SAndi Kleen if (!n) 198b18f3e36SAndi Kleen return !strcasecmp(list, "No_group"); 199b18f3e36SAndi Kleen len = strlen(list); 200b18f3e36SAndi Kleen m = strcasestr(n, list); 201b18f3e36SAndi Kleen if (!m) 202b18f3e36SAndi Kleen return false; 203b18f3e36SAndi Kleen if ((m == n || m[-1] == ';' || m[-1] == ' ') && 204b18f3e36SAndi Kleen (m[len] == 0 || m[len] == ';')) 205b18f3e36SAndi Kleen return true; 206b18f3e36SAndi Kleen return false; 207b18f3e36SAndi Kleen } 208b18f3e36SAndi Kleen 20971b0acceSAndi Kleen struct mep { 21071b0acceSAndi Kleen struct rb_node nd; 21171b0acceSAndi Kleen const char *name; 21271b0acceSAndi Kleen struct strlist *metrics; 21371b0acceSAndi Kleen }; 21471b0acceSAndi Kleen 21571b0acceSAndi Kleen static int mep_cmp(struct rb_node *rb_node, const void *entry) 21671b0acceSAndi Kleen { 21771b0acceSAndi Kleen struct mep *a = container_of(rb_node, struct mep, nd); 21871b0acceSAndi Kleen struct mep *b = (struct mep *)entry; 21971b0acceSAndi Kleen 22071b0acceSAndi Kleen return strcmp(a->name, b->name); 22171b0acceSAndi Kleen } 22271b0acceSAndi Kleen 22371b0acceSAndi Kleen static struct rb_node *mep_new(struct rblist *rl __maybe_unused, 22471b0acceSAndi Kleen const void *entry) 22571b0acceSAndi Kleen { 22671b0acceSAndi Kleen struct mep *me = malloc(sizeof(struct mep)); 22771b0acceSAndi Kleen 22871b0acceSAndi Kleen if (!me) 22971b0acceSAndi Kleen return NULL; 23071b0acceSAndi Kleen memcpy(me, entry, sizeof(struct mep)); 23171b0acceSAndi Kleen me->name = strdup(me->name); 23271b0acceSAndi Kleen if (!me->name) 23371b0acceSAndi Kleen goto out_me; 23471b0acceSAndi Kleen me->metrics = strlist__new(NULL, NULL); 23571b0acceSAndi Kleen if (!me->metrics) 23671b0acceSAndi Kleen goto out_name; 23771b0acceSAndi Kleen return &me->nd; 23871b0acceSAndi Kleen out_name: 239d8f9da24SArnaldo Carvalho de Melo zfree(&me->name); 24071b0acceSAndi Kleen out_me: 24171b0acceSAndi Kleen free(me); 24271b0acceSAndi Kleen return NULL; 24371b0acceSAndi Kleen } 24471b0acceSAndi Kleen 24571b0acceSAndi Kleen static struct mep *mep_lookup(struct rblist *groups, const char *name) 24671b0acceSAndi Kleen { 24771b0acceSAndi Kleen struct rb_node *nd; 24871b0acceSAndi Kleen struct mep me = { 24971b0acceSAndi Kleen .name = name 25071b0acceSAndi Kleen }; 25171b0acceSAndi Kleen nd = rblist__find(groups, &me); 25271b0acceSAndi Kleen if (nd) 25371b0acceSAndi Kleen return container_of(nd, struct mep, nd); 25471b0acceSAndi Kleen rblist__add_node(groups, &me); 25571b0acceSAndi Kleen nd = rblist__find(groups, &me); 25671b0acceSAndi Kleen if (nd) 25771b0acceSAndi Kleen return container_of(nd, struct mep, nd); 25871b0acceSAndi Kleen return NULL; 25971b0acceSAndi Kleen } 26071b0acceSAndi Kleen 26171b0acceSAndi Kleen static void mep_delete(struct rblist *rl __maybe_unused, 26271b0acceSAndi Kleen struct rb_node *nd) 26371b0acceSAndi Kleen { 26471b0acceSAndi Kleen struct mep *me = container_of(nd, struct mep, nd); 26571b0acceSAndi Kleen 26671b0acceSAndi Kleen strlist__delete(me->metrics); 267d8f9da24SArnaldo Carvalho de Melo zfree(&me->name); 26871b0acceSAndi Kleen free(me); 26971b0acceSAndi Kleen } 27071b0acceSAndi Kleen 27171b0acceSAndi Kleen static void metricgroup__print_strlist(struct strlist *metrics, bool raw) 27271b0acceSAndi Kleen { 27371b0acceSAndi Kleen struct str_node *sn; 27471b0acceSAndi Kleen int n = 0; 27571b0acceSAndi Kleen 27671b0acceSAndi Kleen strlist__for_each_entry (sn, metrics) { 27771b0acceSAndi Kleen if (raw) 27871b0acceSAndi Kleen printf("%s%s", n > 0 ? " " : "", sn->s); 27971b0acceSAndi Kleen else 28071b0acceSAndi Kleen printf(" %s\n", sn->s); 28171b0acceSAndi Kleen n++; 28271b0acceSAndi Kleen } 28371b0acceSAndi Kleen if (raw) 28471b0acceSAndi Kleen putchar('\n'); 28571b0acceSAndi Kleen } 28671b0acceSAndi Kleen 28771b0acceSAndi Kleen void metricgroup__print(bool metrics, bool metricgroups, char *filter, 28833bbc571SJiri Olsa bool raw, bool details) 28971b0acceSAndi Kleen { 29054e32dc0SGanapatrao Kulkarni struct pmu_events_map *map = perf_pmu__find_map(NULL); 29171b0acceSAndi Kleen struct pmu_event *pe; 29271b0acceSAndi Kleen int i; 29371b0acceSAndi Kleen struct rblist groups; 29471b0acceSAndi Kleen struct rb_node *node, *next; 29571b0acceSAndi Kleen struct strlist *metriclist = NULL; 29671b0acceSAndi Kleen 29771b0acceSAndi Kleen if (!map) 29871b0acceSAndi Kleen return; 29971b0acceSAndi Kleen 30071b0acceSAndi Kleen if (!metricgroups) { 30171b0acceSAndi Kleen metriclist = strlist__new(NULL, NULL); 30271b0acceSAndi Kleen if (!metriclist) 30371b0acceSAndi Kleen return; 30471b0acceSAndi Kleen } 30571b0acceSAndi Kleen 30671b0acceSAndi Kleen rblist__init(&groups); 30771b0acceSAndi Kleen groups.node_new = mep_new; 30871b0acceSAndi Kleen groups.node_cmp = mep_cmp; 30971b0acceSAndi Kleen groups.node_delete = mep_delete; 31071b0acceSAndi Kleen for (i = 0; ; i++) { 31171b0acceSAndi Kleen const char *g; 31271b0acceSAndi Kleen pe = &map->table[i]; 31371b0acceSAndi Kleen 31471b0acceSAndi Kleen if (!pe->name && !pe->metric_group && !pe->metric_name) 31571b0acceSAndi Kleen break; 31671b0acceSAndi Kleen if (!pe->metric_expr) 31771b0acceSAndi Kleen continue; 31871b0acceSAndi Kleen g = pe->metric_group; 31971b0acceSAndi Kleen if (!g && pe->metric_name) { 32071b0acceSAndi Kleen if (pe->name) 32171b0acceSAndi Kleen continue; 32271b0acceSAndi Kleen g = "No_group"; 32371b0acceSAndi Kleen } 32471b0acceSAndi Kleen if (g) { 32571b0acceSAndi Kleen char *omg; 32671b0acceSAndi Kleen char *mg = strdup(g); 32771b0acceSAndi Kleen 32871b0acceSAndi Kleen if (!mg) 32971b0acceSAndi Kleen return; 33071b0acceSAndi Kleen omg = mg; 33171b0acceSAndi Kleen while ((g = strsep(&mg, ";")) != NULL) { 33271b0acceSAndi Kleen struct mep *me; 33371b0acceSAndi Kleen char *s; 33471b0acceSAndi Kleen 33580e9073fSArnaldo Carvalho de Melo g = skip_spaces(g); 33671b0acceSAndi Kleen if (*g == 0) 33771b0acceSAndi Kleen g = "No_group"; 33871b0acceSAndi Kleen if (filter && !strstr(g, filter)) 33971b0acceSAndi Kleen continue; 34071b0acceSAndi Kleen if (raw) 34171b0acceSAndi Kleen s = (char *)pe->metric_name; 34271b0acceSAndi Kleen else { 34395f04328SMichael Petlan if (asprintf(&s, "%s\n%*s%s]", 34495f04328SMichael Petlan pe->metric_name, 8, "[", pe->desc) < 0) 34571b0acceSAndi Kleen return; 34633bbc571SJiri Olsa 34733bbc571SJiri Olsa if (details) { 34833bbc571SJiri Olsa if (asprintf(&s, "%s\n%*s%s]", 34933bbc571SJiri Olsa s, 8, "[", pe->metric_expr) < 0) 35033bbc571SJiri Olsa return; 35133bbc571SJiri Olsa } 35271b0acceSAndi Kleen } 35371b0acceSAndi Kleen 35471b0acceSAndi Kleen if (!s) 35571b0acceSAndi Kleen continue; 35671b0acceSAndi Kleen 35771b0acceSAndi Kleen if (!metricgroups) { 35871b0acceSAndi Kleen strlist__add(metriclist, s); 35971b0acceSAndi Kleen } else { 36071b0acceSAndi Kleen me = mep_lookup(&groups, g); 36171b0acceSAndi Kleen if (!me) 36271b0acceSAndi Kleen continue; 36371b0acceSAndi Kleen strlist__add(me->metrics, s); 36471b0acceSAndi Kleen } 36571b0acceSAndi Kleen } 36671b0acceSAndi Kleen free(omg); 36771b0acceSAndi Kleen } 36871b0acceSAndi Kleen } 36971b0acceSAndi Kleen 37071b0acceSAndi Kleen if (metricgroups && !raw) 37171b0acceSAndi Kleen printf("\nMetric Groups:\n\n"); 37271b0acceSAndi Kleen else if (metrics && !raw) 37371b0acceSAndi Kleen printf("\nMetrics:\n\n"); 37471b0acceSAndi Kleen 375ca227029SDavidlohr Bueso for (node = rb_first_cached(&groups.entries); node; node = next) { 37671b0acceSAndi Kleen struct mep *me = container_of(node, struct mep, nd); 37771b0acceSAndi Kleen 37871b0acceSAndi Kleen if (metricgroups) 3799c344d15SAndi Kleen printf("%s%s%s", me->name, metrics && !raw ? ":" : "", raw ? " " : "\n"); 38071b0acceSAndi Kleen if (metrics) 38171b0acceSAndi Kleen metricgroup__print_strlist(me->metrics, raw); 38271b0acceSAndi Kleen next = rb_next(node); 38371b0acceSAndi Kleen rblist__remove_node(&groups, node); 38471b0acceSAndi Kleen } 38571b0acceSAndi Kleen if (!metricgroups) 38671b0acceSAndi Kleen metricgroup__print_strlist(metriclist, raw); 38771b0acceSAndi Kleen strlist__delete(metriclist); 38871b0acceSAndi Kleen } 38971b0acceSAndi Kleen 390b18f3e36SAndi Kleen static int metricgroup__add_metric(const char *metric, struct strbuf *events, 391b18f3e36SAndi Kleen struct list_head *group_list) 392b18f3e36SAndi Kleen { 39354e32dc0SGanapatrao Kulkarni struct pmu_events_map *map = perf_pmu__find_map(NULL); 394b18f3e36SAndi Kleen struct pmu_event *pe; 395b18f3e36SAndi Kleen int ret = -EINVAL; 396b18f3e36SAndi Kleen int i, j; 397b18f3e36SAndi Kleen 398b18f3e36SAndi Kleen if (!map) 399b18f3e36SAndi Kleen return 0; 400b18f3e36SAndi Kleen 401b18f3e36SAndi Kleen for (i = 0; ; i++) { 402b18f3e36SAndi Kleen pe = &map->table[i]; 403b18f3e36SAndi Kleen 404b18f3e36SAndi Kleen if (!pe->name && !pe->metric_group && !pe->metric_name) 405b18f3e36SAndi Kleen break; 406b18f3e36SAndi Kleen if (!pe->metric_expr) 407b18f3e36SAndi Kleen continue; 408b18f3e36SAndi Kleen if (match_metric(pe->metric_group, metric) || 409b18f3e36SAndi Kleen match_metric(pe->metric_name, metric)) { 410b18f3e36SAndi Kleen const char **ids; 411b18f3e36SAndi Kleen int idnum; 412b18f3e36SAndi Kleen struct egroup *eg; 413488c3bf7SAndi Kleen bool no_group = false; 414b18f3e36SAndi Kleen 415b18f3e36SAndi Kleen pr_debug("metric expr %s for %s\n", pe->metric_expr, pe->metric_name); 416b18f3e36SAndi Kleen 417b18f3e36SAndi Kleen if (expr__find_other(pe->metric_expr, 418b18f3e36SAndi Kleen NULL, &ids, &idnum) < 0) 419b18f3e36SAndi Kleen continue; 420b18f3e36SAndi Kleen if (events->len > 0) 421b18f3e36SAndi Kleen strbuf_addf(events, ","); 422b18f3e36SAndi Kleen for (j = 0; j < idnum; j++) { 423b18f3e36SAndi Kleen pr_debug("found event %s\n", ids[j]); 424488c3bf7SAndi Kleen /* 425488c3bf7SAndi Kleen * Duration time maps to a software event and can make 426488c3bf7SAndi Kleen * groups not count. Always use it outside a 427488c3bf7SAndi Kleen * group. 428488c3bf7SAndi Kleen */ 429488c3bf7SAndi Kleen if (!strcmp(ids[j], "duration_time")) { 430488c3bf7SAndi Kleen if (j > 0) 431488c3bf7SAndi Kleen strbuf_addf(events, "}:W,"); 432488c3bf7SAndi Kleen strbuf_addf(events, "duration_time"); 433488c3bf7SAndi Kleen no_group = true; 434488c3bf7SAndi Kleen continue; 435b18f3e36SAndi Kleen } 436488c3bf7SAndi Kleen strbuf_addf(events, "%s%s", 437488c3bf7SAndi Kleen j == 0 || no_group ? "{" : ",", 438488c3bf7SAndi Kleen ids[j]); 439488c3bf7SAndi Kleen no_group = false; 440488c3bf7SAndi Kleen } 441488c3bf7SAndi Kleen if (!no_group) 442b18f3e36SAndi Kleen strbuf_addf(events, "}:W"); 443b18f3e36SAndi Kleen 444b18f3e36SAndi Kleen eg = malloc(sizeof(struct egroup)); 445b18f3e36SAndi Kleen if (!eg) { 446b18f3e36SAndi Kleen ret = -ENOMEM; 447b18f3e36SAndi Kleen break; 448b18f3e36SAndi Kleen } 449b18f3e36SAndi Kleen eg->ids = ids; 450b18f3e36SAndi Kleen eg->idnum = idnum; 451b18f3e36SAndi Kleen eg->metric_name = pe->metric_name; 452b18f3e36SAndi Kleen eg->metric_expr = pe->metric_expr; 453b18f3e36SAndi Kleen list_add_tail(&eg->nd, group_list); 454b18f3e36SAndi Kleen ret = 0; 455b18f3e36SAndi Kleen } 456b18f3e36SAndi Kleen } 457b18f3e36SAndi Kleen return ret; 458b18f3e36SAndi Kleen } 459b18f3e36SAndi Kleen 460b18f3e36SAndi Kleen static int metricgroup__add_metric_list(const char *list, struct strbuf *events, 461b18f3e36SAndi Kleen struct list_head *group_list) 462b18f3e36SAndi Kleen { 463b18f3e36SAndi Kleen char *llist, *nlist, *p; 464b18f3e36SAndi Kleen int ret = -EINVAL; 465b18f3e36SAndi Kleen 466b18f3e36SAndi Kleen nlist = strdup(list); 467b18f3e36SAndi Kleen if (!nlist) 468b18f3e36SAndi Kleen return -ENOMEM; 469b18f3e36SAndi Kleen llist = nlist; 470411bc316SAndi Kleen 471411bc316SAndi Kleen strbuf_init(events, 100); 472411bc316SAndi Kleen strbuf_addf(events, "%s", ""); 473411bc316SAndi Kleen 474b18f3e36SAndi Kleen while ((p = strsep(&llist, ",")) != NULL) { 475b18f3e36SAndi Kleen ret = metricgroup__add_metric(p, events, group_list); 476b18f3e36SAndi Kleen if (ret == -EINVAL) { 477b18f3e36SAndi Kleen fprintf(stderr, "Cannot find metric or group `%s'\n", 478b18f3e36SAndi Kleen p); 479b18f3e36SAndi Kleen break; 480b18f3e36SAndi Kleen } 481b18f3e36SAndi Kleen } 482b18f3e36SAndi Kleen free(nlist); 483b18f3e36SAndi Kleen return ret; 484b18f3e36SAndi Kleen } 485b18f3e36SAndi Kleen 486b18f3e36SAndi Kleen static void metricgroup__free_egroups(struct list_head *group_list) 487b18f3e36SAndi Kleen { 488b18f3e36SAndi Kleen struct egroup *eg, *egtmp; 489b18f3e36SAndi Kleen int i; 490b18f3e36SAndi Kleen 491b18f3e36SAndi Kleen list_for_each_entry_safe (eg, egtmp, group_list, nd) { 492b18f3e36SAndi Kleen for (i = 0; i < eg->idnum; i++) 493d8f9da24SArnaldo Carvalho de Melo zfree(&eg->ids[i]); 494d8f9da24SArnaldo Carvalho de Melo zfree(&eg->ids); 495acc7bfb3SArnaldo Carvalho de Melo list_del_init(&eg->nd); 496b18f3e36SAndi Kleen free(eg); 497b18f3e36SAndi Kleen } 498b18f3e36SAndi Kleen } 499b18f3e36SAndi Kleen 500b18f3e36SAndi Kleen int metricgroup__parse_groups(const struct option *opt, 501b18f3e36SAndi Kleen const char *str, 502b18f3e36SAndi Kleen struct rblist *metric_events) 503b18f3e36SAndi Kleen { 504b18f3e36SAndi Kleen struct parse_events_error parse_error; 505*63503dbaSJiri Olsa struct evlist *perf_evlist = *(struct evlist **)opt->value; 506b18f3e36SAndi Kleen struct strbuf extra_events; 507b18f3e36SAndi Kleen LIST_HEAD(group_list); 508b18f3e36SAndi Kleen int ret; 509b18f3e36SAndi Kleen 510b18f3e36SAndi Kleen if (metric_events->nr_entries == 0) 511b18f3e36SAndi Kleen metricgroup__rblist_init(metric_events); 512b18f3e36SAndi Kleen ret = metricgroup__add_metric_list(str, &extra_events, &group_list); 513b18f3e36SAndi Kleen if (ret) 514b18f3e36SAndi Kleen return ret; 515b18f3e36SAndi Kleen pr_debug("adding %s\n", extra_events.buf); 516b18f3e36SAndi Kleen memset(&parse_error, 0, sizeof(struct parse_events_error)); 517b18f3e36SAndi Kleen ret = parse_events(perf_evlist, extra_events.buf, &parse_error); 518b18f3e36SAndi Kleen if (ret) { 519333b5665SAndi Kleen parse_events_print_error(&parse_error, extra_events.buf); 520b18f3e36SAndi Kleen goto out; 521b18f3e36SAndi Kleen } 522b18f3e36SAndi Kleen strbuf_release(&extra_events); 523b18f3e36SAndi Kleen ret = metricgroup__setup_events(&group_list, perf_evlist, 524b18f3e36SAndi Kleen metric_events); 525b18f3e36SAndi Kleen out: 526b18f3e36SAndi Kleen metricgroup__free_egroups(&group_list); 527b18f3e36SAndi Kleen return ret; 528b18f3e36SAndi Kleen } 529742d92ffSThomas Richter 530742d92ffSThomas Richter bool metricgroup__has_metric(const char *metric) 531742d92ffSThomas Richter { 532742d92ffSThomas Richter struct pmu_events_map *map = perf_pmu__find_map(NULL); 533742d92ffSThomas Richter struct pmu_event *pe; 534742d92ffSThomas Richter int i; 535742d92ffSThomas Richter 536742d92ffSThomas Richter if (!map) 537742d92ffSThomas Richter return false; 538742d92ffSThomas Richter 539742d92ffSThomas Richter for (i = 0; ; i++) { 540742d92ffSThomas Richter pe = &map->table[i]; 541742d92ffSThomas Richter 542742d92ffSThomas Richter if (!pe->name && !pe->metric_group && !pe->metric_name) 543742d92ffSThomas Richter break; 544742d92ffSThomas Richter if (!pe->metric_expr) 545742d92ffSThomas Richter continue; 546742d92ffSThomas Richter if (match_metric(pe->metric_name, metric)) 547742d92ffSThomas Richter return true; 548742d92ffSThomas Richter } 549742d92ffSThomas Richter return false; 550742d92ffSThomas Richter } 551