162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2017, Intel Corporation. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci/* Manage metrics and groups of metrics from JSON files */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "metricgroup.h" 962306a36Sopenharmony_ci#include "debug.h" 1062306a36Sopenharmony_ci#include "evlist.h" 1162306a36Sopenharmony_ci#include "evsel.h" 1262306a36Sopenharmony_ci#include "strbuf.h" 1362306a36Sopenharmony_ci#include "pmu.h" 1462306a36Sopenharmony_ci#include "pmus.h" 1562306a36Sopenharmony_ci#include "print-events.h" 1662306a36Sopenharmony_ci#include "smt.h" 1762306a36Sopenharmony_ci#include "expr.h" 1862306a36Sopenharmony_ci#include "rblist.h" 1962306a36Sopenharmony_ci#include <string.h> 2062306a36Sopenharmony_ci#include <errno.h> 2162306a36Sopenharmony_ci#include "strlist.h" 2262306a36Sopenharmony_ci#include <assert.h> 2362306a36Sopenharmony_ci#include <linux/ctype.h> 2462306a36Sopenharmony_ci#include <linux/list_sort.h> 2562306a36Sopenharmony_ci#include <linux/string.h> 2662306a36Sopenharmony_ci#include <linux/zalloc.h> 2762306a36Sopenharmony_ci#include <perf/cpumap.h> 2862306a36Sopenharmony_ci#include <subcmd/parse-options.h> 2962306a36Sopenharmony_ci#include <api/fs/fs.h> 3062306a36Sopenharmony_ci#include "util.h" 3162306a36Sopenharmony_ci#include <asm/bug.h> 3262306a36Sopenharmony_ci#include "cgroup.h" 3362306a36Sopenharmony_ci#include "util/hashmap.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistruct metric_event *metricgroup__lookup(struct rblist *metric_events, 3662306a36Sopenharmony_ci struct evsel *evsel, 3762306a36Sopenharmony_ci bool create) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci struct rb_node *nd; 4062306a36Sopenharmony_ci struct metric_event me = { 4162306a36Sopenharmony_ci .evsel = evsel 4262306a36Sopenharmony_ci }; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci if (!metric_events) 4562306a36Sopenharmony_ci return NULL; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci nd = rblist__find(metric_events, &me); 4862306a36Sopenharmony_ci if (nd) 4962306a36Sopenharmony_ci return container_of(nd, struct metric_event, nd); 5062306a36Sopenharmony_ci if (create) { 5162306a36Sopenharmony_ci rblist__add_node(metric_events, &me); 5262306a36Sopenharmony_ci nd = rblist__find(metric_events, &me); 5362306a36Sopenharmony_ci if (nd) 5462306a36Sopenharmony_ci return container_of(nd, struct metric_event, nd); 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci return NULL; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic int metric_event_cmp(struct rb_node *rb_node, const void *entry) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci struct metric_event *a = container_of(rb_node, 6262306a36Sopenharmony_ci struct metric_event, 6362306a36Sopenharmony_ci nd); 6462306a36Sopenharmony_ci const struct metric_event *b = entry; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (a->evsel == b->evsel) 6762306a36Sopenharmony_ci return 0; 6862306a36Sopenharmony_ci if ((char *)a->evsel < (char *)b->evsel) 6962306a36Sopenharmony_ci return -1; 7062306a36Sopenharmony_ci return +1; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic struct rb_node *metric_event_new(struct rblist *rblist __maybe_unused, 7462306a36Sopenharmony_ci const void *entry) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci struct metric_event *me = malloc(sizeof(struct metric_event)); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (!me) 7962306a36Sopenharmony_ci return NULL; 8062306a36Sopenharmony_ci memcpy(me, entry, sizeof(struct metric_event)); 8162306a36Sopenharmony_ci me->evsel = ((struct metric_event *)entry)->evsel; 8262306a36Sopenharmony_ci me->is_default = false; 8362306a36Sopenharmony_ci INIT_LIST_HEAD(&me->head); 8462306a36Sopenharmony_ci return &me->nd; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic void metric_event_delete(struct rblist *rblist __maybe_unused, 8862306a36Sopenharmony_ci struct rb_node *rb_node) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct metric_event *me = container_of(rb_node, struct metric_event, nd); 9162306a36Sopenharmony_ci struct metric_expr *expr, *tmp; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci list_for_each_entry_safe(expr, tmp, &me->head, nd) { 9462306a36Sopenharmony_ci zfree(&expr->metric_name); 9562306a36Sopenharmony_ci zfree(&expr->metric_refs); 9662306a36Sopenharmony_ci zfree(&expr->metric_events); 9762306a36Sopenharmony_ci free(expr); 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci free(me); 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic void metricgroup__rblist_init(struct rblist *metric_events) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci rblist__init(metric_events); 10662306a36Sopenharmony_ci metric_events->node_cmp = metric_event_cmp; 10762306a36Sopenharmony_ci metric_events->node_new = metric_event_new; 10862306a36Sopenharmony_ci metric_events->node_delete = metric_event_delete; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_civoid metricgroup__rblist_exit(struct rblist *metric_events) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci rblist__exit(metric_events); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/** 11762306a36Sopenharmony_ci * The metric under construction. The data held here will be placed in a 11862306a36Sopenharmony_ci * metric_expr. 11962306a36Sopenharmony_ci */ 12062306a36Sopenharmony_cistruct metric { 12162306a36Sopenharmony_ci struct list_head nd; 12262306a36Sopenharmony_ci /** 12362306a36Sopenharmony_ci * The expression parse context importantly holding the IDs contained 12462306a36Sopenharmony_ci * within the expression. 12562306a36Sopenharmony_ci */ 12662306a36Sopenharmony_ci struct expr_parse_ctx *pctx; 12762306a36Sopenharmony_ci const char *pmu; 12862306a36Sopenharmony_ci /** The name of the metric such as "IPC". */ 12962306a36Sopenharmony_ci const char *metric_name; 13062306a36Sopenharmony_ci /** Modifier on the metric such as "u" or NULL for none. */ 13162306a36Sopenharmony_ci const char *modifier; 13262306a36Sopenharmony_ci /** The expression to parse, for example, "instructions/cycles". */ 13362306a36Sopenharmony_ci const char *metric_expr; 13462306a36Sopenharmony_ci /** Optional threshold expression where zero value is green, otherwise red. */ 13562306a36Sopenharmony_ci const char *metric_threshold; 13662306a36Sopenharmony_ci /** 13762306a36Sopenharmony_ci * The "ScaleUnit" that scales and adds a unit to the metric during 13862306a36Sopenharmony_ci * output. 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_ci const char *metric_unit; 14162306a36Sopenharmony_ci /** 14262306a36Sopenharmony_ci * Optional name of the metric group reported 14362306a36Sopenharmony_ci * if the Default metric group is being processed. 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ci const char *default_metricgroup_name; 14662306a36Sopenharmony_ci /** Optional null terminated array of referenced metrics. */ 14762306a36Sopenharmony_ci struct metric_ref *metric_refs; 14862306a36Sopenharmony_ci /** 14962306a36Sopenharmony_ci * Should events of the metric be grouped? 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_ci bool group_events; 15262306a36Sopenharmony_ci /** 15362306a36Sopenharmony_ci * Parsed events for the metric. Optional as events may be taken from a 15462306a36Sopenharmony_ci * different metric whose group contains all the IDs necessary for this 15562306a36Sopenharmony_ci * one. 15662306a36Sopenharmony_ci */ 15762306a36Sopenharmony_ci struct evlist *evlist; 15862306a36Sopenharmony_ci}; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic void metric__watchdog_constraint_hint(const char *name, bool foot) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci static bool violate_nmi_constraint; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (!foot) { 16562306a36Sopenharmony_ci pr_warning("Not grouping metric %s's events.\n", name); 16662306a36Sopenharmony_ci violate_nmi_constraint = true; 16762306a36Sopenharmony_ci return; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (!violate_nmi_constraint) 17162306a36Sopenharmony_ci return; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci pr_warning("Try disabling the NMI watchdog to comply NO_NMI_WATCHDOG metric constraint:\n" 17462306a36Sopenharmony_ci " echo 0 > /proc/sys/kernel/nmi_watchdog\n" 17562306a36Sopenharmony_ci " perf stat ...\n" 17662306a36Sopenharmony_ci " echo 1 > /proc/sys/kernel/nmi_watchdog\n"); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic bool metric__group_events(const struct pmu_metric *pm) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci switch (pm->event_grouping) { 18262306a36Sopenharmony_ci case MetricNoGroupEvents: 18362306a36Sopenharmony_ci return false; 18462306a36Sopenharmony_ci case MetricNoGroupEventsNmi: 18562306a36Sopenharmony_ci if (!sysctl__nmi_watchdog_enabled()) 18662306a36Sopenharmony_ci return true; 18762306a36Sopenharmony_ci metric__watchdog_constraint_hint(pm->metric_name, /*foot=*/false); 18862306a36Sopenharmony_ci return false; 18962306a36Sopenharmony_ci case MetricNoGroupEventsSmt: 19062306a36Sopenharmony_ci return !smt_on(); 19162306a36Sopenharmony_ci case MetricGroupEvents: 19262306a36Sopenharmony_ci default: 19362306a36Sopenharmony_ci return true; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic void metric__free(struct metric *m) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci if (!m) 20062306a36Sopenharmony_ci return; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci zfree(&m->metric_refs); 20362306a36Sopenharmony_ci expr__ctx_free(m->pctx); 20462306a36Sopenharmony_ci zfree(&m->modifier); 20562306a36Sopenharmony_ci evlist__delete(m->evlist); 20662306a36Sopenharmony_ci free(m); 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic struct metric *metric__new(const struct pmu_metric *pm, 21062306a36Sopenharmony_ci const char *modifier, 21162306a36Sopenharmony_ci bool metric_no_group, 21262306a36Sopenharmony_ci int runtime, 21362306a36Sopenharmony_ci const char *user_requested_cpu_list, 21462306a36Sopenharmony_ci bool system_wide) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci struct metric *m; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci m = zalloc(sizeof(*m)); 21962306a36Sopenharmony_ci if (!m) 22062306a36Sopenharmony_ci return NULL; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci m->pctx = expr__ctx_new(); 22362306a36Sopenharmony_ci if (!m->pctx) 22462306a36Sopenharmony_ci goto out_err; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci m->pmu = pm->pmu ?: "cpu"; 22762306a36Sopenharmony_ci m->metric_name = pm->metric_name; 22862306a36Sopenharmony_ci m->default_metricgroup_name = pm->default_metricgroup_name ?: ""; 22962306a36Sopenharmony_ci m->modifier = NULL; 23062306a36Sopenharmony_ci if (modifier) { 23162306a36Sopenharmony_ci m->modifier = strdup(modifier); 23262306a36Sopenharmony_ci if (!m->modifier) 23362306a36Sopenharmony_ci goto out_err; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci m->metric_expr = pm->metric_expr; 23662306a36Sopenharmony_ci m->metric_threshold = pm->metric_threshold; 23762306a36Sopenharmony_ci m->metric_unit = pm->unit; 23862306a36Sopenharmony_ci m->pctx->sctx.user_requested_cpu_list = NULL; 23962306a36Sopenharmony_ci if (user_requested_cpu_list) { 24062306a36Sopenharmony_ci m->pctx->sctx.user_requested_cpu_list = strdup(user_requested_cpu_list); 24162306a36Sopenharmony_ci if (!m->pctx->sctx.user_requested_cpu_list) 24262306a36Sopenharmony_ci goto out_err; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci m->pctx->sctx.runtime = runtime; 24562306a36Sopenharmony_ci m->pctx->sctx.system_wide = system_wide; 24662306a36Sopenharmony_ci m->group_events = !metric_no_group && metric__group_events(pm); 24762306a36Sopenharmony_ci m->metric_refs = NULL; 24862306a36Sopenharmony_ci m->evlist = NULL; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci return m; 25162306a36Sopenharmony_ciout_err: 25262306a36Sopenharmony_ci metric__free(m); 25362306a36Sopenharmony_ci return NULL; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic bool contains_metric_id(struct evsel **metric_events, int num_events, 25762306a36Sopenharmony_ci const char *metric_id) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci int i; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci for (i = 0; i < num_events; i++) { 26262306a36Sopenharmony_ci if (!strcmp(evsel__metric_id(metric_events[i]), metric_id)) 26362306a36Sopenharmony_ci return true; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci return false; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci/** 26962306a36Sopenharmony_ci * setup_metric_events - Find a group of events in metric_evlist that correspond 27062306a36Sopenharmony_ci * to the IDs from a parsed metric expression. 27162306a36Sopenharmony_ci * @pmu: The PMU for the IDs. 27262306a36Sopenharmony_ci * @ids: the metric IDs to match. 27362306a36Sopenharmony_ci * @metric_evlist: the list of perf events. 27462306a36Sopenharmony_ci * @out_metric_events: holds the created metric events array. 27562306a36Sopenharmony_ci */ 27662306a36Sopenharmony_cistatic int setup_metric_events(const char *pmu, struct hashmap *ids, 27762306a36Sopenharmony_ci struct evlist *metric_evlist, 27862306a36Sopenharmony_ci struct evsel ***out_metric_events) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci struct evsel **metric_events; 28162306a36Sopenharmony_ci const char *metric_id; 28262306a36Sopenharmony_ci struct evsel *ev; 28362306a36Sopenharmony_ci size_t ids_size, matched_events, i; 28462306a36Sopenharmony_ci bool all_pmus = !strcmp(pmu, "all") || perf_pmus__num_core_pmus() == 1 || !is_pmu_core(pmu); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci *out_metric_events = NULL; 28762306a36Sopenharmony_ci ids_size = hashmap__size(ids); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci metric_events = calloc(sizeof(void *), ids_size + 1); 29062306a36Sopenharmony_ci if (!metric_events) 29162306a36Sopenharmony_ci return -ENOMEM; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci matched_events = 0; 29462306a36Sopenharmony_ci evlist__for_each_entry(metric_evlist, ev) { 29562306a36Sopenharmony_ci struct expr_id_data *val_ptr; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* Don't match events for the wrong hybrid PMU. */ 29862306a36Sopenharmony_ci if (!all_pmus && ev->pmu_name && evsel__is_hybrid(ev) && 29962306a36Sopenharmony_ci strcmp(ev->pmu_name, pmu)) 30062306a36Sopenharmony_ci continue; 30162306a36Sopenharmony_ci /* 30262306a36Sopenharmony_ci * Check for duplicate events with the same name. For 30362306a36Sopenharmony_ci * example, uncore_imc/cas_count_read/ will turn into 6 30462306a36Sopenharmony_ci * events per socket on skylakex. Only the first such 30562306a36Sopenharmony_ci * event is placed in metric_events. 30662306a36Sopenharmony_ci */ 30762306a36Sopenharmony_ci metric_id = evsel__metric_id(ev); 30862306a36Sopenharmony_ci if (contains_metric_id(metric_events, matched_events, metric_id)) 30962306a36Sopenharmony_ci continue; 31062306a36Sopenharmony_ci /* 31162306a36Sopenharmony_ci * Does this event belong to the parse context? For 31262306a36Sopenharmony_ci * combined or shared groups, this metric may not care 31362306a36Sopenharmony_ci * about this event. 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_ci if (hashmap__find(ids, metric_id, &val_ptr)) { 31662306a36Sopenharmony_ci pr_debug("Matched metric-id %s to %s\n", metric_id, evsel__name(ev)); 31762306a36Sopenharmony_ci metric_events[matched_events++] = ev; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (matched_events >= ids_size) 32062306a36Sopenharmony_ci break; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci if (matched_events < ids_size) { 32462306a36Sopenharmony_ci free(metric_events); 32562306a36Sopenharmony_ci return -EINVAL; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci for (i = 0; i < ids_size; i++) { 32862306a36Sopenharmony_ci ev = metric_events[i]; 32962306a36Sopenharmony_ci ev->collect_stat = true; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* 33262306a36Sopenharmony_ci * The metric leader points to the identically named 33362306a36Sopenharmony_ci * event in metric_events. 33462306a36Sopenharmony_ci */ 33562306a36Sopenharmony_ci ev->metric_leader = ev; 33662306a36Sopenharmony_ci /* 33762306a36Sopenharmony_ci * Mark two events with identical names in the same 33862306a36Sopenharmony_ci * group (or globally) as being in use as uncore events 33962306a36Sopenharmony_ci * may be duplicated for each pmu. Set the metric leader 34062306a36Sopenharmony_ci * of such events to be the event that appears in 34162306a36Sopenharmony_ci * metric_events. 34262306a36Sopenharmony_ci */ 34362306a36Sopenharmony_ci metric_id = evsel__metric_id(ev); 34462306a36Sopenharmony_ci evlist__for_each_entry_continue(metric_evlist, ev) { 34562306a36Sopenharmony_ci if (!strcmp(evsel__metric_id(ev), metric_id)) 34662306a36Sopenharmony_ci ev->metric_leader = metric_events[i]; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci *out_metric_events = metric_events; 35062306a36Sopenharmony_ci return 0; 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic bool match_metric(const char *n, const char *list) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci int len; 35662306a36Sopenharmony_ci char *m; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (!list) 35962306a36Sopenharmony_ci return false; 36062306a36Sopenharmony_ci if (!strcmp(list, "all")) 36162306a36Sopenharmony_ci return true; 36262306a36Sopenharmony_ci if (!n) 36362306a36Sopenharmony_ci return !strcasecmp(list, "No_group"); 36462306a36Sopenharmony_ci len = strlen(list); 36562306a36Sopenharmony_ci m = strcasestr(n, list); 36662306a36Sopenharmony_ci if (!m) 36762306a36Sopenharmony_ci return false; 36862306a36Sopenharmony_ci if ((m == n || m[-1] == ';' || m[-1] == ' ') && 36962306a36Sopenharmony_ci (m[len] == 0 || m[len] == ';')) 37062306a36Sopenharmony_ci return true; 37162306a36Sopenharmony_ci return false; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic bool match_pm_metric(const struct pmu_metric *pm, const char *pmu, const char *metric) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci const char *pm_pmu = pm->pmu ?: "cpu"; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if (strcmp(pmu, "all") && strcmp(pm_pmu, pmu)) 37962306a36Sopenharmony_ci return false; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci return match_metric(pm->metric_group, metric) || 38262306a36Sopenharmony_ci match_metric(pm->metric_name, metric); 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci/** struct mep - RB-tree node for building printing information. */ 38662306a36Sopenharmony_cistruct mep { 38762306a36Sopenharmony_ci /** nd - RB-tree element. */ 38862306a36Sopenharmony_ci struct rb_node nd; 38962306a36Sopenharmony_ci /** @metric_group: Owned metric group name, separated others with ';'. */ 39062306a36Sopenharmony_ci char *metric_group; 39162306a36Sopenharmony_ci const char *metric_name; 39262306a36Sopenharmony_ci const char *metric_desc; 39362306a36Sopenharmony_ci const char *metric_long_desc; 39462306a36Sopenharmony_ci const char *metric_expr; 39562306a36Sopenharmony_ci const char *metric_threshold; 39662306a36Sopenharmony_ci const char *metric_unit; 39762306a36Sopenharmony_ci}; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic int mep_cmp(struct rb_node *rb_node, const void *entry) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci struct mep *a = container_of(rb_node, struct mep, nd); 40262306a36Sopenharmony_ci struct mep *b = (struct mep *)entry; 40362306a36Sopenharmony_ci int ret; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci ret = strcmp(a->metric_group, b->metric_group); 40662306a36Sopenharmony_ci if (ret) 40762306a36Sopenharmony_ci return ret; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci return strcmp(a->metric_name, b->metric_name); 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic struct rb_node *mep_new(struct rblist *rl __maybe_unused, const void *entry) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct mep *me = malloc(sizeof(struct mep)); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (!me) 41762306a36Sopenharmony_ci return NULL; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci memcpy(me, entry, sizeof(struct mep)); 42062306a36Sopenharmony_ci return &me->nd; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic void mep_delete(struct rblist *rl __maybe_unused, 42462306a36Sopenharmony_ci struct rb_node *nd) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci struct mep *me = container_of(nd, struct mep, nd); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci zfree(&me->metric_group); 42962306a36Sopenharmony_ci free(me); 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic struct mep *mep_lookup(struct rblist *groups, const char *metric_group, 43362306a36Sopenharmony_ci const char *metric_name) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci struct rb_node *nd; 43662306a36Sopenharmony_ci struct mep me = { 43762306a36Sopenharmony_ci .metric_group = strdup(metric_group), 43862306a36Sopenharmony_ci .metric_name = metric_name, 43962306a36Sopenharmony_ci }; 44062306a36Sopenharmony_ci nd = rblist__find(groups, &me); 44162306a36Sopenharmony_ci if (nd) { 44262306a36Sopenharmony_ci free(me.metric_group); 44362306a36Sopenharmony_ci return container_of(nd, struct mep, nd); 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci rblist__add_node(groups, &me); 44662306a36Sopenharmony_ci nd = rblist__find(groups, &me); 44762306a36Sopenharmony_ci if (nd) 44862306a36Sopenharmony_ci return container_of(nd, struct mep, nd); 44962306a36Sopenharmony_ci return NULL; 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic int metricgroup__add_to_mep_groups(const struct pmu_metric *pm, 45362306a36Sopenharmony_ci struct rblist *groups) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci const char *g; 45662306a36Sopenharmony_ci char *omg, *mg; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci mg = strdup(pm->metric_group ?: "No_group"); 45962306a36Sopenharmony_ci if (!mg) 46062306a36Sopenharmony_ci return -ENOMEM; 46162306a36Sopenharmony_ci omg = mg; 46262306a36Sopenharmony_ci while ((g = strsep(&mg, ";")) != NULL) { 46362306a36Sopenharmony_ci struct mep *me; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci g = skip_spaces(g); 46662306a36Sopenharmony_ci if (strlen(g)) 46762306a36Sopenharmony_ci me = mep_lookup(groups, g, pm->metric_name); 46862306a36Sopenharmony_ci else 46962306a36Sopenharmony_ci me = mep_lookup(groups, "No_group", pm->metric_name); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (me) { 47262306a36Sopenharmony_ci me->metric_desc = pm->desc; 47362306a36Sopenharmony_ci me->metric_long_desc = pm->long_desc; 47462306a36Sopenharmony_ci me->metric_expr = pm->metric_expr; 47562306a36Sopenharmony_ci me->metric_threshold = pm->metric_threshold; 47662306a36Sopenharmony_ci me->metric_unit = pm->unit; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci free(omg); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci return 0; 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistruct metricgroup_iter_data { 48562306a36Sopenharmony_ci pmu_metric_iter_fn fn; 48662306a36Sopenharmony_ci void *data; 48762306a36Sopenharmony_ci}; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic int metricgroup__sys_event_iter(const struct pmu_metric *pm, 49062306a36Sopenharmony_ci const struct pmu_metrics_table *table, 49162306a36Sopenharmony_ci void *data) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci struct metricgroup_iter_data *d = data; 49462306a36Sopenharmony_ci struct perf_pmu *pmu = NULL; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci if (!pm->metric_expr || !pm->compat) 49762306a36Sopenharmony_ci return 0; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci while ((pmu = perf_pmus__scan(pmu))) { 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (!pmu->id || strcmp(pmu->id, pm->compat)) 50262306a36Sopenharmony_ci continue; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci return d->fn(pm, table, d->data); 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci return 0; 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic int metricgroup__add_to_mep_groups_callback(const struct pmu_metric *pm, 51062306a36Sopenharmony_ci const struct pmu_metrics_table *table __maybe_unused, 51162306a36Sopenharmony_ci void *vdata) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct rblist *groups = vdata; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci return metricgroup__add_to_mep_groups(pm, groups); 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_civoid metricgroup__print(const struct print_callbacks *print_cb, void *print_state) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci struct rblist groups; 52162306a36Sopenharmony_ci const struct pmu_metrics_table *table; 52262306a36Sopenharmony_ci struct rb_node *node, *next; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci rblist__init(&groups); 52562306a36Sopenharmony_ci groups.node_new = mep_new; 52662306a36Sopenharmony_ci groups.node_cmp = mep_cmp; 52762306a36Sopenharmony_ci groups.node_delete = mep_delete; 52862306a36Sopenharmony_ci table = pmu_metrics_table__find(); 52962306a36Sopenharmony_ci if (table) { 53062306a36Sopenharmony_ci pmu_metrics_table__for_each_metric(table, 53162306a36Sopenharmony_ci metricgroup__add_to_mep_groups_callback, 53262306a36Sopenharmony_ci &groups); 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci { 53562306a36Sopenharmony_ci struct metricgroup_iter_data data = { 53662306a36Sopenharmony_ci .fn = metricgroup__add_to_mep_groups_callback, 53762306a36Sopenharmony_ci .data = &groups, 53862306a36Sopenharmony_ci }; 53962306a36Sopenharmony_ci pmu_for_each_sys_metric(metricgroup__sys_event_iter, &data); 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci for (node = rb_first_cached(&groups.entries); node; node = next) { 54362306a36Sopenharmony_ci struct mep *me = container_of(node, struct mep, nd); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci print_cb->print_metric(print_state, 54662306a36Sopenharmony_ci me->metric_group, 54762306a36Sopenharmony_ci me->metric_name, 54862306a36Sopenharmony_ci me->metric_desc, 54962306a36Sopenharmony_ci me->metric_long_desc, 55062306a36Sopenharmony_ci me->metric_expr, 55162306a36Sopenharmony_ci me->metric_threshold, 55262306a36Sopenharmony_ci me->metric_unit); 55362306a36Sopenharmony_ci next = rb_next(node); 55462306a36Sopenharmony_ci rblist__remove_node(&groups, node); 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cistatic const char *code_characters = ",-=@"; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_cistatic int encode_metric_id(struct strbuf *sb, const char *x) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci char *c; 56362306a36Sopenharmony_ci int ret = 0; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci for (; *x; x++) { 56662306a36Sopenharmony_ci c = strchr(code_characters, *x); 56762306a36Sopenharmony_ci if (c) { 56862306a36Sopenharmony_ci ret = strbuf_addch(sb, '!'); 56962306a36Sopenharmony_ci if (ret) 57062306a36Sopenharmony_ci break; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci ret = strbuf_addch(sb, '0' + (c - code_characters)); 57362306a36Sopenharmony_ci if (ret) 57462306a36Sopenharmony_ci break; 57562306a36Sopenharmony_ci } else { 57662306a36Sopenharmony_ci ret = strbuf_addch(sb, *x); 57762306a36Sopenharmony_ci if (ret) 57862306a36Sopenharmony_ci break; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci return ret; 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_cistatic int decode_metric_id(struct strbuf *sb, const char *x) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci const char *orig = x; 58762306a36Sopenharmony_ci size_t i; 58862306a36Sopenharmony_ci char c; 58962306a36Sopenharmony_ci int ret; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci for (; *x; x++) { 59262306a36Sopenharmony_ci c = *x; 59362306a36Sopenharmony_ci if (*x == '!') { 59462306a36Sopenharmony_ci x++; 59562306a36Sopenharmony_ci i = *x - '0'; 59662306a36Sopenharmony_ci if (i > strlen(code_characters)) { 59762306a36Sopenharmony_ci pr_err("Bad metric-id encoding in: '%s'", orig); 59862306a36Sopenharmony_ci return -1; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci c = code_characters[i]; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci ret = strbuf_addch(sb, c); 60362306a36Sopenharmony_ci if (ret) 60462306a36Sopenharmony_ci return ret; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci return 0; 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic int decode_all_metric_ids(struct evlist *perf_evlist, const char *modifier) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci struct evsel *ev; 61262306a36Sopenharmony_ci struct strbuf sb = STRBUF_INIT; 61362306a36Sopenharmony_ci char *cur; 61462306a36Sopenharmony_ci int ret = 0; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci evlist__for_each_entry(perf_evlist, ev) { 61762306a36Sopenharmony_ci if (!ev->metric_id) 61862306a36Sopenharmony_ci continue; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci ret = strbuf_setlen(&sb, 0); 62162306a36Sopenharmony_ci if (ret) 62262306a36Sopenharmony_ci break; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci ret = decode_metric_id(&sb, ev->metric_id); 62562306a36Sopenharmony_ci if (ret) 62662306a36Sopenharmony_ci break; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci free((char *)ev->metric_id); 62962306a36Sopenharmony_ci ev->metric_id = strdup(sb.buf); 63062306a36Sopenharmony_ci if (!ev->metric_id) { 63162306a36Sopenharmony_ci ret = -ENOMEM; 63262306a36Sopenharmony_ci break; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci /* 63562306a36Sopenharmony_ci * If the name is just the parsed event, use the metric-id to 63662306a36Sopenharmony_ci * give a more friendly display version. 63762306a36Sopenharmony_ci */ 63862306a36Sopenharmony_ci if (strstr(ev->name, "metric-id=")) { 63962306a36Sopenharmony_ci bool has_slash = false; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci zfree(&ev->name); 64262306a36Sopenharmony_ci for (cur = strchr(sb.buf, '@') ; cur; cur = strchr(++cur, '@')) { 64362306a36Sopenharmony_ci *cur = '/'; 64462306a36Sopenharmony_ci has_slash = true; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci if (modifier) { 64862306a36Sopenharmony_ci if (!has_slash && !strchr(sb.buf, ':')) { 64962306a36Sopenharmony_ci ret = strbuf_addch(&sb, ':'); 65062306a36Sopenharmony_ci if (ret) 65162306a36Sopenharmony_ci break; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci ret = strbuf_addstr(&sb, modifier); 65462306a36Sopenharmony_ci if (ret) 65562306a36Sopenharmony_ci break; 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci ev->name = strdup(sb.buf); 65862306a36Sopenharmony_ci if (!ev->name) { 65962306a36Sopenharmony_ci ret = -ENOMEM; 66062306a36Sopenharmony_ci break; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci strbuf_release(&sb); 66562306a36Sopenharmony_ci return ret; 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cistatic int metricgroup__build_event_string(struct strbuf *events, 66962306a36Sopenharmony_ci const struct expr_parse_ctx *ctx, 67062306a36Sopenharmony_ci const char *modifier, 67162306a36Sopenharmony_ci bool group_events) 67262306a36Sopenharmony_ci{ 67362306a36Sopenharmony_ci struct hashmap_entry *cur; 67462306a36Sopenharmony_ci size_t bkt; 67562306a36Sopenharmony_ci bool no_group = true, has_tool_events = false; 67662306a36Sopenharmony_ci bool tool_events[PERF_TOOL_MAX] = {false}; 67762306a36Sopenharmony_ci int ret = 0; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci#define RETURN_IF_NON_ZERO(x) do { if (x) return x; } while (0) 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci hashmap__for_each_entry(ctx->ids, cur, bkt) { 68262306a36Sopenharmony_ci const char *sep, *rsep, *id = cur->pkey; 68362306a36Sopenharmony_ci enum perf_tool_event ev; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci pr_debug("found event %s\n", id); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci /* Always move tool events outside of the group. */ 68862306a36Sopenharmony_ci ev = perf_tool_event__from_str(id); 68962306a36Sopenharmony_ci if (ev != PERF_TOOL_NONE) { 69062306a36Sopenharmony_ci has_tool_events = true; 69162306a36Sopenharmony_ci tool_events[ev] = true; 69262306a36Sopenharmony_ci continue; 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci /* Separate events with commas and open the group if necessary. */ 69562306a36Sopenharmony_ci if (no_group) { 69662306a36Sopenharmony_ci if (group_events) { 69762306a36Sopenharmony_ci ret = strbuf_addch(events, '{'); 69862306a36Sopenharmony_ci RETURN_IF_NON_ZERO(ret); 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci no_group = false; 70262306a36Sopenharmony_ci } else { 70362306a36Sopenharmony_ci ret = strbuf_addch(events, ','); 70462306a36Sopenharmony_ci RETURN_IF_NON_ZERO(ret); 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci /* 70762306a36Sopenharmony_ci * Encode the ID as an event string. Add a qualifier for 70862306a36Sopenharmony_ci * metric_id that is the original name except with characters 70962306a36Sopenharmony_ci * that parse-events can't parse replaced. For example, 71062306a36Sopenharmony_ci * 'msr@tsc@' gets added as msr/tsc,metric-id=msr!3tsc!3/ 71162306a36Sopenharmony_ci */ 71262306a36Sopenharmony_ci sep = strchr(id, '@'); 71362306a36Sopenharmony_ci if (sep != NULL) { 71462306a36Sopenharmony_ci ret = strbuf_add(events, id, sep - id); 71562306a36Sopenharmony_ci RETURN_IF_NON_ZERO(ret); 71662306a36Sopenharmony_ci ret = strbuf_addch(events, '/'); 71762306a36Sopenharmony_ci RETURN_IF_NON_ZERO(ret); 71862306a36Sopenharmony_ci rsep = strrchr(sep, '@'); 71962306a36Sopenharmony_ci ret = strbuf_add(events, sep + 1, rsep - sep - 1); 72062306a36Sopenharmony_ci RETURN_IF_NON_ZERO(ret); 72162306a36Sopenharmony_ci ret = strbuf_addstr(events, ",metric-id="); 72262306a36Sopenharmony_ci RETURN_IF_NON_ZERO(ret); 72362306a36Sopenharmony_ci sep = rsep; 72462306a36Sopenharmony_ci } else { 72562306a36Sopenharmony_ci sep = strchr(id, ':'); 72662306a36Sopenharmony_ci if (sep != NULL) { 72762306a36Sopenharmony_ci ret = strbuf_add(events, id, sep - id); 72862306a36Sopenharmony_ci RETURN_IF_NON_ZERO(ret); 72962306a36Sopenharmony_ci } else { 73062306a36Sopenharmony_ci ret = strbuf_addstr(events, id); 73162306a36Sopenharmony_ci RETURN_IF_NON_ZERO(ret); 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci ret = strbuf_addstr(events, "/metric-id="); 73462306a36Sopenharmony_ci RETURN_IF_NON_ZERO(ret); 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci ret = encode_metric_id(events, id); 73762306a36Sopenharmony_ci RETURN_IF_NON_ZERO(ret); 73862306a36Sopenharmony_ci ret = strbuf_addstr(events, "/"); 73962306a36Sopenharmony_ci RETURN_IF_NON_ZERO(ret); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci if (sep != NULL) { 74262306a36Sopenharmony_ci ret = strbuf_addstr(events, sep + 1); 74362306a36Sopenharmony_ci RETURN_IF_NON_ZERO(ret); 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci if (modifier) { 74662306a36Sopenharmony_ci ret = strbuf_addstr(events, modifier); 74762306a36Sopenharmony_ci RETURN_IF_NON_ZERO(ret); 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci if (!no_group && group_events) { 75162306a36Sopenharmony_ci ret = strbuf_addf(events, "}:W"); 75262306a36Sopenharmony_ci RETURN_IF_NON_ZERO(ret); 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci if (has_tool_events) { 75562306a36Sopenharmony_ci int i; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci perf_tool_event__for_each_event(i) { 75862306a36Sopenharmony_ci if (tool_events[i]) { 75962306a36Sopenharmony_ci if (!no_group) { 76062306a36Sopenharmony_ci ret = strbuf_addch(events, ','); 76162306a36Sopenharmony_ci RETURN_IF_NON_ZERO(ret); 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci no_group = false; 76462306a36Sopenharmony_ci ret = strbuf_addstr(events, perf_tool_event__to_str(i)); 76562306a36Sopenharmony_ci RETURN_IF_NON_ZERO(ret); 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci return ret; 77162306a36Sopenharmony_ci#undef RETURN_IF_NON_ZERO 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ciint __weak arch_get_runtimeparam(const struct pmu_metric *pm __maybe_unused) 77562306a36Sopenharmony_ci{ 77662306a36Sopenharmony_ci return 1; 77762306a36Sopenharmony_ci} 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci/* 78062306a36Sopenharmony_ci * A singly linked list on the stack of the names of metrics being 78162306a36Sopenharmony_ci * processed. Used to identify recursion. 78262306a36Sopenharmony_ci */ 78362306a36Sopenharmony_cistruct visited_metric { 78462306a36Sopenharmony_ci const char *name; 78562306a36Sopenharmony_ci const struct visited_metric *parent; 78662306a36Sopenharmony_ci}; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_cistruct metricgroup_add_iter_data { 78962306a36Sopenharmony_ci struct list_head *metric_list; 79062306a36Sopenharmony_ci const char *pmu; 79162306a36Sopenharmony_ci const char *metric_name; 79262306a36Sopenharmony_ci const char *modifier; 79362306a36Sopenharmony_ci int *ret; 79462306a36Sopenharmony_ci bool *has_match; 79562306a36Sopenharmony_ci bool metric_no_group; 79662306a36Sopenharmony_ci bool metric_no_threshold; 79762306a36Sopenharmony_ci const char *user_requested_cpu_list; 79862306a36Sopenharmony_ci bool system_wide; 79962306a36Sopenharmony_ci struct metric *root_metric; 80062306a36Sopenharmony_ci const struct visited_metric *visited; 80162306a36Sopenharmony_ci const struct pmu_metrics_table *table; 80262306a36Sopenharmony_ci}; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_cistatic bool metricgroup__find_metric(const char *pmu, 80562306a36Sopenharmony_ci const char *metric, 80662306a36Sopenharmony_ci const struct pmu_metrics_table *table, 80762306a36Sopenharmony_ci struct pmu_metric *pm); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_cistatic int add_metric(struct list_head *metric_list, 81062306a36Sopenharmony_ci const struct pmu_metric *pm, 81162306a36Sopenharmony_ci const char *modifier, 81262306a36Sopenharmony_ci bool metric_no_group, 81362306a36Sopenharmony_ci bool metric_no_threshold, 81462306a36Sopenharmony_ci const char *user_requested_cpu_list, 81562306a36Sopenharmony_ci bool system_wide, 81662306a36Sopenharmony_ci struct metric *root_metric, 81762306a36Sopenharmony_ci const struct visited_metric *visited, 81862306a36Sopenharmony_ci const struct pmu_metrics_table *table); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci/** 82162306a36Sopenharmony_ci * resolve_metric - Locate metrics within the root metric and recursively add 82262306a36Sopenharmony_ci * references to them. 82362306a36Sopenharmony_ci * @metric_list: The list the metric is added to. 82462306a36Sopenharmony_ci * @pmu: The PMU name to resolve metrics on, or "all" for all PMUs. 82562306a36Sopenharmony_ci * @modifier: if non-null event modifiers like "u". 82662306a36Sopenharmony_ci * @metric_no_group: Should events written to events be grouped "{}" or 82762306a36Sopenharmony_ci * global. Grouping is the default but due to multiplexing the 82862306a36Sopenharmony_ci * user may override. 82962306a36Sopenharmony_ci * @user_requested_cpu_list: Command line specified CPUs to record on. 83062306a36Sopenharmony_ci * @system_wide: Are events for all processes recorded. 83162306a36Sopenharmony_ci * @root_metric: Metrics may reference other metrics to form a tree. In this 83262306a36Sopenharmony_ci * case the root_metric holds all the IDs and a list of referenced 83362306a36Sopenharmony_ci * metrics. When adding a root this argument is NULL. 83462306a36Sopenharmony_ci * @visited: A singly linked list of metric names being added that is used to 83562306a36Sopenharmony_ci * detect recursion. 83662306a36Sopenharmony_ci * @table: The table that is searched for metrics, most commonly the table for the 83762306a36Sopenharmony_ci * architecture perf is running upon. 83862306a36Sopenharmony_ci */ 83962306a36Sopenharmony_cistatic int resolve_metric(struct list_head *metric_list, 84062306a36Sopenharmony_ci const char *pmu, 84162306a36Sopenharmony_ci const char *modifier, 84262306a36Sopenharmony_ci bool metric_no_group, 84362306a36Sopenharmony_ci bool metric_no_threshold, 84462306a36Sopenharmony_ci const char *user_requested_cpu_list, 84562306a36Sopenharmony_ci bool system_wide, 84662306a36Sopenharmony_ci struct metric *root_metric, 84762306a36Sopenharmony_ci const struct visited_metric *visited, 84862306a36Sopenharmony_ci const struct pmu_metrics_table *table) 84962306a36Sopenharmony_ci{ 85062306a36Sopenharmony_ci struct hashmap_entry *cur; 85162306a36Sopenharmony_ci size_t bkt; 85262306a36Sopenharmony_ci struct to_resolve { 85362306a36Sopenharmony_ci /* The metric to resolve. */ 85462306a36Sopenharmony_ci struct pmu_metric pm; 85562306a36Sopenharmony_ci /* 85662306a36Sopenharmony_ci * The key in the IDs map, this may differ from in case, 85762306a36Sopenharmony_ci * etc. from pm->metric_name. 85862306a36Sopenharmony_ci */ 85962306a36Sopenharmony_ci const char *key; 86062306a36Sopenharmony_ci } *pending = NULL; 86162306a36Sopenharmony_ci int i, ret = 0, pending_cnt = 0; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci /* 86462306a36Sopenharmony_ci * Iterate all the parsed IDs and if there's a matching metric and it to 86562306a36Sopenharmony_ci * the pending array. 86662306a36Sopenharmony_ci */ 86762306a36Sopenharmony_ci hashmap__for_each_entry(root_metric->pctx->ids, cur, bkt) { 86862306a36Sopenharmony_ci struct pmu_metric pm; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci if (metricgroup__find_metric(pmu, cur->pkey, table, &pm)) { 87162306a36Sopenharmony_ci pending = realloc(pending, 87262306a36Sopenharmony_ci (pending_cnt + 1) * sizeof(struct to_resolve)); 87362306a36Sopenharmony_ci if (!pending) 87462306a36Sopenharmony_ci return -ENOMEM; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci memcpy(&pending[pending_cnt].pm, &pm, sizeof(pm)); 87762306a36Sopenharmony_ci pending[pending_cnt].key = cur->pkey; 87862306a36Sopenharmony_ci pending_cnt++; 87962306a36Sopenharmony_ci } 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci /* Remove the metric IDs from the context. */ 88362306a36Sopenharmony_ci for (i = 0; i < pending_cnt; i++) 88462306a36Sopenharmony_ci expr__del_id(root_metric->pctx, pending[i].key); 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci /* 88762306a36Sopenharmony_ci * Recursively add all the metrics, IDs are added to the root metric's 88862306a36Sopenharmony_ci * context. 88962306a36Sopenharmony_ci */ 89062306a36Sopenharmony_ci for (i = 0; i < pending_cnt; i++) { 89162306a36Sopenharmony_ci ret = add_metric(metric_list, &pending[i].pm, modifier, metric_no_group, 89262306a36Sopenharmony_ci metric_no_threshold, user_requested_cpu_list, system_wide, 89362306a36Sopenharmony_ci root_metric, visited, table); 89462306a36Sopenharmony_ci if (ret) 89562306a36Sopenharmony_ci break; 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci free(pending); 89962306a36Sopenharmony_ci return ret; 90062306a36Sopenharmony_ci} 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci/** 90362306a36Sopenharmony_ci * __add_metric - Add a metric to metric_list. 90462306a36Sopenharmony_ci * @metric_list: The list the metric is added to. 90562306a36Sopenharmony_ci * @pm: The pmu_metric containing the metric to be added. 90662306a36Sopenharmony_ci * @modifier: if non-null event modifiers like "u". 90762306a36Sopenharmony_ci * @metric_no_group: Should events written to events be grouped "{}" or 90862306a36Sopenharmony_ci * global. Grouping is the default but due to multiplexing the 90962306a36Sopenharmony_ci * user may override. 91062306a36Sopenharmony_ci * @metric_no_threshold: Should threshold expressions be ignored? 91162306a36Sopenharmony_ci * @runtime: A special argument for the parser only known at runtime. 91262306a36Sopenharmony_ci * @user_requested_cpu_list: Command line specified CPUs to record on. 91362306a36Sopenharmony_ci * @system_wide: Are events for all processes recorded. 91462306a36Sopenharmony_ci * @root_metric: Metrics may reference other metrics to form a tree. In this 91562306a36Sopenharmony_ci * case the root_metric holds all the IDs and a list of referenced 91662306a36Sopenharmony_ci * metrics. When adding a root this argument is NULL. 91762306a36Sopenharmony_ci * @visited: A singly linked list of metric names being added that is used to 91862306a36Sopenharmony_ci * detect recursion. 91962306a36Sopenharmony_ci * @table: The table that is searched for metrics, most commonly the table for the 92062306a36Sopenharmony_ci * architecture perf is running upon. 92162306a36Sopenharmony_ci */ 92262306a36Sopenharmony_cistatic int __add_metric(struct list_head *metric_list, 92362306a36Sopenharmony_ci const struct pmu_metric *pm, 92462306a36Sopenharmony_ci const char *modifier, 92562306a36Sopenharmony_ci bool metric_no_group, 92662306a36Sopenharmony_ci bool metric_no_threshold, 92762306a36Sopenharmony_ci int runtime, 92862306a36Sopenharmony_ci const char *user_requested_cpu_list, 92962306a36Sopenharmony_ci bool system_wide, 93062306a36Sopenharmony_ci struct metric *root_metric, 93162306a36Sopenharmony_ci const struct visited_metric *visited, 93262306a36Sopenharmony_ci const struct pmu_metrics_table *table) 93362306a36Sopenharmony_ci{ 93462306a36Sopenharmony_ci const struct visited_metric *vm; 93562306a36Sopenharmony_ci int ret; 93662306a36Sopenharmony_ci bool is_root = !root_metric; 93762306a36Sopenharmony_ci const char *expr; 93862306a36Sopenharmony_ci struct visited_metric visited_node = { 93962306a36Sopenharmony_ci .name = pm->metric_name, 94062306a36Sopenharmony_ci .parent = visited, 94162306a36Sopenharmony_ci }; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci for (vm = visited; vm; vm = vm->parent) { 94462306a36Sopenharmony_ci if (!strcmp(pm->metric_name, vm->name)) { 94562306a36Sopenharmony_ci pr_err("failed: recursion detected for %s\n", pm->metric_name); 94662306a36Sopenharmony_ci return -1; 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci } 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci if (is_root) { 95162306a36Sopenharmony_ci /* 95262306a36Sopenharmony_ci * This metric is the root of a tree and may reference other 95362306a36Sopenharmony_ci * metrics that are added recursively. 95462306a36Sopenharmony_ci */ 95562306a36Sopenharmony_ci root_metric = metric__new(pm, modifier, metric_no_group, runtime, 95662306a36Sopenharmony_ci user_requested_cpu_list, system_wide); 95762306a36Sopenharmony_ci if (!root_metric) 95862306a36Sopenharmony_ci return -ENOMEM; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci } else { 96162306a36Sopenharmony_ci int cnt = 0; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci /* 96462306a36Sopenharmony_ci * This metric was referenced in a metric higher in the 96562306a36Sopenharmony_ci * tree. Check if the same metric is already resolved in the 96662306a36Sopenharmony_ci * metric_refs list. 96762306a36Sopenharmony_ci */ 96862306a36Sopenharmony_ci if (root_metric->metric_refs) { 96962306a36Sopenharmony_ci for (; root_metric->metric_refs[cnt].metric_name; cnt++) { 97062306a36Sopenharmony_ci if (!strcmp(pm->metric_name, 97162306a36Sopenharmony_ci root_metric->metric_refs[cnt].metric_name)) 97262306a36Sopenharmony_ci return 0; 97362306a36Sopenharmony_ci } 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci /* Create reference. Need space for the entry and the terminator. */ 97762306a36Sopenharmony_ci root_metric->metric_refs = realloc(root_metric->metric_refs, 97862306a36Sopenharmony_ci (cnt + 2) * sizeof(struct metric_ref)); 97962306a36Sopenharmony_ci if (!root_metric->metric_refs) 98062306a36Sopenharmony_ci return -ENOMEM; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci /* 98362306a36Sopenharmony_ci * Intentionally passing just const char pointers, 98462306a36Sopenharmony_ci * from 'pe' object, so they never go away. We don't 98562306a36Sopenharmony_ci * need to change them, so there's no need to create 98662306a36Sopenharmony_ci * our own copy. 98762306a36Sopenharmony_ci */ 98862306a36Sopenharmony_ci root_metric->metric_refs[cnt].metric_name = pm->metric_name; 98962306a36Sopenharmony_ci root_metric->metric_refs[cnt].metric_expr = pm->metric_expr; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci /* Null terminate array. */ 99262306a36Sopenharmony_ci root_metric->metric_refs[cnt+1].metric_name = NULL; 99362306a36Sopenharmony_ci root_metric->metric_refs[cnt+1].metric_expr = NULL; 99462306a36Sopenharmony_ci } 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci /* 99762306a36Sopenharmony_ci * For both the parent and referenced metrics, we parse 99862306a36Sopenharmony_ci * all the metric's IDs and add it to the root context. 99962306a36Sopenharmony_ci */ 100062306a36Sopenharmony_ci ret = 0; 100162306a36Sopenharmony_ci expr = pm->metric_expr; 100262306a36Sopenharmony_ci if (is_root && pm->metric_threshold) { 100362306a36Sopenharmony_ci /* 100462306a36Sopenharmony_ci * Threshold expressions are built off the actual metric. Switch 100562306a36Sopenharmony_ci * to use that in case of additional necessary events. Change 100662306a36Sopenharmony_ci * the visited node name to avoid this being flagged as 100762306a36Sopenharmony_ci * recursion. If the threshold events are disabled, just use the 100862306a36Sopenharmony_ci * metric's name as a reference. This allows metric threshold 100962306a36Sopenharmony_ci * computation if there are sufficient events. 101062306a36Sopenharmony_ci */ 101162306a36Sopenharmony_ci assert(strstr(pm->metric_threshold, pm->metric_name)); 101262306a36Sopenharmony_ci expr = metric_no_threshold ? pm->metric_name : pm->metric_threshold; 101362306a36Sopenharmony_ci visited_node.name = "__threshold__"; 101462306a36Sopenharmony_ci } 101562306a36Sopenharmony_ci if (expr__find_ids(expr, NULL, root_metric->pctx) < 0) { 101662306a36Sopenharmony_ci /* Broken metric. */ 101762306a36Sopenharmony_ci ret = -EINVAL; 101862306a36Sopenharmony_ci } 101962306a36Sopenharmony_ci if (!ret) { 102062306a36Sopenharmony_ci /* Resolve referenced metrics. */ 102162306a36Sopenharmony_ci const char *pmu = pm->pmu ?: "cpu"; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci ret = resolve_metric(metric_list, pmu, modifier, metric_no_group, 102462306a36Sopenharmony_ci metric_no_threshold, user_requested_cpu_list, 102562306a36Sopenharmony_ci system_wide, root_metric, &visited_node, 102662306a36Sopenharmony_ci table); 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci if (ret) { 102962306a36Sopenharmony_ci if (is_root) 103062306a36Sopenharmony_ci metric__free(root_metric); 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci } else if (is_root) 103362306a36Sopenharmony_ci list_add(&root_metric->nd, metric_list); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci return ret; 103662306a36Sopenharmony_ci} 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_cistruct metricgroup__find_metric_data { 103962306a36Sopenharmony_ci const char *pmu; 104062306a36Sopenharmony_ci const char *metric; 104162306a36Sopenharmony_ci struct pmu_metric *pm; 104262306a36Sopenharmony_ci}; 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_cistatic int metricgroup__find_metric_callback(const struct pmu_metric *pm, 104562306a36Sopenharmony_ci const struct pmu_metrics_table *table __maybe_unused, 104662306a36Sopenharmony_ci void *vdata) 104762306a36Sopenharmony_ci{ 104862306a36Sopenharmony_ci struct metricgroup__find_metric_data *data = vdata; 104962306a36Sopenharmony_ci const char *pm_pmu = pm->pmu ?: "cpu"; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci if (strcmp(data->pmu, "all") && strcmp(pm_pmu, data->pmu)) 105262306a36Sopenharmony_ci return 0; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci if (!match_metric(pm->metric_name, data->metric)) 105562306a36Sopenharmony_ci return 0; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci memcpy(data->pm, pm, sizeof(*pm)); 105862306a36Sopenharmony_ci return 1; 105962306a36Sopenharmony_ci} 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_cistatic bool metricgroup__find_metric(const char *pmu, 106262306a36Sopenharmony_ci const char *metric, 106362306a36Sopenharmony_ci const struct pmu_metrics_table *table, 106462306a36Sopenharmony_ci struct pmu_metric *pm) 106562306a36Sopenharmony_ci{ 106662306a36Sopenharmony_ci struct metricgroup__find_metric_data data = { 106762306a36Sopenharmony_ci .pmu = pmu, 106862306a36Sopenharmony_ci .metric = metric, 106962306a36Sopenharmony_ci .pm = pm, 107062306a36Sopenharmony_ci }; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci return pmu_metrics_table__for_each_metric(table, metricgroup__find_metric_callback, &data) 107362306a36Sopenharmony_ci ? true : false; 107462306a36Sopenharmony_ci} 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_cistatic int add_metric(struct list_head *metric_list, 107762306a36Sopenharmony_ci const struct pmu_metric *pm, 107862306a36Sopenharmony_ci const char *modifier, 107962306a36Sopenharmony_ci bool metric_no_group, 108062306a36Sopenharmony_ci bool metric_no_threshold, 108162306a36Sopenharmony_ci const char *user_requested_cpu_list, 108262306a36Sopenharmony_ci bool system_wide, 108362306a36Sopenharmony_ci struct metric *root_metric, 108462306a36Sopenharmony_ci const struct visited_metric *visited, 108562306a36Sopenharmony_ci const struct pmu_metrics_table *table) 108662306a36Sopenharmony_ci{ 108762306a36Sopenharmony_ci int ret = 0; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci pr_debug("metric expr %s for %s\n", pm->metric_expr, pm->metric_name); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci if (!strstr(pm->metric_expr, "?")) { 109262306a36Sopenharmony_ci ret = __add_metric(metric_list, pm, modifier, metric_no_group, 109362306a36Sopenharmony_ci metric_no_threshold, 0, user_requested_cpu_list, 109462306a36Sopenharmony_ci system_wide, root_metric, visited, table); 109562306a36Sopenharmony_ci } else { 109662306a36Sopenharmony_ci int j, count; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci count = arch_get_runtimeparam(pm); 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci /* This loop is added to create multiple 110162306a36Sopenharmony_ci * events depend on count value and add 110262306a36Sopenharmony_ci * those events to metric_list. 110362306a36Sopenharmony_ci */ 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci for (j = 0; j < count && !ret; j++) 110662306a36Sopenharmony_ci ret = __add_metric(metric_list, pm, modifier, metric_no_group, 110762306a36Sopenharmony_ci metric_no_threshold, j, user_requested_cpu_list, 110862306a36Sopenharmony_ci system_wide, root_metric, visited, table); 110962306a36Sopenharmony_ci } 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci return ret; 111262306a36Sopenharmony_ci} 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_cistatic int metricgroup__add_metric_sys_event_iter(const struct pmu_metric *pm, 111562306a36Sopenharmony_ci const struct pmu_metrics_table *table __maybe_unused, 111662306a36Sopenharmony_ci void *data) 111762306a36Sopenharmony_ci{ 111862306a36Sopenharmony_ci struct metricgroup_add_iter_data *d = data; 111962306a36Sopenharmony_ci int ret; 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci if (!match_pm_metric(pm, d->pmu, d->metric_name)) 112262306a36Sopenharmony_ci return 0; 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci ret = add_metric(d->metric_list, pm, d->modifier, d->metric_no_group, 112562306a36Sopenharmony_ci d->metric_no_threshold, d->user_requested_cpu_list, 112662306a36Sopenharmony_ci d->system_wide, d->root_metric, d->visited, d->table); 112762306a36Sopenharmony_ci if (ret) 112862306a36Sopenharmony_ci goto out; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci *(d->has_match) = true; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ciout: 113362306a36Sopenharmony_ci *(d->ret) = ret; 113462306a36Sopenharmony_ci return ret; 113562306a36Sopenharmony_ci} 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci/** 113862306a36Sopenharmony_ci * metric_list_cmp - list_sort comparator that sorts metrics with more events to 113962306a36Sopenharmony_ci * the front. tool events are excluded from the count. 114062306a36Sopenharmony_ci */ 114162306a36Sopenharmony_cistatic int metric_list_cmp(void *priv __maybe_unused, const struct list_head *l, 114262306a36Sopenharmony_ci const struct list_head *r) 114362306a36Sopenharmony_ci{ 114462306a36Sopenharmony_ci const struct metric *left = container_of(l, struct metric, nd); 114562306a36Sopenharmony_ci const struct metric *right = container_of(r, struct metric, nd); 114662306a36Sopenharmony_ci struct expr_id_data *data; 114762306a36Sopenharmony_ci int i, left_count, right_count; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci left_count = hashmap__size(left->pctx->ids); 115062306a36Sopenharmony_ci perf_tool_event__for_each_event(i) { 115162306a36Sopenharmony_ci if (!expr__get_id(left->pctx, perf_tool_event__to_str(i), &data)) 115262306a36Sopenharmony_ci left_count--; 115362306a36Sopenharmony_ci } 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci right_count = hashmap__size(right->pctx->ids); 115662306a36Sopenharmony_ci perf_tool_event__for_each_event(i) { 115762306a36Sopenharmony_ci if (!expr__get_id(right->pctx, perf_tool_event__to_str(i), &data)) 115862306a36Sopenharmony_ci right_count--; 115962306a36Sopenharmony_ci } 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci return right_count - left_count; 116262306a36Sopenharmony_ci} 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci/** 116562306a36Sopenharmony_ci * default_metricgroup_cmp - Implements complex key for the Default metricgroup 116662306a36Sopenharmony_ci * that first sorts by default_metricgroup_name, then 116762306a36Sopenharmony_ci * metric_name. 116862306a36Sopenharmony_ci */ 116962306a36Sopenharmony_cistatic int default_metricgroup_cmp(void *priv __maybe_unused, 117062306a36Sopenharmony_ci const struct list_head *l, 117162306a36Sopenharmony_ci const struct list_head *r) 117262306a36Sopenharmony_ci{ 117362306a36Sopenharmony_ci const struct metric *left = container_of(l, struct metric, nd); 117462306a36Sopenharmony_ci const struct metric *right = container_of(r, struct metric, nd); 117562306a36Sopenharmony_ci int diff = strcmp(right->default_metricgroup_name, left->default_metricgroup_name); 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci if (diff) 117862306a36Sopenharmony_ci return diff; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci return strcmp(right->metric_name, left->metric_name); 118162306a36Sopenharmony_ci} 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_cistruct metricgroup__add_metric_data { 118462306a36Sopenharmony_ci struct list_head *list; 118562306a36Sopenharmony_ci const char *pmu; 118662306a36Sopenharmony_ci const char *metric_name; 118762306a36Sopenharmony_ci const char *modifier; 118862306a36Sopenharmony_ci const char *user_requested_cpu_list; 118962306a36Sopenharmony_ci bool metric_no_group; 119062306a36Sopenharmony_ci bool metric_no_threshold; 119162306a36Sopenharmony_ci bool system_wide; 119262306a36Sopenharmony_ci bool has_match; 119362306a36Sopenharmony_ci}; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_cistatic int metricgroup__add_metric_callback(const struct pmu_metric *pm, 119662306a36Sopenharmony_ci const struct pmu_metrics_table *table, 119762306a36Sopenharmony_ci void *vdata) 119862306a36Sopenharmony_ci{ 119962306a36Sopenharmony_ci struct metricgroup__add_metric_data *data = vdata; 120062306a36Sopenharmony_ci int ret = 0; 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci if (pm->metric_expr && match_pm_metric(pm, data->pmu, data->metric_name)) { 120362306a36Sopenharmony_ci bool metric_no_group = data->metric_no_group || 120462306a36Sopenharmony_ci match_metric(pm->metricgroup_no_group, data->metric_name); 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci data->has_match = true; 120762306a36Sopenharmony_ci ret = add_metric(data->list, pm, data->modifier, metric_no_group, 120862306a36Sopenharmony_ci data->metric_no_threshold, data->user_requested_cpu_list, 120962306a36Sopenharmony_ci data->system_wide, /*root_metric=*/NULL, 121062306a36Sopenharmony_ci /*visited_metrics=*/NULL, table); 121162306a36Sopenharmony_ci } 121262306a36Sopenharmony_ci return ret; 121362306a36Sopenharmony_ci} 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci/** 121662306a36Sopenharmony_ci * metricgroup__add_metric - Find and add a metric, or a metric group. 121762306a36Sopenharmony_ci * @pmu: The PMU name to search for metrics on, or "all" for all PMUs. 121862306a36Sopenharmony_ci * @metric_name: The name of the metric or metric group. For example, "IPC" 121962306a36Sopenharmony_ci * could be the name of a metric and "TopDownL1" the name of a 122062306a36Sopenharmony_ci * metric group. 122162306a36Sopenharmony_ci * @modifier: if non-null event modifiers like "u". 122262306a36Sopenharmony_ci * @metric_no_group: Should events written to events be grouped "{}" or 122362306a36Sopenharmony_ci * global. Grouping is the default but due to multiplexing the 122462306a36Sopenharmony_ci * user may override. 122562306a36Sopenharmony_ci * @user_requested_cpu_list: Command line specified CPUs to record on. 122662306a36Sopenharmony_ci * @system_wide: Are events for all processes recorded. 122762306a36Sopenharmony_ci * @metric_list: The list that the metric or metric group are added to. 122862306a36Sopenharmony_ci * @table: The table that is searched for metrics, most commonly the table for the 122962306a36Sopenharmony_ci * architecture perf is running upon. 123062306a36Sopenharmony_ci */ 123162306a36Sopenharmony_cistatic int metricgroup__add_metric(const char *pmu, const char *metric_name, const char *modifier, 123262306a36Sopenharmony_ci bool metric_no_group, bool metric_no_threshold, 123362306a36Sopenharmony_ci const char *user_requested_cpu_list, 123462306a36Sopenharmony_ci bool system_wide, 123562306a36Sopenharmony_ci struct list_head *metric_list, 123662306a36Sopenharmony_ci const struct pmu_metrics_table *table) 123762306a36Sopenharmony_ci{ 123862306a36Sopenharmony_ci LIST_HEAD(list); 123962306a36Sopenharmony_ci int ret; 124062306a36Sopenharmony_ci bool has_match = false; 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci { 124362306a36Sopenharmony_ci struct metricgroup__add_metric_data data = { 124462306a36Sopenharmony_ci .list = &list, 124562306a36Sopenharmony_ci .pmu = pmu, 124662306a36Sopenharmony_ci .metric_name = metric_name, 124762306a36Sopenharmony_ci .modifier = modifier, 124862306a36Sopenharmony_ci .metric_no_group = metric_no_group, 124962306a36Sopenharmony_ci .metric_no_threshold = metric_no_threshold, 125062306a36Sopenharmony_ci .user_requested_cpu_list = user_requested_cpu_list, 125162306a36Sopenharmony_ci .system_wide = system_wide, 125262306a36Sopenharmony_ci .has_match = false, 125362306a36Sopenharmony_ci }; 125462306a36Sopenharmony_ci /* 125562306a36Sopenharmony_ci * Iterate over all metrics seeing if metric matches either the 125662306a36Sopenharmony_ci * name or group. When it does add the metric to the list. 125762306a36Sopenharmony_ci */ 125862306a36Sopenharmony_ci ret = pmu_metrics_table__for_each_metric(table, metricgroup__add_metric_callback, 125962306a36Sopenharmony_ci &data); 126062306a36Sopenharmony_ci if (ret) 126162306a36Sopenharmony_ci goto out; 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci has_match = data.has_match; 126462306a36Sopenharmony_ci } 126562306a36Sopenharmony_ci { 126662306a36Sopenharmony_ci struct metricgroup_iter_data data = { 126762306a36Sopenharmony_ci .fn = metricgroup__add_metric_sys_event_iter, 126862306a36Sopenharmony_ci .data = (void *) &(struct metricgroup_add_iter_data) { 126962306a36Sopenharmony_ci .metric_list = &list, 127062306a36Sopenharmony_ci .pmu = pmu, 127162306a36Sopenharmony_ci .metric_name = metric_name, 127262306a36Sopenharmony_ci .modifier = modifier, 127362306a36Sopenharmony_ci .metric_no_group = metric_no_group, 127462306a36Sopenharmony_ci .user_requested_cpu_list = user_requested_cpu_list, 127562306a36Sopenharmony_ci .system_wide = system_wide, 127662306a36Sopenharmony_ci .has_match = &has_match, 127762306a36Sopenharmony_ci .ret = &ret, 127862306a36Sopenharmony_ci .table = table, 127962306a36Sopenharmony_ci }, 128062306a36Sopenharmony_ci }; 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci pmu_for_each_sys_metric(metricgroup__sys_event_iter, &data); 128362306a36Sopenharmony_ci } 128462306a36Sopenharmony_ci /* End of pmu events. */ 128562306a36Sopenharmony_ci if (!has_match) 128662306a36Sopenharmony_ci ret = -EINVAL; 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ciout: 128962306a36Sopenharmony_ci /* 129062306a36Sopenharmony_ci * add to metric_list so that they can be released 129162306a36Sopenharmony_ci * even if it's failed 129262306a36Sopenharmony_ci */ 129362306a36Sopenharmony_ci list_splice(&list, metric_list); 129462306a36Sopenharmony_ci return ret; 129562306a36Sopenharmony_ci} 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci/** 129862306a36Sopenharmony_ci * metricgroup__add_metric_list - Find and add metrics, or metric groups, 129962306a36Sopenharmony_ci * specified in a list. 130062306a36Sopenharmony_ci * @pmu: A pmu to restrict the metrics to, or "all" for all PMUS. 130162306a36Sopenharmony_ci * @list: the list of metrics or metric groups. For example, "IPC,CPI,TopDownL1" 130262306a36Sopenharmony_ci * would match the IPC and CPI metrics, and TopDownL1 would match all 130362306a36Sopenharmony_ci * the metrics in the TopDownL1 group. 130462306a36Sopenharmony_ci * @metric_no_group: Should events written to events be grouped "{}" or 130562306a36Sopenharmony_ci * global. Grouping is the default but due to multiplexing the 130662306a36Sopenharmony_ci * user may override. 130762306a36Sopenharmony_ci * @user_requested_cpu_list: Command line specified CPUs to record on. 130862306a36Sopenharmony_ci * @system_wide: Are events for all processes recorded. 130962306a36Sopenharmony_ci * @metric_list: The list that metrics are added to. 131062306a36Sopenharmony_ci * @table: The table that is searched for metrics, most commonly the table for the 131162306a36Sopenharmony_ci * architecture perf is running upon. 131262306a36Sopenharmony_ci */ 131362306a36Sopenharmony_cistatic int metricgroup__add_metric_list(const char *pmu, const char *list, 131462306a36Sopenharmony_ci bool metric_no_group, 131562306a36Sopenharmony_ci bool metric_no_threshold, 131662306a36Sopenharmony_ci const char *user_requested_cpu_list, 131762306a36Sopenharmony_ci bool system_wide, struct list_head *metric_list, 131862306a36Sopenharmony_ci const struct pmu_metrics_table *table) 131962306a36Sopenharmony_ci{ 132062306a36Sopenharmony_ci char *list_itr, *list_copy, *metric_name, *modifier; 132162306a36Sopenharmony_ci int ret, count = 0; 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci list_copy = strdup(list); 132462306a36Sopenharmony_ci if (!list_copy) 132562306a36Sopenharmony_ci return -ENOMEM; 132662306a36Sopenharmony_ci list_itr = list_copy; 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci while ((metric_name = strsep(&list_itr, ",")) != NULL) { 132962306a36Sopenharmony_ci modifier = strchr(metric_name, ':'); 133062306a36Sopenharmony_ci if (modifier) 133162306a36Sopenharmony_ci *modifier++ = '\0'; 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci ret = metricgroup__add_metric(pmu, metric_name, modifier, 133462306a36Sopenharmony_ci metric_no_group, metric_no_threshold, 133562306a36Sopenharmony_ci user_requested_cpu_list, 133662306a36Sopenharmony_ci system_wide, metric_list, table); 133762306a36Sopenharmony_ci if (ret == -EINVAL) 133862306a36Sopenharmony_ci pr_err("Cannot find metric or group `%s'\n", metric_name); 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci if (ret) 134162306a36Sopenharmony_ci break; 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci count++; 134462306a36Sopenharmony_ci } 134562306a36Sopenharmony_ci free(list_copy); 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci if (!ret) { 134862306a36Sopenharmony_ci /* 134962306a36Sopenharmony_ci * Warn about nmi_watchdog if any parsed metrics had the 135062306a36Sopenharmony_ci * NO_NMI_WATCHDOG constraint. 135162306a36Sopenharmony_ci */ 135262306a36Sopenharmony_ci metric__watchdog_constraint_hint(NULL, /*foot=*/true); 135362306a36Sopenharmony_ci /* No metrics. */ 135462306a36Sopenharmony_ci if (count == 0) 135562306a36Sopenharmony_ci return -EINVAL; 135662306a36Sopenharmony_ci } 135762306a36Sopenharmony_ci return ret; 135862306a36Sopenharmony_ci} 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_cistatic void metricgroup__free_metrics(struct list_head *metric_list) 136162306a36Sopenharmony_ci{ 136262306a36Sopenharmony_ci struct metric *m, *tmp; 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci list_for_each_entry_safe (m, tmp, metric_list, nd) { 136562306a36Sopenharmony_ci list_del_init(&m->nd); 136662306a36Sopenharmony_ci metric__free(m); 136762306a36Sopenharmony_ci } 136862306a36Sopenharmony_ci} 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci/** 137162306a36Sopenharmony_ci * find_tool_events - Search for the pressence of tool events in metric_list. 137262306a36Sopenharmony_ci * @metric_list: List to take metrics from. 137362306a36Sopenharmony_ci * @tool_events: Array of false values, indices corresponding to tool events set 137462306a36Sopenharmony_ci * to true if tool event is found. 137562306a36Sopenharmony_ci */ 137662306a36Sopenharmony_cistatic void find_tool_events(const struct list_head *metric_list, 137762306a36Sopenharmony_ci bool tool_events[PERF_TOOL_MAX]) 137862306a36Sopenharmony_ci{ 137962306a36Sopenharmony_ci struct metric *m; 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci list_for_each_entry(m, metric_list, nd) { 138262306a36Sopenharmony_ci int i; 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci perf_tool_event__for_each_event(i) { 138562306a36Sopenharmony_ci struct expr_id_data *data; 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci if (!tool_events[i] && 138862306a36Sopenharmony_ci !expr__get_id(m->pctx, perf_tool_event__to_str(i), &data)) 138962306a36Sopenharmony_ci tool_events[i] = true; 139062306a36Sopenharmony_ci } 139162306a36Sopenharmony_ci } 139262306a36Sopenharmony_ci} 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci/** 139562306a36Sopenharmony_ci * build_combined_expr_ctx - Make an expr_parse_ctx with all !group_events 139662306a36Sopenharmony_ci * metric IDs, as the IDs are held in a set, 139762306a36Sopenharmony_ci * duplicates will be removed. 139862306a36Sopenharmony_ci * @metric_list: List to take metrics from. 139962306a36Sopenharmony_ci * @combined: Out argument for result. 140062306a36Sopenharmony_ci */ 140162306a36Sopenharmony_cistatic int build_combined_expr_ctx(const struct list_head *metric_list, 140262306a36Sopenharmony_ci struct expr_parse_ctx **combined) 140362306a36Sopenharmony_ci{ 140462306a36Sopenharmony_ci struct hashmap_entry *cur; 140562306a36Sopenharmony_ci size_t bkt; 140662306a36Sopenharmony_ci struct metric *m; 140762306a36Sopenharmony_ci char *dup; 140862306a36Sopenharmony_ci int ret; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci *combined = expr__ctx_new(); 141162306a36Sopenharmony_ci if (!*combined) 141262306a36Sopenharmony_ci return -ENOMEM; 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci list_for_each_entry(m, metric_list, nd) { 141562306a36Sopenharmony_ci if (!m->group_events && !m->modifier) { 141662306a36Sopenharmony_ci hashmap__for_each_entry(m->pctx->ids, cur, bkt) { 141762306a36Sopenharmony_ci dup = strdup(cur->pkey); 141862306a36Sopenharmony_ci if (!dup) { 141962306a36Sopenharmony_ci ret = -ENOMEM; 142062306a36Sopenharmony_ci goto err_out; 142162306a36Sopenharmony_ci } 142262306a36Sopenharmony_ci ret = expr__add_id(*combined, dup); 142362306a36Sopenharmony_ci if (ret) 142462306a36Sopenharmony_ci goto err_out; 142562306a36Sopenharmony_ci } 142662306a36Sopenharmony_ci } 142762306a36Sopenharmony_ci } 142862306a36Sopenharmony_ci return 0; 142962306a36Sopenharmony_cierr_out: 143062306a36Sopenharmony_ci expr__ctx_free(*combined); 143162306a36Sopenharmony_ci *combined = NULL; 143262306a36Sopenharmony_ci return ret; 143362306a36Sopenharmony_ci} 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci/** 143662306a36Sopenharmony_ci * parse_ids - Build the event string for the ids and parse them creating an 143762306a36Sopenharmony_ci * evlist. The encoded metric_ids are decoded. 143862306a36Sopenharmony_ci * @metric_no_merge: is metric sharing explicitly disabled. 143962306a36Sopenharmony_ci * @fake_pmu: used when testing metrics not supported by the current CPU. 144062306a36Sopenharmony_ci * @ids: the event identifiers parsed from a metric. 144162306a36Sopenharmony_ci * @modifier: any modifiers added to the events. 144262306a36Sopenharmony_ci * @group_events: should events be placed in a weak group. 144362306a36Sopenharmony_ci * @tool_events: entries set true if the tool event of index could be present in 144462306a36Sopenharmony_ci * the overall list of metrics. 144562306a36Sopenharmony_ci * @out_evlist: the created list of events. 144662306a36Sopenharmony_ci */ 144762306a36Sopenharmony_cistatic int parse_ids(bool metric_no_merge, struct perf_pmu *fake_pmu, 144862306a36Sopenharmony_ci struct expr_parse_ctx *ids, const char *modifier, 144962306a36Sopenharmony_ci bool group_events, const bool tool_events[PERF_TOOL_MAX], 145062306a36Sopenharmony_ci struct evlist **out_evlist) 145162306a36Sopenharmony_ci{ 145262306a36Sopenharmony_ci struct parse_events_error parse_error; 145362306a36Sopenharmony_ci struct evlist *parsed_evlist; 145462306a36Sopenharmony_ci struct strbuf events = STRBUF_INIT; 145562306a36Sopenharmony_ci int ret; 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci *out_evlist = NULL; 145862306a36Sopenharmony_ci if (!metric_no_merge || hashmap__size(ids->ids) == 0) { 145962306a36Sopenharmony_ci bool added_event = false; 146062306a36Sopenharmony_ci int i; 146162306a36Sopenharmony_ci /* 146262306a36Sopenharmony_ci * We may fail to share events between metrics because a tool 146362306a36Sopenharmony_ci * event isn't present in one metric. For example, a ratio of 146462306a36Sopenharmony_ci * cache misses doesn't need duration_time but the same events 146562306a36Sopenharmony_ci * may be used for a misses per second. Events without sharing 146662306a36Sopenharmony_ci * implies multiplexing, that is best avoided, so place 146762306a36Sopenharmony_ci * all tool events in every group. 146862306a36Sopenharmony_ci * 146962306a36Sopenharmony_ci * Also, there may be no ids/events in the expression parsing 147062306a36Sopenharmony_ci * context because of constant evaluation, e.g.: 147162306a36Sopenharmony_ci * event1 if #smt_on else 0 147262306a36Sopenharmony_ci * Add a tool event to avoid a parse error on an empty string. 147362306a36Sopenharmony_ci */ 147462306a36Sopenharmony_ci perf_tool_event__for_each_event(i) { 147562306a36Sopenharmony_ci if (tool_events[i]) { 147662306a36Sopenharmony_ci char *tmp = strdup(perf_tool_event__to_str(i)); 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci if (!tmp) 147962306a36Sopenharmony_ci return -ENOMEM; 148062306a36Sopenharmony_ci ids__insert(ids->ids, tmp); 148162306a36Sopenharmony_ci added_event = true; 148262306a36Sopenharmony_ci } 148362306a36Sopenharmony_ci } 148462306a36Sopenharmony_ci if (!added_event && hashmap__size(ids->ids) == 0) { 148562306a36Sopenharmony_ci char *tmp = strdup("duration_time"); 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci if (!tmp) 148862306a36Sopenharmony_ci return -ENOMEM; 148962306a36Sopenharmony_ci ids__insert(ids->ids, tmp); 149062306a36Sopenharmony_ci } 149162306a36Sopenharmony_ci } 149262306a36Sopenharmony_ci ret = metricgroup__build_event_string(&events, ids, modifier, 149362306a36Sopenharmony_ci group_events); 149462306a36Sopenharmony_ci if (ret) 149562306a36Sopenharmony_ci return ret; 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci parsed_evlist = evlist__new(); 149862306a36Sopenharmony_ci if (!parsed_evlist) { 149962306a36Sopenharmony_ci ret = -ENOMEM; 150062306a36Sopenharmony_ci goto err_out; 150162306a36Sopenharmony_ci } 150262306a36Sopenharmony_ci pr_debug("Parsing metric events '%s'\n", events.buf); 150362306a36Sopenharmony_ci parse_events_error__init(&parse_error); 150462306a36Sopenharmony_ci ret = __parse_events(parsed_evlist, events.buf, /*pmu_filter=*/NULL, 150562306a36Sopenharmony_ci &parse_error, fake_pmu, /*warn_if_reordered=*/false); 150662306a36Sopenharmony_ci if (ret) { 150762306a36Sopenharmony_ci parse_events_error__print(&parse_error, events.buf); 150862306a36Sopenharmony_ci goto err_out; 150962306a36Sopenharmony_ci } 151062306a36Sopenharmony_ci ret = decode_all_metric_ids(parsed_evlist, modifier); 151162306a36Sopenharmony_ci if (ret) 151262306a36Sopenharmony_ci goto err_out; 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci *out_evlist = parsed_evlist; 151562306a36Sopenharmony_ci parsed_evlist = NULL; 151662306a36Sopenharmony_cierr_out: 151762306a36Sopenharmony_ci parse_events_error__exit(&parse_error); 151862306a36Sopenharmony_ci evlist__delete(parsed_evlist); 151962306a36Sopenharmony_ci strbuf_release(&events); 152062306a36Sopenharmony_ci return ret; 152162306a36Sopenharmony_ci} 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_cistatic int parse_groups(struct evlist *perf_evlist, 152462306a36Sopenharmony_ci const char *pmu, const char *str, 152562306a36Sopenharmony_ci bool metric_no_group, 152662306a36Sopenharmony_ci bool metric_no_merge, 152762306a36Sopenharmony_ci bool metric_no_threshold, 152862306a36Sopenharmony_ci const char *user_requested_cpu_list, 152962306a36Sopenharmony_ci bool system_wide, 153062306a36Sopenharmony_ci struct perf_pmu *fake_pmu, 153162306a36Sopenharmony_ci struct rblist *metric_events_list, 153262306a36Sopenharmony_ci const struct pmu_metrics_table *table) 153362306a36Sopenharmony_ci{ 153462306a36Sopenharmony_ci struct evlist *combined_evlist = NULL; 153562306a36Sopenharmony_ci LIST_HEAD(metric_list); 153662306a36Sopenharmony_ci struct metric *m; 153762306a36Sopenharmony_ci bool tool_events[PERF_TOOL_MAX] = {false}; 153862306a36Sopenharmony_ci bool is_default = !strcmp(str, "Default"); 153962306a36Sopenharmony_ci int ret; 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci if (metric_events_list->nr_entries == 0) 154262306a36Sopenharmony_ci metricgroup__rblist_init(metric_events_list); 154362306a36Sopenharmony_ci ret = metricgroup__add_metric_list(pmu, str, metric_no_group, metric_no_threshold, 154462306a36Sopenharmony_ci user_requested_cpu_list, 154562306a36Sopenharmony_ci system_wide, &metric_list, table); 154662306a36Sopenharmony_ci if (ret) 154762306a36Sopenharmony_ci goto out; 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci /* Sort metrics from largest to smallest. */ 155062306a36Sopenharmony_ci list_sort(NULL, &metric_list, metric_list_cmp); 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci if (!metric_no_merge) { 155362306a36Sopenharmony_ci struct expr_parse_ctx *combined = NULL; 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci find_tool_events(&metric_list, tool_events); 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci ret = build_combined_expr_ctx(&metric_list, &combined); 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci if (!ret && combined && hashmap__size(combined->ids)) { 156062306a36Sopenharmony_ci ret = parse_ids(metric_no_merge, fake_pmu, combined, 156162306a36Sopenharmony_ci /*modifier=*/NULL, 156262306a36Sopenharmony_ci /*group_events=*/false, 156362306a36Sopenharmony_ci tool_events, 156462306a36Sopenharmony_ci &combined_evlist); 156562306a36Sopenharmony_ci } 156662306a36Sopenharmony_ci if (combined) 156762306a36Sopenharmony_ci expr__ctx_free(combined); 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci if (ret) 157062306a36Sopenharmony_ci goto out; 157162306a36Sopenharmony_ci } 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci if (is_default) 157462306a36Sopenharmony_ci list_sort(NULL, &metric_list, default_metricgroup_cmp); 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci list_for_each_entry(m, &metric_list, nd) { 157762306a36Sopenharmony_ci struct metric_event *me; 157862306a36Sopenharmony_ci struct evsel **metric_events; 157962306a36Sopenharmony_ci struct evlist *metric_evlist = NULL; 158062306a36Sopenharmony_ci struct metric *n; 158162306a36Sopenharmony_ci struct metric_expr *expr; 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci if (combined_evlist && !m->group_events) { 158462306a36Sopenharmony_ci metric_evlist = combined_evlist; 158562306a36Sopenharmony_ci } else if (!metric_no_merge) { 158662306a36Sopenharmony_ci /* 158762306a36Sopenharmony_ci * See if the IDs for this metric are a subset of an 158862306a36Sopenharmony_ci * earlier metric. 158962306a36Sopenharmony_ci */ 159062306a36Sopenharmony_ci list_for_each_entry(n, &metric_list, nd) { 159162306a36Sopenharmony_ci if (m == n) 159262306a36Sopenharmony_ci break; 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci if (n->evlist == NULL) 159562306a36Sopenharmony_ci continue; 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci if ((!m->modifier && n->modifier) || 159862306a36Sopenharmony_ci (m->modifier && !n->modifier) || 159962306a36Sopenharmony_ci (m->modifier && n->modifier && 160062306a36Sopenharmony_ci strcmp(m->modifier, n->modifier))) 160162306a36Sopenharmony_ci continue; 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci if ((!m->pmu && n->pmu) || 160462306a36Sopenharmony_ci (m->pmu && !n->pmu) || 160562306a36Sopenharmony_ci (m->pmu && n->pmu && strcmp(m->pmu, n->pmu))) 160662306a36Sopenharmony_ci continue; 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci if (expr__subset_of_ids(n->pctx, m->pctx)) { 160962306a36Sopenharmony_ci pr_debug("Events in '%s' fully contained within '%s'\n", 161062306a36Sopenharmony_ci m->metric_name, n->metric_name); 161162306a36Sopenharmony_ci metric_evlist = n->evlist; 161262306a36Sopenharmony_ci break; 161362306a36Sopenharmony_ci } 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci } 161662306a36Sopenharmony_ci } 161762306a36Sopenharmony_ci if (!metric_evlist) { 161862306a36Sopenharmony_ci ret = parse_ids(metric_no_merge, fake_pmu, m->pctx, m->modifier, 161962306a36Sopenharmony_ci m->group_events, tool_events, &m->evlist); 162062306a36Sopenharmony_ci if (ret) 162162306a36Sopenharmony_ci goto out; 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci metric_evlist = m->evlist; 162462306a36Sopenharmony_ci } 162562306a36Sopenharmony_ci ret = setup_metric_events(fake_pmu ? "all" : m->pmu, m->pctx->ids, 162662306a36Sopenharmony_ci metric_evlist, &metric_events); 162762306a36Sopenharmony_ci if (ret) { 162862306a36Sopenharmony_ci pr_err("Cannot resolve IDs for %s: %s\n", 162962306a36Sopenharmony_ci m->metric_name, m->metric_expr); 163062306a36Sopenharmony_ci goto out; 163162306a36Sopenharmony_ci } 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci me = metricgroup__lookup(metric_events_list, metric_events[0], true); 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci expr = malloc(sizeof(struct metric_expr)); 163662306a36Sopenharmony_ci if (!expr) { 163762306a36Sopenharmony_ci ret = -ENOMEM; 163862306a36Sopenharmony_ci free(metric_events); 163962306a36Sopenharmony_ci goto out; 164062306a36Sopenharmony_ci } 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci expr->metric_refs = m->metric_refs; 164362306a36Sopenharmony_ci m->metric_refs = NULL; 164462306a36Sopenharmony_ci expr->metric_expr = m->metric_expr; 164562306a36Sopenharmony_ci if (m->modifier) { 164662306a36Sopenharmony_ci char *tmp; 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci if (asprintf(&tmp, "%s:%s", m->metric_name, m->modifier) < 0) 164962306a36Sopenharmony_ci expr->metric_name = NULL; 165062306a36Sopenharmony_ci else 165162306a36Sopenharmony_ci expr->metric_name = tmp; 165262306a36Sopenharmony_ci } else 165362306a36Sopenharmony_ci expr->metric_name = strdup(m->metric_name); 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci if (!expr->metric_name) { 165662306a36Sopenharmony_ci ret = -ENOMEM; 165762306a36Sopenharmony_ci free(metric_events); 165862306a36Sopenharmony_ci goto out; 165962306a36Sopenharmony_ci } 166062306a36Sopenharmony_ci expr->metric_threshold = m->metric_threshold; 166162306a36Sopenharmony_ci expr->metric_unit = m->metric_unit; 166262306a36Sopenharmony_ci expr->metric_events = metric_events; 166362306a36Sopenharmony_ci expr->runtime = m->pctx->sctx.runtime; 166462306a36Sopenharmony_ci expr->default_metricgroup_name = m->default_metricgroup_name; 166562306a36Sopenharmony_ci me->is_default = is_default; 166662306a36Sopenharmony_ci list_add(&expr->nd, &me->head); 166762306a36Sopenharmony_ci } 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci if (combined_evlist) { 167162306a36Sopenharmony_ci evlist__splice_list_tail(perf_evlist, &combined_evlist->core.entries); 167262306a36Sopenharmony_ci evlist__delete(combined_evlist); 167362306a36Sopenharmony_ci } 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci list_for_each_entry(m, &metric_list, nd) { 167662306a36Sopenharmony_ci if (m->evlist) 167762306a36Sopenharmony_ci evlist__splice_list_tail(perf_evlist, &m->evlist->core.entries); 167862306a36Sopenharmony_ci } 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ciout: 168162306a36Sopenharmony_ci metricgroup__free_metrics(&metric_list); 168262306a36Sopenharmony_ci return ret; 168362306a36Sopenharmony_ci} 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ciint metricgroup__parse_groups(struct evlist *perf_evlist, 168662306a36Sopenharmony_ci const char *pmu, 168762306a36Sopenharmony_ci const char *str, 168862306a36Sopenharmony_ci bool metric_no_group, 168962306a36Sopenharmony_ci bool metric_no_merge, 169062306a36Sopenharmony_ci bool metric_no_threshold, 169162306a36Sopenharmony_ci const char *user_requested_cpu_list, 169262306a36Sopenharmony_ci bool system_wide, 169362306a36Sopenharmony_ci struct rblist *metric_events) 169462306a36Sopenharmony_ci{ 169562306a36Sopenharmony_ci const struct pmu_metrics_table *table = pmu_metrics_table__find(); 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci if (!table) 169862306a36Sopenharmony_ci return -EINVAL; 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci return parse_groups(perf_evlist, pmu, str, metric_no_group, metric_no_merge, 170162306a36Sopenharmony_ci metric_no_threshold, user_requested_cpu_list, system_wide, 170262306a36Sopenharmony_ci /*fake_pmu=*/NULL, metric_events, table); 170362306a36Sopenharmony_ci} 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ciint metricgroup__parse_groups_test(struct evlist *evlist, 170662306a36Sopenharmony_ci const struct pmu_metrics_table *table, 170762306a36Sopenharmony_ci const char *str, 170862306a36Sopenharmony_ci struct rblist *metric_events) 170962306a36Sopenharmony_ci{ 171062306a36Sopenharmony_ci return parse_groups(evlist, "all", str, 171162306a36Sopenharmony_ci /*metric_no_group=*/false, 171262306a36Sopenharmony_ci /*metric_no_merge=*/false, 171362306a36Sopenharmony_ci /*metric_no_threshold=*/false, 171462306a36Sopenharmony_ci /*user_requested_cpu_list=*/NULL, 171562306a36Sopenharmony_ci /*system_wide=*/false, 171662306a36Sopenharmony_ci &perf_pmu__fake, metric_events, table); 171762306a36Sopenharmony_ci} 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_cistruct metricgroup__has_metric_data { 172062306a36Sopenharmony_ci const char *pmu; 172162306a36Sopenharmony_ci const char *metric; 172262306a36Sopenharmony_ci}; 172362306a36Sopenharmony_cistatic int metricgroup__has_metric_callback(const struct pmu_metric *pm, 172462306a36Sopenharmony_ci const struct pmu_metrics_table *table __maybe_unused, 172562306a36Sopenharmony_ci void *vdata) 172662306a36Sopenharmony_ci{ 172762306a36Sopenharmony_ci struct metricgroup__has_metric_data *data = vdata; 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci return match_pm_metric(pm, data->pmu, data->metric) ? 1 : 0; 173062306a36Sopenharmony_ci} 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_cibool metricgroup__has_metric(const char *pmu, const char *metric) 173362306a36Sopenharmony_ci{ 173462306a36Sopenharmony_ci const struct pmu_metrics_table *table = pmu_metrics_table__find(); 173562306a36Sopenharmony_ci struct metricgroup__has_metric_data data = { 173662306a36Sopenharmony_ci .pmu = pmu, 173762306a36Sopenharmony_ci .metric = metric, 173862306a36Sopenharmony_ci }; 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci if (!table) 174162306a36Sopenharmony_ci return false; 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_ci return pmu_metrics_table__for_each_metric(table, metricgroup__has_metric_callback, &data) 174462306a36Sopenharmony_ci ? true : false; 174562306a36Sopenharmony_ci} 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_cistatic int metricgroup__topdown_max_level_callback(const struct pmu_metric *pm, 174862306a36Sopenharmony_ci const struct pmu_metrics_table *table __maybe_unused, 174962306a36Sopenharmony_ci void *data) 175062306a36Sopenharmony_ci{ 175162306a36Sopenharmony_ci unsigned int *max_level = data; 175262306a36Sopenharmony_ci unsigned int level; 175362306a36Sopenharmony_ci const char *p = strstr(pm->metric_group ?: "", "TopdownL"); 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci if (!p || p[8] == '\0') 175662306a36Sopenharmony_ci return 0; 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci level = p[8] - '0'; 175962306a36Sopenharmony_ci if (level > *max_level) 176062306a36Sopenharmony_ci *max_level = level; 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci return 0; 176362306a36Sopenharmony_ci} 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ciunsigned int metricgroups__topdown_max_level(void) 176662306a36Sopenharmony_ci{ 176762306a36Sopenharmony_ci unsigned int max_level = 0; 176862306a36Sopenharmony_ci const struct pmu_metrics_table *table = pmu_metrics_table__find(); 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci if (!table) 177162306a36Sopenharmony_ci return false; 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci pmu_metrics_table__for_each_metric(table, metricgroup__topdown_max_level_callback, 177462306a36Sopenharmony_ci &max_level); 177562306a36Sopenharmony_ci return max_level; 177662306a36Sopenharmony_ci} 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_ciint metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp, 177962306a36Sopenharmony_ci struct rblist *new_metric_events, 178062306a36Sopenharmony_ci struct rblist *old_metric_events) 178162306a36Sopenharmony_ci{ 178262306a36Sopenharmony_ci unsigned int i; 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci for (i = 0; i < rblist__nr_entries(old_metric_events); i++) { 178562306a36Sopenharmony_ci struct rb_node *nd; 178662306a36Sopenharmony_ci struct metric_event *old_me, *new_me; 178762306a36Sopenharmony_ci struct metric_expr *old_expr, *new_expr; 178862306a36Sopenharmony_ci struct evsel *evsel; 178962306a36Sopenharmony_ci size_t alloc_size; 179062306a36Sopenharmony_ci int idx, nr; 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_ci nd = rblist__entry(old_metric_events, i); 179362306a36Sopenharmony_ci old_me = container_of(nd, struct metric_event, nd); 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci evsel = evlist__find_evsel(evlist, old_me->evsel->core.idx); 179662306a36Sopenharmony_ci if (!evsel) 179762306a36Sopenharmony_ci return -EINVAL; 179862306a36Sopenharmony_ci new_me = metricgroup__lookup(new_metric_events, evsel, true); 179962306a36Sopenharmony_ci if (!new_me) 180062306a36Sopenharmony_ci return -ENOMEM; 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci pr_debug("copying metric event for cgroup '%s': %s (idx=%d)\n", 180362306a36Sopenharmony_ci cgrp ? cgrp->name : "root", evsel->name, evsel->core.idx); 180462306a36Sopenharmony_ci 180562306a36Sopenharmony_ci list_for_each_entry(old_expr, &old_me->head, nd) { 180662306a36Sopenharmony_ci new_expr = malloc(sizeof(*new_expr)); 180762306a36Sopenharmony_ci if (!new_expr) 180862306a36Sopenharmony_ci return -ENOMEM; 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci new_expr->metric_expr = old_expr->metric_expr; 181162306a36Sopenharmony_ci new_expr->metric_threshold = old_expr->metric_threshold; 181262306a36Sopenharmony_ci new_expr->metric_name = strdup(old_expr->metric_name); 181362306a36Sopenharmony_ci if (!new_expr->metric_name) 181462306a36Sopenharmony_ci return -ENOMEM; 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci new_expr->metric_unit = old_expr->metric_unit; 181762306a36Sopenharmony_ci new_expr->runtime = old_expr->runtime; 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci if (old_expr->metric_refs) { 182062306a36Sopenharmony_ci /* calculate number of metric_events */ 182162306a36Sopenharmony_ci for (nr = 0; old_expr->metric_refs[nr].metric_name; nr++) 182262306a36Sopenharmony_ci continue; 182362306a36Sopenharmony_ci alloc_size = sizeof(*new_expr->metric_refs); 182462306a36Sopenharmony_ci new_expr->metric_refs = calloc(nr + 1, alloc_size); 182562306a36Sopenharmony_ci if (!new_expr->metric_refs) { 182662306a36Sopenharmony_ci free(new_expr); 182762306a36Sopenharmony_ci return -ENOMEM; 182862306a36Sopenharmony_ci } 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_ci memcpy(new_expr->metric_refs, old_expr->metric_refs, 183162306a36Sopenharmony_ci nr * alloc_size); 183262306a36Sopenharmony_ci } else { 183362306a36Sopenharmony_ci new_expr->metric_refs = NULL; 183462306a36Sopenharmony_ci } 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_ci /* calculate number of metric_events */ 183762306a36Sopenharmony_ci for (nr = 0; old_expr->metric_events[nr]; nr++) 183862306a36Sopenharmony_ci continue; 183962306a36Sopenharmony_ci alloc_size = sizeof(*new_expr->metric_events); 184062306a36Sopenharmony_ci new_expr->metric_events = calloc(nr + 1, alloc_size); 184162306a36Sopenharmony_ci if (!new_expr->metric_events) { 184262306a36Sopenharmony_ci zfree(&new_expr->metric_refs); 184362306a36Sopenharmony_ci free(new_expr); 184462306a36Sopenharmony_ci return -ENOMEM; 184562306a36Sopenharmony_ci } 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci /* copy evsel in the same position */ 184862306a36Sopenharmony_ci for (idx = 0; idx < nr; idx++) { 184962306a36Sopenharmony_ci evsel = old_expr->metric_events[idx]; 185062306a36Sopenharmony_ci evsel = evlist__find_evsel(evlist, evsel->core.idx); 185162306a36Sopenharmony_ci if (evsel == NULL) { 185262306a36Sopenharmony_ci zfree(&new_expr->metric_events); 185362306a36Sopenharmony_ci zfree(&new_expr->metric_refs); 185462306a36Sopenharmony_ci free(new_expr); 185562306a36Sopenharmony_ci return -EINVAL; 185662306a36Sopenharmony_ci } 185762306a36Sopenharmony_ci new_expr->metric_events[idx] = evsel; 185862306a36Sopenharmony_ci } 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci list_add(&new_expr->nd, &new_me->head); 186162306a36Sopenharmony_ci } 186262306a36Sopenharmony_ci } 186362306a36Sopenharmony_ci return 0; 186462306a36Sopenharmony_ci} 1865