summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tools/perf/tests/parse-metric.c34
-rw-r--r--tools/perf/util/expr.c2
-rw-r--r--tools/perf/util/expr.h9
-rw-r--r--tools/perf/util/metricgroup.c122
4 files changed, 152 insertions, 15 deletions
diff --git a/tools/perf/tests/parse-metric.c b/tools/perf/tests/parse-metric.c
index 7a7dea833a6e..d7cc3bf64854 100644
--- a/tools/perf/tests/parse-metric.c
+++ b/tools/perf/tests/parse-metric.c
@@ -57,6 +57,18 @@ static struct pmu_event pme_test[] = {
.metric_expr = "d_ratio(dcache_l2_all_miss, dcache_l2_all)",
.metric_name = "DCache_L2_Misses",
},
+{
+ .metric_expr = "ipc + m2",
+ .metric_name = "M1",
+},
+{
+ .metric_expr = "ipc + m1",
+ .metric_name = "M2",
+},
+{
+ .metric_expr = "1/m3",
+ .metric_name = "M3",
+}
};
static struct pmu_events_map map = {
@@ -139,8 +151,8 @@ static int compute_metric(const char *name, struct value *vals, double *ratio)
err = metricgroup__parse_groups_test(evlist, &map, name,
false, false,
&metric_events);
-
- TEST_ASSERT_VAL("failed to parse metric", err == 0);
+ if (err)
+ return err;
if (perf_evlist__alloc_stats(evlist, false))
return -1;
@@ -264,11 +276,29 @@ static int test_dcache_l2(void)
return 0;
}
+static int test_recursion_fail(void)
+{
+ double ratio;
+ struct value vals[] = {
+ { .event = "inst_retired.any", .val = 300 },
+ { .event = "cpu_clk_unhalted.thread", .val = 200 },
+ { .event = NULL, },
+ };
+
+ TEST_ASSERT_VAL("failed to find recursion",
+ compute_metric("M1", vals, &ratio) == -1);
+
+ TEST_ASSERT_VAL("failed to find recursion",
+ compute_metric("M3", vals, &ratio) == -1);
+ return 0;
+}
+
int test__parse_metric(struct test *test __maybe_unused, int subtest __maybe_unused)
{
TEST_ASSERT_VAL("IPC failed", test_ipc() == 0);
TEST_ASSERT_VAL("frontend failed", test_frontend() == 0);
TEST_ASSERT_VAL("cache_miss_cycles failed", test_cache_miss_cycles() == 0);
TEST_ASSERT_VAL("DCache_L2 failed", test_dcache_l2() == 0);
+ TEST_ASSERT_VAL("recursion fail failed", test_recursion_fail() == 0);
return 0;
}
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index a346ca590513..53482ef53c41 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -47,6 +47,8 @@ int expr__add_id(struct expr_parse_ctx *ctx, const char *id)
if (!data_ptr)
return -ENOMEM;
+ data_ptr->parent = ctx->parent;
+
ret = hashmap__set(&ctx->ids, id, data_ptr,
(const void **)&old_key, (void **)&old_data);
if (ret)
diff --git a/tools/perf/util/expr.h b/tools/perf/util/expr.h
index 9ed208d93418..fc2b5e824a66 100644
--- a/tools/perf/util/expr.h
+++ b/tools/perf/util/expr.h
@@ -13,8 +13,14 @@
struct metric_ref;
+struct expr_id {
+ char *id;
+ struct expr_id *parent;
+};
+
struct expr_parse_ctx {
- struct hashmap ids;
+ struct hashmap ids;
+ struct expr_id *parent;
};
struct expr_id_data {
@@ -25,6 +31,7 @@ struct expr_id_data {
const char *metric_expr;
bool counted;
} ref;
+ struct expr_id *parent;
};
bool is_ref;
diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c
index 86665c502443..bf28d0c37da8 100644
--- a/tools/perf/util/metricgroup.c
+++ b/tools/perf/util/metricgroup.c
@@ -24,6 +24,7 @@
#include <subcmd/parse-options.h>
#include <api/fs/fs.h>
#include "util.h"
+#include <asm/bug.h>
struct metric_event *metricgroup__lookup(struct rblist *metric_events,
struct evsel *evsel,
@@ -126,6 +127,28 @@ struct egroup {
bool has_constraint;
};
+#define RECURSION_ID_MAX 1000
+
+struct expr_ids {
+ struct expr_id id[RECURSION_ID_MAX];
+ int cnt;
+};
+
+static struct expr_id *expr_ids__alloc(struct expr_ids *ids)
+{
+ if (ids->cnt >= RECURSION_ID_MAX)
+ return NULL;
+ return &ids->id[ids->cnt++];
+}
+
+static void expr_ids__exit(struct expr_ids *ids)
+{
+ int i;
+
+ for (i = 0; i < ids->cnt; i++)
+ free(ids->id[i].id);
+}
+
/**
* Find a group of events in perf_evlist that correpond to those from a parsed
* metric expression. Note, as find_evsel_group is called in the same order as
@@ -620,7 +643,9 @@ static int __add_metric(struct list_head *group_list,
struct pmu_event *pe,
bool metric_no_group,
int runtime,
- struct egroup **egp)
+ struct egroup **egp,
+ struct expr_id *parent,
+ struct expr_ids *ids)
{
struct metric_ref_node *ref;
struct egroup *eg;
@@ -630,7 +655,7 @@ static int __add_metric(struct list_head *group_list,
* We got in here for the parent group,
* allocate it and put it on the list.
*/
- eg = malloc(sizeof(*eg));
+ eg = zalloc(sizeof(*eg));
if (!eg)
return -ENOMEM;
@@ -643,6 +668,18 @@ static int __add_metric(struct list_head *group_list,
INIT_LIST_HEAD(&eg->metric_refs);
eg->metric_refs_cnt = 0;
*egp = eg;
+
+ parent = expr_ids__alloc(ids);
+ if (!parent) {
+ free(eg);
+ return -EINVAL;
+ }
+
+ parent->id = strdup(pe->metric_name);
+ if (!parent->id) {
+ free(eg);
+ return -ENOMEM;
+ }
} else {
/*
* We got here for the referenced metric, via the
@@ -668,6 +705,10 @@ static int __add_metric(struct list_head *group_list,
eg->metric_refs_cnt++;
}
+ /* Force all found IDs in metric to have us as parent ID. */
+ WARN_ON_ONCE(!parent);
+ eg->pctx.parent = parent;
+
/*
* For both the parent and referenced metrics, we parse
* all the metric's IDs and add it to the parent context.
@@ -728,15 +769,62 @@ static struct pmu_event *find_metric(const char *metric, struct pmu_events_map *
return NULL;
}
+static int recursion_check(struct egroup *eg, const char *id, struct expr_id **parent,
+ struct expr_ids *ids)
+{
+ struct expr_id_data *data;
+ struct expr_id *p;
+ int ret;
+
+ /*
+ * We get the parent referenced by 'id' argument and
+ * traverse through all the parent object IDs to check
+ * if we already processed 'id', if we did, it's recursion
+ * and we fail.
+ */
+ ret = expr__get_id(&eg->pctx, id, &data);
+ if (ret)
+ return ret;
+
+ p = data->parent;
+
+ while (p->parent) {
+ if (!strcmp(p->id, id)) {
+ pr_err("failed: recursion detected for %s\n", id);
+ return -1;
+ }
+ p = p->parent;
+ }
+
+ /*
+ * If we are over the limit of static entris, the metric
+ * is too difficult/nested to process, fail as well.
+ */
+ p = expr_ids__alloc(ids);
+ if (!p) {
+ pr_err("failed: too many nested metrics\n");
+ return -EINVAL;
+ }
+
+ p->id = strdup(id);
+ p->parent = data->parent;
+ *parent = p;
+
+ return p->id ? 0 : -ENOMEM;
+}
+
static int add_metric(struct list_head *group_list,
struct pmu_event *pe,
bool metric_no_group,
- struct egroup **egp);
+ struct egroup **egp,
+ struct expr_id *parent,
+ struct expr_ids *ids);
static int __resolve_metric(struct egroup *eg,
bool metric_no_group,
struct list_head *group_list,
- struct pmu_events_map *map)
+ struct pmu_events_map *map,
+ struct expr_ids *ids)
{
struct hashmap_entry *cur;
size_t bkt;
@@ -750,18 +838,23 @@ static int __resolve_metric(struct egroup *eg,
do {
all = true;
hashmap__for_each_entry((&eg->pctx.ids), cur, bkt) {
+ struct expr_id *parent;
struct pmu_event *pe;
pe = find_metric(cur->key, map);
if (!pe)
continue;
+ ret = recursion_check(eg, cur->key, &parent, ids);
+ if (ret)
+ return ret;
+
all = false;
/* The metric key itself needs to go out.. */
expr__del_id(&eg->pctx, cur->key);
/* ... and it gets resolved to the parent context. */
- ret = add_metric(group_list, pe, metric_no_group, &eg);
+ ret = add_metric(group_list, pe, metric_no_group, &eg, parent, ids);
if (ret)
return ret;
@@ -778,13 +871,14 @@ static int __resolve_metric(struct egroup *eg,
static int resolve_metric(bool metric_no_group,
struct list_head *metric_list,
- struct pmu_events_map *map)
+ struct pmu_events_map *map,
+ struct expr_ids *ids)
{
struct egroup *eg;
int err;
list_for_each_entry(eg, metric_list, nd) {
- err = __resolve_metric(eg, metric_no_group, metric_list, map);
+ err = __resolve_metric(eg, metric_no_group, metric_list, map, ids);
if (err)
return err;
}
@@ -794,7 +888,9 @@ static int resolve_metric(bool metric_no_group,
static int add_metric(struct list_head *group_list,
struct pmu_event *pe,
bool metric_no_group,
- struct egroup **egp)
+ struct egroup **egp,
+ struct expr_id *parent,
+ struct expr_ids *ids)
{
struct egroup *orig = *egp;
int ret = 0;
@@ -802,7 +898,7 @@ static int add_metric(struct list_head *group_list,
pr_debug("metric expr %s for %s\n", pe->metric_expr, pe->metric_name);
if (!strstr(pe->metric_expr, "?")) {
- ret = __add_metric(group_list, pe, metric_no_group, 1, egp);
+ ret = __add_metric(group_list, pe, metric_no_group, 1, egp, parent, ids);
} else {
int j, count;
@@ -814,7 +910,7 @@ static int add_metric(struct list_head *group_list,
*/
for (j = 0; j < count && !ret; j++, *egp = orig)
- ret = __add_metric(group_list, pe, metric_no_group, j, egp);
+ ret = __add_metric(group_list, pe, metric_no_group, j, egp, parent, ids);
}
return ret;
@@ -825,6 +921,7 @@ static int metricgroup__add_metric(const char *metric, bool metric_no_group,
struct list_head *group_list,
struct pmu_events_map *map)
{
+ struct expr_ids ids = { .cnt = 0, };
struct pmu_event *pe;
struct egroup *eg;
LIST_HEAD(list);
@@ -835,7 +932,7 @@ static int metricgroup__add_metric(const char *metric, bool metric_no_group,
has_match = true;
eg = NULL;
- ret = add_metric(&list, pe, metric_no_group, &eg);
+ ret = add_metric(&list, pe, metric_no_group, &eg, NULL, &ids);
if (ret)
return ret;
@@ -844,7 +941,7 @@ static int metricgroup__add_metric(const char *metric, bool metric_no_group,
* included in the expression.
*/
ret = resolve_metric(metric_no_group,
- &list, map);
+ &list, map, &ids);
if (ret)
return ret;
}
@@ -867,6 +964,7 @@ static int metricgroup__add_metric(const char *metric, bool metric_no_group,
}
list_splice(&list, group_list);
+ expr_ids__exit(&ids);
return 0;
}