perf metric: Compute and print threshold values

Compute the threshold metric and use it to color the metric value as
red or green. The threshold expression is used to generate the set of
events as the threshold may require additional events. A later patch
make this behavior optional with a --metric-no-threshold flag.

Signed-off-by: Ian Rogers <irogers@google.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Alexandre Torgue <alexandre.torgue@foss.st.com>
Cc: Andrii Nakryiko <andrii@kernel.org>
Cc: Athira Rajeev <atrajeev@linux.vnet.ibm.com>
Cc: Caleb Biggers <caleb.biggers@intel.com>
Cc: Eduard Zingerman <eddyz87@gmail.com>
Cc: Florian Fischer <florian.fischer@muhq.space>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@arm.com>
Cc: Jing Zhang <renyu.zj@linux.alibaba.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: John Garry <john.g.garry@oracle.com>
Cc: Kajol Jain <kjain@linux.ibm.com>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Leo Yan <leo.yan@linaro.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Perry Taylor <perry.taylor@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ravi Bangoria <ravi.bangoria@amd.com>
Cc: Sandipan Das <sandipan.das@amd.com>
Cc: Sean Christopherson <seanjc@google.com>
Cc: Stephane Eranian <eranian@google.com>
Cc: Suzuki Poulouse <suzuki.poulose@arm.com>
Cc: Xing Zhengjun <zhengjun.xing@linux.intel.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-stm32@st-md-mailman.stormreply.com
Link: https://lore.kernel.org/r/20230219092848.639226-37-irogers@google.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Ian Rogers 2023-02-19 01:28:33 -08:00 committed by Arnaldo Carvalho de Melo
parent c7551a2e33
commit d0a3052f6f
3 changed files with 38 additions and 11 deletions

View File

@ -129,6 +129,8 @@ struct metric {
const char *modifier; const char *modifier;
/** The expression to parse, for example, "instructions/cycles". */ /** The expression to parse, for example, "instructions/cycles". */
const char *metric_expr; 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 * The "ScaleUnit" that scales and adds a unit to the metric during
* output. * output.
@ -222,6 +224,7 @@ static struct metric *metric__new(const struct pmu_metric *pm,
goto out_err; goto out_err;
} }
m->metric_expr = pm->metric_expr; m->metric_expr = pm->metric_expr;
m->metric_threshold = pm->metric_threshold;
m->metric_unit = pm->unit; m->metric_unit = pm->unit;
m->pctx->sctx.user_requested_cpu_list = NULL; m->pctx->sctx.user_requested_cpu_list = NULL;
if (user_requested_cpu_list) { if (user_requested_cpu_list) {
@ -901,6 +904,7 @@ static int __add_metric(struct list_head *metric_list,
const struct visited_metric *vm; const struct visited_metric *vm;
int ret; int ret;
bool is_root = !root_metric; bool is_root = !root_metric;
const char *expr;
struct visited_metric visited_node = { struct visited_metric visited_node = {
.name = pm->metric_name, .name = pm->metric_name,
.parent = visited, .parent = visited,
@ -963,16 +967,29 @@ static int __add_metric(struct list_head *metric_list,
* For both the parent and referenced metrics, we parse * For both the parent and referenced metrics, we parse
* all the metric's IDs and add it to the root context. * 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. */ /* Broken metric. */
ret = -EINVAL; ret = -EINVAL;
} else { }
if (!ret) {
/* Resolve referenced metrics. */ /* Resolve referenced metrics. */
ret = resolve_metric(metric_list, modifier, metric_no_group, ret = resolve_metric(metric_list, modifier, metric_no_group,
user_requested_cpu_list, system_wide, user_requested_cpu_list, system_wide,
root_metric, &visited_node, table); root_metric, &visited_node, table);
} }
if (ret) { if (ret) {
if (is_root) if (is_root)
metric__free(root_metric); metric__free(root_metric);
@ -1554,6 +1571,7 @@ static int parse_groups(struct evlist *perf_evlist, const char *str,
free(metric_events); free(metric_events);
goto out; goto out;
} }
expr->metric_threshold = m->metric_threshold;
expr->metric_unit = m->metric_unit; expr->metric_unit = m->metric_unit;
expr->metric_events = metric_events; expr->metric_events = metric_events;
expr->runtime = m->pctx->sctx.runtime; expr->runtime = m->pctx->sctx.runtime;

View File

@ -47,6 +47,7 @@ struct metric_expr {
const char *metric_expr; const char *metric_expr;
/** The name of the meric such as "IPC". */ /** The name of the meric such as "IPC". */
const char *metric_name; const char *metric_name;
const char *metric_threshold;
/** /**
* The "ScaleUnit" that scales and adds a unit to the metric during * The "ScaleUnit" that scales and adds a unit to the metric during
* output. For example, "6.4e-05MiB" means to scale the resulting metric * output. For example, "6.4e-05MiB" means to scale the resulting metric

View File

@ -777,6 +777,7 @@ static int prepare_metric(struct evsel **metric_events,
static void generic_metric(struct perf_stat_config *config, static void generic_metric(struct perf_stat_config *config,
const char *metric_expr, const char *metric_expr,
const char *metric_threshold,
struct evsel **metric_events, struct evsel **metric_events,
struct metric_ref *metric_refs, struct metric_ref *metric_refs,
char *name, char *name,
@ -789,9 +790,10 @@ static void generic_metric(struct perf_stat_config *config,
{ {
print_metric_t print_metric = out->print_metric; print_metric_t print_metric = out->print_metric;
struct expr_parse_ctx *pctx; struct expr_parse_ctx *pctx;
double ratio, scale; double ratio, scale, threshold;
int i; int i;
void *ctxp = out->ctx; void *ctxp = out->ctx;
const char *color = NULL;
pctx = expr__ctx_new(); pctx = expr__ctx_new();
if (!pctx) if (!pctx)
@ -811,6 +813,12 @@ static void generic_metric(struct perf_stat_config *config,
char *unit; char *unit;
char metric_bf[64]; 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 (metric_unit && metric_name) {
if (perf_pmu__convert_scale(metric_unit, if (perf_pmu__convert_scale(metric_unit,
&unit, &scale) >= 0) { &unit, &scale) >= 0) {
@ -823,22 +831,22 @@ static void generic_metric(struct perf_stat_config *config,
scnprintf(metric_bf, sizeof(metric_bf), scnprintf(metric_bf, sizeof(metric_bf),
"%s %s", unit, metric_name); "%s %s", unit, metric_name);
print_metric(config, ctxp, NULL, "%8.1f", print_metric(config, ctxp, color, "%8.1f",
metric_bf, ratio); metric_bf, ratio);
} else { } else {
print_metric(config, ctxp, NULL, "%8.2f", print_metric(config, ctxp, color, "%8.2f",
metric_name ? metric_name ?
metric_name : metric_name :
out->force_header ? name : "", out->force_header ? name : "",
ratio); ratio);
} }
} else { } else {
print_metric(config, ctxp, NULL, NULL, print_metric(config, ctxp, color, /*unit=*/NULL,
out->force_header ? out->force_header ?
(metric_name ? metric_name : name) : "", 0); (metric_name ? metric_name : name) : "", 0);
} }
} else { } else {
print_metric(config, ctxp, NULL, NULL, print_metric(config, ctxp, color, /*unit=*/NULL,
out->force_header ? out->force_header ?
(metric_name ? metric_name : name) : "", 0); (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) { list_for_each_entry (mexp, &me->head, nd) {
if (num++ > 0) if (num++ > 0)
out->new_line(config, ctxp); out->new_line(config, ctxp);
generic_metric(config, mexp->metric_expr, mexp->metric_events, generic_metric(config, mexp->metric_expr, mexp->metric_threshold,
mexp->metric_refs, evsel->name, mexp->metric_name, mexp->metric_events, mexp->metric_refs, evsel->name,
mexp->metric_unit, mexp->runtime, mexp->metric_name, mexp->metric_unit, mexp->runtime,
map_idx, out, st); map_idx, out, st);
} }
} }