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; 126485fcaedSIan Rogers /** The name of the metric such as "IPC". */ 127b18f3e36SAndi Kleen const char *metric_name; 128b85a4d61SIan Rogers /** Modifier on the metric such as "u" or NULL for none. */ 129b85a4d61SIan Rogers const char *modifier; 130485fcaedSIan Rogers /** The expression to parse, for example, "instructions/cycles". */ 131b18f3e36SAndi Kleen const char *metric_expr; 132d0a3052fSIan Rogers /** Optional threshold expression where zero value is green, otherwise red. */ 133d0a3052fSIan Rogers const char *metric_threshold; 134485fcaedSIan Rogers /** 135485fcaedSIan Rogers * The "ScaleUnit" that scales and adds a unit to the metric during 136485fcaedSIan Rogers * output. 137485fcaedSIan Rogers */ 138287f2649SJin Yao const char *metric_unit; 13946bdc0bfSIan Rogers /** Optional null terminated array of referenced metrics. */ 14046bdc0bfSIan Rogers struct metric_ref *metric_refs; 141485fcaedSIan Rogers /** 142180a5013SIan Rogers * Should events of the metric be grouped? 143485fcaedSIan Rogers */ 144180a5013SIan Rogers bool group_events; 1455ecd5a0cSIan Rogers /** 1465ecd5a0cSIan Rogers * Parsed events for the metric. Optional as events may be taken from a 1475ecd5a0cSIan Rogers * different metric whose group contains all the IDs necessary for this 1485ecd5a0cSIan Rogers * one. 1495ecd5a0cSIan Rogers */ 1505ecd5a0cSIan Rogers struct evlist *evlist; 151b18f3e36SAndi Kleen }; 152b18f3e36SAndi Kleen 153180a5013SIan Rogers static void metric__watchdog_constraint_hint(const char *name, bool foot) 1543d81d761SIan Rogers { 1553d81d761SIan Rogers static bool violate_nmi_constraint; 1563d81d761SIan Rogers 1573d81d761SIan Rogers if (!foot) { 158180a5013SIan Rogers pr_warning("Not grouping metric %s's events.\n", name); 1593d81d761SIan Rogers violate_nmi_constraint = true; 1603d81d761SIan Rogers return; 1613d81d761SIan Rogers } 1623d81d761SIan Rogers 1633d81d761SIan Rogers if (!violate_nmi_constraint) 1643d81d761SIan Rogers return; 1653d81d761SIan Rogers 1663d81d761SIan Rogers pr_warning("Try disabling the NMI watchdog to comply NO_NMI_WATCHDOG metric constraint:\n" 1673d81d761SIan Rogers " echo 0 > /proc/sys/kernel/nmi_watchdog\n" 1683d81d761SIan Rogers " perf stat ...\n" 1693d81d761SIan Rogers " echo 1 > /proc/sys/kernel/nmi_watchdog\n"); 1703d81d761SIan Rogers } 1713d81d761SIan Rogers 172180a5013SIan Rogers static bool metric__group_events(const struct pmu_metric *pm) 1733d81d761SIan Rogers { 17490053634SIan Rogers switch (pm->event_grouping) { 17590053634SIan Rogers case MetricNoGroupEvents: 17690053634SIan Rogers return false; 17790053634SIan Rogers case MetricNoGroupEventsNmi: 17890053634SIan Rogers if (!sysctl__nmi_watchdog_enabled()) 179180a5013SIan Rogers return true; 180180a5013SIan Rogers metric__watchdog_constraint_hint(pm->metric_name, /*foot=*/false); 181180a5013SIan Rogers return false; 18290053634SIan Rogers case MetricNoGroupEventsSmt: 18390053634SIan Rogers return !smt_on(); 18490053634SIan Rogers case MetricGroupEvents: 18590053634SIan Rogers default: 186180a5013SIan Rogers return true; 1873d81d761SIan Rogers } 18890053634SIan Rogers } 1893d81d761SIan Rogers 1901725e9cdSIan Rogers static void metric__free(struct metric *m) 1911725e9cdSIan Rogers { 1921725e9cdSIan Rogers if (!m) 1931725e9cdSIan Rogers return; 1941725e9cdSIan Rogers 19511ff9bcdSArnaldo Carvalho de Melo zfree(&m->metric_refs); 1961725e9cdSIan Rogers expr__ctx_free(m->pctx); 19711ff9bcdSArnaldo Carvalho de Melo zfree(&m->modifier); 1981725e9cdSIan Rogers evlist__delete(m->evlist); 1991725e9cdSIan Rogers free(m); 2001725e9cdSIan Rogers } 2011725e9cdSIan Rogers 202db95818eSIan Rogers static struct metric *metric__new(const struct pmu_metric *pm, 203b85a4d61SIan Rogers const char *modifier, 2043d81d761SIan Rogers bool metric_no_group, 2051725e9cdSIan Rogers int runtime, 2061725e9cdSIan Rogers const char *user_requested_cpu_list, 2071725e9cdSIan Rogers bool system_wide) 2083d81d761SIan Rogers { 2093d81d761SIan Rogers struct metric *m; 2103d81d761SIan Rogers 2113d81d761SIan Rogers m = zalloc(sizeof(*m)); 2123d81d761SIan Rogers if (!m) 2133d81d761SIan Rogers return NULL; 2143d81d761SIan Rogers 2153d81d761SIan Rogers m->pctx = expr__ctx_new(); 2161725e9cdSIan Rogers if (!m->pctx) 2171725e9cdSIan Rogers goto out_err; 2183d81d761SIan Rogers 219db95818eSIan Rogers m->metric_name = pm->metric_name; 2201725e9cdSIan Rogers m->modifier = NULL; 2211725e9cdSIan Rogers if (modifier) { 2221725e9cdSIan Rogers m->modifier = strdup(modifier); 2231725e9cdSIan Rogers if (!m->modifier) 2241725e9cdSIan Rogers goto out_err; 225b85a4d61SIan Rogers } 226db95818eSIan Rogers m->metric_expr = pm->metric_expr; 227d0a3052fSIan Rogers m->metric_threshold = pm->metric_threshold; 228db95818eSIan Rogers m->metric_unit = pm->unit; 2291725e9cdSIan Rogers m->pctx->sctx.user_requested_cpu_list = NULL; 2301725e9cdSIan Rogers if (user_requested_cpu_list) { 2311725e9cdSIan Rogers m->pctx->sctx.user_requested_cpu_list = strdup(user_requested_cpu_list); 2321725e9cdSIan Rogers if (!m->pctx->sctx.user_requested_cpu_list) 2331725e9cdSIan Rogers goto out_err; 2341725e9cdSIan Rogers } 2351a6abddeSIan Rogers m->pctx->sctx.runtime = runtime; 2361725e9cdSIan Rogers m->pctx->sctx.system_wide = system_wide; 237180a5013SIan Rogers m->group_events = !metric_no_group && metric__group_events(pm); 23846bdc0bfSIan Rogers m->metric_refs = NULL; 2395ecd5a0cSIan Rogers m->evlist = NULL; 2403d81d761SIan Rogers 2413d81d761SIan Rogers return m; 2421725e9cdSIan Rogers out_err: 2431725e9cdSIan Rogers metric__free(m); 2441725e9cdSIan Rogers return NULL; 2453d81d761SIan Rogers } 2463d81d761SIan Rogers 247ec5c5b3dSIan Rogers static bool contains_metric_id(struct evsel **metric_events, int num_events, 248ec5c5b3dSIan Rogers const char *metric_id) 249dcc81be0SIan Rogers { 250dcc81be0SIan Rogers int i; 251dcc81be0SIan Rogers 252dcc81be0SIan Rogers for (i = 0; i < num_events; i++) { 253ec5c5b3dSIan Rogers if (!strcmp(evsel__metric_id(metric_events[i]), metric_id)) 254dcc81be0SIan Rogers return true; 255dcc81be0SIan Rogers } 256dcc81be0SIan Rogers return false; 257dcc81be0SIan Rogers } 258dcc81be0SIan Rogers 2592440689dSIan Rogers /** 2605ecd5a0cSIan Rogers * setup_metric_events - Find a group of events in metric_evlist that correspond 2615ecd5a0cSIan Rogers * to the IDs from a parsed metric expression. 2625ecd5a0cSIan Rogers * @ids: the metric IDs to match. 2635ecd5a0cSIan Rogers * @metric_evlist: the list of perf events. 2645ecd5a0cSIan Rogers * @out_metric_events: holds the created metric events array. 2652440689dSIan Rogers */ 2665ecd5a0cSIan Rogers static int setup_metric_events(struct hashmap *ids, 2675ecd5a0cSIan Rogers struct evlist *metric_evlist, 26817b3867dSIan Rogers struct evsel ***out_metric_events) 269b18f3e36SAndi Kleen { 2705ecd5a0cSIan Rogers struct evsel **metric_events; 271ec5c5b3dSIan Rogers const char *metric_id; 2725ecd5a0cSIan Rogers struct evsel *ev; 2735ecd5a0cSIan Rogers size_t ids_size, matched_events, i; 2742440689dSIan Rogers 2755ecd5a0cSIan Rogers *out_metric_events = NULL; 2765ecd5a0cSIan Rogers ids_size = hashmap__size(ids); 277b18f3e36SAndi Kleen 2785ecd5a0cSIan Rogers metric_events = calloc(sizeof(void *), ids_size + 1); 2795ecd5a0cSIan Rogers if (!metric_events) 2805ecd5a0cSIan Rogers return -ENOMEM; 2815ecd5a0cSIan Rogers 2822440689dSIan Rogers matched_events = 0; 2835ecd5a0cSIan Rogers evlist__for_each_entry(metric_evlist, ev) { 2845ecd5a0cSIan Rogers struct expr_id_data *val_ptr; 2855ecd5a0cSIan Rogers 28605530a79SIan Rogers /* 287762a05c5SIan Rogers * Check for duplicate events with the same name. For 288762a05c5SIan Rogers * example, uncore_imc/cas_count_read/ will turn into 6 289762a05c5SIan Rogers * events per socket on skylakex. Only the first such 2905ecd5a0cSIan Rogers * event is placed in metric_events. 29105530a79SIan Rogers */ 292ec5c5b3dSIan Rogers metric_id = evsel__metric_id(ev); 293ec5c5b3dSIan Rogers if (contains_metric_id(metric_events, matched_events, metric_id)) 29405530a79SIan Rogers continue; 2955ecd5a0cSIan Rogers /* 2965ecd5a0cSIan Rogers * Does this event belong to the parse context? For 2975ecd5a0cSIan Rogers * combined or shared groups, this metric may not care 2985ecd5a0cSIan Rogers * about this event. 2995ecd5a0cSIan Rogers */ 300c302378bSEduard Zingerman if (hashmap__find(ids, metric_id, &val_ptr)) { 3012440689dSIan Rogers metric_events[matched_events++] = ev; 302dcc81be0SIan Rogers 3035ecd5a0cSIan Rogers if (matched_events >= ids_size) 3042440689dSIan Rogers break; 3052440689dSIan Rogers } 306762a05c5SIan Rogers } 3075ecd5a0cSIan Rogers if (matched_events < ids_size) { 3085ecd5a0cSIan Rogers free(metric_events); 3095ecd5a0cSIan Rogers return -EINVAL; 310b18f3e36SAndi Kleen } 3115ecd5a0cSIan Rogers for (i = 0; i < ids_size; i++) { 31258fc90fdSKajol Jain ev = metric_events[i]; 3135ecd5a0cSIan Rogers ev->collect_stat = true; 3145ecd5a0cSIan Rogers 315dcc81be0SIan Rogers /* 3165ecd5a0cSIan Rogers * The metric leader points to the identically named 3175ecd5a0cSIan Rogers * event in metric_events. 318dcc81be0SIan Rogers */ 319dcc81be0SIan Rogers ev->metric_leader = ev; 320dcc81be0SIan Rogers /* 3215ecd5a0cSIan Rogers * Mark two events with identical names in the same 3225ecd5a0cSIan Rogers * group (or globally) as being in use as uncore events 3235ecd5a0cSIan Rogers * may be duplicated for each pmu. Set the metric leader 3245ecd5a0cSIan Rogers * of such events to be the event that appears in 3255ecd5a0cSIan Rogers * metric_events. 326dcc81be0SIan Rogers */ 327ec5c5b3dSIan Rogers metric_id = evsel__metric_id(ev); 3285ecd5a0cSIan Rogers evlist__for_each_entry_continue(metric_evlist, ev) { 329d3e2bb43SIan Rogers if (!strcmp(evsel__metric_id(ev), metric_id)) 330dcc81be0SIan Rogers ev->metric_leader = metric_events[i]; 331dcc81be0SIan Rogers } 332dcc81be0SIan Rogers } 3335ecd5a0cSIan Rogers *out_metric_events = metric_events; 3345ecd5a0cSIan Rogers return 0; 335b18f3e36SAndi Kleen } 336b18f3e36SAndi Kleen 337b18f3e36SAndi Kleen static bool match_metric(const char *n, const char *list) 338b18f3e36SAndi Kleen { 339b18f3e36SAndi Kleen int len; 340b18f3e36SAndi Kleen char *m; 341b18f3e36SAndi Kleen 342b18f3e36SAndi Kleen if (!list) 343b18f3e36SAndi Kleen return false; 344b18f3e36SAndi Kleen if (!strcmp(list, "all")) 345b18f3e36SAndi Kleen return true; 346b18f3e36SAndi Kleen if (!n) 347b18f3e36SAndi Kleen return !strcasecmp(list, "No_group"); 348b18f3e36SAndi Kleen len = strlen(list); 349b18f3e36SAndi Kleen m = strcasestr(n, list); 350b18f3e36SAndi Kleen if (!m) 351b18f3e36SAndi Kleen return false; 352b18f3e36SAndi Kleen if ((m == n || m[-1] == ';' || m[-1] == ' ') && 353b18f3e36SAndi Kleen (m[len] == 0 || m[len] == ';')) 354b18f3e36SAndi Kleen return true; 355b18f3e36SAndi Kleen return false; 356b18f3e36SAndi Kleen } 357b18f3e36SAndi Kleen 358db95818eSIan Rogers static bool match_pm_metric(const struct pmu_metric *pm, const char *metric) 359be335ec2SJohn Garry { 360db95818eSIan Rogers return match_metric(pm->metric_group, metric) || 361db95818eSIan Rogers match_metric(pm->metric_name, metric); 362be335ec2SJohn Garry } 363be335ec2SJohn Garry 364e5c6109fSIan Rogers /** struct mep - RB-tree node for building printing information. */ 36571b0acceSAndi Kleen struct mep { 366e5c6109fSIan Rogers /** nd - RB-tree element. */ 36771b0acceSAndi Kleen struct rb_node nd; 368e5c6109fSIan Rogers /** @metric_group: Owned metric group name, separated others with ';'. */ 369e5c6109fSIan Rogers char *metric_group; 370e5c6109fSIan Rogers const char *metric_name; 371e5c6109fSIan Rogers const char *metric_desc; 372e5c6109fSIan Rogers const char *metric_long_desc; 373e5c6109fSIan Rogers const char *metric_expr; 374c7551a2eSIan Rogers const char *metric_threshold; 375e5c6109fSIan Rogers const char *metric_unit; 37671b0acceSAndi Kleen }; 37771b0acceSAndi Kleen 37871b0acceSAndi Kleen static int mep_cmp(struct rb_node *rb_node, const void *entry) 37971b0acceSAndi Kleen { 38071b0acceSAndi Kleen struct mep *a = container_of(rb_node, struct mep, nd); 38171b0acceSAndi Kleen struct mep *b = (struct mep *)entry; 382e5c6109fSIan Rogers int ret; 38371b0acceSAndi Kleen 384e5c6109fSIan Rogers ret = strcmp(a->metric_group, b->metric_group); 385e5c6109fSIan Rogers if (ret) 386e5c6109fSIan Rogers return ret; 387e5c6109fSIan Rogers 388e5c6109fSIan Rogers return strcmp(a->metric_name, b->metric_name); 38971b0acceSAndi Kleen } 39071b0acceSAndi Kleen 391e5c6109fSIan Rogers static struct rb_node *mep_new(struct rblist *rl __maybe_unused, const void *entry) 39271b0acceSAndi Kleen { 39371b0acceSAndi Kleen struct mep *me = malloc(sizeof(struct mep)); 39471b0acceSAndi Kleen 39571b0acceSAndi Kleen if (!me) 39671b0acceSAndi Kleen return NULL; 39771b0acceSAndi Kleen 398e5c6109fSIan Rogers memcpy(me, entry, sizeof(struct mep)); 399e5c6109fSIan Rogers return &me->nd; 40071b0acceSAndi Kleen } 40171b0acceSAndi Kleen 40271b0acceSAndi Kleen static void mep_delete(struct rblist *rl __maybe_unused, 40371b0acceSAndi Kleen struct rb_node *nd) 40471b0acceSAndi Kleen { 40571b0acceSAndi Kleen struct mep *me = container_of(nd, struct mep, nd); 40671b0acceSAndi Kleen 407e5c6109fSIan Rogers zfree(&me->metric_group); 40871b0acceSAndi Kleen free(me); 40971b0acceSAndi Kleen } 41071b0acceSAndi Kleen 411e5c6109fSIan Rogers static struct mep *mep_lookup(struct rblist *groups, const char *metric_group, 412e5c6109fSIan Rogers const char *metric_name) 41371b0acceSAndi Kleen { 414e5c6109fSIan Rogers struct rb_node *nd; 415e5c6109fSIan Rogers struct mep me = { 416e5c6109fSIan Rogers .metric_group = strdup(metric_group), 417e5c6109fSIan Rogers .metric_name = metric_name, 418e5c6109fSIan Rogers }; 419e5c6109fSIan Rogers nd = rblist__find(groups, &me); 420e5c6109fSIan Rogers if (nd) { 421e5c6109fSIan Rogers free(me.metric_group); 422e5c6109fSIan Rogers return container_of(nd, struct mep, nd); 42371b0acceSAndi Kleen } 424e5c6109fSIan Rogers rblist__add_node(groups, &me); 425e5c6109fSIan Rogers nd = rblist__find(groups, &me); 426e5c6109fSIan Rogers if (nd) 427e5c6109fSIan Rogers return container_of(nd, struct mep, nd); 428e5c6109fSIan Rogers return NULL; 42971b0acceSAndi Kleen } 43071b0acceSAndi Kleen 431db95818eSIan Rogers static int metricgroup__add_to_mep_groups(const struct pmu_metric *pm, 432e5c6109fSIan Rogers struct rblist *groups) 433f6fe1e48SJohn Garry { 434f6fe1e48SJohn Garry const char *g; 435f6fe1e48SJohn Garry char *omg, *mg; 436f6fe1e48SJohn Garry 437db95818eSIan Rogers mg = strdup(pm->metric_group ?: "No_group"); 438f6fe1e48SJohn Garry if (!mg) 439f6fe1e48SJohn Garry return -ENOMEM; 440f6fe1e48SJohn Garry omg = mg; 441f6fe1e48SJohn Garry while ((g = strsep(&mg, ";")) != NULL) { 442f6fe1e48SJohn Garry struct mep *me; 443f6fe1e48SJohn Garry 444f6fe1e48SJohn Garry g = skip_spaces(g); 445e5c6109fSIan Rogers if (strlen(g)) 446db95818eSIan Rogers me = mep_lookup(groups, g, pm->metric_name); 447e5c6109fSIan Rogers else 448db95818eSIan Rogers me = mep_lookup(groups, "No_group", pm->metric_name); 449f6fe1e48SJohn Garry 450e5c6109fSIan Rogers if (me) { 451db95818eSIan Rogers me->metric_desc = pm->desc; 452db95818eSIan Rogers me->metric_long_desc = pm->long_desc; 453db95818eSIan Rogers me->metric_expr = pm->metric_expr; 454c7551a2eSIan Rogers me->metric_threshold = pm->metric_threshold; 455db95818eSIan Rogers me->metric_unit = pm->unit; 456f6fe1e48SJohn Garry } 457f6fe1e48SJohn Garry } 458f6fe1e48SJohn Garry free(omg); 459f6fe1e48SJohn Garry 460f6fe1e48SJohn Garry return 0; 461f6fe1e48SJohn Garry } 462f6fe1e48SJohn Garry 463a36fadb1SJohn Garry struct metricgroup_iter_data { 464db95818eSIan Rogers pmu_metric_iter_fn fn; 465a36fadb1SJohn Garry void *data; 466a36fadb1SJohn Garry }; 467a36fadb1SJohn Garry 468db95818eSIan Rogers static int metricgroup__sys_event_iter(const struct pmu_metric *pm, 469f8ea2c15SIan Rogers const struct pmu_metrics_table *table, 47029be2fe0SIan Rogers void *data) 471a36fadb1SJohn Garry { 472a36fadb1SJohn Garry struct metricgroup_iter_data *d = data; 473a36fadb1SJohn Garry struct perf_pmu *pmu = NULL; 474a36fadb1SJohn Garry 475db95818eSIan Rogers if (!pm->metric_expr || !pm->compat) 476a36fadb1SJohn Garry return 0; 477a36fadb1SJohn Garry 478a36fadb1SJohn Garry while ((pmu = perf_pmu__scan(pmu))) { 479a36fadb1SJohn Garry 480db95818eSIan Rogers if (!pmu->id || strcmp(pmu->id, pm->compat)) 481a36fadb1SJohn Garry continue; 482a36fadb1SJohn Garry 483db95818eSIan Rogers return d->fn(pm, table, d->data); 484a36fadb1SJohn Garry } 485a36fadb1SJohn Garry return 0; 486a36fadb1SJohn Garry } 487a36fadb1SJohn Garry 488db95818eSIan Rogers static int metricgroup__add_to_mep_groups_callback(const struct pmu_metric *pm, 489f8ea2c15SIan Rogers const struct pmu_metrics_table *table __maybe_unused, 490660842e4SIan Rogers void *vdata) 491660842e4SIan Rogers { 492e5c6109fSIan Rogers struct rblist *groups = vdata; 493660842e4SIan Rogers 494db95818eSIan Rogers return metricgroup__add_to_mep_groups(pm, groups); 495660842e4SIan Rogers } 496660842e4SIan Rogers 497e5c6109fSIan Rogers void metricgroup__print(const struct print_callbacks *print_cb, void *print_state) 49871b0acceSAndi Kleen { 49971b0acceSAndi Kleen struct rblist groups; 500f8ea2c15SIan Rogers const struct pmu_metrics_table *table; 501e5c6109fSIan Rogers struct rb_node *node, *next; 50271b0acceSAndi Kleen 50371b0acceSAndi Kleen rblist__init(&groups); 50471b0acceSAndi Kleen groups.node_new = mep_new; 50571b0acceSAndi Kleen groups.node_cmp = mep_cmp; 50671b0acceSAndi Kleen groups.node_delete = mep_delete; 507f8ea2c15SIan Rogers table = pmu_metrics_table__find(); 508660842e4SIan Rogers if (table) { 509f8ea2c15SIan Rogers pmu_metrics_table_for_each_metric(table, 510e5c6109fSIan Rogers metricgroup__add_to_mep_groups_callback, 511e5c6109fSIan Rogers &groups); 5120e0ae874SJin Yao } 513a36fadb1SJohn Garry { 514a36fadb1SJohn Garry struct metricgroup_iter_data data = { 515e5c6109fSIan Rogers .fn = metricgroup__add_to_mep_groups_callback, 516e5c6109fSIan Rogers .data = &groups, 517a36fadb1SJohn Garry }; 518db95818eSIan Rogers pmu_for_each_sys_metric(metricgroup__sys_event_iter, &data); 519a36fadb1SJohn Garry } 520a36fadb1SJohn Garry 521ca227029SDavidlohr Bueso for (node = rb_first_cached(&groups.entries); node; node = next) { 52271b0acceSAndi Kleen struct mep *me = container_of(node, struct mep, nd); 52371b0acceSAndi Kleen 524e5c6109fSIan Rogers print_cb->print_metric(print_state, 525e5c6109fSIan Rogers me->metric_group, 526e5c6109fSIan Rogers me->metric_name, 527e5c6109fSIan Rogers me->metric_desc, 528e5c6109fSIan Rogers me->metric_long_desc, 529e5c6109fSIan Rogers me->metric_expr, 530c7551a2eSIan Rogers me->metric_threshold, 531e5c6109fSIan Rogers me->metric_unit); 53271b0acceSAndi Kleen next = rb_next(node); 53371b0acceSAndi Kleen rblist__remove_node(&groups, node); 53471b0acceSAndi Kleen } 53571b0acceSAndi Kleen } 53671b0acceSAndi Kleen 537ec5c5b3dSIan Rogers static const char *code_characters = ",-=@"; 538ec5c5b3dSIan Rogers 539ec5c5b3dSIan Rogers static int encode_metric_id(struct strbuf *sb, const char *x) 540ec5c5b3dSIan Rogers { 541ec5c5b3dSIan Rogers char *c; 542ec5c5b3dSIan Rogers int ret = 0; 543ec5c5b3dSIan Rogers 544ec5c5b3dSIan Rogers for (; *x; x++) { 545ec5c5b3dSIan Rogers c = strchr(code_characters, *x); 546ec5c5b3dSIan Rogers if (c) { 547ec5c5b3dSIan Rogers ret = strbuf_addch(sb, '!'); 548ec5c5b3dSIan Rogers if (ret) 549ec5c5b3dSIan Rogers break; 550ec5c5b3dSIan Rogers 551ec5c5b3dSIan Rogers ret = strbuf_addch(sb, '0' + (c - code_characters)); 552ec5c5b3dSIan Rogers if (ret) 553ec5c5b3dSIan Rogers break; 554ec5c5b3dSIan Rogers } else { 555ec5c5b3dSIan Rogers ret = strbuf_addch(sb, *x); 556ec5c5b3dSIan Rogers if (ret) 557ec5c5b3dSIan Rogers break; 558ec5c5b3dSIan Rogers } 559ec5c5b3dSIan Rogers } 560ec5c5b3dSIan Rogers return ret; 561ec5c5b3dSIan Rogers } 562ec5c5b3dSIan Rogers 563ec5c5b3dSIan Rogers static int decode_metric_id(struct strbuf *sb, const char *x) 564ec5c5b3dSIan Rogers { 565ec5c5b3dSIan Rogers const char *orig = x; 566ec5c5b3dSIan Rogers size_t i; 567ec5c5b3dSIan Rogers char c; 568ec5c5b3dSIan Rogers int ret; 569ec5c5b3dSIan Rogers 570ec5c5b3dSIan Rogers for (; *x; x++) { 571ec5c5b3dSIan Rogers c = *x; 572ec5c5b3dSIan Rogers if (*x == '!') { 573ec5c5b3dSIan Rogers x++; 574ec5c5b3dSIan Rogers i = *x - '0'; 575ec5c5b3dSIan Rogers if (i > strlen(code_characters)) { 576ec5c5b3dSIan Rogers pr_err("Bad metric-id encoding in: '%s'", orig); 577ec5c5b3dSIan Rogers return -1; 578ec5c5b3dSIan Rogers } 579ec5c5b3dSIan Rogers c = code_characters[i]; 580ec5c5b3dSIan Rogers } 581ec5c5b3dSIan Rogers ret = strbuf_addch(sb, c); 582ec5c5b3dSIan Rogers if (ret) 583ec5c5b3dSIan Rogers return ret; 584ec5c5b3dSIan Rogers } 585ec5c5b3dSIan Rogers return 0; 586ec5c5b3dSIan Rogers } 587ec5c5b3dSIan Rogers 588b85a4d61SIan Rogers static int decode_all_metric_ids(struct evlist *perf_evlist, const char *modifier) 589ec5c5b3dSIan Rogers { 590ec5c5b3dSIan Rogers struct evsel *ev; 591ec5c5b3dSIan Rogers struct strbuf sb = STRBUF_INIT; 592ec5c5b3dSIan Rogers char *cur; 593ec5c5b3dSIan Rogers int ret = 0; 594ec5c5b3dSIan Rogers 595ec5c5b3dSIan Rogers evlist__for_each_entry(perf_evlist, ev) { 596ec5c5b3dSIan Rogers if (!ev->metric_id) 597ec5c5b3dSIan Rogers continue; 598ec5c5b3dSIan Rogers 599ec5c5b3dSIan Rogers ret = strbuf_setlen(&sb, 0); 600ec5c5b3dSIan Rogers if (ret) 601ec5c5b3dSIan Rogers break; 602ec5c5b3dSIan Rogers 603ec5c5b3dSIan Rogers ret = decode_metric_id(&sb, ev->metric_id); 604ec5c5b3dSIan Rogers if (ret) 605ec5c5b3dSIan Rogers break; 606ec5c5b3dSIan Rogers 607ec5c5b3dSIan Rogers free((char *)ev->metric_id); 608ec5c5b3dSIan Rogers ev->metric_id = strdup(sb.buf); 609ec5c5b3dSIan Rogers if (!ev->metric_id) { 610ec5c5b3dSIan Rogers ret = -ENOMEM; 611ec5c5b3dSIan Rogers break; 612ec5c5b3dSIan Rogers } 613ec5c5b3dSIan Rogers /* 614ec5c5b3dSIan Rogers * If the name is just the parsed event, use the metric-id to 615ec5c5b3dSIan Rogers * give a more friendly display version. 616ec5c5b3dSIan Rogers */ 617ec5c5b3dSIan Rogers if (strstr(ev->name, "metric-id=")) { 618b85a4d61SIan Rogers bool has_slash = false; 619ec5c5b3dSIan Rogers 62011ff9bcdSArnaldo Carvalho de Melo zfree(&ev->name); 621b85a4d61SIan Rogers for (cur = strchr(sb.buf, '@') ; cur; cur = strchr(++cur, '@')) { 622b85a4d61SIan Rogers *cur = '/'; 623b85a4d61SIan Rogers has_slash = true; 624b85a4d61SIan Rogers } 625b85a4d61SIan Rogers 626b85a4d61SIan Rogers if (modifier) { 627b85a4d61SIan Rogers if (!has_slash && !strchr(sb.buf, ':')) { 628b85a4d61SIan Rogers ret = strbuf_addch(&sb, ':'); 629b85a4d61SIan Rogers if (ret) 630b85a4d61SIan Rogers break; 631b85a4d61SIan Rogers } 632b85a4d61SIan Rogers ret = strbuf_addstr(&sb, modifier); 633b85a4d61SIan Rogers if (ret) 634b85a4d61SIan Rogers break; 635b85a4d61SIan Rogers } 636ec5c5b3dSIan Rogers ev->name = strdup(sb.buf); 637ec5c5b3dSIan Rogers if (!ev->name) { 638ec5c5b3dSIan Rogers ret = -ENOMEM; 639ec5c5b3dSIan Rogers break; 640ec5c5b3dSIan Rogers } 641ec5c5b3dSIan Rogers } 642ec5c5b3dSIan Rogers } 643ec5c5b3dSIan Rogers strbuf_release(&sb); 644ec5c5b3dSIan Rogers return ret; 645ec5c5b3dSIan Rogers } 646ec5c5b3dSIan Rogers 647ec5c5b3dSIan Rogers static int metricgroup__build_event_string(struct strbuf *events, 648ec5c5b3dSIan Rogers const struct expr_parse_ctx *ctx, 649b85a4d61SIan Rogers const char *modifier, 650180a5013SIan Rogers bool group_events) 651f742634aSKan Liang { 652ded80bdaSIan Rogers struct hashmap_entry *cur; 6534e21c13aSIan Rogers size_t bkt; 6549aa09230SIan Rogers bool no_group = true, has_tool_events = false; 6559aa09230SIan Rogers bool tool_events[PERF_TOOL_MAX] = {false}; 656ec5c5b3dSIan Rogers int ret = 0; 657ec5c5b3dSIan Rogers 658ec5c5b3dSIan Rogers #define RETURN_IF_NON_ZERO(x) do { if (x) return x; } while (0) 659f742634aSKan Liang 660cb94a02eSIan Rogers hashmap__for_each_entry(ctx->ids, cur, bkt) { 661c302378bSEduard Zingerman const char *sep, *rsep, *id = cur->pkey; 6629aa09230SIan Rogers enum perf_tool_event ev; 663ec5c5b3dSIan Rogers 664ec5c5b3dSIan Rogers pr_debug("found event %s\n", id); 6659aa09230SIan Rogers 6669aa09230SIan Rogers /* Always move tool events outside of the group. */ 6679aa09230SIan Rogers ev = perf_tool_event__from_str(id); 6689aa09230SIan Rogers if (ev != PERF_TOOL_NONE) { 6699aa09230SIan Rogers has_tool_events = true; 6709aa09230SIan Rogers tool_events[ev] = true; 671f742634aSKan Liang continue; 672f742634aSKan Liang } 673ec5c5b3dSIan Rogers /* Separate events with commas and open the group if necessary. */ 674ec5c5b3dSIan Rogers if (no_group) { 675180a5013SIan Rogers if (group_events) { 676ec5c5b3dSIan Rogers ret = strbuf_addch(events, '{'); 677ec5c5b3dSIan Rogers RETURN_IF_NON_ZERO(ret); 678ec5c5b3dSIan Rogers } 679ec5c5b3dSIan Rogers 680f742634aSKan Liang no_group = false; 681ec5c5b3dSIan Rogers } else { 682ec5c5b3dSIan Rogers ret = strbuf_addch(events, ','); 683ec5c5b3dSIan Rogers RETURN_IF_NON_ZERO(ret); 684f742634aSKan Liang } 685ec5c5b3dSIan Rogers /* 686ec5c5b3dSIan Rogers * Encode the ID as an event string. Add a qualifier for 687ec5c5b3dSIan Rogers * metric_id that is the original name except with characters 688ec5c5b3dSIan Rogers * that parse-events can't parse replaced. For example, 689ec5c5b3dSIan Rogers * 'msr@tsc@' gets added as msr/tsc,metric-id=msr!3tsc!3/ 690ec5c5b3dSIan Rogers */ 691ec5c5b3dSIan Rogers sep = strchr(id, '@'); 692ec5c5b3dSIan Rogers if (sep != NULL) { 693ec5c5b3dSIan Rogers ret = strbuf_add(events, id, sep - id); 694ec5c5b3dSIan Rogers RETURN_IF_NON_ZERO(ret); 695ec5c5b3dSIan Rogers ret = strbuf_addch(events, '/'); 696ec5c5b3dSIan Rogers RETURN_IF_NON_ZERO(ret); 697ec5c5b3dSIan Rogers rsep = strrchr(sep, '@'); 698ec5c5b3dSIan Rogers ret = strbuf_add(events, sep + 1, rsep - sep - 1); 699ec5c5b3dSIan Rogers RETURN_IF_NON_ZERO(ret); 700ec5c5b3dSIan Rogers ret = strbuf_addstr(events, ",metric-id="); 701ec5c5b3dSIan Rogers RETURN_IF_NON_ZERO(ret); 702ec5c5b3dSIan Rogers sep = rsep; 703ec5c5b3dSIan Rogers } else { 704ec5c5b3dSIan Rogers sep = strchr(id, ':'); 705ec5c5b3dSIan Rogers if (sep != NULL) { 706ec5c5b3dSIan Rogers ret = strbuf_add(events, id, sep - id); 707ec5c5b3dSIan Rogers RETURN_IF_NON_ZERO(ret); 708ec5c5b3dSIan Rogers } else { 709ec5c5b3dSIan Rogers ret = strbuf_addstr(events, id); 710ec5c5b3dSIan Rogers RETURN_IF_NON_ZERO(ret); 711f742634aSKan Liang } 712ec5c5b3dSIan Rogers ret = strbuf_addstr(events, "/metric-id="); 713ec5c5b3dSIan Rogers RETURN_IF_NON_ZERO(ret); 714ec5c5b3dSIan Rogers } 715ec5c5b3dSIan Rogers ret = encode_metric_id(events, id); 716ec5c5b3dSIan Rogers RETURN_IF_NON_ZERO(ret); 717ec5c5b3dSIan Rogers ret = strbuf_addstr(events, "/"); 718ec5c5b3dSIan Rogers RETURN_IF_NON_ZERO(ret); 719f742634aSKan Liang 720ec5c5b3dSIan Rogers if (sep != NULL) { 721ec5c5b3dSIan Rogers ret = strbuf_addstr(events, sep + 1); 722ec5c5b3dSIan Rogers RETURN_IF_NON_ZERO(ret); 723e2ce1059SIan Rogers } 724b85a4d61SIan Rogers if (modifier) { 725b85a4d61SIan Rogers ret = strbuf_addstr(events, modifier); 726b85a4d61SIan Rogers RETURN_IF_NON_ZERO(ret); 727b85a4d61SIan Rogers } 728ab483d8bSKan Liang } 729180a5013SIan Rogers if (!no_group && group_events) { 73060344f1aSZhengjun Xing ret = strbuf_addf(events, "}:W"); 7319aa09230SIan Rogers RETURN_IF_NON_ZERO(ret); 7329aa09230SIan Rogers } 7339aa09230SIan Rogers if (has_tool_events) { 7349aa09230SIan Rogers int i; 7359aa09230SIan Rogers 7369aa09230SIan Rogers perf_tool_event__for_each_event(i) { 7379aa09230SIan Rogers if (tool_events[i]) { 7389aa09230SIan Rogers if (!no_group) { 7399aa09230SIan Rogers ret = strbuf_addch(events, ','); 7409aa09230SIan Rogers RETURN_IF_NON_ZERO(ret); 7419aa09230SIan Rogers } 7429aa09230SIan Rogers no_group = false; 7439aa09230SIan Rogers ret = strbuf_addstr(events, perf_tool_event__to_str(i)); 7449aa09230SIan Rogers RETURN_IF_NON_ZERO(ret); 7459aa09230SIan Rogers } 7469aa09230SIan Rogers } 7479aa09230SIan Rogers } 748ec5c5b3dSIan Rogers 749ec5c5b3dSIan Rogers return ret; 750ec5c5b3dSIan Rogers #undef RETURN_IF_NON_ZERO 751ec5c5b3dSIan Rogers } 752ab483d8bSKan Liang 753db95818eSIan Rogers int __weak arch_get_runtimeparam(const struct pmu_metric *pm __maybe_unused) 7541e1a873dSKajol Jain { 7551e1a873dSKajol Jain return 1; 7561e1a873dSKajol Jain } 7571e1a873dSKajol Jain 75880be6434SIan Rogers /* 75980be6434SIan Rogers * A singly linked list on the stack of the names of metrics being 76080be6434SIan Rogers * processed. Used to identify recursion. 76180be6434SIan Rogers */ 76280be6434SIan Rogers struct visited_metric { 76380be6434SIan Rogers const char *name; 76480be6434SIan Rogers const struct visited_metric *parent; 76580be6434SIan Rogers }; 76680be6434SIan Rogers 767be335ec2SJohn Garry struct metricgroup_add_iter_data { 768be335ec2SJohn Garry struct list_head *metric_list; 76968074811SIan Rogers const char *metric_name; 770b85a4d61SIan Rogers const char *modifier; 771be335ec2SJohn Garry int *ret; 772be335ec2SJohn Garry bool *has_match; 773be335ec2SJohn Garry bool metric_no_group; 7741fd09e29SIan Rogers bool metric_no_threshold; 7751725e9cdSIan Rogers const char *user_requested_cpu_list; 7761725e9cdSIan Rogers bool system_wide; 77780be6434SIan Rogers struct metric *root_metric; 77880be6434SIan Rogers const struct visited_metric *visited; 779f8ea2c15SIan Rogers const struct pmu_metrics_table *table; 780be335ec2SJohn Garry }; 781be335ec2SJohn Garry 782d3abd7b8SIan Rogers static bool metricgroup__find_metric(const char *metric, 783f8ea2c15SIan Rogers const struct pmu_metrics_table *table, 784db95818eSIan Rogers struct pmu_metric *pm); 785d3abd7b8SIan Rogers 78680be6434SIan Rogers static int add_metric(struct list_head *metric_list, 787db95818eSIan Rogers const struct pmu_metric *pm, 788b85a4d61SIan Rogers const char *modifier, 78980be6434SIan Rogers 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); 79680be6434SIan Rogers 79780be6434SIan Rogers /** 79880be6434SIan Rogers * resolve_metric - Locate metrics within the root metric and recursively add 79980be6434SIan Rogers * references to them. 80080be6434SIan Rogers * @metric_list: The list the metric is added to. 801b85a4d61SIan Rogers * @modifier: if non-null event modifiers like "u". 80280be6434SIan Rogers * @metric_no_group: Should events written to events be grouped "{}" or 80380be6434SIan Rogers * global. Grouping is the default but due to multiplexing the 80480be6434SIan Rogers * user may override. 8051725e9cdSIan Rogers * @user_requested_cpu_list: Command line specified CPUs to record on. 8061725e9cdSIan Rogers * @system_wide: Are events for all processes recorded. 80780be6434SIan Rogers * @root_metric: Metrics may reference other metrics to form a tree. In this 80880be6434SIan Rogers * case the root_metric holds all the IDs and a list of referenced 80980be6434SIan Rogers * metrics. When adding a root this argument is NULL. 81080be6434SIan Rogers * @visited: A singly linked list of metric names being added that is used to 81180be6434SIan Rogers * detect recursion. 812eeac7730SIan Rogers * @table: The table that is searched for metrics, most commonly the table for the 81380be6434SIan Rogers * architecture perf is running upon. 81480be6434SIan Rogers */ 81580be6434SIan Rogers static int resolve_metric(struct list_head *metric_list, 816b85a4d61SIan Rogers const char *modifier, 81780be6434SIan Rogers bool metric_no_group, 8181fd09e29SIan Rogers bool metric_no_threshold, 8191725e9cdSIan Rogers const char *user_requested_cpu_list, 8201725e9cdSIan Rogers bool system_wide, 82180be6434SIan Rogers struct metric *root_metric, 82280be6434SIan Rogers const struct visited_metric *visited, 823f8ea2c15SIan Rogers const struct pmu_metrics_table *table) 82480be6434SIan Rogers { 82580be6434SIan Rogers struct hashmap_entry *cur; 82680be6434SIan Rogers size_t bkt; 82780be6434SIan Rogers struct to_resolve { 82880be6434SIan Rogers /* The metric to resolve. */ 829db95818eSIan Rogers struct pmu_metric pm; 83080be6434SIan Rogers /* 83180be6434SIan Rogers * The key in the IDs map, this may differ from in case, 832db95818eSIan Rogers * etc. from pm->metric_name. 83380be6434SIan Rogers */ 83480be6434SIan Rogers const char *key; 83580be6434SIan Rogers } *pending = NULL; 83680be6434SIan Rogers int i, ret = 0, pending_cnt = 0; 83780be6434SIan Rogers 83880be6434SIan Rogers /* 83980be6434SIan Rogers * Iterate all the parsed IDs and if there's a matching metric and it to 84080be6434SIan Rogers * the pending array. 84180be6434SIan Rogers */ 84280be6434SIan Rogers hashmap__for_each_entry(root_metric->pctx->ids, cur, bkt) { 843db95818eSIan Rogers struct pmu_metric pm; 84480be6434SIan Rogers 845db95818eSIan Rogers if (metricgroup__find_metric(cur->pkey, table, &pm)) { 84680be6434SIan Rogers pending = realloc(pending, 84780be6434SIan Rogers (pending_cnt + 1) * sizeof(struct to_resolve)); 84880be6434SIan Rogers if (!pending) 84980be6434SIan Rogers return -ENOMEM; 85080be6434SIan Rogers 851db95818eSIan Rogers memcpy(&pending[pending_cnt].pm, &pm, sizeof(pm)); 852c302378bSEduard Zingerman pending[pending_cnt].key = cur->pkey; 85380be6434SIan Rogers pending_cnt++; 85480be6434SIan Rogers } 85580be6434SIan Rogers } 85680be6434SIan Rogers 85780be6434SIan Rogers /* Remove the metric IDs from the context. */ 85880be6434SIan Rogers for (i = 0; i < pending_cnt; i++) 85980be6434SIan Rogers expr__del_id(root_metric->pctx, pending[i].key); 86080be6434SIan Rogers 86180be6434SIan Rogers /* 86280be6434SIan Rogers * Recursively add all the metrics, IDs are added to the root metric's 86380be6434SIan Rogers * context. 86480be6434SIan Rogers */ 86580be6434SIan Rogers for (i = 0; i < pending_cnt; i++) { 866db95818eSIan Rogers ret = add_metric(metric_list, &pending[i].pm, modifier, metric_no_group, 8671fd09e29SIan Rogers metric_no_threshold, user_requested_cpu_list, system_wide, 8681fd09e29SIan Rogers root_metric, visited, table); 86980be6434SIan Rogers if (ret) 87080be6434SIan Rogers break; 87180be6434SIan Rogers } 87280be6434SIan Rogers 87380be6434SIan Rogers free(pending); 87480be6434SIan Rogers return ret; 87580be6434SIan Rogers } 87680be6434SIan Rogers 87768074811SIan Rogers /** 87868074811SIan Rogers * __add_metric - Add a metric to metric_list. 87968074811SIan Rogers * @metric_list: The list the metric is added to. 880db95818eSIan Rogers * @pm: The pmu_metric containing the metric to be added. 881b85a4d61SIan Rogers * @modifier: if non-null event modifiers like "u". 88268074811SIan Rogers * @metric_no_group: Should events written to events be grouped "{}" or 88368074811SIan Rogers * global. Grouping is the default but due to multiplexing the 88468074811SIan Rogers * user may override. 8851fd09e29SIan Rogers * @metric_no_threshold: Should threshold expressions be ignored? 88668074811SIan Rogers * @runtime: A special argument for the parser only known at runtime. 8871725e9cdSIan Rogers * @user_requested_cpu_list: Command line specified CPUs to record on. 8881725e9cdSIan Rogers * @system_wide: Are events for all processes recorded. 88980be6434SIan Rogers * @root_metric: Metrics may reference other metrics to form a tree. In this 89080be6434SIan Rogers * case the root_metric holds all the IDs and a list of referenced 89180be6434SIan Rogers * metrics. When adding a root this argument is NULL. 89280be6434SIan Rogers * @visited: A singly linked list of metric names being added that is used to 89380be6434SIan Rogers * detect recursion. 894eeac7730SIan Rogers * @table: The table that is searched for metrics, most commonly the table for the 89580be6434SIan Rogers * architecture perf is running upon. 89668074811SIan Rogers */ 897119e521aSJiri Olsa static int __add_metric(struct list_head *metric_list, 898db95818eSIan Rogers const struct pmu_metric *pm, 899b85a4d61SIan Rogers const char *modifier, 90005530a79SIan Rogers bool metric_no_group, 9011fd09e29SIan Rogers bool metric_no_threshold, 90283de0b7dSJiri Olsa int runtime, 9031725e9cdSIan Rogers const char *user_requested_cpu_list, 9041725e9cdSIan Rogers bool system_wide, 90580be6434SIan Rogers struct metric *root_metric, 90680be6434SIan Rogers const struct visited_metric *visited, 907f8ea2c15SIan Rogers const struct pmu_metrics_table *table) 90847352abaSKajol Jain { 90980be6434SIan Rogers const struct visited_metric *vm; 91080be6434SIan Rogers int ret; 91180be6434SIan Rogers bool is_root = !root_metric; 912d0a3052fSIan Rogers const char *expr; 91380be6434SIan Rogers struct visited_metric visited_node = { 914db95818eSIan Rogers .name = pm->metric_name, 91580be6434SIan Rogers .parent = visited, 91680be6434SIan Rogers }; 91747352abaSKajol Jain 91880be6434SIan Rogers for (vm = visited; vm; vm = vm->parent) { 919db95818eSIan Rogers if (!strcmp(pm->metric_name, vm->name)) { 920db95818eSIan Rogers pr_err("failed: recursion detected for %s\n", pm->metric_name); 92180be6434SIan Rogers return -1; 92280be6434SIan Rogers } 92380be6434SIan Rogers } 92480be6434SIan Rogers 92580be6434SIan Rogers if (is_root) { 92683de0b7dSJiri Olsa /* 92780be6434SIan Rogers * This metric is the root of a tree and may reference other 92880be6434SIan Rogers * metrics that are added recursively. 92983de0b7dSJiri Olsa */ 930db95818eSIan Rogers root_metric = metric__new(pm, modifier, metric_no_group, runtime, 9311725e9cdSIan Rogers user_requested_cpu_list, system_wide); 93280be6434SIan Rogers if (!root_metric) 93347352abaSKajol Jain return -ENOMEM; 93447352abaSKajol Jain 93583de0b7dSJiri Olsa } else { 93646bdc0bfSIan Rogers int cnt = 0; 93746bdc0bfSIan Rogers 93883de0b7dSJiri Olsa /* 939a3de7690SIan Rogers * This metric was referenced in a metric higher in the 940a3de7690SIan Rogers * tree. Check if the same metric is already resolved in the 941a3de7690SIan Rogers * metric_refs list. 94283de0b7dSJiri Olsa */ 94346bdc0bfSIan Rogers if (root_metric->metric_refs) { 94446bdc0bfSIan Rogers for (; root_metric->metric_refs[cnt].metric_name; cnt++) { 945db95818eSIan Rogers if (!strcmp(pm->metric_name, 94646bdc0bfSIan Rogers root_metric->metric_refs[cnt].metric_name)) 947a3de7690SIan Rogers return 0; 948a3de7690SIan Rogers } 94946bdc0bfSIan Rogers } 950a3de7690SIan Rogers 95146bdc0bfSIan Rogers /* Create reference. Need space for the entry and the terminator. */ 95246bdc0bfSIan Rogers root_metric->metric_refs = realloc(root_metric->metric_refs, 95346bdc0bfSIan Rogers (cnt + 2) * sizeof(struct metric_ref)); 95446bdc0bfSIan Rogers if (!root_metric->metric_refs) 95583de0b7dSJiri Olsa return -ENOMEM; 95683de0b7dSJiri Olsa 95783de0b7dSJiri Olsa /* 95883de0b7dSJiri Olsa * Intentionally passing just const char pointers, 95983de0b7dSJiri Olsa * from 'pe' object, so they never go away. We don't 96083de0b7dSJiri Olsa * need to change them, so there's no need to create 96183de0b7dSJiri Olsa * our own copy. 96283de0b7dSJiri Olsa */ 963db95818eSIan Rogers root_metric->metric_refs[cnt].metric_name = pm->metric_name; 964db95818eSIan Rogers root_metric->metric_refs[cnt].metric_expr = pm->metric_expr; 96583de0b7dSJiri Olsa 96646bdc0bfSIan Rogers /* Null terminate array. */ 96746bdc0bfSIan Rogers root_metric->metric_refs[cnt+1].metric_name = NULL; 96846bdc0bfSIan Rogers root_metric->metric_refs[cnt+1].metric_expr = NULL; 96983de0b7dSJiri Olsa } 97083de0b7dSJiri Olsa 97183de0b7dSJiri Olsa /* 97283de0b7dSJiri Olsa * For both the parent and referenced metrics, we parse 97380be6434SIan Rogers * all the metric's IDs and add it to the root context. 97483de0b7dSJiri Olsa */ 975d0a3052fSIan Rogers ret = 0; 976d0a3052fSIan Rogers expr = pm->metric_expr; 977d0a3052fSIan Rogers if (is_root && pm->metric_threshold) { 978d0a3052fSIan Rogers /* 979d0a3052fSIan Rogers * Threshold expressions are built off the actual metric. Switch 980d0a3052fSIan Rogers * to use that in case of additional necessary events. Change 981d0a3052fSIan Rogers * the visited node name to avoid this being flagged as 9821fd09e29SIan Rogers * recursion. If the threshold events are disabled, just use the 9831fd09e29SIan Rogers * metric's name as a reference. This allows metric threshold 9841fd09e29SIan Rogers * computation if there are sufficient events. 985d0a3052fSIan Rogers */ 986d0a3052fSIan Rogers assert(strstr(pm->metric_threshold, pm->metric_name)); 9871fd09e29SIan Rogers expr = metric_no_threshold ? pm->metric_name : pm->metric_threshold; 988d0a3052fSIan Rogers visited_node.name = "__threshold__"; 989d0a3052fSIan Rogers } 990d0a3052fSIan Rogers if (expr__find_ids(expr, NULL, root_metric->pctx) < 0) { 99180be6434SIan Rogers /* Broken metric. */ 99280be6434SIan Rogers ret = -EINVAL; 993d0a3052fSIan Rogers } 994d0a3052fSIan Rogers if (!ret) { 99580be6434SIan Rogers /* Resolve referenced metrics. */ 9961725e9cdSIan Rogers ret = resolve_metric(metric_list, modifier, metric_no_group, 9971fd09e29SIan Rogers metric_no_threshold, user_requested_cpu_list, 9981fd09e29SIan Rogers system_wide, root_metric, &visited_node, table); 999ded80bdaSIan Rogers } 100080be6434SIan Rogers if (ret) { 100180be6434SIan Rogers if (is_root) 100280be6434SIan Rogers metric__free(root_metric); 100383de0b7dSJiri Olsa 100480be6434SIan Rogers } else if (is_root) 100580be6434SIan Rogers list_add(&root_metric->nd, metric_list); 10066bf2102bSIan Rogers 100780be6434SIan Rogers return ret; 100847352abaSKajol Jain } 100947352abaSKajol Jain 1010660842e4SIan Rogers struct metricgroup__find_metric_data { 1011660842e4SIan Rogers const char *metric; 1012db95818eSIan Rogers struct pmu_metric *pm; 1013660842e4SIan Rogers }; 1014ce391940SJiri Olsa 1015db95818eSIan Rogers static int metricgroup__find_metric_callback(const struct pmu_metric *pm, 1016f8ea2c15SIan Rogers const struct pmu_metrics_table *table __maybe_unused, 1017660842e4SIan Rogers void *vdata) 1018660842e4SIan Rogers { 1019660842e4SIan Rogers struct metricgroup__find_metric_data *data = vdata; 1020660842e4SIan Rogers 1021db95818eSIan Rogers if (!match_metric(pm->metric_name, data->metric)) 1022660842e4SIan Rogers return 0; 1023660842e4SIan Rogers 1024db95818eSIan Rogers memcpy(data->pm, pm, sizeof(*pm)); 1025d3abd7b8SIan Rogers return 1; 1026660842e4SIan Rogers } 1027ce391940SJiri Olsa 1028d3abd7b8SIan Rogers static bool metricgroup__find_metric(const char *metric, 1029f8ea2c15SIan Rogers const struct pmu_metrics_table *table, 1030db95818eSIan Rogers struct pmu_metric *pm) 103183de0b7dSJiri Olsa { 1032660842e4SIan Rogers struct metricgroup__find_metric_data data = { 1033660842e4SIan Rogers .metric = metric, 1034db95818eSIan Rogers .pm = pm, 1035660842e4SIan Rogers }; 103683de0b7dSJiri Olsa 1037f8ea2c15SIan Rogers return pmu_metrics_table_for_each_metric(table, metricgroup__find_metric_callback, &data) 1038d3abd7b8SIan Rogers ? true : false; 103983de0b7dSJiri Olsa } 104083de0b7dSJiri Olsa 1041119e521aSJiri Olsa static int add_metric(struct list_head *metric_list, 1042db95818eSIan Rogers const struct pmu_metric *pm, 1043b85a4d61SIan Rogers const char *modifier, 104483de0b7dSJiri Olsa bool metric_no_group, 10451fd09e29SIan Rogers bool metric_no_threshold, 10461725e9cdSIan Rogers const char *user_requested_cpu_list, 10471725e9cdSIan Rogers bool system_wide, 104880be6434SIan Rogers struct metric *root_metric, 104980be6434SIan Rogers const struct visited_metric *visited, 1050f8ea2c15SIan Rogers const struct pmu_metrics_table *table) 1051a29c164aSJiri Olsa { 1052a29c164aSJiri Olsa int ret = 0; 1053a29c164aSJiri Olsa 1054db95818eSIan Rogers pr_debug("metric expr %s for %s\n", pm->metric_expr, pm->metric_name); 1055a29c164aSJiri Olsa 1056db95818eSIan Rogers if (!strstr(pm->metric_expr, "?")) { 10571fd09e29SIan Rogers ret = __add_metric(metric_list, pm, modifier, metric_no_group, 10581fd09e29SIan Rogers metric_no_threshold, 0, user_requested_cpu_list, 10591fd09e29SIan Rogers system_wide, root_metric, visited, table); 1060a29c164aSJiri Olsa } else { 1061a29c164aSJiri Olsa int j, count; 1062a29c164aSJiri Olsa 1063db95818eSIan Rogers count = arch_get_runtimeparam(pm); 1064a29c164aSJiri Olsa 1065a29c164aSJiri Olsa /* This loop is added to create multiple 1066a29c164aSJiri Olsa * events depend on count value and add 1067119e521aSJiri Olsa * those events to metric_list. 1068a29c164aSJiri Olsa */ 1069a29c164aSJiri Olsa 107080be6434SIan Rogers for (j = 0; j < count && !ret; j++) 10711fd09e29SIan Rogers ret = __add_metric(metric_list, pm, modifier, metric_no_group, 10721fd09e29SIan Rogers metric_no_threshold, j, user_requested_cpu_list, 10731fd09e29SIan Rogers system_wide, root_metric, visited, table); 1074a29c164aSJiri Olsa } 1075a29c164aSJiri Olsa 1076a29c164aSJiri Olsa return ret; 1077a29c164aSJiri Olsa } 1078a29c164aSJiri Olsa 1079db95818eSIan Rogers static int metricgroup__add_metric_sys_event_iter(const struct pmu_metric *pm, 1080f8ea2c15SIan Rogers const struct pmu_metrics_table *table __maybe_unused, 1081be335ec2SJohn Garry void *data) 1082be335ec2SJohn Garry { 1083be335ec2SJohn Garry struct metricgroup_add_iter_data *d = data; 1084be335ec2SJohn Garry int ret; 1085be335ec2SJohn Garry 1086db95818eSIan Rogers if (!match_pm_metric(pm, d->metric_name)) 1087be335ec2SJohn Garry return 0; 1088be335ec2SJohn Garry 1089db95818eSIan Rogers ret = add_metric(d->metric_list, pm, d->modifier, d->metric_no_group, 10901fd09e29SIan Rogers d->metric_no_threshold, d->user_requested_cpu_list, 10911fd09e29SIan Rogers d->system_wide, d->root_metric, d->visited, d->table); 1092be335ec2SJohn Garry if (ret) 1093fe7a98b9SJohn Garry goto out; 1094be335ec2SJohn Garry 1095be335ec2SJohn Garry *(d->has_match) = true; 1096be335ec2SJohn Garry 1097fe7a98b9SJohn Garry out: 1098fe7a98b9SJohn Garry *(d->ret) = ret; 1099fe7a98b9SJohn Garry return ret; 1100be335ec2SJohn Garry } 1101be335ec2SJohn Garry 11026b6b16b3SIan Rogers /** 11036b6b16b3SIan Rogers * metric_list_cmp - list_sort comparator that sorts metrics with more events to 11049aa09230SIan Rogers * the front. tool events are excluded from the count. 11056b6b16b3SIan Rogers */ 110680be6434SIan Rogers static int metric_list_cmp(void *priv __maybe_unused, const struct list_head *l, 110780be6434SIan Rogers const struct list_head *r) 110880be6434SIan Rogers { 110980be6434SIan Rogers const struct metric *left = container_of(l, struct metric, nd); 111080be6434SIan Rogers const struct metric *right = container_of(r, struct metric, nd); 11116b6b16b3SIan Rogers struct expr_id_data *data; 11129aa09230SIan Rogers int i, left_count, right_count; 111380be6434SIan Rogers 11146b6b16b3SIan Rogers left_count = hashmap__size(left->pctx->ids); 11159aa09230SIan Rogers perf_tool_event__for_each_event(i) { 11169aa09230SIan Rogers if (!expr__get_id(left->pctx, perf_tool_event__to_str(i), &data)) 11176b6b16b3SIan Rogers left_count--; 11189aa09230SIan Rogers } 11196b6b16b3SIan Rogers 11206b6b16b3SIan Rogers right_count = hashmap__size(right->pctx->ids); 11219aa09230SIan Rogers perf_tool_event__for_each_event(i) { 11229aa09230SIan Rogers if (!expr__get_id(right->pctx, perf_tool_event__to_str(i), &data)) 11236b6b16b3SIan Rogers right_count--; 11249aa09230SIan Rogers } 11256b6b16b3SIan Rogers 11266b6b16b3SIan Rogers return right_count - left_count; 112780be6434SIan Rogers } 112880be6434SIan Rogers 1129660842e4SIan Rogers struct metricgroup__add_metric_data { 1130660842e4SIan Rogers struct list_head *list; 1131660842e4SIan Rogers const char *metric_name; 1132660842e4SIan Rogers const char *modifier; 11331725e9cdSIan Rogers const char *user_requested_cpu_list; 1134660842e4SIan Rogers bool metric_no_group; 11351fd09e29SIan Rogers bool metric_no_threshold; 11361725e9cdSIan Rogers bool system_wide; 1137660842e4SIan Rogers bool has_match; 1138660842e4SIan Rogers }; 1139660842e4SIan Rogers 1140db95818eSIan Rogers static int metricgroup__add_metric_callback(const struct pmu_metric *pm, 1141f8ea2c15SIan Rogers const struct pmu_metrics_table *table, 1142660842e4SIan Rogers void *vdata) 1143660842e4SIan Rogers { 1144660842e4SIan Rogers struct metricgroup__add_metric_data *data = vdata; 1145660842e4SIan Rogers int ret = 0; 1146660842e4SIan Rogers 1147ccc66c60SIan Rogers if (pm->metric_expr && match_pm_metric(pm, data->metric_name)) { 1148ccc66c60SIan Rogers bool metric_no_group = data->metric_no_group || 1149ccc66c60SIan Rogers match_metric(data->metric_name, pm->metricgroup_no_group); 1150660842e4SIan Rogers 1151660842e4SIan Rogers data->has_match = true; 1152ccc66c60SIan Rogers ret = add_metric(data->list, pm, data->modifier, metric_no_group, 11531fd09e29SIan Rogers data->metric_no_threshold, data->user_requested_cpu_list, 11541fd09e29SIan Rogers data->system_wide, /*root_metric=*/NULL, 11551fd09e29SIan Rogers /*visited_metrics=*/NULL, table); 1156660842e4SIan Rogers } 1157660842e4SIan Rogers return ret; 1158660842e4SIan Rogers } 1159660842e4SIan Rogers 116068074811SIan Rogers /** 116168074811SIan Rogers * metricgroup__add_metric - Find and add a metric, or a metric group. 116268074811SIan Rogers * @metric_name: The name of the metric or metric group. For example, "IPC" 116368074811SIan Rogers * could be the name of a metric and "TopDownL1" the name of a 116468074811SIan Rogers * metric group. 1165b85a4d61SIan Rogers * @modifier: if non-null event modifiers like "u". 116668074811SIan Rogers * @metric_no_group: Should events written to events be grouped "{}" or 116768074811SIan Rogers * global. Grouping is the default but due to multiplexing the 116868074811SIan Rogers * user may override. 11691725e9cdSIan Rogers * @user_requested_cpu_list: Command line specified CPUs to record on. 11701725e9cdSIan Rogers * @system_wide: Are events for all processes recorded. 117168074811SIan Rogers * @metric_list: The list that the metric or metric group are added to. 1172eeac7730SIan Rogers * @table: The table that is searched for metrics, most commonly the table for the 117368074811SIan Rogers * architecture perf is running upon. 117468074811SIan Rogers */ 117517b3867dSIan Rogers static int metricgroup__add_metric(const char *metric_name, const char *modifier, 11761fd09e29SIan Rogers bool metric_no_group, bool metric_no_threshold, 11771725e9cdSIan Rogers const char *user_requested_cpu_list, 11781725e9cdSIan Rogers bool system_wide, 1179119e521aSJiri Olsa struct list_head *metric_list, 1180f8ea2c15SIan Rogers const struct pmu_metrics_table *table) 1181b18f3e36SAndi Kleen { 118298461d9dSJiri Olsa LIST_HEAD(list); 1183660842e4SIan Rogers int ret; 118490810399SIan Rogers bool has_match = false; 1185b18f3e36SAndi Kleen 1186660842e4SIan Rogers { 1187660842e4SIan Rogers struct metricgroup__add_metric_data data = { 1188660842e4SIan Rogers .list = &list, 1189660842e4SIan Rogers .metric_name = metric_name, 1190660842e4SIan Rogers .modifier = modifier, 1191660842e4SIan Rogers .metric_no_group = metric_no_group, 11921fd09e29SIan Rogers .metric_no_threshold = metric_no_threshold, 11931725e9cdSIan Rogers .user_requested_cpu_list = user_requested_cpu_list, 11941725e9cdSIan Rogers .system_wide = system_wide, 1195660842e4SIan Rogers .has_match = false, 1196660842e4SIan Rogers }; 119768074811SIan Rogers /* 1198660842e4SIan Rogers * Iterate over all metrics seeing if metric matches either the 1199660842e4SIan Rogers * name or group. When it does add the metric to the list. 120068074811SIan Rogers */ 1201f8ea2c15SIan Rogers ret = pmu_metrics_table_for_each_metric(table, metricgroup__add_metric_callback, 1202660842e4SIan Rogers &data); 120390810399SIan Rogers if (ret) 120427adafcdSNamhyung Kim goto out; 1205ce391940SJiri Olsa 1206660842e4SIan Rogers has_match = data.has_match; 1207660842e4SIan Rogers } 1208be335ec2SJohn Garry { 1209be335ec2SJohn Garry struct metricgroup_iter_data data = { 1210be335ec2SJohn Garry .fn = metricgroup__add_metric_sys_event_iter, 1211be335ec2SJohn Garry .data = (void *) &(struct metricgroup_add_iter_data) { 1212be335ec2SJohn Garry .metric_list = &list, 121368074811SIan Rogers .metric_name = metric_name, 1214b85a4d61SIan Rogers .modifier = modifier, 1215be335ec2SJohn Garry .metric_no_group = metric_no_group, 12161725e9cdSIan Rogers .user_requested_cpu_list = user_requested_cpu_list, 12171725e9cdSIan Rogers .system_wide = system_wide, 1218be335ec2SJohn Garry .has_match = &has_match, 1219be335ec2SJohn Garry .ret = &ret, 1220eeac7730SIan Rogers .table = table, 1221be335ec2SJohn Garry }, 1222be335ec2SJohn Garry }; 1223be335ec2SJohn Garry 1224db95818eSIan Rogers pmu_for_each_sys_metric(metricgroup__sys_event_iter, &data); 1225be335ec2SJohn Garry } 1226ce391940SJiri Olsa /* End of pmu events. */ 12275ecd5a0cSIan Rogers if (!has_match) 122827adafcdSNamhyung Kim ret = -EINVAL; 122998461d9dSJiri Olsa 123027adafcdSNamhyung Kim out: 123127adafcdSNamhyung Kim /* 123227adafcdSNamhyung Kim * add to metric_list so that they can be released 123327adafcdSNamhyung Kim * even if it's failed 123427adafcdSNamhyung Kim */ 1235119e521aSJiri Olsa list_splice(&list, metric_list); 123627adafcdSNamhyung Kim return ret; 123790810399SIan Rogers } 1238b18f3e36SAndi Kleen 123968074811SIan Rogers /** 124068074811SIan Rogers * metricgroup__add_metric_list - Find and add metrics, or metric groups, 124168074811SIan Rogers * specified in a list. 124268074811SIan Rogers * @list: the list of metrics or metric groups. For example, "IPC,CPI,TopDownL1" 124368074811SIan Rogers * would match the IPC and CPI metrics, and TopDownL1 would match all 124468074811SIan Rogers * the metrics in the TopDownL1 group. 124568074811SIan Rogers * @metric_no_group: Should events written to events be grouped "{}" or 124668074811SIan Rogers * global. Grouping is the default but due to multiplexing the 124768074811SIan Rogers * user may override. 12481725e9cdSIan Rogers * @user_requested_cpu_list: Command line specified CPUs to record on. 12491725e9cdSIan Rogers * @system_wide: Are events for all processes recorded. 125068074811SIan Rogers * @metric_list: The list that metrics are added to. 1251eeac7730SIan Rogers * @table: The table that is searched for metrics, most commonly the table for the 125268074811SIan Rogers * architecture perf is running upon. 125368074811SIan Rogers */ 125405530a79SIan Rogers static int metricgroup__add_metric_list(const char *list, bool metric_no_group, 12551fd09e29SIan Rogers bool metric_no_threshold, 12561725e9cdSIan Rogers const char *user_requested_cpu_list, 12571725e9cdSIan Rogers bool system_wide, struct list_head *metric_list, 1258f8ea2c15SIan Rogers const struct pmu_metrics_table *table) 1259b18f3e36SAndi Kleen { 1260b85a4d61SIan Rogers char *list_itr, *list_copy, *metric_name, *modifier; 1261ec5c5b3dSIan Rogers int ret, count = 0; 1262b18f3e36SAndi Kleen 1263b85a4d61SIan Rogers list_copy = strdup(list); 1264b85a4d61SIan Rogers if (!list_copy) 1265b18f3e36SAndi Kleen return -ENOMEM; 1266b85a4d61SIan Rogers list_itr = list_copy; 1267411bc316SAndi Kleen 1268b85a4d61SIan Rogers while ((metric_name = strsep(&list_itr, ",")) != NULL) { 1269b85a4d61SIan Rogers modifier = strchr(metric_name, ':'); 1270b85a4d61SIan Rogers if (modifier) 1271b85a4d61SIan Rogers *modifier++ = '\0'; 1272b85a4d61SIan Rogers 1273b85a4d61SIan Rogers ret = metricgroup__add_metric(metric_name, modifier, 12741fd09e29SIan Rogers metric_no_group, metric_no_threshold, 12751fd09e29SIan Rogers user_requested_cpu_list, 12761725e9cdSIan Rogers system_wide, metric_list, table); 1277ec5c5b3dSIan Rogers if (ret == -EINVAL) 1278b85a4d61SIan Rogers pr_err("Cannot find metric or group `%s'\n", metric_name); 1279ec5c5b3dSIan Rogers 1280ec5c5b3dSIan Rogers if (ret) 1281b18f3e36SAndi Kleen break; 1282ec5c5b3dSIan Rogers 1283ec5c5b3dSIan Rogers count++; 1284b18f3e36SAndi Kleen } 1285b85a4d61SIan Rogers free(list_copy); 1286ab483d8bSKan Liang 1287ec5c5b3dSIan Rogers if (!ret) { 1288ec5c5b3dSIan Rogers /* 1289ec5c5b3dSIan Rogers * Warn about nmi_watchdog if any parsed metrics had the 1290ec5c5b3dSIan Rogers * NO_NMI_WATCHDOG constraint. 1291ec5c5b3dSIan Rogers */ 1292180a5013SIan Rogers metric__watchdog_constraint_hint(NULL, /*foot=*/true); 1293ec5c5b3dSIan Rogers /* No metrics. */ 1294ec5c5b3dSIan Rogers if (count == 0) 1295ec5c5b3dSIan Rogers return -EINVAL; 1296ec5c5b3dSIan Rogers } 1297b18f3e36SAndi Kleen return ret; 1298b18f3e36SAndi Kleen } 1299b18f3e36SAndi Kleen 1300119e521aSJiri Olsa static void metricgroup__free_metrics(struct list_head *metric_list) 1301b18f3e36SAndi Kleen { 1302a0c05b36SJiri Olsa struct metric *m, *tmp; 1303b18f3e36SAndi Kleen 1304119e521aSJiri Olsa list_for_each_entry_safe (m, tmp, metric_list, nd) { 1305a0c05b36SJiri Olsa list_del_init(&m->nd); 13063d81d761SIan Rogers metric__free(m); 1307b18f3e36SAndi Kleen } 1308b18f3e36SAndi Kleen } 1309b18f3e36SAndi Kleen 13105ecd5a0cSIan Rogers /** 13118586d274SIan Rogers * find_tool_events - Search for the pressence of tool events in metric_list. 13128586d274SIan Rogers * @metric_list: List to take metrics from. 13138586d274SIan Rogers * @tool_events: Array of false values, indices corresponding to tool events set 13148586d274SIan Rogers * to true if tool event is found. 13158586d274SIan Rogers */ 13168586d274SIan Rogers static void find_tool_events(const struct list_head *metric_list, 13178586d274SIan Rogers bool tool_events[PERF_TOOL_MAX]) 13188586d274SIan Rogers { 13198586d274SIan Rogers struct metric *m; 13208586d274SIan Rogers 13218586d274SIan Rogers list_for_each_entry(m, metric_list, nd) { 13228586d274SIan Rogers int i; 13238586d274SIan Rogers 13248586d274SIan Rogers perf_tool_event__for_each_event(i) { 13258586d274SIan Rogers struct expr_id_data *data; 13268586d274SIan Rogers 13278586d274SIan Rogers if (!tool_events[i] && 13288586d274SIan Rogers !expr__get_id(m->pctx, perf_tool_event__to_str(i), &data)) 13298586d274SIan Rogers tool_events[i] = true; 13308586d274SIan Rogers } 13318586d274SIan Rogers } 13328586d274SIan Rogers } 13338586d274SIan Rogers 13348586d274SIan Rogers /** 1335180a5013SIan Rogers * build_combined_expr_ctx - Make an expr_parse_ctx with all !group_events 13365ecd5a0cSIan Rogers * metric IDs, as the IDs are held in a set, 13375ecd5a0cSIan Rogers * duplicates will be removed. 13385ecd5a0cSIan Rogers * @metric_list: List to take metrics from. 13395ecd5a0cSIan Rogers * @combined: Out argument for result. 13405ecd5a0cSIan Rogers */ 13415ecd5a0cSIan Rogers static int build_combined_expr_ctx(const struct list_head *metric_list, 13425ecd5a0cSIan Rogers struct expr_parse_ctx **combined) 13435ecd5a0cSIan Rogers { 13445ecd5a0cSIan Rogers struct hashmap_entry *cur; 13455ecd5a0cSIan Rogers size_t bkt; 13465ecd5a0cSIan Rogers struct metric *m; 13475ecd5a0cSIan Rogers char *dup; 13485ecd5a0cSIan Rogers int ret; 13495ecd5a0cSIan Rogers 13505ecd5a0cSIan Rogers *combined = expr__ctx_new(); 13515ecd5a0cSIan Rogers if (!*combined) 13525ecd5a0cSIan Rogers return -ENOMEM; 13535ecd5a0cSIan Rogers 13545ecd5a0cSIan Rogers list_for_each_entry(m, metric_list, nd) { 1355180a5013SIan Rogers if (!m->group_events && !m->modifier) { 13565ecd5a0cSIan Rogers hashmap__for_each_entry(m->pctx->ids, cur, bkt) { 1357c302378bSEduard Zingerman dup = strdup(cur->pkey); 13585ecd5a0cSIan Rogers if (!dup) { 13595ecd5a0cSIan Rogers ret = -ENOMEM; 13605ecd5a0cSIan Rogers goto err_out; 13615ecd5a0cSIan Rogers } 13625ecd5a0cSIan Rogers ret = expr__add_id(*combined, dup); 13635ecd5a0cSIan Rogers if (ret) 13645ecd5a0cSIan Rogers goto err_out; 13655ecd5a0cSIan Rogers } 13665ecd5a0cSIan Rogers } 13675ecd5a0cSIan Rogers } 13685ecd5a0cSIan Rogers return 0; 13695ecd5a0cSIan Rogers err_out: 13705ecd5a0cSIan Rogers expr__ctx_free(*combined); 13715ecd5a0cSIan Rogers *combined = NULL; 13725ecd5a0cSIan Rogers return ret; 13735ecd5a0cSIan Rogers } 13745ecd5a0cSIan Rogers 13755ecd5a0cSIan Rogers /** 13765ecd5a0cSIan Rogers * parse_ids - Build the event string for the ids and parse them creating an 13775ecd5a0cSIan Rogers * evlist. The encoded metric_ids are decoded. 13786b6b16b3SIan Rogers * @metric_no_merge: is metric sharing explicitly disabled. 13795ecd5a0cSIan Rogers * @fake_pmu: used when testing metrics not supported by the current CPU. 13805ecd5a0cSIan Rogers * @ids: the event identifiers parsed from a metric. 1381b85a4d61SIan Rogers * @modifier: any modifiers added to the events. 1382180a5013SIan Rogers * @group_events: should events be placed in a weak group. 13838586d274SIan Rogers * @tool_events: entries set true if the tool event of index could be present in 13848586d274SIan Rogers * the overall list of metrics. 13855ecd5a0cSIan Rogers * @out_evlist: the created list of events. 13865ecd5a0cSIan Rogers */ 13876b6b16b3SIan Rogers static int parse_ids(bool metric_no_merge, struct perf_pmu *fake_pmu, 13886b6b16b3SIan Rogers struct expr_parse_ctx *ids, const char *modifier, 1389180a5013SIan Rogers bool group_events, const bool tool_events[PERF_TOOL_MAX], 13908586d274SIan Rogers struct evlist **out_evlist) 13915ecd5a0cSIan Rogers { 13925ecd5a0cSIan Rogers struct parse_events_error parse_error; 13935ecd5a0cSIan Rogers struct evlist *parsed_evlist; 13945ecd5a0cSIan Rogers struct strbuf events = STRBUF_INIT; 13955ecd5a0cSIan Rogers int ret; 13965ecd5a0cSIan Rogers 13975ecd5a0cSIan Rogers *out_evlist = NULL; 13986b6b16b3SIan Rogers if (!metric_no_merge || hashmap__size(ids->ids) == 0) { 1399c788ef61SIan Rogers bool added_event = false; 14009aa09230SIan Rogers int i; 14015ecd5a0cSIan Rogers /* 14029aa09230SIan Rogers * We may fail to share events between metrics because a tool 14039aa09230SIan Rogers * event isn't present in one metric. For example, a ratio of 14049aa09230SIan Rogers * cache misses doesn't need duration_time but the same events 14059aa09230SIan Rogers * may be used for a misses per second. Events without sharing 14069aa09230SIan Rogers * implies multiplexing, that is best avoided, so place 14079aa09230SIan Rogers * all tool events in every group. 14086b6b16b3SIan Rogers * 14096b6b16b3SIan Rogers * Also, there may be no ids/events in the expression parsing 14106b6b16b3SIan Rogers * context because of constant evaluation, e.g.: 14115ecd5a0cSIan Rogers * event1 if #smt_on else 0 14129aa09230SIan Rogers * Add a tool event to avoid a parse error on an empty string. 14135ecd5a0cSIan Rogers */ 14149aa09230SIan Rogers perf_tool_event__for_each_event(i) { 14158586d274SIan Rogers if (tool_events[i]) { 14169aa09230SIan Rogers char *tmp = strdup(perf_tool_event__to_str(i)); 14179aa09230SIan Rogers 14185ecd5a0cSIan Rogers if (!tmp) 14195ecd5a0cSIan Rogers return -ENOMEM; 14205ecd5a0cSIan Rogers ids__insert(ids->ids, tmp); 1421c788ef61SIan Rogers added_event = true; 14225ecd5a0cSIan Rogers } 14239aa09230SIan Rogers } 1424c788ef61SIan Rogers if (!added_event && hashmap__size(ids->ids) == 0) { 1425c788ef61SIan Rogers char *tmp = strdup("duration_time"); 1426c788ef61SIan Rogers 1427c788ef61SIan Rogers if (!tmp) 1428c788ef61SIan Rogers return -ENOMEM; 1429c788ef61SIan Rogers ids__insert(ids->ids, tmp); 1430c788ef61SIan Rogers } 14318586d274SIan Rogers } 1432b85a4d61SIan Rogers ret = metricgroup__build_event_string(&events, ids, modifier, 1433180a5013SIan Rogers group_events); 14345ecd5a0cSIan Rogers if (ret) 14355ecd5a0cSIan Rogers return ret; 14365ecd5a0cSIan Rogers 14375ecd5a0cSIan Rogers parsed_evlist = evlist__new(); 14385ecd5a0cSIan Rogers if (!parsed_evlist) { 14395ecd5a0cSIan Rogers ret = -ENOMEM; 14405ecd5a0cSIan Rogers goto err_out; 14415ecd5a0cSIan Rogers } 14425ecd5a0cSIan Rogers pr_debug("Parsing metric events '%s'\n", events.buf); 144307eafd4eSIan Rogers parse_events_error__init(&parse_error); 1444*411ad22eSIan Rogers ret = __parse_events(parsed_evlist, events.buf, /*pmu_filter=*/NULL, 1445*411ad22eSIan Rogers &parse_error, fake_pmu, /*warn_if_reordered=*/false); 14465ecd5a0cSIan Rogers if (ret) { 14476c191289SIan Rogers parse_events_error__print(&parse_error, events.buf); 14485ecd5a0cSIan Rogers goto err_out; 14495ecd5a0cSIan Rogers } 1450b85a4d61SIan Rogers ret = decode_all_metric_ids(parsed_evlist, modifier); 14515ecd5a0cSIan Rogers if (ret) 14525ecd5a0cSIan Rogers goto err_out; 14535ecd5a0cSIan Rogers 14545ecd5a0cSIan Rogers *out_evlist = parsed_evlist; 14555ecd5a0cSIan Rogers parsed_evlist = NULL; 14565ecd5a0cSIan Rogers err_out: 145707eafd4eSIan Rogers parse_events_error__exit(&parse_error); 14585ecd5a0cSIan Rogers evlist__delete(parsed_evlist); 14595ecd5a0cSIan Rogers strbuf_release(&events); 14605ecd5a0cSIan Rogers return ret; 14615ecd5a0cSIan Rogers } 14625ecd5a0cSIan Rogers 14638b4468a2SJiri Olsa static int parse_groups(struct evlist *perf_evlist, const char *str, 146405530a79SIan Rogers bool metric_no_group, 146505530a79SIan Rogers bool metric_no_merge, 14661fd09e29SIan Rogers bool metric_no_threshold, 14671725e9cdSIan Rogers const char *user_requested_cpu_list, 14681725e9cdSIan Rogers bool system_wide, 146968173bdaSJiri Olsa struct perf_pmu *fake_pmu, 14705ecd5a0cSIan Rogers struct rblist *metric_events_list, 1471f8ea2c15SIan Rogers const struct pmu_metrics_table *table) 1472b18f3e36SAndi Kleen { 14735ecd5a0cSIan Rogers struct evlist *combined_evlist = NULL; 1474119e521aSJiri Olsa LIST_HEAD(metric_list); 14755ecd5a0cSIan Rogers struct metric *m; 14768586d274SIan Rogers bool tool_events[PERF_TOOL_MAX] = {false}; 1477b18f3e36SAndi Kleen int ret; 1478b18f3e36SAndi Kleen 14795ecd5a0cSIan Rogers if (metric_events_list->nr_entries == 0) 14805ecd5a0cSIan Rogers metricgroup__rblist_init(metric_events_list); 14811fd09e29SIan Rogers ret = metricgroup__add_metric_list(str, metric_no_group, metric_no_threshold, 14821725e9cdSIan Rogers user_requested_cpu_list, 14831725e9cdSIan Rogers system_wide, &metric_list, table); 1484ec5c5b3dSIan Rogers if (ret) 1485ec5c5b3dSIan Rogers goto out; 1486ec5c5b3dSIan Rogers 14875ecd5a0cSIan Rogers /* Sort metrics from largest to smallest. */ 14885ecd5a0cSIan Rogers list_sort(NULL, &metric_list, metric_list_cmp); 14895ecd5a0cSIan Rogers 14905ecd5a0cSIan Rogers if (!metric_no_merge) { 14915ecd5a0cSIan Rogers struct expr_parse_ctx *combined = NULL; 14925ecd5a0cSIan Rogers 14938586d274SIan Rogers find_tool_events(&metric_list, tool_events); 14948586d274SIan Rogers 14955ecd5a0cSIan Rogers ret = build_combined_expr_ctx(&metric_list, &combined); 14965ecd5a0cSIan Rogers 14975ecd5a0cSIan Rogers if (!ret && combined && hashmap__size(combined->ids)) { 14986b6b16b3SIan Rogers ret = parse_ids(metric_no_merge, fake_pmu, combined, 14996b6b16b3SIan Rogers /*modifier=*/NULL, 1500180a5013SIan Rogers /*group_events=*/false, 15018586d274SIan Rogers tool_events, 150217b3867dSIan Rogers &combined_evlist); 15035ecd5a0cSIan Rogers } 15045ecd5a0cSIan Rogers if (combined) 15055ecd5a0cSIan Rogers expr__ctx_free(combined); 15065ecd5a0cSIan Rogers 15075ecd5a0cSIan Rogers if (ret) 15085ecd5a0cSIan Rogers goto out; 15095ecd5a0cSIan Rogers } 15105ecd5a0cSIan Rogers 15115ecd5a0cSIan Rogers list_for_each_entry(m, &metric_list, nd) { 15125ecd5a0cSIan Rogers struct metric_event *me; 15135ecd5a0cSIan Rogers struct evsel **metric_events; 15145ecd5a0cSIan Rogers struct evlist *metric_evlist = NULL; 15155ecd5a0cSIan Rogers struct metric *n; 15165ecd5a0cSIan Rogers struct metric_expr *expr; 15175ecd5a0cSIan Rogers 1518180a5013SIan Rogers if (combined_evlist && !m->group_events) { 15195ecd5a0cSIan Rogers metric_evlist = combined_evlist; 15205ecd5a0cSIan Rogers } else if (!metric_no_merge) { 15215ecd5a0cSIan Rogers /* 15225ecd5a0cSIan Rogers * See if the IDs for this metric are a subset of an 15235ecd5a0cSIan Rogers * earlier metric. 15245ecd5a0cSIan Rogers */ 15255ecd5a0cSIan Rogers list_for_each_entry(n, &metric_list, nd) { 15265ecd5a0cSIan Rogers if (m == n) 15275ecd5a0cSIan Rogers break; 15285ecd5a0cSIan Rogers 15295ecd5a0cSIan Rogers if (n->evlist == NULL) 15305ecd5a0cSIan Rogers continue; 15315ecd5a0cSIan Rogers 1532b85a4d61SIan Rogers if ((!m->modifier && n->modifier) || 1533b85a4d61SIan Rogers (m->modifier && !n->modifier) || 1534b85a4d61SIan Rogers (m->modifier && n->modifier && 1535b85a4d61SIan Rogers strcmp(m->modifier, n->modifier))) 1536b85a4d61SIan Rogers continue; 1537b85a4d61SIan Rogers 15385ecd5a0cSIan Rogers if (expr__subset_of_ids(n->pctx, m->pctx)) { 15395ecd5a0cSIan Rogers pr_debug("Events in '%s' fully contained within '%s'\n", 15405ecd5a0cSIan Rogers m->metric_name, n->metric_name); 15415ecd5a0cSIan Rogers metric_evlist = n->evlist; 15425ecd5a0cSIan Rogers break; 15435ecd5a0cSIan Rogers } 15445ecd5a0cSIan Rogers 15455ecd5a0cSIan Rogers } 15465ecd5a0cSIan Rogers } 15475ecd5a0cSIan Rogers if (!metric_evlist) { 154817b3867dSIan Rogers ret = parse_ids(metric_no_merge, fake_pmu, m->pctx, m->modifier, 1549180a5013SIan Rogers m->group_events, tool_events, &m->evlist); 15505ecd5a0cSIan Rogers if (ret) 15515ecd5a0cSIan Rogers goto out; 15525ecd5a0cSIan Rogers 15535ecd5a0cSIan Rogers metric_evlist = m->evlist; 15545ecd5a0cSIan Rogers } 155517b3867dSIan Rogers ret = setup_metric_events(m->pctx->ids, metric_evlist, &metric_events); 15565ecd5a0cSIan Rogers if (ret) { 15575ecd5a0cSIan Rogers pr_debug("Cannot resolve IDs for %s: %s\n", 15585ecd5a0cSIan Rogers m->metric_name, m->metric_expr); 15595ecd5a0cSIan Rogers goto out; 15605ecd5a0cSIan Rogers } 15615ecd5a0cSIan Rogers 15625ecd5a0cSIan Rogers me = metricgroup__lookup(metric_events_list, metric_events[0], true); 15635ecd5a0cSIan Rogers 15645ecd5a0cSIan Rogers expr = malloc(sizeof(struct metric_expr)); 15655ecd5a0cSIan Rogers if (!expr) { 15665ecd5a0cSIan Rogers ret = -ENOMEM; 15675ecd5a0cSIan Rogers free(metric_events); 15685ecd5a0cSIan Rogers goto out; 15695ecd5a0cSIan Rogers } 15705ecd5a0cSIan Rogers 15715ecd5a0cSIan Rogers expr->metric_refs = m->metric_refs; 15725ecd5a0cSIan Rogers m->metric_refs = NULL; 15735ecd5a0cSIan Rogers expr->metric_expr = m->metric_expr; 1574b85a4d61SIan Rogers if (m->modifier) { 1575b85a4d61SIan Rogers char *tmp; 1576b85a4d61SIan Rogers 1577b85a4d61SIan Rogers if (asprintf(&tmp, "%s:%s", m->metric_name, m->modifier) < 0) 1578b85a4d61SIan Rogers expr->metric_name = NULL; 1579b85a4d61SIan Rogers else 1580b85a4d61SIan Rogers expr->metric_name = tmp; 1581b85a4d61SIan Rogers } else 1582b85a4d61SIan Rogers expr->metric_name = strdup(m->metric_name); 1583b85a4d61SIan Rogers 1584b85a4d61SIan Rogers if (!expr->metric_name) { 1585b85a4d61SIan Rogers ret = -ENOMEM; 1586b85a4d61SIan Rogers free(metric_events); 1587b85a4d61SIan Rogers goto out; 1588b85a4d61SIan Rogers } 1589d0a3052fSIan Rogers expr->metric_threshold = m->metric_threshold; 15905ecd5a0cSIan Rogers expr->metric_unit = m->metric_unit; 15915ecd5a0cSIan Rogers expr->metric_events = metric_events; 15921a6abddeSIan Rogers expr->runtime = m->pctx->sctx.runtime; 15935ecd5a0cSIan Rogers list_add(&expr->nd, &me->head); 15945ecd5a0cSIan Rogers } 15955ecd5a0cSIan Rogers 15965ecd5a0cSIan Rogers 1597aba8c5e3SIan Rogers if (combined_evlist) { 15985ecd5a0cSIan Rogers evlist__splice_list_tail(perf_evlist, &combined_evlist->core.entries); 1599aba8c5e3SIan Rogers evlist__delete(combined_evlist); 1600aba8c5e3SIan Rogers } 16015ecd5a0cSIan Rogers 16025ecd5a0cSIan Rogers list_for_each_entry(m, &metric_list, nd) { 16035ecd5a0cSIan Rogers if (m->evlist) 16045ecd5a0cSIan Rogers evlist__splice_list_tail(perf_evlist, &m->evlist->core.entries); 16055ecd5a0cSIan Rogers } 16065ecd5a0cSIan Rogers 1607b18f3e36SAndi Kleen out: 1608119e521aSJiri Olsa metricgroup__free_metrics(&metric_list); 1609b18f3e36SAndi Kleen return ret; 1610b18f3e36SAndi Kleen } 1611742d92ffSThomas Richter 1612a4b8cfcaSIan Rogers int metricgroup__parse_groups(struct evlist *perf_evlist, 16138b4468a2SJiri Olsa const char *str, 16148b4468a2SJiri Olsa bool metric_no_group, 16158b4468a2SJiri Olsa bool metric_no_merge, 16161fd09e29SIan Rogers bool metric_no_threshold, 16171725e9cdSIan Rogers const char *user_requested_cpu_list, 16181725e9cdSIan Rogers bool system_wide, 16198b4468a2SJiri Olsa struct rblist *metric_events) 16208b4468a2SJiri Olsa { 1621f8ea2c15SIan Rogers const struct pmu_metrics_table *table = pmu_metrics_table__find(); 16228b4468a2SJiri Olsa 16233f5df3acSIan Rogers if (!table) 16243f5df3acSIan Rogers return -EINVAL; 16253f5df3acSIan Rogers 16261725e9cdSIan Rogers return parse_groups(perf_evlist, str, metric_no_group, metric_no_merge, 16271fd09e29SIan Rogers metric_no_threshold, user_requested_cpu_list, system_wide, 16281725e9cdSIan Rogers /*fake_pmu=*/NULL, metric_events, table); 16298b4468a2SJiri Olsa } 16308b4468a2SJiri Olsa 1631f78ac00aSJiri Olsa int metricgroup__parse_groups_test(struct evlist *evlist, 1632f8ea2c15SIan Rogers const struct pmu_metrics_table *table, 1633f78ac00aSJiri Olsa const char *str, 1634f78ac00aSJiri Olsa struct rblist *metric_events) 1635f78ac00aSJiri Olsa { 16361fd09e29SIan Rogers return parse_groups(evlist, str, 16371fd09e29SIan Rogers /*metric_no_group=*/false, 16381fd09e29SIan Rogers /*metric_no_merge=*/false, 16391fd09e29SIan Rogers /*metric_no_threshold=*/false, 16401725e9cdSIan Rogers /*user_requested_cpu_list=*/NULL, 16411725e9cdSIan Rogers /*system_wide=*/false, 16421725e9cdSIan Rogers &perf_pmu__fake, metric_events, table); 1643f78ac00aSJiri Olsa } 1644f78ac00aSJiri Olsa 1645db95818eSIan Rogers static int metricgroup__has_metric_callback(const struct pmu_metric *pm, 1646f8ea2c15SIan Rogers const struct pmu_metrics_table *table __maybe_unused, 1647660842e4SIan Rogers void *vdata) 1648660842e4SIan Rogers { 1649660842e4SIan Rogers const char *metric = vdata; 1650660842e4SIan Rogers 165194b1a603SIan Rogers if (match_metric(pm->metric_name, metric) || 165294b1a603SIan Rogers match_metric(pm->metric_group, metric)) 1653660842e4SIan Rogers return 1; 1654660842e4SIan Rogers 1655660842e4SIan Rogers return 0; 1656660842e4SIan Rogers } 1657660842e4SIan Rogers 1658742d92ffSThomas Richter bool metricgroup__has_metric(const char *metric) 1659742d92ffSThomas Richter { 1660f8ea2c15SIan Rogers const struct pmu_metrics_table *table = pmu_metrics_table__find(); 1661742d92ffSThomas Richter 1662eeac7730SIan Rogers if (!table) 1663742d92ffSThomas Richter return false; 1664742d92ffSThomas Richter 1665f8ea2c15SIan Rogers return pmu_metrics_table_for_each_metric(table, metricgroup__has_metric_callback, 1666660842e4SIan Rogers (void *)metric) ? true : false; 1667742d92ffSThomas Richter } 1668b214ba8cSNamhyung Kim 16691647cd5bSIan Rogers static int metricgroup__topdown_max_level_callback(const struct pmu_metric *pm, 16701647cd5bSIan Rogers const struct pmu_metrics_table *table __maybe_unused, 16711647cd5bSIan Rogers void *data) 16721647cd5bSIan Rogers { 16731647cd5bSIan Rogers unsigned int *max_level = data; 16741647cd5bSIan Rogers unsigned int level; 16751647cd5bSIan Rogers const char *p = strstr(pm->metric_group, "TopdownL"); 16761647cd5bSIan Rogers 16771647cd5bSIan Rogers if (!p || p[8] == '\0') 16781647cd5bSIan Rogers return 0; 16791647cd5bSIan Rogers 16801647cd5bSIan Rogers level = p[8] - '0'; 16811647cd5bSIan Rogers if (level > *max_level) 16821647cd5bSIan Rogers *max_level = level; 16831647cd5bSIan Rogers 16841647cd5bSIan Rogers return 0; 16851647cd5bSIan Rogers } 16861647cd5bSIan Rogers 16871647cd5bSIan Rogers unsigned int metricgroups__topdown_max_level(void) 16881647cd5bSIan Rogers { 16891647cd5bSIan Rogers unsigned int max_level = 0; 16901647cd5bSIan Rogers const struct pmu_metrics_table *table = pmu_metrics_table__find(); 16911647cd5bSIan Rogers 16921647cd5bSIan Rogers if (!table) 16931647cd5bSIan Rogers return false; 16941647cd5bSIan Rogers 16951647cd5bSIan Rogers pmu_metrics_table_for_each_metric(table, metricgroup__topdown_max_level_callback, 16961647cd5bSIan Rogers &max_level); 16971647cd5bSIan Rogers return max_level; 16981647cd5bSIan Rogers } 16991647cd5bSIan Rogers 1700b214ba8cSNamhyung Kim int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp, 1701b214ba8cSNamhyung Kim struct rblist *new_metric_events, 1702b214ba8cSNamhyung Kim struct rblist *old_metric_events) 1703b214ba8cSNamhyung Kim { 170484f879c5SXin Gao unsigned int i; 1705b214ba8cSNamhyung Kim 1706b214ba8cSNamhyung Kim for (i = 0; i < rblist__nr_entries(old_metric_events); i++) { 1707b214ba8cSNamhyung Kim struct rb_node *nd; 1708b214ba8cSNamhyung Kim struct metric_event *old_me, *new_me; 1709b214ba8cSNamhyung Kim struct metric_expr *old_expr, *new_expr; 1710b214ba8cSNamhyung Kim struct evsel *evsel; 1711b214ba8cSNamhyung Kim size_t alloc_size; 1712b214ba8cSNamhyung Kim int idx, nr; 1713b214ba8cSNamhyung Kim 1714b214ba8cSNamhyung Kim nd = rblist__entry(old_metric_events, i); 1715b214ba8cSNamhyung Kim old_me = container_of(nd, struct metric_event, nd); 1716b214ba8cSNamhyung Kim 171738fe0e01SJiri Olsa evsel = evlist__find_evsel(evlist, old_me->evsel->core.idx); 1718b214ba8cSNamhyung Kim if (!evsel) 1719b214ba8cSNamhyung Kim return -EINVAL; 1720b214ba8cSNamhyung Kim new_me = metricgroup__lookup(new_metric_events, evsel, true); 1721b214ba8cSNamhyung Kim if (!new_me) 1722b214ba8cSNamhyung Kim return -ENOMEM; 1723b214ba8cSNamhyung Kim 1724b214ba8cSNamhyung Kim pr_debug("copying metric event for cgroup '%s': %s (idx=%d)\n", 172538fe0e01SJiri Olsa cgrp ? cgrp->name : "root", evsel->name, evsel->core.idx); 1726b214ba8cSNamhyung Kim 1727b214ba8cSNamhyung Kim list_for_each_entry(old_expr, &old_me->head, nd) { 1728b214ba8cSNamhyung Kim new_expr = malloc(sizeof(*new_expr)); 1729b214ba8cSNamhyung Kim if (!new_expr) 1730b214ba8cSNamhyung Kim return -ENOMEM; 1731b214ba8cSNamhyung Kim 1732b214ba8cSNamhyung Kim new_expr->metric_expr = old_expr->metric_expr; 17336c73f819SIan Rogers new_expr->metric_threshold = old_expr->metric_threshold; 1734b85a4d61SIan Rogers new_expr->metric_name = strdup(old_expr->metric_name); 1735b85a4d61SIan Rogers if (!new_expr->metric_name) 1736b85a4d61SIan Rogers return -ENOMEM; 1737b85a4d61SIan Rogers 1738b214ba8cSNamhyung Kim new_expr->metric_unit = old_expr->metric_unit; 1739b214ba8cSNamhyung Kim new_expr->runtime = old_expr->runtime; 1740b214ba8cSNamhyung Kim 1741b214ba8cSNamhyung Kim if (old_expr->metric_refs) { 1742b214ba8cSNamhyung Kim /* calculate number of metric_events */ 1743b214ba8cSNamhyung Kim for (nr = 0; old_expr->metric_refs[nr].metric_name; nr++) 1744b214ba8cSNamhyung Kim continue; 1745b214ba8cSNamhyung Kim alloc_size = sizeof(*new_expr->metric_refs); 1746b214ba8cSNamhyung Kim new_expr->metric_refs = calloc(nr + 1, alloc_size); 1747b214ba8cSNamhyung Kim if (!new_expr->metric_refs) { 1748b214ba8cSNamhyung Kim free(new_expr); 1749b214ba8cSNamhyung Kim return -ENOMEM; 1750b214ba8cSNamhyung Kim } 1751b214ba8cSNamhyung Kim 1752b214ba8cSNamhyung Kim memcpy(new_expr->metric_refs, old_expr->metric_refs, 1753b214ba8cSNamhyung Kim nr * alloc_size); 1754b214ba8cSNamhyung Kim } else { 1755b214ba8cSNamhyung Kim new_expr->metric_refs = NULL; 1756b214ba8cSNamhyung Kim } 1757b214ba8cSNamhyung Kim 1758b214ba8cSNamhyung Kim /* calculate number of metric_events */ 1759b214ba8cSNamhyung Kim for (nr = 0; old_expr->metric_events[nr]; nr++) 1760b214ba8cSNamhyung Kim continue; 1761b214ba8cSNamhyung Kim alloc_size = sizeof(*new_expr->metric_events); 1762b214ba8cSNamhyung Kim new_expr->metric_events = calloc(nr + 1, alloc_size); 1763b214ba8cSNamhyung Kim if (!new_expr->metric_events) { 176411ff9bcdSArnaldo Carvalho de Melo zfree(&new_expr->metric_refs); 1765b214ba8cSNamhyung Kim free(new_expr); 1766b214ba8cSNamhyung Kim return -ENOMEM; 1767b214ba8cSNamhyung Kim } 1768b214ba8cSNamhyung Kim 1769b214ba8cSNamhyung Kim /* copy evsel in the same position */ 1770b214ba8cSNamhyung Kim for (idx = 0; idx < nr; idx++) { 1771b214ba8cSNamhyung Kim evsel = old_expr->metric_events[idx]; 177238fe0e01SJiri Olsa evsel = evlist__find_evsel(evlist, evsel->core.idx); 1773b214ba8cSNamhyung Kim if (evsel == NULL) { 177411ff9bcdSArnaldo Carvalho de Melo zfree(&new_expr->metric_events); 177511ff9bcdSArnaldo Carvalho de Melo zfree(&new_expr->metric_refs); 1776b214ba8cSNamhyung Kim free(new_expr); 1777b214ba8cSNamhyung Kim return -EINVAL; 1778b214ba8cSNamhyung Kim } 1779b214ba8cSNamhyung Kim new_expr->metric_events[idx] = evsel; 1780b214ba8cSNamhyung Kim } 1781b214ba8cSNamhyung Kim 1782b214ba8cSNamhyung Kim list_add(&new_expr->nd, &new_me->head); 1783b214ba8cSNamhyung Kim } 1784b214ba8cSNamhyung Kim } 1785b214ba8cSNamhyung Kim return 0; 1786b214ba8cSNamhyung Kim } 1787