diff options
Diffstat (limited to 'tools/perf/util/pmu.c')
| -rw-r--r-- | tools/perf/util/pmu.c | 105 | 
1 files changed, 97 insertions, 8 deletions
| diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index d2fb597c9a8c..afd68524ffa9 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -234,6 +234,74 @@ static int perf_pmu__parse_snapshot(struct perf_pmu_alias *alias,  	return 0;  } +static void perf_pmu_assign_str(char *name, const char *field, char **old_str, +				char **new_str) +{ +	if (!*old_str) +		goto set_new; + +	if (*new_str) {	/* Have new string, check with old */ +		if (strcasecmp(*old_str, *new_str)) +			pr_debug("alias %s differs in field '%s'\n", +				 name, field); +		zfree(old_str); +	} else		/* Nothing new --> keep old string */ +		return; +set_new: +	*old_str = *new_str; +	*new_str = NULL; +} + +static void perf_pmu_update_alias(struct perf_pmu_alias *old, +				  struct perf_pmu_alias *newalias) +{ +	perf_pmu_assign_str(old->name, "desc", &old->desc, &newalias->desc); +	perf_pmu_assign_str(old->name, "long_desc", &old->long_desc, +			    &newalias->long_desc); +	perf_pmu_assign_str(old->name, "topic", &old->topic, &newalias->topic); +	perf_pmu_assign_str(old->name, "metric_expr", &old->metric_expr, +			    &newalias->metric_expr); +	perf_pmu_assign_str(old->name, "metric_name", &old->metric_name, +			    &newalias->metric_name); +	perf_pmu_assign_str(old->name, "value", &old->str, &newalias->str); +	old->scale = newalias->scale; +	old->per_pkg = newalias->per_pkg; +	old->snapshot = newalias->snapshot; +	memcpy(old->unit, newalias->unit, sizeof(old->unit)); +} + +/* Delete an alias entry. */ +static void perf_pmu_free_alias(struct perf_pmu_alias *newalias) +{ +	zfree(&newalias->name); +	zfree(&newalias->desc); +	zfree(&newalias->long_desc); +	zfree(&newalias->topic); +	zfree(&newalias->str); +	zfree(&newalias->metric_expr); +	zfree(&newalias->metric_name); +	parse_events_terms__purge(&newalias->terms); +	free(newalias); +} + +/* Merge an alias, search in alias list. If this name is already + * present merge both of them to combine all information. + */ +static bool perf_pmu_merge_alias(struct perf_pmu_alias *newalias, +				 struct list_head *alist) +{ +	struct perf_pmu_alias *a; + +	list_for_each_entry(a, alist, list) { +		if (!strcasecmp(newalias->name, a->name)) { +			perf_pmu_update_alias(a, newalias); +			perf_pmu_free_alias(newalias); +			return true; +		} +	} +	return false; +} +  static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name,  				 char *desc, char *val,  				 char *long_desc, char *topic, @@ -241,9 +309,11 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name,  				 char *metric_expr,  				 char *metric_name)  { +	struct parse_events_term *term;  	struct perf_pmu_alias *alias;  	int ret;  	int num; +	char newval[256];  	alias = malloc(sizeof(*alias));  	if (!alias) @@ -262,6 +332,27 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name,  		return ret;  	} +	/* Scan event and remove leading zeroes, spaces, newlines, some +	 * platforms have terms specified as +	 * event=0x0091 (read from files ../<PMU>/events/<FILE> +	 * and terms specified as event=0x91 (read from JSON files). +	 * +	 * Rebuild string to make alias->str member comparable. +	 */ +	memset(newval, 0, sizeof(newval)); +	ret = 0; +	list_for_each_entry(term, &alias->terms, list) { +		if (ret) +			ret += scnprintf(newval + ret, sizeof(newval) - ret, +					 ","); +		if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM) +			ret += scnprintf(newval + ret, sizeof(newval) - ret, +					 "%s=%#x", term->config, term->val.num); +		else if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) +			ret += scnprintf(newval + ret, sizeof(newval) - ret, +					 "%s=%s", term->config, term->val.str); +	} +  	alias->name = strdup(name);  	if (dir) {  		/* @@ -285,9 +376,10 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name,  		snprintf(alias->unit, sizeof(alias->unit), "%s", unit);  	}  	alias->per_pkg = perpkg && sscanf(perpkg, "%d", &num) == 1 && num == 1; -	alias->str = strdup(val); +	alias->str = strdup(newval); -	list_add_tail(&alias->list, list); +	if (!perf_pmu_merge_alias(alias, list)) +		list_add_tail(&alias->list, list);  	return 0;  } @@ -303,6 +395,9 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI  	buf[ret] = 0; +	/* Remove trailing newline from sysfs file */ +	rtrim(buf); +  	return __perf_pmu__new_alias(list, dir, name, NULL, buf, NULL, NULL, NULL,  				     NULL, NULL, NULL);  } @@ -557,12 +652,6 @@ static int is_arm_pmu_core(const char *name)  	if (stat(path, &st) == 0)  		return 1; -	/* Look for cpu sysfs (specific to s390) */ -	scnprintf(path, PATH_MAX, "%s/bus/event_source/devices/%s", -		  sysfs, name); -	if (stat(path, &st) == 0 && !strncmp(name, "cpum_", 5)) -		return 1; -  	return 0;  } | 
