diff options
Diffstat (limited to 'tools/perf/util/metricgroup.c')
-rw-r--r-- | tools/perf/util/metricgroup.c | 200 |
1 files changed, 136 insertions, 64 deletions
diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c index f3559be95541..c566c6859302 100644 --- a/tools/perf/util/metricgroup.c +++ b/tools/perf/util/metricgroup.c @@ -13,6 +13,7 @@ #include "pmu.h" #include "pmu-hybrid.h" #include "print-events.h" +#include "smt.h" #include "expr.h" #include "rblist.h" #include <string.h> @@ -89,9 +90,9 @@ static void metric_event_delete(struct rblist *rblist __maybe_unused, struct metric_expr *expr, *tmp; list_for_each_entry_safe(expr, tmp, &me->head, nd) { - free((char *)expr->metric_name); - free(expr->metric_refs); - free(expr->metric_events); + zfree(&expr->metric_name); + zfree(&expr->metric_refs); + zfree(&expr->metric_events); free(expr); } @@ -128,6 +129,8 @@ struct metric { const char *modifier; /** The expression to parse, for example, "instructions/cycles". */ const char *metric_expr; + /** Optional threshold expression where zero value is green, otherwise red. */ + const char *metric_threshold; /** * The "ScaleUnit" that scales and adds a unit to the metric during * output. @@ -136,10 +139,9 @@ struct metric { /** Optional null terminated array of referenced metrics. */ struct metric_ref *metric_refs; /** - * Is there a constraint on the group of events? In which case the - * events won't be grouped. + * Should events of the metric be grouped? */ - bool has_constraint; + bool group_events; /** * Parsed events for the metric. Optional as events may be taken from a * different metric whose group contains all the IDs necessary for this @@ -148,12 +150,12 @@ struct metric { struct evlist *evlist; }; -static void metricgroup___watchdog_constraint_hint(const char *name, bool foot) +static void metric__watchdog_constraint_hint(const char *name, bool foot) { static bool violate_nmi_constraint; if (!foot) { - pr_warning("Splitting metric group %s into standalone metrics.\n", name); + pr_warning("Not grouping metric %s's events.\n", name); violate_nmi_constraint = true; return; } @@ -167,18 +169,22 @@ static void metricgroup___watchdog_constraint_hint(const char *name, bool foot) " echo 1 > /proc/sys/kernel/nmi_watchdog\n"); } -static bool metricgroup__has_constraint(const struct pmu_metric *pm) +static bool metric__group_events(const struct pmu_metric *pm) { - if (!pm->metric_constraint) + switch (pm->event_grouping) { + case MetricNoGroupEvents: return false; - - if (!strcmp(pm->metric_constraint, "NO_NMI_WATCHDOG") && - sysctl__nmi_watchdog_enabled()) { - metricgroup___watchdog_constraint_hint(pm->metric_name, false); + case MetricNoGroupEventsNmi: + if (!sysctl__nmi_watchdog_enabled()) + return true; + metric__watchdog_constraint_hint(pm->metric_name, /*foot=*/false); + return false; + case MetricNoGroupEventsSmt: + return !smt_on(); + case MetricGroupEvents: + default: return true; } - - return false; } static void metric__free(struct metric *m) @@ -186,9 +192,9 @@ static void metric__free(struct metric *m) if (!m) return; - free(m->metric_refs); + zfree(&m->metric_refs); expr__ctx_free(m->pctx); - free((char *)m->modifier); + zfree(&m->modifier); evlist__delete(m->evlist); free(m); } @@ -218,6 +224,7 @@ static struct metric *metric__new(const struct pmu_metric *pm, goto out_err; } m->metric_expr = pm->metric_expr; + m->metric_threshold = pm->metric_threshold; m->metric_unit = pm->unit; m->pctx->sctx.user_requested_cpu_list = NULL; if (user_requested_cpu_list) { @@ -227,7 +234,7 @@ static struct metric *metric__new(const struct pmu_metric *pm, } m->pctx->sctx.runtime = runtime; m->pctx->sctx.system_wide = system_wide; - m->has_constraint = metric_no_group || metricgroup__has_constraint(pm); + m->group_events = !metric_no_group && metric__group_events(pm); m->metric_refs = NULL; m->evlist = NULL; @@ -364,6 +371,7 @@ struct mep { const char *metric_desc; const char *metric_long_desc; const char *metric_expr; + const char *metric_threshold; const char *metric_unit; }; @@ -443,6 +451,7 @@ static int metricgroup__add_to_mep_groups(const struct pmu_metric *pm, me->metric_desc = pm->desc; me->metric_long_desc = pm->long_desc; me->metric_expr = pm->metric_expr; + me->metric_threshold = pm->metric_threshold; me->metric_unit = pm->unit; } } @@ -518,6 +527,7 @@ void metricgroup__print(const struct print_callbacks *print_cb, void *print_stat me->metric_desc, me->metric_long_desc, me->metric_expr, + me->metric_threshold, me->metric_unit); next = rb_next(node); rblist__remove_node(&groups, node); @@ -607,7 +617,7 @@ static int decode_all_metric_ids(struct evlist *perf_evlist, const char *modifie if (strstr(ev->name, "metric-id=")) { bool has_slash = false; - free(ev->name); + zfree(&ev->name); for (cur = strchr(sb.buf, '@') ; cur; cur = strchr(++cur, '@')) { *cur = '/'; has_slash = true; @@ -637,7 +647,7 @@ static int decode_all_metric_ids(struct evlist *perf_evlist, const char *modifie static int metricgroup__build_event_string(struct strbuf *events, const struct expr_parse_ctx *ctx, const char *modifier, - bool has_constraint) + bool group_events) { struct hashmap_entry *cur; size_t bkt; @@ -662,7 +672,7 @@ static int metricgroup__build_event_string(struct strbuf *events, } /* Separate events with commas and open the group if necessary. */ if (no_group) { - if (!has_constraint) { + if (group_events) { ret = strbuf_addch(events, '{'); RETURN_IF_NON_ZERO(ret); } @@ -716,7 +726,7 @@ static int metricgroup__build_event_string(struct strbuf *events, RETURN_IF_NON_ZERO(ret); } } - if (!no_group && !has_constraint) { + if (!no_group && group_events) { ret = strbuf_addf(events, "}:W"); RETURN_IF_NON_ZERO(ret); } @@ -761,6 +771,7 @@ struct metricgroup_add_iter_data { int *ret; bool *has_match; bool metric_no_group; + bool metric_no_threshold; const char *user_requested_cpu_list; bool system_wide; struct metric *root_metric; @@ -776,6 +787,7 @@ static int add_metric(struct list_head *metric_list, const struct pmu_metric *pm, const char *modifier, bool metric_no_group, + bool metric_no_threshold, const char *user_requested_cpu_list, bool system_wide, struct metric *root_metric, @@ -803,6 +815,7 @@ static int add_metric(struct list_head *metric_list, static int resolve_metric(struct list_head *metric_list, const char *modifier, bool metric_no_group, + bool metric_no_threshold, const char *user_requested_cpu_list, bool system_wide, struct metric *root_metric, @@ -851,8 +864,8 @@ static int resolve_metric(struct list_head *metric_list, */ for (i = 0; i < pending_cnt; i++) { ret = add_metric(metric_list, &pending[i].pm, modifier, metric_no_group, - user_requested_cpu_list, system_wide, root_metric, visited, - table); + metric_no_threshold, user_requested_cpu_list, system_wide, + root_metric, visited, table); if (ret) break; } @@ -869,6 +882,7 @@ static int resolve_metric(struct list_head *metric_list, * @metric_no_group: Should events written to events be grouped "{}" or * global. Grouping is the default but due to multiplexing the * user may override. + * @metric_no_threshold: Should threshold expressions be ignored? * @runtime: A special argument for the parser only known at runtime. * @user_requested_cpu_list: Command line specified CPUs to record on. * @system_wide: Are events for all processes recorded. @@ -884,6 +898,7 @@ static int __add_metric(struct list_head *metric_list, const struct pmu_metric *pm, const char *modifier, bool metric_no_group, + bool metric_no_threshold, int runtime, const char *user_requested_cpu_list, bool system_wide, @@ -894,6 +909,7 @@ static int __add_metric(struct list_head *metric_list, const struct visited_metric *vm; int ret; bool is_root = !root_metric; + const char *expr; struct visited_metric visited_node = { .name = pm->metric_name, .parent = visited, @@ -956,16 +972,31 @@ static int __add_metric(struct list_head *metric_list, * For both the parent and referenced metrics, we parse * all the metric's IDs and add it to the root context. */ - if (expr__find_ids(pm->metric_expr, NULL, root_metric->pctx) < 0) { + ret = 0; + expr = pm->metric_expr; + if (is_root && pm->metric_threshold) { + /* + * Threshold expressions are built off the actual metric. Switch + * to use that in case of additional necessary events. Change + * the visited node name to avoid this being flagged as + * recursion. If the threshold events are disabled, just use the + * metric's name as a reference. This allows metric threshold + * computation if there are sufficient events. + */ + assert(strstr(pm->metric_threshold, pm->metric_name)); + expr = metric_no_threshold ? pm->metric_name : pm->metric_threshold; + visited_node.name = "__threshold__"; + } + if (expr__find_ids(expr, NULL, root_metric->pctx) < 0) { /* Broken metric. */ ret = -EINVAL; - } else { + } + if (!ret) { /* Resolve referenced metrics. */ ret = resolve_metric(metric_list, modifier, metric_no_group, - user_requested_cpu_list, system_wide, - root_metric, &visited_node, table); + metric_no_threshold, user_requested_cpu_list, + system_wide, root_metric, &visited_node, table); } - if (ret) { if (is_root) metric__free(root_metric); @@ -1011,6 +1042,7 @@ static int add_metric(struct list_head *metric_list, const struct pmu_metric *pm, const char *modifier, bool metric_no_group, + bool metric_no_threshold, const char *user_requested_cpu_list, bool system_wide, struct metric *root_metric, @@ -1022,9 +1054,9 @@ static int add_metric(struct list_head *metric_list, pr_debug("metric expr %s for %s\n", pm->metric_expr, pm->metric_name); if (!strstr(pm->metric_expr, "?")) { - ret = __add_metric(metric_list, pm, modifier, metric_no_group, 0, - user_requested_cpu_list, system_wide, root_metric, - visited, table); + ret = __add_metric(metric_list, pm, modifier, metric_no_group, + metric_no_threshold, 0, user_requested_cpu_list, + system_wide, root_metric, visited, table); } else { int j, count; @@ -1036,9 +1068,9 @@ static int add_metric(struct list_head *metric_list, */ for (j = 0; j < count && !ret; j++) - ret = __add_metric(metric_list, pm, modifier, metric_no_group, j, - user_requested_cpu_list, system_wide, - root_metric, visited, table); + ret = __add_metric(metric_list, pm, modifier, metric_no_group, + metric_no_threshold, j, user_requested_cpu_list, + system_wide, root_metric, visited, table); } return ret; @@ -1055,8 +1087,8 @@ static int metricgroup__add_metric_sys_event_iter(const struct pmu_metric *pm, return 0; ret = add_metric(d->metric_list, pm, d->modifier, d->metric_no_group, - d->user_requested_cpu_list, d->system_wide, - d->root_metric, d->visited, d->table); + d->metric_no_threshold, d->user_requested_cpu_list, + d->system_wide, d->root_metric, d->visited, d->table); if (ret) goto out; @@ -1100,6 +1132,7 @@ struct metricgroup__add_metric_data { const char *modifier; const char *user_requested_cpu_list; bool metric_no_group; + bool metric_no_threshold; bool system_wide; bool has_match; }; @@ -1117,8 +1150,9 @@ static int metricgroup__add_metric_callback(const struct pmu_metric *pm, data->has_match = true; ret = add_metric(data->list, pm, data->modifier, data->metric_no_group, - data->user_requested_cpu_list, data->system_wide, - /*root_metric=*/NULL, /*visited_metrics=*/NULL, table); + data->metric_no_threshold, data->user_requested_cpu_list, + data->system_wide, /*root_metric=*/NULL, + /*visited_metrics=*/NULL, table); } return ret; } @@ -1139,7 +1173,7 @@ static int metricgroup__add_metric_callback(const struct pmu_metric *pm, * architecture perf is running upon. */ static int metricgroup__add_metric(const char *metric_name, const char *modifier, - bool metric_no_group, + bool metric_no_group, bool metric_no_threshold, const char *user_requested_cpu_list, bool system_wide, struct list_head *metric_list, @@ -1155,6 +1189,7 @@ static int metricgroup__add_metric(const char *metric_name, const char *modifier .metric_name = metric_name, .modifier = modifier, .metric_no_group = metric_no_group, + .metric_no_threshold = metric_no_threshold, .user_requested_cpu_list = user_requested_cpu_list, .system_wide = system_wide, .has_match = false, @@ -1217,6 +1252,7 @@ out: * architecture perf is running upon. */ static int metricgroup__add_metric_list(const char *list, bool metric_no_group, + bool metric_no_threshold, const char *user_requested_cpu_list, bool system_wide, struct list_head *metric_list, const struct pmu_metrics_table *table) @@ -1235,7 +1271,8 @@ static int metricgroup__add_metric_list(const char *list, bool metric_no_group, *modifier++ = '\0'; ret = metricgroup__add_metric(metric_name, modifier, - metric_no_group, user_requested_cpu_list, + metric_no_group, metric_no_threshold, + user_requested_cpu_list, system_wide, metric_list, table); if (ret == -EINVAL) pr_err("Cannot find metric or group `%s'\n", metric_name); @@ -1252,7 +1289,7 @@ static int metricgroup__add_metric_list(const char *list, bool metric_no_group, * Warn about nmi_watchdog if any parsed metrics had the * NO_NMI_WATCHDOG constraint. */ - metricgroup___watchdog_constraint_hint(NULL, true); + metric__watchdog_constraint_hint(NULL, /*foot=*/true); /* No metrics. */ if (count == 0) return -EINVAL; @@ -1295,7 +1332,7 @@ static void find_tool_events(const struct list_head *metric_list, } /** - * build_combined_expr_ctx - Make an expr_parse_ctx with all has_constraint + * build_combined_expr_ctx - Make an expr_parse_ctx with all !group_events * metric IDs, as the IDs are held in a set, * duplicates will be removed. * @metric_list: List to take metrics from. @@ -1315,7 +1352,7 @@ static int build_combined_expr_ctx(const struct list_head *metric_list, return -ENOMEM; list_for_each_entry(m, metric_list, nd) { - if (m->has_constraint && !m->modifier) { + if (!m->group_events && !m->modifier) { hashmap__for_each_entry(m->pctx->ids, cur, bkt) { dup = strdup(cur->pkey); if (!dup) { @@ -1342,14 +1379,14 @@ err_out: * @fake_pmu: used when testing metrics not supported by the current CPU. * @ids: the event identifiers parsed from a metric. * @modifier: any modifiers added to the events. - * @has_constraint: false if events should be placed in a weak group. + * @group_events: should events be placed in a weak group. * @tool_events: entries set true if the tool event of index could be present in * the overall list of metrics. * @out_evlist: the created list of events. */ static int parse_ids(bool metric_no_merge, struct perf_pmu *fake_pmu, struct expr_parse_ctx *ids, const char *modifier, - bool has_constraint, const bool tool_events[PERF_TOOL_MAX], + bool group_events, const bool tool_events[PERF_TOOL_MAX], struct evlist **out_evlist) { struct parse_events_error parse_error; @@ -1393,7 +1430,7 @@ static int parse_ids(bool metric_no_merge, struct perf_pmu *fake_pmu, } } ret = metricgroup__build_event_string(&events, ids, modifier, - has_constraint); + group_events); if (ret) return ret; @@ -1404,7 +1441,8 @@ static int parse_ids(bool metric_no_merge, struct perf_pmu *fake_pmu, } pr_debug("Parsing metric events '%s'\n", events.buf); parse_events_error__init(&parse_error); - ret = __parse_events(parsed_evlist, events.buf, &parse_error, fake_pmu); + ret = __parse_events(parsed_evlist, events.buf, &parse_error, fake_pmu, + /*warn_if_reordered=*/false); if (ret) { parse_events_error__print(&parse_error, events.buf); goto err_out; @@ -1425,6 +1463,7 @@ err_out: static int parse_groups(struct evlist *perf_evlist, const char *str, bool metric_no_group, bool metric_no_merge, + bool metric_no_threshold, const char *user_requested_cpu_list, bool system_wide, struct perf_pmu *fake_pmu, @@ -1439,7 +1478,7 @@ static int parse_groups(struct evlist *perf_evlist, const char *str, if (metric_events_list->nr_entries == 0) metricgroup__rblist_init(metric_events_list); - ret = metricgroup__add_metric_list(str, metric_no_group, + ret = metricgroup__add_metric_list(str, metric_no_group, metric_no_threshold, user_requested_cpu_list, system_wide, &metric_list, table); if (ret) @@ -1458,7 +1497,7 @@ static int parse_groups(struct evlist *perf_evlist, const char *str, if (!ret && combined && hashmap__size(combined->ids)) { ret = parse_ids(metric_no_merge, fake_pmu, combined, /*modifier=*/NULL, - /*has_constraint=*/true, + /*group_events=*/false, tool_events, &combined_evlist); } @@ -1476,7 +1515,7 @@ static int parse_groups(struct evlist *perf_evlist, const char *str, struct metric *n; struct metric_expr *expr; - if (combined_evlist && m->has_constraint) { + if (combined_evlist && !m->group_events) { metric_evlist = combined_evlist; } else if (!metric_no_merge) { /* @@ -1507,7 +1546,7 @@ static int parse_groups(struct evlist *perf_evlist, const char *str, } if (!metric_evlist) { ret = parse_ids(metric_no_merge, fake_pmu, m->pctx, m->modifier, - m->has_constraint, tool_events, &m->evlist); + m->group_events, tool_events, &m->evlist); if (ret) goto out; @@ -1547,6 +1586,7 @@ static int parse_groups(struct evlist *perf_evlist, const char *str, free(metric_events); goto out; } + expr->metric_threshold = m->metric_threshold; expr->metric_unit = m->metric_unit; expr->metric_events = metric_events; expr->runtime = m->pctx->sctx.runtime; @@ -1573,6 +1613,7 @@ int metricgroup__parse_groups(struct evlist *perf_evlist, const char *str, bool metric_no_group, bool metric_no_merge, + bool metric_no_threshold, const char *user_requested_cpu_list, bool system_wide, struct rblist *metric_events) @@ -1583,18 +1624,19 @@ int metricgroup__parse_groups(struct evlist *perf_evlist, return -EINVAL; return parse_groups(perf_evlist, str, metric_no_group, metric_no_merge, - user_requested_cpu_list, system_wide, + metric_no_threshold, user_requested_cpu_list, system_wide, /*fake_pmu=*/NULL, metric_events, table); } int metricgroup__parse_groups_test(struct evlist *evlist, const struct pmu_metrics_table *table, const char *str, - bool metric_no_group, - bool metric_no_merge, struct rblist *metric_events) { - return parse_groups(evlist, str, metric_no_group, metric_no_merge, + return parse_groups(evlist, str, + /*metric_no_group=*/false, + /*metric_no_merge=*/false, + /*metric_no_threshold=*/false, /*user_requested_cpu_list=*/NULL, /*system_wide=*/false, &perf_pmu__fake, metric_events, table); @@ -1606,10 +1648,8 @@ static int metricgroup__has_metric_callback(const struct pmu_metric *pm, { const char *metric = vdata; - if (!pm->metric_expr) - return 0; - - if (match_metric(pm->metric_name, metric)) + if (match_metric(pm->metric_name, metric) || + match_metric(pm->metric_group, metric)) return 1; return 0; @@ -1626,6 +1666,37 @@ bool metricgroup__has_metric(const char *metric) (void *)metric) ? true : false; } +static int metricgroup__topdown_max_level_callback(const struct pmu_metric *pm, + const struct pmu_metrics_table *table __maybe_unused, + void *data) +{ + unsigned int *max_level = data; + unsigned int level; + const char *p = strstr(pm->metric_group, "TopdownL"); + + if (!p || p[8] == '\0') + return 0; + + level = p[8] - '0'; + if (level > *max_level) + *max_level = level; + + return 0; +} + +unsigned int metricgroups__topdown_max_level(void) +{ + unsigned int max_level = 0; + const struct pmu_metrics_table *table = pmu_metrics_table__find(); + + if (!table) + return false; + + pmu_metrics_table_for_each_metric(table, metricgroup__topdown_max_level_callback, + &max_level); + return max_level; +} + int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp, struct rblist *new_metric_events, struct rblist *old_metric_events) @@ -1659,6 +1730,7 @@ int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp, return -ENOMEM; new_expr->metric_expr = old_expr->metric_expr; + new_expr->metric_threshold = old_expr->metric_threshold; new_expr->metric_name = strdup(old_expr->metric_name); if (!new_expr->metric_name) return -ENOMEM; @@ -1689,7 +1761,7 @@ int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp, alloc_size = sizeof(*new_expr->metric_events); new_expr->metric_events = calloc(nr + 1, alloc_size); if (!new_expr->metric_events) { - free(new_expr->metric_refs); + zfree(&new_expr->metric_refs); free(new_expr); return -ENOMEM; } @@ -1699,8 +1771,8 @@ int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp, evsel = old_expr->metric_events[idx]; evsel = evlist__find_evsel(evlist, evsel->core.idx); if (evsel == NULL) { - free(new_expr->metric_events); - free(new_expr->metric_refs); + zfree(&new_expr->metric_events); + zfree(&new_expr->metric_refs); free(new_expr); return -EINVAL; } |