diff options
Diffstat (limited to 'tools/perf/util/metricgroup.c')
-rw-r--r-- | tools/perf/util/metricgroup.c | 122 |
1 files changed, 110 insertions, 12 deletions
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; } |