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" 140e0ae874SJin Yao #include "pmu-hybrid.h" 15e5c6109fSIan Rogers #include "print-events.h" 1690053634SIan Rogers #include "smt.h" 17b18f3e36SAndi Kleen #include "expr.h" 18b18f3e36SAndi Kleen #include "rblist.h" 19b18f3e36SAndi Kleen #include <string.h> 20b18f3e36SAndi Kleen #include <errno.h> 21b18f3e36SAndi Kleen #include "strlist.h" 22b18f3e36SAndi Kleen #include <assert.h> 23bd9860bfSArnaldo Carvalho de Melo #include <linux/ctype.h> 2480be6434SIan Rogers #include <linux/list_sort.h> 25b4209025SArnaldo Carvalho de Melo #include <linux/string.h> 26d8f9da24SArnaldo Carvalho de Melo #include <linux/zalloc.h> 271725e9cdSIan Rogers #include <perf/cpumap.h> 280b8026e8SArnaldo Carvalho de Melo #include <subcmd/parse-options.h> 29ab483d8bSKan Liang #include <api/fs/fs.h> 30ab483d8bSKan Liang #include "util.h" 31f6fb0960SJiri Olsa #include <asm/bug.h> 32b214ba8cSNamhyung Kim #include "cgroup.h" 33bd560973SIan Rogers #include "util/hashmap.h" 34b18f3e36SAndi Kleen 35b18f3e36SAndi Kleen struct metric_event *metricgroup__lookup(struct rblist *metric_events, 3632dcd021SJiri Olsa struct evsel *evsel, 37b18f3e36SAndi Kleen bool create) 38b18f3e36SAndi Kleen { 39b18f3e36SAndi Kleen struct rb_node *nd; 40b18f3e36SAndi Kleen struct metric_event me = { 41b18f3e36SAndi Kleen .evsel = evsel 42b18f3e36SAndi Kleen }; 434bd1bef8SAndi Kleen 444bd1bef8SAndi Kleen if (!metric_events) 454bd1bef8SAndi Kleen return NULL; 464bd1bef8SAndi Kleen 47b18f3e36SAndi Kleen nd = rblist__find(metric_events, &me); 48b18f3e36SAndi Kleen if (nd) 49b18f3e36SAndi Kleen return container_of(nd, struct metric_event, nd); 50b18f3e36SAndi Kleen if (create) { 51b18f3e36SAndi Kleen rblist__add_node(metric_events, &me); 52b18f3e36SAndi Kleen nd = rblist__find(metric_events, &me); 53b18f3e36SAndi Kleen if (nd) 54b18f3e36SAndi Kleen return container_of(nd, struct metric_event, nd); 55b18f3e36SAndi Kleen } 56b18f3e36SAndi Kleen return NULL; 57b18f3e36SAndi Kleen } 58b18f3e36SAndi Kleen 59b18f3e36SAndi Kleen static int metric_event_cmp(struct rb_node *rb_node, const void *entry) 60b18f3e36SAndi Kleen { 61b18f3e36SAndi Kleen struct metric_event *a = container_of(rb_node, 62b18f3e36SAndi Kleen struct metric_event, 63b18f3e36SAndi Kleen nd); 64b18f3e36SAndi Kleen const struct metric_event *b = entry; 65b18f3e36SAndi Kleen 66b18f3e36SAndi Kleen if (a->evsel == b->evsel) 67b18f3e36SAndi Kleen return 0; 68b18f3e36SAndi Kleen if ((char *)a->evsel < (char *)b->evsel) 69b18f3e36SAndi Kleen return -1; 70b18f3e36SAndi Kleen return +1; 71b18f3e36SAndi Kleen } 72b18f3e36SAndi Kleen 73b18f3e36SAndi Kleen static struct rb_node *metric_event_new(struct rblist *rblist __maybe_unused, 74b18f3e36SAndi Kleen const void *entry) 75b18f3e36SAndi Kleen { 76b18f3e36SAndi Kleen struct metric_event *me = malloc(sizeof(struct metric_event)); 77b18f3e36SAndi Kleen 78b18f3e36SAndi Kleen if (!me) 79b18f3e36SAndi Kleen return NULL; 80b18f3e36SAndi Kleen memcpy(me, entry, sizeof(struct metric_event)); 81b18f3e36SAndi Kleen me->evsel = ((struct metric_event *)entry)->evsel; 82b18f3e36SAndi Kleen INIT_LIST_HEAD(&me->head); 83b18f3e36SAndi Kleen return &me->nd; 84b18f3e36SAndi Kleen } 85b18f3e36SAndi Kleen 869afe5658SJiri Olsa static void metric_event_delete(struct rblist *rblist __maybe_unused, 879afe5658SJiri Olsa struct rb_node *rb_node) 889afe5658SJiri Olsa { 899afe5658SJiri Olsa struct metric_event *me = container_of(rb_node, struct metric_event, nd); 909afe5658SJiri Olsa struct metric_expr *expr, *tmp; 919afe5658SJiri Olsa 929afe5658SJiri Olsa list_for_each_entry_safe(expr, tmp, &me->head, nd) { 9311ff9bcdSArnaldo Carvalho de Melo zfree(&expr->metric_name); 9411ff9bcdSArnaldo Carvalho de Melo zfree(&expr->metric_refs); 9511ff9bcdSArnaldo Carvalho de Melo zfree(&expr->metric_events); 969afe5658SJiri Olsa free(expr); 979afe5658SJiri Olsa } 989afe5658SJiri Olsa 999afe5658SJiri Olsa free(me); 1009afe5658SJiri Olsa } 1019afe5658SJiri Olsa 102b18f3e36SAndi Kleen static void metricgroup__rblist_init(struct rblist *metric_events) 103b18f3e36SAndi Kleen { 104b18f3e36SAndi Kleen rblist__init(metric_events); 105b18f3e36SAndi Kleen metric_events->node_cmp = metric_event_cmp; 106b18f3e36SAndi Kleen metric_events->node_new = metric_event_new; 1079afe5658SJiri Olsa metric_events->node_delete = metric_event_delete; 1089afe5658SJiri Olsa } 1099afe5658SJiri Olsa 1109afe5658SJiri Olsa void metricgroup__rblist_exit(struct rblist *metric_events) 1119afe5658SJiri Olsa { 1129afe5658SJiri Olsa rblist__exit(metric_events); 113b18f3e36SAndi Kleen } 114b18f3e36SAndi Kleen 115485fcaedSIan Rogers /** 116485fcaedSIan Rogers * The metric under construction. The data held here will be placed in a 117485fcaedSIan Rogers * metric_expr. 118485fcaedSIan Rogers */ 119a0c05b36SJiri Olsa struct metric { 120b18f3e36SAndi Kleen struct list_head nd; 121485fcaedSIan Rogers /** 122485fcaedSIan Rogers * The expression parse context importantly holding the IDs contained 123485fcaedSIan Rogers * within the expression. 124485fcaedSIan Rogers */ 125cb94a02eSIan Rogers struct expr_parse_ctx *pctx; 126bd3846d0SIan Rogers const char *pmu; 127485fcaedSIan Rogers /** The name of the metric such as "IPC". */ 128b18f3e36SAndi Kleen const char *metric_name; 129b85a4d61SIan Rogers /** Modifier on the metric such as "u" or NULL for none. */ 130b85a4d61SIan Rogers const char *modifier; 131485fcaedSIan Rogers /** The expression to parse, for example, "instructions/cycles". */ 132b18f3e36SAndi Kleen const char *metric_expr; 133d0a3052fSIan Rogers /** Optional threshold expression where zero value is green, otherwise red. */ 134d0a3052fSIan Rogers const char *metric_threshold; 135485fcaedSIan Rogers /** 136485fcaedSIan Rogers * The "ScaleUnit" that scales and adds a unit to the metric during 137485fcaedSIan Rogers * output. 138485fcaedSIan Rogers */ 139287f2649SJin Yao const char *metric_unit; 14046bdc0bfSIan Rogers /** Optional null terminated array of referenced metrics. */ 14146bdc0bfSIan Rogers struct metric_ref *metric_refs; 142485fcaedSIan Rogers /** 143180a5013SIan Rogers * Should events of the metric be grouped? 144485fcaedSIan Rogers */ 145180a5013SIan Rogers bool group_events; 1465ecd5a0cSIan Rogers /** 1475ecd5a0cSIan Rogers * Parsed events for the metric. Optional as events may be taken from a 1485ecd5a0cSIan Rogers * different metric whose group contains all the IDs necessary for this 1495ecd5a0cSIan Rogers * one. 1505ecd5a0cSIan Rogers */ 1515ecd5a0cSIan Rogers struct evlist *evlist; 152b18f3e36SAndi Kleen }; 153b18f3e36SAndi Kleen 154180a5013SIan Rogers static void metric__watchdog_constraint_hint(const char *name, bool foot) 1553d81d761SIan Rogers { 1563d81d761SIan Rogers static bool violate_nmi_constraint; 1573d81d761SIan Rogers 1583d81d761SIan Rogers if (!foot) { 159180a5013SIan Rogers pr_warning("Not grouping metric %s's events.\n", name); 1603d81d761SIan Rogers violate_nmi_constraint = true; 1613d81d761SIan Rogers return; 1623d81d761SIan Rogers } 1633d81d761SIan Rogers 1643d81d761SIan Rogers if (!violate_nmi_constraint) 1653d81d761SIan Rogers return; 1663d81d761SIan Rogers 1673d81d761SIan Rogers pr_warning("Try disabling the NMI watchdog to comply NO_NMI_WATCHDOG metric constraint:\n" 1683d81d761SIan Rogers " echo 0 > /proc/sys/kernel/nmi_watchdog\n" 1693d81d761SIan Rogers " perf stat ...\n" 1703d81d761SIan Rogers " echo 1 > /proc/sys/kernel/nmi_watchdog\n"); 1713d81d761SIan Rogers } 1723d81d761SIan Rogers 173180a5013SIan Rogers static bool metric__group_events(const struct pmu_metric *pm) 1743d81d761SIan Rogers { 17590053634SIan Rogers switch (pm->event_grouping) { 17690053634SIan Rogers case MetricNoGroupEvents: 17790053634SIan Rogers return false; 17890053634SIan Rogers case MetricNoGroupEventsNmi: 17990053634SIan Rogers if (!sysctl__nmi_watchdog_enabled()) 180180a5013SIan Rogers return true; 181180a5013SIan Rogers metric__watchdog_constraint_hint(pm->metric_name, /*foot=*/false); 182180a5013SIan Rogers return false; 18390053634SIan Rogers case MetricNoGroupEventsSmt: 18490053634SIan Rogers return !smt_on(); 18590053634SIan Rogers case MetricGroupEvents: 18690053634SIan Rogers default: 187180a5013SIan Rogers return true; 1883d81d761SIan Rogers } 18990053634SIan Rogers } 1903d81d761SIan Rogers 1911725e9cdSIan Rogers static void metric__free(struct metric *m) 1921725e9cdSIan Rogers { 1931725e9cdSIan Rogers if (!m) 1941725e9cdSIan Rogers return; 1951725e9cdSIan Rogers 19611ff9bcdSArnaldo Carvalho de Melo zfree(&m->metric_refs); 1971725e9cdSIan Rogers expr__ctx_free(m->pctx); 19811ff9bcdSArnaldo Carvalho de Melo zfree(&m->modifier); 1991725e9cdSIan Rogers evlist__delete(m->evlist); 2001725e9cdSIan Rogers free(m); 2011725e9cdSIan Rogers } 2021725e9cdSIan Rogers 203db95818eSIan Rogers static struct metric *metric__new(const struct pmu_metric *pm, 204b85a4d61SIan Rogers const char *modifier, 2053d81d761SIan Rogers bool metric_no_group, 2061725e9cdSIan Rogers int runtime, 2071725e9cdSIan Rogers const char *user_requested_cpu_list, 2081725e9cdSIan Rogers bool system_wide) 2093d81d761SIan Rogers { 2103d81d761SIan Rogers struct metric *m; 2113d81d761SIan Rogers 2123d81d761SIan Rogers m = zalloc(sizeof(*m)); 2133d81d761SIan Rogers if (!m) 2143d81d761SIan Rogers return NULL; 2153d81d761SIan Rogers 2163d81d761SIan Rogers m->pctx = expr__ctx_new(); 2171725e9cdSIan Rogers if (!m->pctx) 2181725e9cdSIan Rogers goto out_err; 2193d81d761SIan Rogers 220bd3846d0SIan Rogers m->pmu = pm->pmu ?: "cpu"; 221db95818eSIan Rogers m->metric_name = pm->metric_name; 2221725e9cdSIan Rogers m->modifier = NULL; 2231725e9cdSIan Rogers if (modifier) { 2241725e9cdSIan Rogers m->modifier = strdup(modifier); 2251725e9cdSIan Rogers if (!m->modifier) 2261725e9cdSIan Rogers goto out_err; 227b85a4d61SIan Rogers } 228db95818eSIan Rogers m->metric_expr = pm->metric_expr; 229d0a3052fSIan Rogers m->metric_threshold = pm->metric_threshold; 230db95818eSIan Rogers m->metric_unit = pm->unit; 2311725e9cdSIan Rogers m->pctx->sctx.user_requested_cpu_list = NULL; 2321725e9cdSIan Rogers if (user_requested_cpu_list) { 2331725e9cdSIan Rogers m->pctx->sctx.user_requested_cpu_list = strdup(user_requested_cpu_list); 2341725e9cdSIan Rogers if (!m->pctx->sctx.user_requested_cpu_list) 2351725e9cdSIan Rogers goto out_err; 2361725e9cdSIan Rogers } 2371a6abddeSIan Rogers m->pctx->sctx.runtime = runtime; 2381725e9cdSIan Rogers m->pctx->sctx.system_wide = system_wide; 239180a5013SIan Rogers m->group_events = !metric_no_group && metric__group_events(pm); 24046bdc0bfSIan Rogers m->metric_refs = NULL; 2415ecd5a0cSIan Rogers m->evlist = NULL; 2423d81d761SIan Rogers 2433d81d761SIan Rogers return m; 2441725e9cdSIan Rogers out_err: 2451725e9cdSIan Rogers metric__free(m); 2461725e9cdSIan Rogers return NULL; 2473d81d761SIan Rogers } 2483d81d761SIan Rogers 249ec5c5b3dSIan Rogers static bool contains_metric_id(struct evsel **metric_events, int num_events, 250ec5c5b3dSIan Rogers const char *metric_id) 251dcc81be0SIan Rogers { 252dcc81be0SIan Rogers int i; 253dcc81be0SIan Rogers 254dcc81be0SIan Rogers for (i = 0; i < num_events; i++) { 255ec5c5b3dSIan Rogers if (!strcmp(evsel__metric_id(metric_events[i]), metric_id)) 256dcc81be0SIan Rogers return true; 257dcc81be0SIan Rogers } 258dcc81be0SIan Rogers return false; 259dcc81be0SIan Rogers } 260dcc81be0SIan Rogers 2612440689dSIan Rogers /** 2625ecd5a0cSIan Rogers * setup_metric_events - Find a group of events in metric_evlist that correspond 2635ecd5a0cSIan Rogers * to the IDs from a parsed metric expression. 264bd3846d0SIan Rogers * @pmu: The PMU for the IDs. 2655ecd5a0cSIan Rogers * @ids: the metric IDs to match. 2665ecd5a0cSIan Rogers * @metric_evlist: the list of perf events. 2675ecd5a0cSIan Rogers * @out_metric_events: holds the created metric events array. 2682440689dSIan Rogers */ 269bd3846d0SIan Rogers static int setup_metric_events(const char *pmu, struct hashmap *ids, 2705ecd5a0cSIan Rogers struct evlist *metric_evlist, 27117b3867dSIan Rogers struct evsel ***out_metric_events) 272b18f3e36SAndi Kleen { 2735ecd5a0cSIan Rogers struct evsel **metric_events; 274ec5c5b3dSIan Rogers const char *metric_id; 2755ecd5a0cSIan Rogers struct evsel *ev; 2765ecd5a0cSIan Rogers size_t ids_size, matched_events, i; 277*8a4859c5SIan Rogers bool all_pmus = !strcmp(pmu, "all") || !perf_pmu__is_hybrid(pmu); 2782440689dSIan Rogers 2795ecd5a0cSIan Rogers *out_metric_events = NULL; 2805ecd5a0cSIan Rogers ids_size = hashmap__size(ids); 281b18f3e36SAndi Kleen 2825ecd5a0cSIan Rogers metric_events = calloc(sizeof(void *), ids_size + 1); 2835ecd5a0cSIan Rogers if (!metric_events) 2845ecd5a0cSIan Rogers return -ENOMEM; 2855ecd5a0cSIan Rogers 2862440689dSIan Rogers matched_events = 0; 2875ecd5a0cSIan Rogers evlist__for_each_entry(metric_evlist, ev) { 2885ecd5a0cSIan Rogers struct expr_id_data *val_ptr; 2895ecd5a0cSIan Rogers 290*8a4859c5SIan Rogers /* Don't match events for the wrong hybrid PMU. */ 291*8a4859c5SIan Rogers if (!all_pmus && ev->pmu_name && 292*8a4859c5SIan Rogers perf_pmu__is_hybrid(ev->pmu_name) && 293*8a4859c5SIan Rogers strcmp(ev->pmu_name, pmu)) 294bd3846d0SIan Rogers continue; 29505530a79SIan Rogers /* 296762a05c5SIan Rogers * Check for duplicate events with the same name. For 297762a05c5SIan Rogers * example, uncore_imc/cas_count_read/ will turn into 6 298762a05c5SIan Rogers * events per socket on skylakex. Only the first such 2995ecd5a0cSIan Rogers * event is placed in metric_events. 30005530a79SIan Rogers */ 301ec5c5b3dSIan Rogers metric_id = evsel__metric_id(ev); 302ec5c5b3dSIan Rogers if (contains_metric_id(metric_events, matched_events, metric_id)) 30305530a79SIan Rogers continue; 3045ecd5a0cSIan Rogers /* 3055ecd5a0cSIan Rogers * Does this event belong to the parse context? For 3065ecd5a0cSIan Rogers * combined or shared groups, this metric may not care 3075ecd5a0cSIan Rogers * about this event. 3085ecd5a0cSIan Rogers */ 309c302378bSEduard Zingerman if (hashmap__find(ids, metric_id, &val_ptr)) { 310*8a4859c5SIan Rogers pr_debug("Matched metric-id %s to %s\n", metric_id, evsel__name(ev)); 3112440689dSIan Rogers metric_events[matched_events++] = ev; 312dcc81be0SIan Rogers 3135ecd5a0cSIan Rogers if (matched_events >= ids_size) 3142440689dSIan Rogers break; 3152440689dSIan Rogers } 316762a05c5SIan Rogers } 3175ecd5a0cSIan Rogers if (matched_events < ids_size) { 3185ecd5a0cSIan Rogers free(metric_events); 3195ecd5a0cSIan Rogers return -EINVAL; 320b18f3e36SAndi Kleen } 3215ecd5a0cSIan Rogers for (i = 0; i < ids_size; i++) { 32258fc90fdSKajol Jain ev = metric_events[i]; 3235ecd5a0cSIan Rogers ev->collect_stat = true; 3245ecd5a0cSIan Rogers 325dcc81be0SIan Rogers /* 3265ecd5a0cSIan Rogers * The metric leader points to the identically named 3275ecd5a0cSIan Rogers * event in metric_events. 328dcc81be0SIan Rogers */ 329dcc81be0SIan Rogers ev->metric_leader = ev; 330dcc81be0SIan Rogers /* 3315ecd5a0cSIan Rogers * Mark two events with identical names in the same 3325ecd5a0cSIan Rogers * group (or globally) as being in use as uncore events 3335ecd5a0cSIan Rogers * may be duplicated for each pmu. Set the metric leader 3345ecd5a0cSIan Rogers * of such events to be the event that appears in 3355ecd5a0cSIan Rogers * metric_events. 336dcc81be0SIan Rogers */ 337ec5c5b3dSIan Rogers metric_id = evsel__metric_id(ev); 3385ecd5a0cSIan Rogers evlist__for_each_entry_continue(metric_evlist, ev) { 339d3e2bb43SIan Rogers if (!strcmp(evsel__metric_id(ev), metric_id)) 340dcc81be0SIan Rogers ev->metric_leader = metric_events[i]; 341dcc81be0SIan Rogers } 342dcc81be0SIan Rogers } 3435ecd5a0cSIan Rogers *out_metric_events = metric_events; 3445ecd5a0cSIan Rogers return 0; 345b18f3e36SAndi Kleen } 346b18f3e36SAndi Kleen 347b18f3e36SAndi Kleen static bool match_metric(const char *n, const char *list) 348b18f3e36SAndi Kleen { 349b18f3e36SAndi Kleen int len; 350b18f3e36SAndi Kleen char *m; 351b18f3e36SAndi Kleen 352b18f3e36SAndi Kleen if (!list) 353b18f3e36SAndi Kleen return false; 354b18f3e36SAndi Kleen if (!strcmp(list, "all")) 355b18f3e36SAndi Kleen return true; 356b18f3e36SAndi Kleen if (!n) 357b18f3e36SAndi Kleen return !strcasecmp(list, "No_group"); 358b18f3e36SAndi Kleen len = strlen(list); 359b18f3e36SAndi Kleen m = strcasestr(n, list); 360b18f3e36SAndi Kleen if (!m) 361b18f3e36SAndi Kleen return false; 362b18f3e36SAndi Kleen if ((m == n || m[-1] == ';' || m[-1] == ' ') && 363b18f3e36SAndi Kleen (m[len] == 0 || m[len] == ';')) 364b18f3e36SAndi Kleen return true; 365b18f3e36SAndi Kleen return false; 366b18f3e36SAndi Kleen } 367b18f3e36SAndi Kleen 368bd3846d0SIan Rogers static bool match_pm_metric(const struct pmu_metric *pm, const char *pmu, const char *metric) 369be335ec2SJohn Garry { 370bd3846d0SIan Rogers const char *pm_pmu = pm->pmu ?: "cpu"; 371bd3846d0SIan Rogers 372bd3846d0SIan Rogers if (strcmp(pmu, "all") && strcmp(pm_pmu, pmu)) 373bd3846d0SIan Rogers return false; 374bd3846d0SIan Rogers 375db95818eSIan Rogers return match_metric(pm->metric_group, metric) || 376db95818eSIan Rogers match_metric(pm->metric_name, metric); 377be335ec2SJohn Garry } 378be335ec2SJohn Garry 379e5c6109fSIan Rogers /** struct mep - RB-tree node for building printing information. */ 38071b0acceSAndi Kleen struct mep { 381e5c6109fSIan Rogers /** nd - RB-tree element. */ 38271b0acceSAndi Kleen struct rb_node nd; 383e5c6109fSIan Rogers /** @metric_group: Owned metric group name, separated others with ';'. */ 384e5c6109fSIan Rogers char *metric_group; 385e5c6109fSIan Rogers const char *metric_name; 386e5c6109fSIan Rogers const char *metric_desc; 387e5c6109fSIan Rogers const char *metric_long_desc; 388e5c6109fSIan Rogers const char *metric_expr; 389c7551a2eSIan Rogers const char *metric_threshold; 390e5c6109fSIan Rogers const char *metric_unit; 39171b0acceSAndi Kleen }; 39271b0acceSAndi Kleen 39371b0acceSAndi Kleen static int mep_cmp(struct rb_node *rb_node, const void *entry) 39471b0acceSAndi Kleen { 39571b0acceSAndi Kleen struct mep *a = container_of(rb_node, struct mep, nd); 39671b0acceSAndi Kleen struct mep *b = (struct mep *)entry; 397e5c6109fSIan Rogers int ret; 39871b0acceSAndi Kleen 399e5c6109fSIan Rogers ret = strcmp(a->metric_group, b->metric_group); 400e5c6109fSIan Rogers if (ret) 401e5c6109fSIan Rogers return ret; 402e5c6109fSIan Rogers 403e5c6109fSIan Rogers return strcmp(a->metric_name, b->metric_name); 40471b0acceSAndi Kleen } 40571b0acceSAndi Kleen 406e5c6109fSIan Rogers static struct rb_node *mep_new(struct rblist *rl __maybe_unused, const void *entry) 40771b0acceSAndi Kleen { 40871b0acceSAndi Kleen struct mep *me = malloc(sizeof(struct mep)); 40971b0acceSAndi Kleen 41071b0acceSAndi Kleen if (!me) 41171b0acceSAndi Kleen return NULL; 41271b0acceSAndi Kleen 413e5c6109fSIan Rogers memcpy(me, entry, sizeof(struct mep)); 414e5c6109fSIan Rogers return &me->nd; 41571b0acceSAndi Kleen } 41671b0acceSAndi Kleen 41771b0acceSAndi Kleen static void mep_delete(struct rblist *rl __maybe_unused, 41871b0acceSAndi Kleen struct rb_node *nd) 41971b0acceSAndi Kleen { 42071b0acceSAndi Kleen struct mep *me = container_of(nd, struct mep, nd); 42171b0acceSAndi Kleen 422e5c6109fSIan Rogers zfree(&me->metric_group); 42371b0acceSAndi Kleen free(me); 42471b0acceSAndi Kleen } 42571b0acceSAndi Kleen 426e5c6109fSIan Rogers static struct mep *mep_lookup(struct rblist *groups, const char *metric_group, 427e5c6109fSIan Rogers const char *metric_name) 42871b0acceSAndi Kleen { 429e5c6109fSIan Rogers struct rb_node *nd; 430e5c6109fSIan Rogers struct mep me = { 431e5c6109fSIan Rogers .metric_group = strdup(metric_group), 432e5c6109fSIan Rogers .metric_name = metric_name, 433e5c6109fSIan Rogers }; 434e5c6109fSIan Rogers nd = rblist__find(groups, &me); 435e5c6109fSIan Rogers if (nd) { 436e5c6109fSIan Rogers free(me.metric_group); 437e5c6109fSIan Rogers return container_of(nd, struct mep, nd); 43871b0acceSAndi Kleen } 439e5c6109fSIan Rogers rblist__add_node(groups, &me); 440e5c6109fSIan Rogers nd = rblist__find(groups, &me); 441e5c6109fSIan Rogers if (nd) 442e5c6109fSIan Rogers return container_of(nd, struct mep, nd); 443e5c6109fSIan Rogers return NULL; 44471b0acceSAndi Kleen } 44571b0acceSAndi Kleen 446db95818eSIan Rogers static int metricgroup__add_to_mep_groups(const struct pmu_metric *pm, 447e5c6109fSIan Rogers struct rblist *groups) 448f6fe1e48SJohn Garry { 449f6fe1e48SJohn Garry const char *g; 450f6fe1e48SJohn Garry char *omg, *mg; 451f6fe1e48SJohn Garry 452db95818eSIan Rogers mg = strdup(pm->metric_group ?: "No_group"); 453f6fe1e48SJohn Garry if (!mg) 454f6fe1e48SJohn Garry return -ENOMEM; 455f6fe1e48SJohn Garry omg = mg; 456f6fe1e48SJohn Garry while ((g = strsep(&mg, ";")) != NULL) { 457f6fe1e48SJohn Garry struct mep *me; 458f6fe1e48SJohn Garry 459f6fe1e48SJohn Garry g = skip_spaces(g); 460e5c6109fSIan Rogers if (strlen(g)) 461db95818eSIan Rogers me = mep_lookup(groups, g, pm->metric_name); 462e5c6109fSIan Rogers else 463db95818eSIan Rogers me = mep_lookup(groups, "No_group", pm->metric_name); 464f6fe1e48SJohn Garry 465e5c6109fSIan Rogers if (me) { 466db95818eSIan Rogers me->metric_desc = pm->desc; 467db95818eSIan Rogers me->metric_long_desc = pm->long_desc; 468db95818eSIan Rogers me->metric_expr = pm->metric_expr; 469c7551a2eSIan Rogers me->metric_threshold = pm->metric_threshold; 470db95818eSIan Rogers me->metric_unit = pm->unit; 471f6fe1e48SJohn Garry } 472f6fe1e48SJohn Garry } 473f6fe1e48SJohn Garry free(omg); 474f6fe1e48SJohn Garry 475f6fe1e48SJohn Garry return 0; 476f6fe1e48SJohn Garry } 477f6fe1e48SJohn Garry 478a36fadb1SJohn Garry struct metricgroup_iter_data { 479db95818eSIan Rogers pmu_metric_iter_fn fn; 480a36fadb1SJohn Garry void *data; 481a36fadb1SJohn Garry }; 482a36fadb1SJohn Garry 483db95818eSIan Rogers static int metricgroup__sys_event_iter(const struct pmu_metric *pm, 484f8ea2c15SIan Rogers const struct pmu_metrics_table *table, 48529be2fe0SIan Rogers void *data) 486a36fadb1SJohn Garry { 487a36fadb1SJohn Garry struct metricgroup_iter_data *d = data; 488a36fadb1SJohn Garry struct perf_pmu *pmu = NULL; 489a36fadb1SJohn Garry 490db95818eSIan Rogers if (!pm->metric_expr || !pm->compat) 491a36fadb1SJohn Garry return 0; 492a36fadb1SJohn Garry 493a36fadb1SJohn Garry while ((pmu = perf_pmu__scan(pmu))) { 494a36fadb1SJohn Garry 495db95818eSIan Rogers if (!pmu->id || strcmp(pmu->id, pm->compat)) 496a36fadb1SJohn Garry continue; 497a36fadb1SJohn Garry 498db95818eSIan Rogers return d->fn(pm, table, d->data); 499a36fadb1SJohn Garry } 500a36fadb1SJohn Garry return 0; 501a36fadb1SJohn Garry } 502a36fadb1SJohn Garry 503db95818eSIan Rogers static int metricgroup__add_to_mep_groups_callback(const struct pmu_metric *pm, 504f8ea2c15SIan Rogers const struct pmu_metrics_table *table __maybe_unused, 505660842e4SIan Rogers void *vdata) 506660842e4SIan Rogers { 507e5c6109fSIan Rogers struct rblist *groups = vdata; 508660842e4SIan Rogers 509db95818eSIan Rogers return metricgroup__add_to_mep_groups(pm, groups); 510660842e4SIan Rogers } 511660842e4SIan Rogers 512e5c6109fSIan Rogers void metricgroup__print(const struct print_callbacks *print_cb, void *print_state) 51371b0acceSAndi Kleen { 51471b0acceSAndi Kleen struct rblist groups; 515f8ea2c15SIan Rogers const struct pmu_metrics_table *table; 516e5c6109fSIan Rogers struct rb_node *node, *next; 51771b0acceSAndi Kleen 51871b0acceSAndi Kleen rblist__init(&groups); 51971b0acceSAndi Kleen groups.node_new = mep_new; 52071b0acceSAndi Kleen groups.node_cmp = mep_cmp; 52171b0acceSAndi Kleen groups.node_delete = mep_delete; 522f8ea2c15SIan Rogers table = pmu_metrics_table__find(); 523660842e4SIan Rogers if (table) { 524f8ea2c15SIan Rogers pmu_metrics_table_for_each_metric(table, 525e5c6109fSIan Rogers metricgroup__add_to_mep_groups_callback, 526e5c6109fSIan Rogers &groups); 5270e0ae874SJin Yao } 528a36fadb1SJohn Garry { 529a36fadb1SJohn Garry struct metricgroup_iter_data data = { 530e5c6109fSIan Rogers .fn = metricgroup__add_to_mep_groups_callback, 531e5c6109fSIan Rogers .data = &groups, 532a36fadb1SJohn Garry }; 533db95818eSIan Rogers pmu_for_each_sys_metric(metricgroup__sys_event_iter, &data); 534a36fadb1SJohn Garry } 535a36fadb1SJohn Garry 536ca227029SDavidlohr Bueso for (node = rb_first_cached(&groups.entries); node; node = next) { 53771b0acceSAndi Kleen struct mep *me = container_of(node, struct mep, nd); 53871b0acceSAndi Kleen 539e5c6109fSIan Rogers print_cb->print_metric(print_state, 540e5c6109fSIan Rogers me->metric_group, 541e5c6109fSIan Rogers me->metric_name, 542e5c6109fSIan Rogers me->metric_desc, 543e5c6109fSIan Rogers me->metric_long_desc, 544e5c6109fSIan Rogers me->metric_expr, 545c7551a2eSIan Rogers me->metric_threshold, 546e5c6109fSIan Rogers me->metric_unit); 54771b0acceSAndi Kleen next = rb_next(node); 54871b0acceSAndi Kleen rblist__remove_node(&groups, node); 54971b0acceSAndi Kleen } 55071b0acceSAndi Kleen } 55171b0acceSAndi Kleen 552ec5c5b3dSIan Rogers static const char *code_characters = ",-=@"; 553ec5c5b3dSIan Rogers 554ec5c5b3dSIan Rogers static int encode_metric_id(struct strbuf *sb, const char *x) 555ec5c5b3dSIan Rogers { 556ec5c5b3dSIan Rogers char *c; 557ec5c5b3dSIan Rogers int ret = 0; 558ec5c5b3dSIan Rogers 559ec5c5b3dSIan Rogers for (; *x; x++) { 560ec5c5b3dSIan Rogers c = strchr(code_characters, *x); 561ec5c5b3dSIan Rogers if (c) { 562ec5c5b3dSIan Rogers ret = strbuf_addch(sb, '!'); 563ec5c5b3dSIan Rogers if (ret) 564ec5c5b3dSIan Rogers break; 565ec5c5b3dSIan Rogers 566ec5c5b3dSIan Rogers ret = strbuf_addch(sb, '0' + (c - code_characters)); 567ec5c5b3dSIan Rogers if (ret) 568ec5c5b3dSIan Rogers break; 569ec5c5b3dSIan Rogers } else { 570ec5c5b3dSIan Rogers ret = strbuf_addch(sb, *x); 571ec5c5b3dSIan Rogers if (ret) 572ec5c5b3dSIan Rogers break; 573ec5c5b3dSIan Rogers } 574ec5c5b3dSIan Rogers } 575ec5c5b3dSIan Rogers return ret; 576ec5c5b3dSIan Rogers } 577ec5c5b3dSIan Rogers 578ec5c5b3dSIan Rogers static int decode_metric_id(struct strbuf *sb, const char *x) 579ec5c5b3dSIan Rogers { 580ec5c5b3dSIan Rogers const char *orig = x; 581ec5c5b3dSIan Rogers size_t i; 582ec5c5b3dSIan Rogers char c; 583ec5c5b3dSIan Rogers int ret; 584ec5c5b3dSIan Rogers 585ec5c5b3dSIan Rogers for (; *x; x++) { 586ec5c5b3dSIan Rogers c = *x; 587ec5c5b3dSIan Rogers if (*x == '!') { 588ec5c5b3dSIan Rogers x++; 589ec5c5b3dSIan Rogers i = *x - '0'; 590ec5c5b3dSIan Rogers if (i > strlen(code_characters)) { 591ec5c5b3dSIan Rogers pr_err("Bad metric-id encoding in: '%s'", orig); 592ec5c5b3dSIan Rogers return -1; 593ec5c5b3dSIan Rogers } 594ec5c5b3dSIan Rogers c = code_characters[i]; 595ec5c5b3dSIan Rogers } 596ec5c5b3dSIan Rogers ret = strbuf_addch(sb, c); 597ec5c5b3dSIan Rogers if (ret) 598ec5c5b3dSIan Rogers return ret; 599ec5c5b3dSIan Rogers } 600ec5c5b3dSIan Rogers return 0; 601ec5c5b3dSIan Rogers } 602ec5c5b3dSIan Rogers 603b85a4d61SIan Rogers static int decode_all_metric_ids(struct evlist *perf_evlist, const char *modifier) 604ec5c5b3dSIan Rogers { 605ec5c5b3dSIan Rogers struct evsel *ev; 606ec5c5b3dSIan Rogers struct strbuf sb = STRBUF_INIT; 607ec5c5b3dSIan Rogers char *cur; 608ec5c5b3dSIan Rogers int ret = 0; 609ec5c5b3dSIan Rogers 610ec5c5b3dSIan Rogers evlist__for_each_entry(perf_evlist, ev) { 611ec5c5b3dSIan Rogers if (!ev->metric_id) 612ec5c5b3dSIan Rogers continue; 613ec5c5b3dSIan Rogers 614ec5c5b3dSIan Rogers ret = strbuf_setlen(&sb, 0); 615ec5c5b3dSIan Rogers if (ret) 616ec5c5b3dSIan Rogers break; 617ec5c5b3dSIan Rogers 618ec5c5b3dSIan Rogers ret = decode_metric_id(&sb, ev->metric_id); 619ec5c5b3dSIan Rogers if (ret) 620ec5c5b3dSIan Rogers break; 621ec5c5b3dSIan Rogers 622ec5c5b3dSIan Rogers free((char *)ev->metric_id); 623ec5c5b3dSIan Rogers ev->metric_id = strdup(sb.buf); 624ec5c5b3dSIan Rogers if (!ev->metric_id) { 625ec5c5b3dSIan Rogers ret = -ENOMEM; 626ec5c5b3dSIan Rogers break; 627ec5c5b3dSIan Rogers } 628ec5c5b3dSIan Rogers /* 629ec5c5b3dSIan Rogers * If the name is just the parsed event, use the metric-id to 630ec5c5b3dSIan Rogers * give a more friendly display version. 631ec5c5b3dSIan Rogers */ 632ec5c5b3dSIan Rogers if (strstr(ev->name, "metric-id=")) { 633b85a4d61SIan Rogers bool has_slash = false; 634ec5c5b3dSIan Rogers 63511ff9bcdSArnaldo Carvalho de Melo zfree(&ev->name); 636b85a4d61SIan Rogers for (cur = strchr(sb.buf, '@') ; cur; cur = strchr(++cur, '@')) { 637b85a4d61SIan Rogers *cur = '/'; 638b85a4d61SIan Rogers has_slash = true; 639b85a4d61SIan Rogers } 640b85a4d61SIan Rogers 641b85a4d61SIan Rogers if (modifier) { 642b85a4d61SIan Rogers if (!has_slash && !strchr(sb.buf, ':')) { 643b85a4d61SIan Rogers ret = strbuf_addch(&sb, ':'); 644b85a4d61SIan Rogers if (ret) 645b85a4d61SIan Rogers break; 646b85a4d61SIan Rogers } 647b85a4d61SIan Rogers ret = strbuf_addstr(&sb, modifier); 648b85a4d61SIan Rogers if (ret) 649b85a4d61SIan Rogers break; 650b85a4d61SIan Rogers } 651ec5c5b3dSIan Rogers ev->name = strdup(sb.buf); 652ec5c5b3dSIan Rogers if (!ev->name) { 653ec5c5b3dSIan Rogers ret = -ENOMEM; 654ec5c5b3dSIan Rogers break; 655ec5c5b3dSIan Rogers } 656ec5c5b3dSIan Rogers } 657ec5c5b3dSIan Rogers } 658ec5c5b3dSIan Rogers strbuf_release(&sb); 659ec5c5b3dSIan Rogers return ret; 660ec5c5b3dSIan Rogers } 661ec5c5b3dSIan Rogers 662ec5c5b3dSIan Rogers static int metricgroup__build_event_string(struct strbuf *events, 663ec5c5b3dSIan Rogers const struct expr_parse_ctx *ctx, 664b85a4d61SIan Rogers const char *modifier, 665180a5013SIan Rogers bool group_events) 666f742634aSKan Liang { 667ded80bdaSIan Rogers struct hashmap_entry *cur; 6684e21c13aSIan Rogers size_t bkt; 6699aa09230SIan Rogers bool no_group = true, has_tool_events = false; 6709aa09230SIan Rogers bool tool_events[PERF_TOOL_MAX] = {false}; 671ec5c5b3dSIan Rogers int ret = 0; 672ec5c5b3dSIan Rogers 673ec5c5b3dSIan Rogers #define RETURN_IF_NON_ZERO(x) do { if (x) return x; } while (0) 674f742634aSKan Liang 675cb94a02eSIan Rogers hashmap__for_each_entry(ctx->ids, cur, bkt) { 676c302378bSEduard Zingerman const char *sep, *rsep, *id = cur->pkey; 6779aa09230SIan Rogers enum perf_tool_event ev; 678ec5c5b3dSIan Rogers 679ec5c5b3dSIan Rogers pr_debug("found event %s\n", id); 6809aa09230SIan Rogers 6819aa09230SIan Rogers /* Always move tool events outside of the group. */ 6829aa09230SIan Rogers ev = perf_tool_event__from_str(id); 6839aa09230SIan Rogers if (ev != PERF_TOOL_NONE) { 6849aa09230SIan Rogers has_tool_events = true; 6859aa09230SIan Rogers tool_events[ev] = true; 686f742634aSKan Liang continue; 687f742634aSKan Liang } 688ec5c5b3dSIan Rogers /* Separate events with commas and open the group if necessary. */ 689ec5c5b3dSIan Rogers if (no_group) { 690180a5013SIan Rogers if (group_events) { 691ec5c5b3dSIan Rogers ret = strbuf_addch(events, '{'); 692ec5c5b3dSIan Rogers RETURN_IF_NON_ZERO(ret); 693ec5c5b3dSIan Rogers } 694ec5c5b3dSIan Rogers 695f742634aSKan Liang no_group = false; 696ec5c5b3dSIan Rogers } else { 697ec5c5b3dSIan Rogers ret = strbuf_addch(events, ','); 698ec5c5b3dSIan Rogers RETURN_IF_NON_ZERO(ret); 699f742634aSKan Liang } 700ec5c5b3dSIan Rogers /* 701ec5c5b3dSIan Rogers * Encode the ID as an event string. Add a qualifier for 702ec5c5b3dSIan Rogers * metric_id that is the original name except with characters 703ec5c5b3dSIan Rogers * that parse-events can't parse replaced. For example, 704ec5c5b3dSIan Rogers * 'msr@tsc@' gets added as msr/tsc,metric-id=msr!3tsc!3/ 705ec5c5b3dSIan Rogers */ 706ec5c5b3dSIan Rogers sep = strchr(id, '@'); 707ec5c5b3dSIan Rogers if (sep != NULL) { 708ec5c5b3dSIan Rogers ret = strbuf_add(events, id, sep - id); 709ec5c5b3dSIan Rogers RETURN_IF_NON_ZERO(ret); 710ec5c5b3dSIan Rogers ret = strbuf_addch(events, '/'); 711ec5c5b3dSIan Rogers RETURN_IF_NON_ZERO(ret); 712ec5c5b3dSIan Rogers rsep = strrchr(sep, '@'); 713ec5c5b3dSIan Rogers ret = strbuf_add(events, sep + 1, rsep - sep - 1); 714ec5c5b3dSIan Rogers RETURN_IF_NON_ZERO(ret); 715ec5c5b3dSIan Rogers ret = strbuf_addstr(events, ",metric-id="); 716ec5c5b3dSIan Rogers RETURN_IF_NON_ZERO(ret); 717ec5c5b3dSIan Rogers sep = rsep; 718ec5c5b3dSIan Rogers } else { 719ec5c5b3dSIan Rogers sep = strchr(id, ':'); 720ec5c5b3dSIan Rogers if (sep != NULL) { 721ec5c5b3dSIan Rogers ret = strbuf_add(events, id, sep - id); 722ec5c5b3dSIan Rogers RETURN_IF_NON_ZERO(ret); 723ec5c5b3dSIan Rogers } else { 724ec5c5b3dSIan Rogers ret = strbuf_addstr(events, id); 725ec5c5b3dSIan Rogers RETURN_IF_NON_ZERO(ret); 726f742634aSKan Liang } 727ec5c5b3dSIan Rogers ret = strbuf_addstr(events, "/metric-id="); 728ec5c5b3dSIan Rogers RETURN_IF_NON_ZERO(ret); 729ec5c5b3dSIan Rogers } 730ec5c5b3dSIan Rogers ret = encode_metric_id(events, id); 731ec5c5b3dSIan Rogers RETURN_IF_NON_ZERO(ret); 732ec5c5b3dSIan Rogers ret = strbuf_addstr(events, "/"); 733ec5c5b3dSIan Rogers RETURN_IF_NON_ZERO(ret); 734f742634aSKan Liang 735ec5c5b3dSIan Rogers if (sep != NULL) { 736ec5c5b3dSIan Rogers ret = strbuf_addstr(events, sep + 1); 737ec5c5b3dSIan Rogers RETURN_IF_NON_ZERO(ret); 738e2ce1059SIan Rogers } 739b85a4d61SIan Rogers if (modifier) { 740b85a4d61SIan Rogers ret = strbuf_addstr(events, modifier); 741b85a4d61SIan Rogers RETURN_IF_NON_ZERO(ret); 742b85a4d61SIan Rogers } 743ab483d8bSKan Liang } 744180a5013SIan Rogers if (!no_group && group_events) { 74560344f1aSZhengjun Xing ret = strbuf_addf(events, "}:W"); 7469aa09230SIan Rogers RETURN_IF_NON_ZERO(ret); 7479aa09230SIan Rogers } 7489aa09230SIan Rogers if (has_tool_events) { 7499aa09230SIan Rogers int i; 7509aa09230SIan Rogers 7519aa09230SIan Rogers perf_tool_event__for_each_event(i) { 7529aa09230SIan Rogers if (tool_events[i]) { 7539aa09230SIan Rogers if (!no_group) { 7549aa09230SIan Rogers ret = strbuf_addch(events, ','); 7559aa09230SIan Rogers RETURN_IF_NON_ZERO(ret); 7569aa09230SIan Rogers } 7579aa09230SIan Rogers no_group = false; 7589aa09230SIan Rogers ret = strbuf_addstr(events, perf_tool_event__to_str(i)); 7599aa09230SIan Rogers RETURN_IF_NON_ZERO(ret); 7609aa09230SIan Rogers } 7619aa09230SIan Rogers } 7629aa09230SIan Rogers } 763ec5c5b3dSIan Rogers 764ec5c5b3dSIan Rogers return ret; 765ec5c5b3dSIan Rogers #undef RETURN_IF_NON_ZERO 766ec5c5b3dSIan Rogers } 767ab483d8bSKan Liang 768db95818eSIan Rogers int __weak arch_get_runtimeparam(const struct pmu_metric *pm __maybe_unused) 7691e1a873dSKajol Jain { 7701e1a873dSKajol Jain return 1; 7711e1a873dSKajol Jain } 7721e1a873dSKajol Jain 77380be6434SIan Rogers /* 77480be6434SIan Rogers * A singly linked list on the stack of the names of metrics being 77580be6434SIan Rogers * processed. Used to identify recursion. 77680be6434SIan Rogers */ 77780be6434SIan Rogers struct visited_metric { 77880be6434SIan Rogers const char *name; 77980be6434SIan Rogers const struct visited_metric *parent; 78080be6434SIan Rogers }; 78180be6434SIan Rogers 782be335ec2SJohn Garry struct metricgroup_add_iter_data { 783be335ec2SJohn Garry struct list_head *metric_list; 784bd3846d0SIan Rogers const char *pmu; 78568074811SIan Rogers const char *metric_name; 786b85a4d61SIan Rogers const char *modifier; 787be335ec2SJohn Garry int *ret; 788be335ec2SJohn Garry bool *has_match; 789be335ec2SJohn Garry bool metric_no_group; 7901fd09e29SIan Rogers bool metric_no_threshold; 7911725e9cdSIan Rogers const char *user_requested_cpu_list; 7921725e9cdSIan Rogers bool system_wide; 79380be6434SIan Rogers struct metric *root_metric; 79480be6434SIan Rogers const struct visited_metric *visited; 795f8ea2c15SIan Rogers const struct pmu_metrics_table *table; 796be335ec2SJohn Garry }; 797be335ec2SJohn Garry 798bd3846d0SIan Rogers static bool metricgroup__find_metric(const char *pmu, 799bd3846d0SIan Rogers const char *metric, 800f8ea2c15SIan Rogers const struct pmu_metrics_table *table, 801db95818eSIan Rogers struct pmu_metric *pm); 802d3abd7b8SIan Rogers 80380be6434SIan Rogers static int add_metric(struct list_head *metric_list, 804db95818eSIan Rogers const struct pmu_metric *pm, 805b85a4d61SIan Rogers const char *modifier, 80680be6434SIan Rogers bool metric_no_group, 8071fd09e29SIan Rogers bool metric_no_threshold, 8081725e9cdSIan Rogers const char *user_requested_cpu_list, 8091725e9cdSIan Rogers bool system_wide, 81080be6434SIan Rogers struct metric *root_metric, 81180be6434SIan Rogers const struct visited_metric *visited, 812f8ea2c15SIan Rogers const struct pmu_metrics_table *table); 81380be6434SIan Rogers 81480be6434SIan Rogers /** 81580be6434SIan Rogers * resolve_metric - Locate metrics within the root metric and recursively add 81680be6434SIan Rogers * references to them. 81780be6434SIan Rogers * @metric_list: The list the metric is added to. 818bd3846d0SIan Rogers * @pmu: The PMU name to resolve metrics on, or "all" for all PMUs. 819b85a4d61SIan Rogers * @modifier: if non-null event modifiers like "u". 82080be6434SIan Rogers * @metric_no_group: Should events written to events be grouped "{}" or 82180be6434SIan Rogers * global. Grouping is the default but due to multiplexing the 82280be6434SIan Rogers * user may override. 8231725e9cdSIan Rogers * @user_requested_cpu_list: Command line specified CPUs to record on. 8241725e9cdSIan Rogers * @system_wide: Are events for all processes recorded. 82580be6434SIan Rogers * @root_metric: Metrics may reference other metrics to form a tree. In this 82680be6434SIan Rogers * case the root_metric holds all the IDs and a list of referenced 82780be6434SIan Rogers * metrics. When adding a root this argument is NULL. 82880be6434SIan Rogers * @visited: A singly linked list of metric names being added that is used to 82980be6434SIan Rogers * detect recursion. 830eeac7730SIan Rogers * @table: The table that is searched for metrics, most commonly the table for the 83180be6434SIan Rogers * architecture perf is running upon. 83280be6434SIan Rogers */ 83380be6434SIan Rogers static int resolve_metric(struct list_head *metric_list, 834bd3846d0SIan Rogers const char *pmu, 835b85a4d61SIan Rogers const char *modifier, 83680be6434SIan Rogers bool metric_no_group, 8371fd09e29SIan Rogers bool metric_no_threshold, 8381725e9cdSIan Rogers const char *user_requested_cpu_list, 8391725e9cdSIan Rogers bool system_wide, 84080be6434SIan Rogers struct metric *root_metric, 84180be6434SIan Rogers const struct visited_metric *visited, 842f8ea2c15SIan Rogers const struct pmu_metrics_table *table) 84380be6434SIan Rogers { 84480be6434SIan Rogers struct hashmap_entry *cur; 84580be6434SIan Rogers size_t bkt; 84680be6434SIan Rogers struct to_resolve { 84780be6434SIan Rogers /* The metric to resolve. */ 848db95818eSIan Rogers struct pmu_metric pm; 84980be6434SIan Rogers /* 85080be6434SIan Rogers * The key in the IDs map, this may differ from in case, 851db95818eSIan Rogers * etc. from pm->metric_name. 85280be6434SIan Rogers */ 85380be6434SIan Rogers const char *key; 85480be6434SIan Rogers } *pending = NULL; 85580be6434SIan Rogers int i, ret = 0, pending_cnt = 0; 85680be6434SIan Rogers 85780be6434SIan Rogers /* 85880be6434SIan Rogers * Iterate all the parsed IDs and if there's a matching metric and it to 85980be6434SIan Rogers * the pending array. 86080be6434SIan Rogers */ 86180be6434SIan Rogers hashmap__for_each_entry(root_metric->pctx->ids, cur, bkt) { 862db95818eSIan Rogers struct pmu_metric pm; 86380be6434SIan Rogers 864bd3846d0SIan Rogers if (metricgroup__find_metric(pmu, cur->pkey, table, &pm)) { 86580be6434SIan Rogers pending = realloc(pending, 86680be6434SIan Rogers (pending_cnt + 1) * sizeof(struct to_resolve)); 86780be6434SIan Rogers if (!pending) 86880be6434SIan Rogers return -ENOMEM; 86980be6434SIan Rogers 870db95818eSIan Rogers memcpy(&pending[pending_cnt].pm, &pm, sizeof(pm)); 871c302378bSEduard Zingerman pending[pending_cnt].key = cur->pkey; 87280be6434SIan Rogers pending_cnt++; 87380be6434SIan Rogers } 87480be6434SIan Rogers } 87580be6434SIan Rogers 87680be6434SIan Rogers /* Remove the metric IDs from the context. */ 87780be6434SIan Rogers for (i = 0; i < pending_cnt; i++) 87880be6434SIan Rogers expr__del_id(root_metric->pctx, pending[i].key); 87980be6434SIan Rogers 88080be6434SIan Rogers /* 88180be6434SIan Rogers * Recursively add all the metrics, IDs are added to the root metric's 88280be6434SIan Rogers * context. 88380be6434SIan Rogers */ 88480be6434SIan Rogers for (i = 0; i < pending_cnt; i++) { 885db95818eSIan Rogers ret = add_metric(metric_list, &pending[i].pm, modifier, metric_no_group, 8861fd09e29SIan Rogers metric_no_threshold, user_requested_cpu_list, system_wide, 8871fd09e29SIan Rogers root_metric, visited, table); 88880be6434SIan Rogers if (ret) 88980be6434SIan Rogers break; 89080be6434SIan Rogers } 89180be6434SIan Rogers 89280be6434SIan Rogers free(pending); 89380be6434SIan Rogers return ret; 89480be6434SIan Rogers } 89580be6434SIan Rogers 89668074811SIan Rogers /** 89768074811SIan Rogers * __add_metric - Add a metric to metric_list. 89868074811SIan Rogers * @metric_list: The list the metric is added to. 899db95818eSIan Rogers * @pm: The pmu_metric containing the metric to be added. 900b85a4d61SIan Rogers * @modifier: if non-null event modifiers like "u". 90168074811SIan Rogers * @metric_no_group: Should events written to events be grouped "{}" or 90268074811SIan Rogers * global. Grouping is the default but due to multiplexing the 90368074811SIan Rogers * user may override. 9041fd09e29SIan Rogers * @metric_no_threshold: Should threshold expressions be ignored? 90568074811SIan Rogers * @runtime: A special argument for the parser only known at runtime. 9061725e9cdSIan Rogers * @user_requested_cpu_list: Command line specified CPUs to record on. 9071725e9cdSIan Rogers * @system_wide: Are events for all processes recorded. 90880be6434SIan Rogers * @root_metric: Metrics may reference other metrics to form a tree. In this 90980be6434SIan Rogers * case the root_metric holds all the IDs and a list of referenced 91080be6434SIan Rogers * metrics. When adding a root this argument is NULL. 91180be6434SIan Rogers * @visited: A singly linked list of metric names being added that is used to 91280be6434SIan Rogers * detect recursion. 913eeac7730SIan Rogers * @table: The table that is searched for metrics, most commonly the table for the 91480be6434SIan Rogers * architecture perf is running upon. 91568074811SIan Rogers */ 916119e521aSJiri Olsa static int __add_metric(struct list_head *metric_list, 917db95818eSIan Rogers const struct pmu_metric *pm, 918b85a4d61SIan Rogers const char *modifier, 91905530a79SIan Rogers bool metric_no_group, 9201fd09e29SIan Rogers bool metric_no_threshold, 92183de0b7dSJiri Olsa int runtime, 9221725e9cdSIan Rogers const char *user_requested_cpu_list, 9231725e9cdSIan Rogers bool system_wide, 92480be6434SIan Rogers struct metric *root_metric, 92580be6434SIan Rogers const struct visited_metric *visited, 926f8ea2c15SIan Rogers const struct pmu_metrics_table *table) 92747352abaSKajol Jain { 92880be6434SIan Rogers const struct visited_metric *vm; 92980be6434SIan Rogers int ret; 93080be6434SIan Rogers bool is_root = !root_metric; 931d0a3052fSIan Rogers const char *expr; 93280be6434SIan Rogers struct visited_metric visited_node = { 933db95818eSIan Rogers .name = pm->metric_name, 93480be6434SIan Rogers .parent = visited, 93580be6434SIan Rogers }; 93647352abaSKajol Jain 93780be6434SIan Rogers for (vm = visited; vm; vm = vm->parent) { 938db95818eSIan Rogers if (!strcmp(pm->metric_name, vm->name)) { 939db95818eSIan Rogers pr_err("failed: recursion detected for %s\n", pm->metric_name); 94080be6434SIan Rogers return -1; 94180be6434SIan Rogers } 94280be6434SIan Rogers } 94380be6434SIan Rogers 94480be6434SIan Rogers if (is_root) { 94583de0b7dSJiri Olsa /* 94680be6434SIan Rogers * This metric is the root of a tree and may reference other 94780be6434SIan Rogers * metrics that are added recursively. 94883de0b7dSJiri Olsa */ 949db95818eSIan Rogers root_metric = metric__new(pm, modifier, metric_no_group, runtime, 9501725e9cdSIan Rogers user_requested_cpu_list, system_wide); 95180be6434SIan Rogers if (!root_metric) 95247352abaSKajol Jain return -ENOMEM; 95347352abaSKajol Jain 95483de0b7dSJiri Olsa } else { 95546bdc0bfSIan Rogers int cnt = 0; 95646bdc0bfSIan Rogers 95783de0b7dSJiri Olsa /* 958a3de7690SIan Rogers * This metric was referenced in a metric higher in the 959a3de7690SIan Rogers * tree. Check if the same metric is already resolved in the 960a3de7690SIan Rogers * metric_refs list. 96183de0b7dSJiri Olsa */ 96246bdc0bfSIan Rogers if (root_metric->metric_refs) { 96346bdc0bfSIan Rogers for (; root_metric->metric_refs[cnt].metric_name; cnt++) { 964db95818eSIan Rogers if (!strcmp(pm->metric_name, 96546bdc0bfSIan Rogers root_metric->metric_refs[cnt].metric_name)) 966a3de7690SIan Rogers return 0; 967a3de7690SIan Rogers } 96846bdc0bfSIan Rogers } 969a3de7690SIan Rogers 97046bdc0bfSIan Rogers /* Create reference. Need space for the entry and the terminator. */ 97146bdc0bfSIan Rogers root_metric->metric_refs = realloc(root_metric->metric_refs, 97246bdc0bfSIan Rogers (cnt + 2) * sizeof(struct metric_ref)); 97346bdc0bfSIan Rogers if (!root_metric->metric_refs) 97483de0b7dSJiri Olsa return -ENOMEM; 97583de0b7dSJiri Olsa 97683de0b7dSJiri Olsa /* 97783de0b7dSJiri Olsa * Intentionally passing just const char pointers, 97883de0b7dSJiri Olsa * from 'pe' object, so they never go away. We don't 97983de0b7dSJiri Olsa * need to change them, so there's no need to create 98083de0b7dSJiri Olsa * our own copy. 98183de0b7dSJiri Olsa */ 982db95818eSIan Rogers root_metric->metric_refs[cnt].metric_name = pm->metric_name; 983db95818eSIan Rogers root_metric->metric_refs[cnt].metric_expr = pm->metric_expr; 98483de0b7dSJiri Olsa 98546bdc0bfSIan Rogers /* Null terminate array. */ 98646bdc0bfSIan Rogers root_metric->metric_refs[cnt+1].metric_name = NULL; 98746bdc0bfSIan Rogers root_metric->metric_refs[cnt+1].metric_expr = NULL; 98883de0b7dSJiri Olsa } 98983de0b7dSJiri Olsa 99083de0b7dSJiri Olsa /* 99183de0b7dSJiri Olsa * For both the parent and referenced metrics, we parse 99280be6434SIan Rogers * all the metric's IDs and add it to the root context. 99383de0b7dSJiri Olsa */ 994d0a3052fSIan Rogers ret = 0; 995d0a3052fSIan Rogers expr = pm->metric_expr; 996d0a3052fSIan Rogers if (is_root && pm->metric_threshold) { 997d0a3052fSIan Rogers /* 998d0a3052fSIan Rogers * Threshold expressions are built off the actual metric. Switch 999d0a3052fSIan Rogers * to use that in case of additional necessary events. Change 1000d0a3052fSIan Rogers * the visited node name to avoid this being flagged as 10011fd09e29SIan Rogers * recursion. If the threshold events are disabled, just use the 10021fd09e29SIan Rogers * metric's name as a reference. This allows metric threshold 10031fd09e29SIan Rogers * computation if there are sufficient events. 1004d0a3052fSIan Rogers */ 1005d0a3052fSIan Rogers assert(strstr(pm->metric_threshold, pm->metric_name)); 10061fd09e29SIan Rogers expr = metric_no_threshold ? pm->metric_name : pm->metric_threshold; 1007d0a3052fSIan Rogers visited_node.name = "__threshold__"; 1008d0a3052fSIan Rogers } 1009d0a3052fSIan Rogers if (expr__find_ids(expr, NULL, root_metric->pctx) < 0) { 101080be6434SIan Rogers /* Broken metric. */ 101180be6434SIan Rogers ret = -EINVAL; 1012d0a3052fSIan Rogers } 1013d0a3052fSIan Rogers if (!ret) { 101480be6434SIan Rogers /* Resolve referenced metrics. */ 1015bd3846d0SIan Rogers const char *pmu = pm->pmu ?: "cpu"; 1016bd3846d0SIan Rogers 1017bd3846d0SIan Rogers ret = resolve_metric(metric_list, pmu, modifier, metric_no_group, 10181fd09e29SIan Rogers metric_no_threshold, user_requested_cpu_list, 1019bd3846d0SIan Rogers system_wide, root_metric, &visited_node, 1020bd3846d0SIan Rogers table); 1021ded80bdaSIan Rogers } 102280be6434SIan Rogers if (ret) { 102380be6434SIan Rogers if (is_root) 102480be6434SIan Rogers metric__free(root_metric); 102583de0b7dSJiri Olsa 102680be6434SIan Rogers } else if (is_root) 102780be6434SIan Rogers list_add(&root_metric->nd, metric_list); 10286bf2102bSIan Rogers 102980be6434SIan Rogers return ret; 103047352abaSKajol Jain } 103147352abaSKajol Jain 1032660842e4SIan Rogers struct metricgroup__find_metric_data { 1033bd3846d0SIan Rogers const char *pmu; 1034660842e4SIan Rogers const char *metric; 1035db95818eSIan Rogers struct pmu_metric *pm; 1036660842e4SIan Rogers }; 1037ce391940SJiri Olsa 1038db95818eSIan Rogers static int metricgroup__find_metric_callback(const struct pmu_metric *pm, 1039f8ea2c15SIan Rogers const struct pmu_metrics_table *table __maybe_unused, 1040660842e4SIan Rogers void *vdata) 1041660842e4SIan Rogers { 1042660842e4SIan Rogers struct metricgroup__find_metric_data *data = vdata; 1043bd3846d0SIan Rogers const char *pm_pmu = pm->pmu ?: "cpu"; 1044bd3846d0SIan Rogers 1045bd3846d0SIan Rogers if (strcmp(data->pmu, "all") && strcmp(pm_pmu, data->pmu)) 1046bd3846d0SIan Rogers return 0; 1047660842e4SIan Rogers 1048db95818eSIan Rogers if (!match_metric(pm->metric_name, data->metric)) 1049660842e4SIan Rogers return 0; 1050660842e4SIan Rogers 1051db95818eSIan Rogers memcpy(data->pm, pm, sizeof(*pm)); 1052d3abd7b8SIan Rogers return 1; 1053660842e4SIan Rogers } 1054ce391940SJiri Olsa 1055bd3846d0SIan Rogers static bool metricgroup__find_metric(const char *pmu, 1056bd3846d0SIan Rogers const char *metric, 1057f8ea2c15SIan Rogers const struct pmu_metrics_table *table, 1058db95818eSIan Rogers struct pmu_metric *pm) 105983de0b7dSJiri Olsa { 1060660842e4SIan Rogers struct metricgroup__find_metric_data data = { 1061bd3846d0SIan Rogers .pmu = pmu, 1062660842e4SIan Rogers .metric = metric, 1063db95818eSIan Rogers .pm = pm, 1064660842e4SIan Rogers }; 106583de0b7dSJiri Olsa 1066f8ea2c15SIan Rogers return pmu_metrics_table_for_each_metric(table, metricgroup__find_metric_callback, &data) 1067d3abd7b8SIan Rogers ? true : false; 106883de0b7dSJiri Olsa } 106983de0b7dSJiri Olsa 1070119e521aSJiri Olsa static int add_metric(struct list_head *metric_list, 1071db95818eSIan Rogers const struct pmu_metric *pm, 1072b85a4d61SIan Rogers const char *modifier, 107383de0b7dSJiri Olsa bool metric_no_group, 10741fd09e29SIan Rogers bool metric_no_threshold, 10751725e9cdSIan Rogers const char *user_requested_cpu_list, 10761725e9cdSIan Rogers bool system_wide, 107780be6434SIan Rogers struct metric *root_metric, 107880be6434SIan Rogers const struct visited_metric *visited, 1079f8ea2c15SIan Rogers const struct pmu_metrics_table *table) 1080a29c164aSJiri Olsa { 1081a29c164aSJiri Olsa int ret = 0; 1082a29c164aSJiri Olsa 1083db95818eSIan Rogers pr_debug("metric expr %s for %s\n", pm->metric_expr, pm->metric_name); 1084a29c164aSJiri Olsa 1085db95818eSIan Rogers if (!strstr(pm->metric_expr, "?")) { 10861fd09e29SIan Rogers ret = __add_metric(metric_list, pm, modifier, metric_no_group, 10871fd09e29SIan Rogers metric_no_threshold, 0, user_requested_cpu_list, 10881fd09e29SIan Rogers system_wide, root_metric, visited, table); 1089a29c164aSJiri Olsa } else { 1090a29c164aSJiri Olsa int j, count; 1091a29c164aSJiri Olsa 1092db95818eSIan Rogers count = arch_get_runtimeparam(pm); 1093a29c164aSJiri Olsa 1094a29c164aSJiri Olsa /* This loop is added to create multiple 1095a29c164aSJiri Olsa * events depend on count value and add 1096119e521aSJiri Olsa * those events to metric_list. 1097a29c164aSJiri Olsa */ 1098a29c164aSJiri Olsa 109980be6434SIan Rogers for (j = 0; j < count && !ret; j++) 11001fd09e29SIan Rogers ret = __add_metric(metric_list, pm, modifier, metric_no_group, 11011fd09e29SIan Rogers metric_no_threshold, j, user_requested_cpu_list, 11021fd09e29SIan Rogers system_wide, root_metric, visited, table); 1103a29c164aSJiri Olsa } 1104a29c164aSJiri Olsa 1105a29c164aSJiri Olsa return ret; 1106a29c164aSJiri Olsa } 1107a29c164aSJiri Olsa 1108db95818eSIan Rogers static int metricgroup__add_metric_sys_event_iter(const struct pmu_metric *pm, 1109f8ea2c15SIan Rogers const struct pmu_metrics_table *table __maybe_unused, 1110be335ec2SJohn Garry void *data) 1111be335ec2SJohn Garry { 1112be335ec2SJohn Garry struct metricgroup_add_iter_data *d = data; 1113be335ec2SJohn Garry int ret; 1114be335ec2SJohn Garry 1115bd3846d0SIan Rogers if (!match_pm_metric(pm, d->pmu, d->metric_name)) 1116be335ec2SJohn Garry return 0; 1117be335ec2SJohn Garry 1118db95818eSIan Rogers ret = add_metric(d->metric_list, pm, d->modifier, d->metric_no_group, 11191fd09e29SIan Rogers d->metric_no_threshold, d->user_requested_cpu_list, 11201fd09e29SIan Rogers d->system_wide, d->root_metric, d->visited, d->table); 1121be335ec2SJohn Garry if (ret) 1122fe7a98b9SJohn Garry goto out; 1123be335ec2SJohn Garry 1124be335ec2SJohn Garry *(d->has_match) = true; 1125be335ec2SJohn Garry 1126fe7a98b9SJohn Garry out: 1127fe7a98b9SJohn Garry *(d->ret) = ret; 1128fe7a98b9SJohn Garry return ret; 1129be335ec2SJohn Garry } 1130be335ec2SJohn Garry 11316b6b16b3SIan Rogers /** 11326b6b16b3SIan Rogers * metric_list_cmp - list_sort comparator that sorts metrics with more events to 11339aa09230SIan Rogers * the front. tool events are excluded from the count. 11346b6b16b3SIan Rogers */ 113580be6434SIan Rogers static int metric_list_cmp(void *priv __maybe_unused, const struct list_head *l, 113680be6434SIan Rogers const struct list_head *r) 113780be6434SIan Rogers { 113880be6434SIan Rogers const struct metric *left = container_of(l, struct metric, nd); 113980be6434SIan Rogers const struct metric *right = container_of(r, struct metric, nd); 11406b6b16b3SIan Rogers struct expr_id_data *data; 11419aa09230SIan Rogers int i, left_count, right_count; 114280be6434SIan Rogers 11436b6b16b3SIan Rogers left_count = hashmap__size(left->pctx->ids); 11449aa09230SIan Rogers perf_tool_event__for_each_event(i) { 11459aa09230SIan Rogers if (!expr__get_id(left->pctx, perf_tool_event__to_str(i), &data)) 11466b6b16b3SIan Rogers left_count--; 11479aa09230SIan Rogers } 11486b6b16b3SIan Rogers 11496b6b16b3SIan Rogers right_count = hashmap__size(right->pctx->ids); 11509aa09230SIan Rogers perf_tool_event__for_each_event(i) { 11519aa09230SIan Rogers if (!expr__get_id(right->pctx, perf_tool_event__to_str(i), &data)) 11526b6b16b3SIan Rogers right_count--; 11539aa09230SIan Rogers } 11546b6b16b3SIan Rogers 11556b6b16b3SIan Rogers return right_count - left_count; 115680be6434SIan Rogers } 115780be6434SIan Rogers 1158660842e4SIan Rogers struct metricgroup__add_metric_data { 1159660842e4SIan Rogers struct list_head *list; 1160bd3846d0SIan Rogers const char *pmu; 1161660842e4SIan Rogers const char *metric_name; 1162660842e4SIan Rogers const char *modifier; 11631725e9cdSIan Rogers const char *user_requested_cpu_list; 1164660842e4SIan Rogers bool metric_no_group; 11651fd09e29SIan Rogers bool metric_no_threshold; 11661725e9cdSIan Rogers bool system_wide; 1167660842e4SIan Rogers bool has_match; 1168660842e4SIan Rogers }; 1169660842e4SIan Rogers 1170db95818eSIan Rogers static int metricgroup__add_metric_callback(const struct pmu_metric *pm, 1171f8ea2c15SIan Rogers const struct pmu_metrics_table *table, 1172660842e4SIan Rogers void *vdata) 1173660842e4SIan Rogers { 1174660842e4SIan Rogers struct metricgroup__add_metric_data *data = vdata; 1175660842e4SIan Rogers int ret = 0; 1176660842e4SIan Rogers 1177bd3846d0SIan Rogers if (pm->metric_expr && match_pm_metric(pm, data->pmu, data->metric_name)) { 1178ccc66c60SIan Rogers bool metric_no_group = data->metric_no_group || 1179ccc66c60SIan Rogers match_metric(data->metric_name, pm->metricgroup_no_group); 1180660842e4SIan Rogers 1181660842e4SIan Rogers data->has_match = true; 1182ccc66c60SIan Rogers ret = add_metric(data->list, pm, data->modifier, metric_no_group, 11831fd09e29SIan Rogers data->metric_no_threshold, data->user_requested_cpu_list, 11841fd09e29SIan Rogers data->system_wide, /*root_metric=*/NULL, 11851fd09e29SIan Rogers /*visited_metrics=*/NULL, table); 1186660842e4SIan Rogers } 1187660842e4SIan Rogers return ret; 1188660842e4SIan Rogers } 1189660842e4SIan Rogers 119068074811SIan Rogers /** 119168074811SIan Rogers * metricgroup__add_metric - Find and add a metric, or a metric group. 1192bd3846d0SIan Rogers * @pmu: The PMU name to search for metrics on, or "all" for all PMUs. 119368074811SIan Rogers * @metric_name: The name of the metric or metric group. For example, "IPC" 119468074811SIan Rogers * could be the name of a metric and "TopDownL1" the name of a 119568074811SIan Rogers * metric group. 1196b85a4d61SIan Rogers * @modifier: if non-null event modifiers like "u". 119768074811SIan Rogers * @metric_no_group: Should events written to events be grouped "{}" or 119868074811SIan Rogers * global. Grouping is the default but due to multiplexing the 119968074811SIan Rogers * user may override. 12001725e9cdSIan Rogers * @user_requested_cpu_list: Command line specified CPUs to record on. 12011725e9cdSIan Rogers * @system_wide: Are events for all processes recorded. 120268074811SIan Rogers * @metric_list: The list that the metric or metric group are added to. 1203eeac7730SIan Rogers * @table: The table that is searched for metrics, most commonly the table for the 120468074811SIan Rogers * architecture perf is running upon. 120568074811SIan Rogers */ 1206bd3846d0SIan Rogers static int metricgroup__add_metric(const char *pmu, const char *metric_name, const char *modifier, 12071fd09e29SIan Rogers bool metric_no_group, bool metric_no_threshold, 12081725e9cdSIan Rogers const char *user_requested_cpu_list, 12091725e9cdSIan Rogers bool system_wide, 1210119e521aSJiri Olsa struct list_head *metric_list, 1211f8ea2c15SIan Rogers const struct pmu_metrics_table *table) 1212b18f3e36SAndi Kleen { 121398461d9dSJiri Olsa LIST_HEAD(list); 1214660842e4SIan Rogers int ret; 121590810399SIan Rogers bool has_match = false; 1216b18f3e36SAndi Kleen 1217660842e4SIan Rogers { 1218660842e4SIan Rogers struct metricgroup__add_metric_data data = { 1219660842e4SIan Rogers .list = &list, 1220bd3846d0SIan Rogers .pmu = pmu, 1221660842e4SIan Rogers .metric_name = metric_name, 1222660842e4SIan Rogers .modifier = modifier, 1223660842e4SIan Rogers .metric_no_group = metric_no_group, 12241fd09e29SIan Rogers .metric_no_threshold = metric_no_threshold, 12251725e9cdSIan Rogers .user_requested_cpu_list = user_requested_cpu_list, 12261725e9cdSIan Rogers .system_wide = system_wide, 1227660842e4SIan Rogers .has_match = false, 1228660842e4SIan Rogers }; 122968074811SIan Rogers /* 1230660842e4SIan Rogers * Iterate over all metrics seeing if metric matches either the 1231660842e4SIan Rogers * name or group. When it does add the metric to the list. 123268074811SIan Rogers */ 1233f8ea2c15SIan Rogers ret = pmu_metrics_table_for_each_metric(table, metricgroup__add_metric_callback, 1234660842e4SIan Rogers &data); 123590810399SIan Rogers if (ret) 123627adafcdSNamhyung Kim goto out; 1237ce391940SJiri Olsa 1238660842e4SIan Rogers has_match = data.has_match; 1239660842e4SIan Rogers } 1240be335ec2SJohn Garry { 1241be335ec2SJohn Garry struct metricgroup_iter_data data = { 1242be335ec2SJohn Garry .fn = metricgroup__add_metric_sys_event_iter, 1243be335ec2SJohn Garry .data = (void *) &(struct metricgroup_add_iter_data) { 1244be335ec2SJohn Garry .metric_list = &list, 1245bd3846d0SIan Rogers .pmu = pmu, 124668074811SIan Rogers .metric_name = metric_name, 1247b85a4d61SIan Rogers .modifier = modifier, 1248be335ec2SJohn Garry .metric_no_group = metric_no_group, 12491725e9cdSIan Rogers .user_requested_cpu_list = user_requested_cpu_list, 12501725e9cdSIan Rogers .system_wide = system_wide, 1251be335ec2SJohn Garry .has_match = &has_match, 1252be335ec2SJohn Garry .ret = &ret, 1253eeac7730SIan Rogers .table = table, 1254be335ec2SJohn Garry }, 1255be335ec2SJohn Garry }; 1256be335ec2SJohn Garry 1257db95818eSIan Rogers pmu_for_each_sys_metric(metricgroup__sys_event_iter, &data); 1258be335ec2SJohn Garry } 1259ce391940SJiri Olsa /* End of pmu events. */ 12605ecd5a0cSIan Rogers if (!has_match) 126127adafcdSNamhyung Kim ret = -EINVAL; 126298461d9dSJiri Olsa 126327adafcdSNamhyung Kim out: 126427adafcdSNamhyung Kim /* 126527adafcdSNamhyung Kim * add to metric_list so that they can be released 126627adafcdSNamhyung Kim * even if it's failed 126727adafcdSNamhyung Kim */ 1268119e521aSJiri Olsa list_splice(&list, metric_list); 126927adafcdSNamhyung Kim return ret; 127090810399SIan Rogers } 1271b18f3e36SAndi Kleen 127268074811SIan Rogers /** 127368074811SIan Rogers * metricgroup__add_metric_list - Find and add metrics, or metric groups, 127468074811SIan Rogers * specified in a list. 1275bd3846d0SIan Rogers * @pmu: A pmu to restrict the metrics to, or "all" for all PMUS. 127668074811SIan Rogers * @list: the list of metrics or metric groups. For example, "IPC,CPI,TopDownL1" 127768074811SIan Rogers * would match the IPC and CPI metrics, and TopDownL1 would match all 127868074811SIan Rogers * the metrics in the TopDownL1 group. 127968074811SIan Rogers * @metric_no_group: Should events written to events be grouped "{}" or 128068074811SIan Rogers * global. Grouping is the default but due to multiplexing the 128168074811SIan Rogers * user may override. 12821725e9cdSIan Rogers * @user_requested_cpu_list: Command line specified CPUs to record on. 12831725e9cdSIan Rogers * @system_wide: Are events for all processes recorded. 128468074811SIan Rogers * @metric_list: The list that metrics are added to. 1285eeac7730SIan Rogers * @table: The table that is searched for metrics, most commonly the table for the 128668074811SIan Rogers * architecture perf is running upon. 128768074811SIan Rogers */ 1288bd3846d0SIan Rogers static int metricgroup__add_metric_list(const char *pmu, const char *list, 1289bd3846d0SIan Rogers bool metric_no_group, 12901fd09e29SIan Rogers bool metric_no_threshold, 12911725e9cdSIan Rogers const char *user_requested_cpu_list, 12921725e9cdSIan Rogers bool system_wide, struct list_head *metric_list, 1293f8ea2c15SIan Rogers const struct pmu_metrics_table *table) 1294b18f3e36SAndi Kleen { 1295b85a4d61SIan Rogers char *list_itr, *list_copy, *metric_name, *modifier; 1296ec5c5b3dSIan Rogers int ret, count = 0; 1297b18f3e36SAndi Kleen 1298b85a4d61SIan Rogers list_copy = strdup(list); 1299b85a4d61SIan Rogers if (!list_copy) 1300b18f3e36SAndi Kleen return -ENOMEM; 1301b85a4d61SIan Rogers list_itr = list_copy; 1302411bc316SAndi Kleen 1303b85a4d61SIan Rogers while ((metric_name = strsep(&list_itr, ",")) != NULL) { 1304b85a4d61SIan Rogers modifier = strchr(metric_name, ':'); 1305b85a4d61SIan Rogers if (modifier) 1306b85a4d61SIan Rogers *modifier++ = '\0'; 1307b85a4d61SIan Rogers 1308bd3846d0SIan Rogers ret = metricgroup__add_metric(pmu, metric_name, modifier, 13091fd09e29SIan Rogers metric_no_group, metric_no_threshold, 13101fd09e29SIan Rogers user_requested_cpu_list, 13111725e9cdSIan Rogers system_wide, metric_list, table); 1312ec5c5b3dSIan Rogers if (ret == -EINVAL) 1313b85a4d61SIan Rogers pr_err("Cannot find metric or group `%s'\n", metric_name); 1314ec5c5b3dSIan Rogers 1315ec5c5b3dSIan Rogers if (ret) 1316b18f3e36SAndi Kleen break; 1317ec5c5b3dSIan Rogers 1318ec5c5b3dSIan Rogers count++; 1319b18f3e36SAndi Kleen } 1320b85a4d61SIan Rogers free(list_copy); 1321ab483d8bSKan Liang 1322ec5c5b3dSIan Rogers if (!ret) { 1323ec5c5b3dSIan Rogers /* 1324ec5c5b3dSIan Rogers * Warn about nmi_watchdog if any parsed metrics had the 1325ec5c5b3dSIan Rogers * NO_NMI_WATCHDOG constraint. 1326ec5c5b3dSIan Rogers */ 1327180a5013SIan Rogers metric__watchdog_constraint_hint(NULL, /*foot=*/true); 1328ec5c5b3dSIan Rogers /* No metrics. */ 1329ec5c5b3dSIan Rogers if (count == 0) 1330ec5c5b3dSIan Rogers return -EINVAL; 1331ec5c5b3dSIan Rogers } 1332b18f3e36SAndi Kleen return ret; 1333b18f3e36SAndi Kleen } 1334b18f3e36SAndi Kleen 1335119e521aSJiri Olsa static void metricgroup__free_metrics(struct list_head *metric_list) 1336b18f3e36SAndi Kleen { 1337a0c05b36SJiri Olsa struct metric *m, *tmp; 1338b18f3e36SAndi Kleen 1339119e521aSJiri Olsa list_for_each_entry_safe (m, tmp, metric_list, nd) { 1340a0c05b36SJiri Olsa list_del_init(&m->nd); 13413d81d761SIan Rogers metric__free(m); 1342b18f3e36SAndi Kleen } 1343b18f3e36SAndi Kleen } 1344b18f3e36SAndi Kleen 13455ecd5a0cSIan Rogers /** 13468586d274SIan Rogers * find_tool_events - Search for the pressence of tool events in metric_list. 13478586d274SIan Rogers * @metric_list: List to take metrics from. 13488586d274SIan Rogers * @tool_events: Array of false values, indices corresponding to tool events set 13498586d274SIan Rogers * to true if tool event is found. 13508586d274SIan Rogers */ 13518586d274SIan Rogers static void find_tool_events(const struct list_head *metric_list, 13528586d274SIan Rogers bool tool_events[PERF_TOOL_MAX]) 13538586d274SIan Rogers { 13548586d274SIan Rogers struct metric *m; 13558586d274SIan Rogers 13568586d274SIan Rogers list_for_each_entry(m, metric_list, nd) { 13578586d274SIan Rogers int i; 13588586d274SIan Rogers 13598586d274SIan Rogers perf_tool_event__for_each_event(i) { 13608586d274SIan Rogers struct expr_id_data *data; 13618586d274SIan Rogers 13628586d274SIan Rogers if (!tool_events[i] && 13638586d274SIan Rogers !expr__get_id(m->pctx, perf_tool_event__to_str(i), &data)) 13648586d274SIan Rogers tool_events[i] = true; 13658586d274SIan Rogers } 13668586d274SIan Rogers } 13678586d274SIan Rogers } 13688586d274SIan Rogers 13698586d274SIan Rogers /** 1370180a5013SIan Rogers * build_combined_expr_ctx - Make an expr_parse_ctx with all !group_events 13715ecd5a0cSIan Rogers * metric IDs, as the IDs are held in a set, 13725ecd5a0cSIan Rogers * duplicates will be removed. 13735ecd5a0cSIan Rogers * @metric_list: List to take metrics from. 13745ecd5a0cSIan Rogers * @combined: Out argument for result. 13755ecd5a0cSIan Rogers */ 13765ecd5a0cSIan Rogers static int build_combined_expr_ctx(const struct list_head *metric_list, 13775ecd5a0cSIan Rogers struct expr_parse_ctx **combined) 13785ecd5a0cSIan Rogers { 13795ecd5a0cSIan Rogers struct hashmap_entry *cur; 13805ecd5a0cSIan Rogers size_t bkt; 13815ecd5a0cSIan Rogers struct metric *m; 13825ecd5a0cSIan Rogers char *dup; 13835ecd5a0cSIan Rogers int ret; 13845ecd5a0cSIan Rogers 13855ecd5a0cSIan Rogers *combined = expr__ctx_new(); 13865ecd5a0cSIan Rogers if (!*combined) 13875ecd5a0cSIan Rogers return -ENOMEM; 13885ecd5a0cSIan Rogers 13895ecd5a0cSIan Rogers list_for_each_entry(m, metric_list, nd) { 1390180a5013SIan Rogers if (!m->group_events && !m->modifier) { 13915ecd5a0cSIan Rogers hashmap__for_each_entry(m->pctx->ids, cur, bkt) { 1392c302378bSEduard Zingerman dup = strdup(cur->pkey); 13935ecd5a0cSIan Rogers if (!dup) { 13945ecd5a0cSIan Rogers ret = -ENOMEM; 13955ecd5a0cSIan Rogers goto err_out; 13965ecd5a0cSIan Rogers } 13975ecd5a0cSIan Rogers ret = expr__add_id(*combined, dup); 13985ecd5a0cSIan Rogers if (ret) 13995ecd5a0cSIan Rogers goto err_out; 14005ecd5a0cSIan Rogers } 14015ecd5a0cSIan Rogers } 14025ecd5a0cSIan Rogers } 14035ecd5a0cSIan Rogers return 0; 14045ecd5a0cSIan Rogers err_out: 14055ecd5a0cSIan Rogers expr__ctx_free(*combined); 14065ecd5a0cSIan Rogers *combined = NULL; 14075ecd5a0cSIan Rogers return ret; 14085ecd5a0cSIan Rogers } 14095ecd5a0cSIan Rogers 14105ecd5a0cSIan Rogers /** 14115ecd5a0cSIan Rogers * parse_ids - Build the event string for the ids and parse them creating an 14125ecd5a0cSIan Rogers * evlist. The encoded metric_ids are decoded. 14136b6b16b3SIan Rogers * @metric_no_merge: is metric sharing explicitly disabled. 14145ecd5a0cSIan Rogers * @fake_pmu: used when testing metrics not supported by the current CPU. 14155ecd5a0cSIan Rogers * @ids: the event identifiers parsed from a metric. 1416b85a4d61SIan Rogers * @modifier: any modifiers added to the events. 1417180a5013SIan Rogers * @group_events: should events be placed in a weak group. 14188586d274SIan Rogers * @tool_events: entries set true if the tool event of index could be present in 14198586d274SIan Rogers * the overall list of metrics. 14205ecd5a0cSIan Rogers * @out_evlist: the created list of events. 14215ecd5a0cSIan Rogers */ 14226b6b16b3SIan Rogers static int parse_ids(bool metric_no_merge, struct perf_pmu *fake_pmu, 14236b6b16b3SIan Rogers struct expr_parse_ctx *ids, const char *modifier, 1424180a5013SIan Rogers bool group_events, const bool tool_events[PERF_TOOL_MAX], 14258586d274SIan Rogers struct evlist **out_evlist) 14265ecd5a0cSIan Rogers { 14275ecd5a0cSIan Rogers struct parse_events_error parse_error; 14285ecd5a0cSIan Rogers struct evlist *parsed_evlist; 14295ecd5a0cSIan Rogers struct strbuf events = STRBUF_INIT; 14305ecd5a0cSIan Rogers int ret; 14315ecd5a0cSIan Rogers 14325ecd5a0cSIan Rogers *out_evlist = NULL; 14336b6b16b3SIan Rogers if (!metric_no_merge || hashmap__size(ids->ids) == 0) { 1434c788ef61SIan Rogers bool added_event = false; 14359aa09230SIan Rogers int i; 14365ecd5a0cSIan Rogers /* 14379aa09230SIan Rogers * We may fail to share events between metrics because a tool 14389aa09230SIan Rogers * event isn't present in one metric. For example, a ratio of 14399aa09230SIan Rogers * cache misses doesn't need duration_time but the same events 14409aa09230SIan Rogers * may be used for a misses per second. Events without sharing 14419aa09230SIan Rogers * implies multiplexing, that is best avoided, so place 14429aa09230SIan Rogers * all tool events in every group. 14436b6b16b3SIan Rogers * 14446b6b16b3SIan Rogers * Also, there may be no ids/events in the expression parsing 14456b6b16b3SIan Rogers * context because of constant evaluation, e.g.: 14465ecd5a0cSIan Rogers * event1 if #smt_on else 0 14479aa09230SIan Rogers * Add a tool event to avoid a parse error on an empty string. 14485ecd5a0cSIan Rogers */ 14499aa09230SIan Rogers perf_tool_event__for_each_event(i) { 14508586d274SIan Rogers if (tool_events[i]) { 14519aa09230SIan Rogers char *tmp = strdup(perf_tool_event__to_str(i)); 14529aa09230SIan Rogers 14535ecd5a0cSIan Rogers if (!tmp) 14545ecd5a0cSIan Rogers return -ENOMEM; 14555ecd5a0cSIan Rogers ids__insert(ids->ids, tmp); 1456c788ef61SIan Rogers added_event = true; 14575ecd5a0cSIan Rogers } 14589aa09230SIan Rogers } 1459c788ef61SIan Rogers if (!added_event && hashmap__size(ids->ids) == 0) { 1460c788ef61SIan Rogers char *tmp = strdup("duration_time"); 1461c788ef61SIan Rogers 1462c788ef61SIan Rogers if (!tmp) 1463c788ef61SIan Rogers return -ENOMEM; 1464c788ef61SIan Rogers ids__insert(ids->ids, tmp); 1465c788ef61SIan Rogers } 14668586d274SIan Rogers } 1467b85a4d61SIan Rogers ret = metricgroup__build_event_string(&events, ids, modifier, 1468180a5013SIan Rogers group_events); 14695ecd5a0cSIan Rogers if (ret) 14705ecd5a0cSIan Rogers return ret; 14715ecd5a0cSIan Rogers 14725ecd5a0cSIan Rogers parsed_evlist = evlist__new(); 14735ecd5a0cSIan Rogers if (!parsed_evlist) { 14745ecd5a0cSIan Rogers ret = -ENOMEM; 14755ecd5a0cSIan Rogers goto err_out; 14765ecd5a0cSIan Rogers } 14775ecd5a0cSIan Rogers pr_debug("Parsing metric events '%s'\n", events.buf); 147807eafd4eSIan Rogers parse_events_error__init(&parse_error); 1479411ad22eSIan Rogers ret = __parse_events(parsed_evlist, events.buf, /*pmu_filter=*/NULL, 1480411ad22eSIan Rogers &parse_error, fake_pmu, /*warn_if_reordered=*/false); 14815ecd5a0cSIan Rogers if (ret) { 14826c191289SIan Rogers parse_events_error__print(&parse_error, events.buf); 14835ecd5a0cSIan Rogers goto err_out; 14845ecd5a0cSIan Rogers } 1485b85a4d61SIan Rogers ret = decode_all_metric_ids(parsed_evlist, modifier); 14865ecd5a0cSIan Rogers if (ret) 14875ecd5a0cSIan Rogers goto err_out; 14885ecd5a0cSIan Rogers 14895ecd5a0cSIan Rogers *out_evlist = parsed_evlist; 14905ecd5a0cSIan Rogers parsed_evlist = NULL; 14915ecd5a0cSIan Rogers err_out: 149207eafd4eSIan Rogers parse_events_error__exit(&parse_error); 14935ecd5a0cSIan Rogers evlist__delete(parsed_evlist); 14945ecd5a0cSIan Rogers strbuf_release(&events); 14955ecd5a0cSIan Rogers return ret; 14965ecd5a0cSIan Rogers } 14975ecd5a0cSIan Rogers 1498bd3846d0SIan Rogers static int parse_groups(struct evlist *perf_evlist, 1499bd3846d0SIan Rogers const char *pmu, const char *str, 150005530a79SIan Rogers bool metric_no_group, 150105530a79SIan Rogers bool metric_no_merge, 15021fd09e29SIan Rogers bool metric_no_threshold, 15031725e9cdSIan Rogers const char *user_requested_cpu_list, 15041725e9cdSIan Rogers bool system_wide, 150568173bdaSJiri Olsa struct perf_pmu *fake_pmu, 15065ecd5a0cSIan Rogers struct rblist *metric_events_list, 1507f8ea2c15SIan Rogers const struct pmu_metrics_table *table) 1508b18f3e36SAndi Kleen { 15095ecd5a0cSIan Rogers struct evlist *combined_evlist = NULL; 1510119e521aSJiri Olsa LIST_HEAD(metric_list); 15115ecd5a0cSIan Rogers struct metric *m; 15128586d274SIan Rogers bool tool_events[PERF_TOOL_MAX] = {false}; 1513b18f3e36SAndi Kleen int ret; 1514b18f3e36SAndi Kleen 15155ecd5a0cSIan Rogers if (metric_events_list->nr_entries == 0) 15165ecd5a0cSIan Rogers metricgroup__rblist_init(metric_events_list); 1517bd3846d0SIan Rogers ret = metricgroup__add_metric_list(pmu, str, metric_no_group, metric_no_threshold, 15181725e9cdSIan Rogers user_requested_cpu_list, 15191725e9cdSIan Rogers system_wide, &metric_list, table); 1520ec5c5b3dSIan Rogers if (ret) 1521ec5c5b3dSIan Rogers goto out; 1522ec5c5b3dSIan Rogers 15235ecd5a0cSIan Rogers /* Sort metrics from largest to smallest. */ 15245ecd5a0cSIan Rogers list_sort(NULL, &metric_list, metric_list_cmp); 15255ecd5a0cSIan Rogers 15265ecd5a0cSIan Rogers if (!metric_no_merge) { 15275ecd5a0cSIan Rogers struct expr_parse_ctx *combined = NULL; 15285ecd5a0cSIan Rogers 15298586d274SIan Rogers find_tool_events(&metric_list, tool_events); 15308586d274SIan Rogers 15315ecd5a0cSIan Rogers ret = build_combined_expr_ctx(&metric_list, &combined); 15325ecd5a0cSIan Rogers 15335ecd5a0cSIan Rogers if (!ret && combined && hashmap__size(combined->ids)) { 15346b6b16b3SIan Rogers ret = parse_ids(metric_no_merge, fake_pmu, combined, 15356b6b16b3SIan Rogers /*modifier=*/NULL, 1536180a5013SIan Rogers /*group_events=*/false, 15378586d274SIan Rogers tool_events, 153817b3867dSIan Rogers &combined_evlist); 15395ecd5a0cSIan Rogers } 15405ecd5a0cSIan Rogers if (combined) 15415ecd5a0cSIan Rogers expr__ctx_free(combined); 15425ecd5a0cSIan Rogers 15435ecd5a0cSIan Rogers if (ret) 15445ecd5a0cSIan Rogers goto out; 15455ecd5a0cSIan Rogers } 15465ecd5a0cSIan Rogers 15475ecd5a0cSIan Rogers list_for_each_entry(m, &metric_list, nd) { 15485ecd5a0cSIan Rogers struct metric_event *me; 15495ecd5a0cSIan Rogers struct evsel **metric_events; 15505ecd5a0cSIan Rogers struct evlist *metric_evlist = NULL; 15515ecd5a0cSIan Rogers struct metric *n; 15525ecd5a0cSIan Rogers struct metric_expr *expr; 15535ecd5a0cSIan Rogers 1554180a5013SIan Rogers if (combined_evlist && !m->group_events) { 15555ecd5a0cSIan Rogers metric_evlist = combined_evlist; 15565ecd5a0cSIan Rogers } else if (!metric_no_merge) { 15575ecd5a0cSIan Rogers /* 15585ecd5a0cSIan Rogers * See if the IDs for this metric are a subset of an 15595ecd5a0cSIan Rogers * earlier metric. 15605ecd5a0cSIan Rogers */ 15615ecd5a0cSIan Rogers list_for_each_entry(n, &metric_list, nd) { 15625ecd5a0cSIan Rogers if (m == n) 15635ecd5a0cSIan Rogers break; 15645ecd5a0cSIan Rogers 15655ecd5a0cSIan Rogers if (n->evlist == NULL) 15665ecd5a0cSIan Rogers continue; 15675ecd5a0cSIan Rogers 1568b85a4d61SIan Rogers if ((!m->modifier && n->modifier) || 1569b85a4d61SIan Rogers (m->modifier && !n->modifier) || 1570b85a4d61SIan Rogers (m->modifier && n->modifier && 1571b85a4d61SIan Rogers strcmp(m->modifier, n->modifier))) 1572b85a4d61SIan Rogers continue; 1573b85a4d61SIan Rogers 1574bd3846d0SIan Rogers if ((!m->pmu && n->pmu) || 1575bd3846d0SIan Rogers (m->pmu && !n->pmu) || 1576bd3846d0SIan Rogers (m->pmu && n->pmu && strcmp(m->pmu, n->pmu))) 1577bd3846d0SIan Rogers continue; 1578bd3846d0SIan Rogers 15795ecd5a0cSIan Rogers if (expr__subset_of_ids(n->pctx, m->pctx)) { 15805ecd5a0cSIan Rogers pr_debug("Events in '%s' fully contained within '%s'\n", 15815ecd5a0cSIan Rogers m->metric_name, n->metric_name); 15825ecd5a0cSIan Rogers metric_evlist = n->evlist; 15835ecd5a0cSIan Rogers break; 15845ecd5a0cSIan Rogers } 15855ecd5a0cSIan Rogers 15865ecd5a0cSIan Rogers } 15875ecd5a0cSIan Rogers } 15885ecd5a0cSIan Rogers if (!metric_evlist) { 158917b3867dSIan Rogers ret = parse_ids(metric_no_merge, fake_pmu, m->pctx, m->modifier, 1590180a5013SIan Rogers m->group_events, tool_events, &m->evlist); 15915ecd5a0cSIan Rogers if (ret) 15925ecd5a0cSIan Rogers goto out; 15935ecd5a0cSIan Rogers 15945ecd5a0cSIan Rogers metric_evlist = m->evlist; 15955ecd5a0cSIan Rogers } 1596bd3846d0SIan Rogers ret = setup_metric_events(fake_pmu ? "all" : m->pmu, m->pctx->ids, 1597bd3846d0SIan Rogers metric_evlist, &metric_events); 15985ecd5a0cSIan Rogers if (ret) { 1599*8a4859c5SIan Rogers pr_err("Cannot resolve IDs for %s: %s\n", 16005ecd5a0cSIan Rogers m->metric_name, m->metric_expr); 16015ecd5a0cSIan Rogers goto out; 16025ecd5a0cSIan Rogers } 16035ecd5a0cSIan Rogers 16045ecd5a0cSIan Rogers me = metricgroup__lookup(metric_events_list, metric_events[0], true); 16055ecd5a0cSIan Rogers 16065ecd5a0cSIan Rogers expr = malloc(sizeof(struct metric_expr)); 16075ecd5a0cSIan Rogers if (!expr) { 16085ecd5a0cSIan Rogers ret = -ENOMEM; 16095ecd5a0cSIan Rogers free(metric_events); 16105ecd5a0cSIan Rogers goto out; 16115ecd5a0cSIan Rogers } 16125ecd5a0cSIan Rogers 16135ecd5a0cSIan Rogers expr->metric_refs = m->metric_refs; 16145ecd5a0cSIan Rogers m->metric_refs = NULL; 16155ecd5a0cSIan Rogers expr->metric_expr = m->metric_expr; 1616b85a4d61SIan Rogers if (m->modifier) { 1617b85a4d61SIan Rogers char *tmp; 1618b85a4d61SIan Rogers 1619b85a4d61SIan Rogers if (asprintf(&tmp, "%s:%s", m->metric_name, m->modifier) < 0) 1620b85a4d61SIan Rogers expr->metric_name = NULL; 1621b85a4d61SIan Rogers else 1622b85a4d61SIan Rogers expr->metric_name = tmp; 1623b85a4d61SIan Rogers } else 1624b85a4d61SIan Rogers expr->metric_name = strdup(m->metric_name); 1625b85a4d61SIan Rogers 1626b85a4d61SIan Rogers if (!expr->metric_name) { 1627b85a4d61SIan Rogers ret = -ENOMEM; 1628b85a4d61SIan Rogers free(metric_events); 1629b85a4d61SIan Rogers goto out; 1630b85a4d61SIan Rogers } 1631d0a3052fSIan Rogers expr->metric_threshold = m->metric_threshold; 16325ecd5a0cSIan Rogers expr->metric_unit = m->metric_unit; 16335ecd5a0cSIan Rogers expr->metric_events = metric_events; 16341a6abddeSIan Rogers expr->runtime = m->pctx->sctx.runtime; 16355ecd5a0cSIan Rogers list_add(&expr->nd, &me->head); 16365ecd5a0cSIan Rogers } 16375ecd5a0cSIan Rogers 16385ecd5a0cSIan Rogers 1639aba8c5e3SIan Rogers if (combined_evlist) { 16405ecd5a0cSIan Rogers evlist__splice_list_tail(perf_evlist, &combined_evlist->core.entries); 1641aba8c5e3SIan Rogers evlist__delete(combined_evlist); 1642aba8c5e3SIan Rogers } 16435ecd5a0cSIan Rogers 16445ecd5a0cSIan Rogers list_for_each_entry(m, &metric_list, nd) { 16455ecd5a0cSIan Rogers if (m->evlist) 16465ecd5a0cSIan Rogers evlist__splice_list_tail(perf_evlist, &m->evlist->core.entries); 16475ecd5a0cSIan Rogers } 16485ecd5a0cSIan Rogers 1649b18f3e36SAndi Kleen out: 1650119e521aSJiri Olsa metricgroup__free_metrics(&metric_list); 1651b18f3e36SAndi Kleen return ret; 1652b18f3e36SAndi Kleen } 1653742d92ffSThomas Richter 1654a4b8cfcaSIan Rogers int metricgroup__parse_groups(struct evlist *perf_evlist, 1655dae47d39SIan Rogers const char *pmu, 16568b4468a2SJiri Olsa const char *str, 16578b4468a2SJiri Olsa bool metric_no_group, 16588b4468a2SJiri Olsa bool metric_no_merge, 16591fd09e29SIan Rogers bool metric_no_threshold, 16601725e9cdSIan Rogers const char *user_requested_cpu_list, 16611725e9cdSIan Rogers bool system_wide, 16628b4468a2SJiri Olsa struct rblist *metric_events) 16638b4468a2SJiri Olsa { 1664f8ea2c15SIan Rogers const struct pmu_metrics_table *table = pmu_metrics_table__find(); 16658b4468a2SJiri Olsa 16663f5df3acSIan Rogers if (!table) 16673f5df3acSIan Rogers return -EINVAL; 16683f5df3acSIan Rogers 1669dae47d39SIan Rogers return parse_groups(perf_evlist, pmu, str, metric_no_group, metric_no_merge, 16701fd09e29SIan Rogers metric_no_threshold, user_requested_cpu_list, system_wide, 16711725e9cdSIan Rogers /*fake_pmu=*/NULL, metric_events, table); 16728b4468a2SJiri Olsa } 16738b4468a2SJiri Olsa 1674f78ac00aSJiri Olsa int metricgroup__parse_groups_test(struct evlist *evlist, 1675f8ea2c15SIan Rogers const struct pmu_metrics_table *table, 1676f78ac00aSJiri Olsa const char *str, 1677f78ac00aSJiri Olsa struct rblist *metric_events) 1678f78ac00aSJiri Olsa { 1679bd3846d0SIan Rogers return parse_groups(evlist, "all", str, 16801fd09e29SIan Rogers /*metric_no_group=*/false, 16811fd09e29SIan Rogers /*metric_no_merge=*/false, 16821fd09e29SIan Rogers /*metric_no_threshold=*/false, 16831725e9cdSIan Rogers /*user_requested_cpu_list=*/NULL, 16841725e9cdSIan Rogers /*system_wide=*/false, 16851725e9cdSIan Rogers &perf_pmu__fake, metric_events, table); 1686f78ac00aSJiri Olsa } 1687f78ac00aSJiri Olsa 1688bd3846d0SIan Rogers struct metricgroup__has_metric_data { 1689bd3846d0SIan Rogers const char *pmu; 1690bd3846d0SIan Rogers const char *metric; 1691bd3846d0SIan Rogers }; 1692db95818eSIan Rogers static int metricgroup__has_metric_callback(const struct pmu_metric *pm, 1693f8ea2c15SIan Rogers const struct pmu_metrics_table *table __maybe_unused, 1694660842e4SIan Rogers void *vdata) 1695660842e4SIan Rogers { 1696bd3846d0SIan Rogers struct metricgroup__has_metric_data *data = vdata; 1697660842e4SIan Rogers 1698bd3846d0SIan Rogers return match_pm_metric(pm, data->pmu, data->metric) ? 1 : 0; 1699660842e4SIan Rogers } 1700660842e4SIan Rogers 1701bd3846d0SIan Rogers bool metricgroup__has_metric(const char *pmu, const char *metric) 1702742d92ffSThomas Richter { 1703f8ea2c15SIan Rogers const struct pmu_metrics_table *table = pmu_metrics_table__find(); 1704bd3846d0SIan Rogers struct metricgroup__has_metric_data data = { 1705bd3846d0SIan Rogers .pmu = pmu, 1706bd3846d0SIan Rogers .metric = metric, 1707bd3846d0SIan Rogers }; 1708742d92ffSThomas Richter 1709eeac7730SIan Rogers if (!table) 1710742d92ffSThomas Richter return false; 1711742d92ffSThomas Richter 1712bd3846d0SIan Rogers return pmu_metrics_table_for_each_metric(table, metricgroup__has_metric_callback, &data) 1713bd3846d0SIan Rogers ? true : false; 1714742d92ffSThomas Richter } 1715b214ba8cSNamhyung Kim 17161647cd5bSIan Rogers static int metricgroup__topdown_max_level_callback(const struct pmu_metric *pm, 17171647cd5bSIan Rogers const struct pmu_metrics_table *table __maybe_unused, 17181647cd5bSIan Rogers void *data) 17191647cd5bSIan Rogers { 17201647cd5bSIan Rogers unsigned int *max_level = data; 17211647cd5bSIan Rogers unsigned int level; 17221647cd5bSIan Rogers const char *p = strstr(pm->metric_group, "TopdownL"); 17231647cd5bSIan Rogers 17241647cd5bSIan Rogers if (!p || p[8] == '\0') 17251647cd5bSIan Rogers return 0; 17261647cd5bSIan Rogers 17271647cd5bSIan Rogers level = p[8] - '0'; 17281647cd5bSIan Rogers if (level > *max_level) 17291647cd5bSIan Rogers *max_level = level; 17301647cd5bSIan Rogers 17311647cd5bSIan Rogers return 0; 17321647cd5bSIan Rogers } 17331647cd5bSIan Rogers 17341647cd5bSIan Rogers unsigned int metricgroups__topdown_max_level(void) 17351647cd5bSIan Rogers { 17361647cd5bSIan Rogers unsigned int max_level = 0; 17371647cd5bSIan Rogers const struct pmu_metrics_table *table = pmu_metrics_table__find(); 17381647cd5bSIan Rogers 17391647cd5bSIan Rogers if (!table) 17401647cd5bSIan Rogers return false; 17411647cd5bSIan Rogers 17421647cd5bSIan Rogers pmu_metrics_table_for_each_metric(table, metricgroup__topdown_max_level_callback, 17431647cd5bSIan Rogers &max_level); 17441647cd5bSIan Rogers return max_level; 17451647cd5bSIan Rogers } 17461647cd5bSIan Rogers 1747b214ba8cSNamhyung Kim int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp, 1748b214ba8cSNamhyung Kim struct rblist *new_metric_events, 1749b214ba8cSNamhyung Kim struct rblist *old_metric_events) 1750b214ba8cSNamhyung Kim { 175184f879c5SXin Gao unsigned int i; 1752b214ba8cSNamhyung Kim 1753b214ba8cSNamhyung Kim for (i = 0; i < rblist__nr_entries(old_metric_events); i++) { 1754b214ba8cSNamhyung Kim struct rb_node *nd; 1755b214ba8cSNamhyung Kim struct metric_event *old_me, *new_me; 1756b214ba8cSNamhyung Kim struct metric_expr *old_expr, *new_expr; 1757b214ba8cSNamhyung Kim struct evsel *evsel; 1758b214ba8cSNamhyung Kim size_t alloc_size; 1759b214ba8cSNamhyung Kim int idx, nr; 1760b214ba8cSNamhyung Kim 1761b214ba8cSNamhyung Kim nd = rblist__entry(old_metric_events, i); 1762b214ba8cSNamhyung Kim old_me = container_of(nd, struct metric_event, nd); 1763b214ba8cSNamhyung Kim 176438fe0e01SJiri Olsa evsel = evlist__find_evsel(evlist, old_me->evsel->core.idx); 1765b214ba8cSNamhyung Kim if (!evsel) 1766b214ba8cSNamhyung Kim return -EINVAL; 1767b214ba8cSNamhyung Kim new_me = metricgroup__lookup(new_metric_events, evsel, true); 1768b214ba8cSNamhyung Kim if (!new_me) 1769b214ba8cSNamhyung Kim return -ENOMEM; 1770b214ba8cSNamhyung Kim 1771b214ba8cSNamhyung Kim pr_debug("copying metric event for cgroup '%s': %s (idx=%d)\n", 177238fe0e01SJiri Olsa cgrp ? cgrp->name : "root", evsel->name, evsel->core.idx); 1773b214ba8cSNamhyung Kim 1774b214ba8cSNamhyung Kim list_for_each_entry(old_expr, &old_me->head, nd) { 1775b214ba8cSNamhyung Kim new_expr = malloc(sizeof(*new_expr)); 1776b214ba8cSNamhyung Kim if (!new_expr) 1777b214ba8cSNamhyung Kim return -ENOMEM; 1778b214ba8cSNamhyung Kim 1779b214ba8cSNamhyung Kim new_expr->metric_expr = old_expr->metric_expr; 17806c73f819SIan Rogers new_expr->metric_threshold = old_expr->metric_threshold; 1781b85a4d61SIan Rogers new_expr->metric_name = strdup(old_expr->metric_name); 1782b85a4d61SIan Rogers if (!new_expr->metric_name) 1783b85a4d61SIan Rogers return -ENOMEM; 1784b85a4d61SIan Rogers 1785b214ba8cSNamhyung Kim new_expr->metric_unit = old_expr->metric_unit; 1786b214ba8cSNamhyung Kim new_expr->runtime = old_expr->runtime; 1787b214ba8cSNamhyung Kim 1788b214ba8cSNamhyung Kim if (old_expr->metric_refs) { 1789b214ba8cSNamhyung Kim /* calculate number of metric_events */ 1790b214ba8cSNamhyung Kim for (nr = 0; old_expr->metric_refs[nr].metric_name; nr++) 1791b214ba8cSNamhyung Kim continue; 1792b214ba8cSNamhyung Kim alloc_size = sizeof(*new_expr->metric_refs); 1793b214ba8cSNamhyung Kim new_expr->metric_refs = calloc(nr + 1, alloc_size); 1794b214ba8cSNamhyung Kim if (!new_expr->metric_refs) { 1795b214ba8cSNamhyung Kim free(new_expr); 1796b214ba8cSNamhyung Kim return -ENOMEM; 1797b214ba8cSNamhyung Kim } 1798b214ba8cSNamhyung Kim 1799b214ba8cSNamhyung Kim memcpy(new_expr->metric_refs, old_expr->metric_refs, 1800b214ba8cSNamhyung Kim nr * alloc_size); 1801b214ba8cSNamhyung Kim } else { 1802b214ba8cSNamhyung Kim new_expr->metric_refs = NULL; 1803b214ba8cSNamhyung Kim } 1804b214ba8cSNamhyung Kim 1805b214ba8cSNamhyung Kim /* calculate number of metric_events */ 1806b214ba8cSNamhyung Kim for (nr = 0; old_expr->metric_events[nr]; nr++) 1807b214ba8cSNamhyung Kim continue; 1808b214ba8cSNamhyung Kim alloc_size = sizeof(*new_expr->metric_events); 1809b214ba8cSNamhyung Kim new_expr->metric_events = calloc(nr + 1, alloc_size); 1810b214ba8cSNamhyung Kim if (!new_expr->metric_events) { 181111ff9bcdSArnaldo Carvalho de Melo zfree(&new_expr->metric_refs); 1812b214ba8cSNamhyung Kim free(new_expr); 1813b214ba8cSNamhyung Kim return -ENOMEM; 1814b214ba8cSNamhyung Kim } 1815b214ba8cSNamhyung Kim 1816b214ba8cSNamhyung Kim /* copy evsel in the same position */ 1817b214ba8cSNamhyung Kim for (idx = 0; idx < nr; idx++) { 1818b214ba8cSNamhyung Kim evsel = old_expr->metric_events[idx]; 181938fe0e01SJiri Olsa evsel = evlist__find_evsel(evlist, evsel->core.idx); 1820b214ba8cSNamhyung Kim if (evsel == NULL) { 182111ff9bcdSArnaldo Carvalho de Melo zfree(&new_expr->metric_events); 182211ff9bcdSArnaldo Carvalho de Melo zfree(&new_expr->metric_refs); 1823b214ba8cSNamhyung Kim free(new_expr); 1824b214ba8cSNamhyung Kim return -EINVAL; 1825b214ba8cSNamhyung Kim } 1826b214ba8cSNamhyung Kim new_expr->metric_events[idx] = evsel; 1827b214ba8cSNamhyung Kim } 1828b214ba8cSNamhyung Kim 1829b214ba8cSNamhyung Kim list_add(&new_expr->nd, &new_me->head); 1830b214ba8cSNamhyung Kim } 1831b214ba8cSNamhyung Kim } 1832b214ba8cSNamhyung Kim return 0; 1833b214ba8cSNamhyung Kim } 1834