diff options
Diffstat (limited to 'tools/perf/util/metricgroup.c')
| -rw-r--r-- | tools/perf/util/metricgroup.c | 208 | 
1 files changed, 140 insertions, 68 deletions
diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c index f3559be95541..5e9c657dd3f7 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;  }; @@ -1111,14 +1144,15 @@ static int metricgroup__add_metric_callback(const struct pmu_metric *pm,  	struct metricgroup__add_metric_data *data = vdata;  	int ret = 0; -	if (pm->metric_expr && -		(match_metric(pm->metric_group, data->metric_name) || -		 match_metric(pm->metric_name, data->metric_name))) { +	if (pm->metric_expr && match_pm_metric(pm, data->metric_name)) { +		bool metric_no_group = data->metric_no_group || +			match_metric(data->metric_name, pm->metricgroup_no_group);  		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); +		ret = add_metric(data->list, pm, data->modifier, metric_no_group, +				 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;  				}  |