summaryrefslogtreecommitdiff
path: root/tools/perf/util/metricgroup.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util/metricgroup.c')
-rw-r--r--tools/perf/util/metricgroup.c200
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;
}