diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c index b1d56a73223d4..d83885697125e 100644 --- a/tools/perf/util/metricgroup.c +++ b/tools/perf/util/metricgroup.c @@ -129,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. @@ -222,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) { @@ -901,6 +904,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, @@ -963,16 +967,29 @@ 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. + */ + assert(strstr(pm->metric_threshold, pm->metric_name)); + expr = 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); } - if (ret) { if (is_root) metric__free(root_metric); @@ -1554,6 +1571,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; diff --git a/tools/perf/util/metricgroup.h b/tools/perf/util/metricgroup.h index 84030321a057d..32eb3a5381fb7 100644 --- a/tools/perf/util/metricgroup.h +++ b/tools/perf/util/metricgroup.h @@ -47,6 +47,7 @@ struct metric_expr { const char *metric_expr; /** The name of the meric such as "IPC". */ const char *metric_name; + const char *metric_threshold; /** * The "ScaleUnit" that scales and adds a unit to the metric during * output. For example, "6.4e-05MiB" means to scale the resulting metric diff --git a/tools/perf/util/stat-shadow.c b/tools/perf/util/stat-shadow.c index 806b32156459a..a41f186c6ec89 100644 --- a/tools/perf/util/stat-shadow.c +++ b/tools/perf/util/stat-shadow.c @@ -777,6 +777,7 @@ static int prepare_metric(struct evsel **metric_events, static void generic_metric(struct perf_stat_config *config, const char *metric_expr, + const char *metric_threshold, struct evsel **metric_events, struct metric_ref *metric_refs, char *name, @@ -789,9 +790,10 @@ static void generic_metric(struct perf_stat_config *config, { print_metric_t print_metric = out->print_metric; struct expr_parse_ctx *pctx; - double ratio, scale; + double ratio, scale, threshold; int i; void *ctxp = out->ctx; + const char *color = NULL; pctx = expr__ctx_new(); if (!pctx) @@ -811,6 +813,12 @@ static void generic_metric(struct perf_stat_config *config, char *unit; char metric_bf[64]; + if (metric_threshold && + expr__parse(&threshold, pctx, metric_threshold) == 0) { + color = fpclassify(threshold) == FP_ZERO + ? PERF_COLOR_GREEN : PERF_COLOR_RED; + } + if (metric_unit && metric_name) { if (perf_pmu__convert_scale(metric_unit, &unit, &scale) >= 0) { @@ -823,22 +831,22 @@ static void generic_metric(struct perf_stat_config *config, scnprintf(metric_bf, sizeof(metric_bf), "%s %s", unit, metric_name); - print_metric(config, ctxp, NULL, "%8.1f", + print_metric(config, ctxp, color, "%8.1f", metric_bf, ratio); } else { - print_metric(config, ctxp, NULL, "%8.2f", + print_metric(config, ctxp, color, "%8.2f", metric_name ? metric_name : out->force_header ? name : "", ratio); } } else { - print_metric(config, ctxp, NULL, NULL, + print_metric(config, ctxp, color, /*unit=*/NULL, out->force_header ? (metric_name ? metric_name : name) : "", 0); } } else { - print_metric(config, ctxp, NULL, NULL, + print_metric(config, ctxp, color, /*unit=*/NULL, out->force_header ? (metric_name ? metric_name : name) : "", 0); } @@ -1214,9 +1222,9 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config, list_for_each_entry (mexp, &me->head, nd) { if (num++ > 0) out->new_line(config, ctxp); - generic_metric(config, mexp->metric_expr, mexp->metric_events, - mexp->metric_refs, evsel->name, mexp->metric_name, - mexp->metric_unit, mexp->runtime, + generic_metric(config, mexp->metric_expr, mexp->metric_threshold, + mexp->metric_events, mexp->metric_refs, evsel->name, + mexp->metric_name, mexp->metric_unit, mexp->runtime, map_idx, out, st); } }