diff options
Diffstat (limited to 'tools/perf/util/pmu.c')
| -rw-r--r-- | tools/perf/util/pmu.c | 461 | 
1 files changed, 331 insertions, 130 deletions
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index c256b29defad..ad209c88a124 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -24,6 +24,8 @@  #include "evsel.h"  #include "pmu.h"  #include "pmus.h" +#include "pmu-bison.h" +#include "pmu-flex.h"  #include "parse-events.h"  #include "print-events.h"  #include "header.h" @@ -31,6 +33,7 @@  #include "strbuf.h"  #include "fncache.h"  #include "pmu-hybrid.h" +#include "util/evsel_config.h"  struct perf_pmu perf_pmu__fake; @@ -57,42 +60,54 @@ struct perf_pmu_format {  	struct list_head list;  }; -int perf_pmu_parse(struct list_head *list, char *name); -extern FILE *perf_pmu_in; -  static bool hybrid_scanned; +static struct perf_pmu *perf_pmu__find2(int dirfd, const char *name); +  /*   * Parse & process all the sysfs attributes located under   * the directory specified in 'dir' parameter.   */ -int perf_pmu__format_parse(char *dir, struct list_head *head) +int perf_pmu__format_parse(int dirfd, struct list_head *head)  {  	struct dirent *evt_ent;  	DIR *format_dir;  	int ret = 0; -	format_dir = opendir(dir); +	format_dir = fdopendir(dirfd);  	if (!format_dir)  		return -EINVAL;  	while (!ret && (evt_ent = readdir(format_dir))) { -		char path[PATH_MAX];  		char *name = evt_ent->d_name; +		int fd; +		void *scanner;  		FILE *file;  		if (!strcmp(name, ".") || !strcmp(name, ".."))  			continue; -		snprintf(path, PATH_MAX, "%s/%s", dir, name);  		ret = -EINVAL; -		file = fopen(path, "r"); -		if (!file) +		fd = openat(dirfd, name, O_RDONLY); +		if (fd < 0) +			break; + +		file = fdopen(fd, "r"); +		if (!file) { +			close(fd); +			break; +		} + +		ret = perf_pmu_lex_init(&scanner); +		if (ret) { +			fclose(file);  			break; +		} -		perf_pmu_in = file; -		ret = perf_pmu_parse(head, name); +		perf_pmu_set_in(file, scanner); +		ret = perf_pmu_parse(head, name, scanner); +		perf_pmu_lex_destroy(scanner);  		fclose(file);  	} @@ -105,17 +120,16 @@ int perf_pmu__format_parse(char *dir, struct list_head *head)   * located at:   * /sys/bus/event_source/devices/<dev>/format as sysfs group attributes.   */ -static int pmu_format(const char *name, struct list_head *format) +static int pmu_format(int dirfd, const char *name, struct list_head *format)  { -	char path[PATH_MAX]; - -	if (!perf_pmu__pathname_scnprintf(path, sizeof(path), name, "format")) -		return -1; +	int fd; -	if (!file_available(path)) +	fd = perf_pmu__pathname_fd(dirfd, name, "format", O_DIRECTORY); +	if (fd < 0)  		return 0; -	if (perf_pmu__format_parse(path, format)) +	/* it'll close the fd */ +	if (perf_pmu__format_parse(fd, format))  		return -1;  	return 0; @@ -158,7 +172,7 @@ out:  	return ret;  } -static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char *name) +static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, int dirfd, char *name)  {  	struct stat st;  	ssize_t sret; @@ -166,9 +180,9 @@ static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char *  	int fd, ret = -1;  	char path[PATH_MAX]; -	scnprintf(path, PATH_MAX, "%s/%s.scale", dir, name); +	scnprintf(path, PATH_MAX, "%s.scale", name); -	fd = open(path, O_RDONLY); +	fd = openat(dirfd, path, O_RDONLY);  	if (fd == -1)  		return -1; @@ -190,15 +204,15 @@ error:  	return ret;  } -static int perf_pmu__parse_unit(struct perf_pmu_alias *alias, char *dir, char *name) +static int perf_pmu__parse_unit(struct perf_pmu_alias *alias, int dirfd, char *name)  {  	char path[PATH_MAX];  	ssize_t sret;  	int fd; -	scnprintf(path, PATH_MAX, "%s/%s.unit", dir, name); +	scnprintf(path, PATH_MAX, "%s.unit", name); -	fd = open(path, O_RDONLY); +	fd = openat(dirfd, path, O_RDONLY);  	if (fd == -1)  		return -1; @@ -221,14 +235,14 @@ error:  }  static int -perf_pmu__parse_per_pkg(struct perf_pmu_alias *alias, char *dir, char *name) +perf_pmu__parse_per_pkg(struct perf_pmu_alias *alias, int dirfd, char *name)  {  	char path[PATH_MAX];  	int fd; -	scnprintf(path, PATH_MAX, "%s/%s.per-pkg", dir, name); +	scnprintf(path, PATH_MAX, "%s.per-pkg", name); -	fd = open(path, O_RDONLY); +	fd = openat(dirfd, path, O_RDONLY);  	if (fd == -1)  		return -1; @@ -239,14 +253,14 @@ perf_pmu__parse_per_pkg(struct perf_pmu_alias *alias, char *dir, char *name)  }  static int perf_pmu__parse_snapshot(struct perf_pmu_alias *alias, -				    char *dir, char *name) +				    int dirfd, char *name)  {  	char path[PATH_MAX];  	int fd; -	scnprintf(path, PATH_MAX, "%s/%s.snapshot", dir, name); +	scnprintf(path, PATH_MAX, "%s.snapshot", name); -	fd = open(path, O_RDONLY); +	fd = openat(dirfd, path, O_RDONLY);  	if (fd == -1)  		return -1; @@ -300,6 +314,16 @@ void perf_pmu_free_alias(struct perf_pmu_alias *newalias)  	free(newalias);  } +static void perf_pmu__del_aliases(struct perf_pmu *pmu) +{ +	struct perf_pmu_alias *alias, *tmp; + +	list_for_each_entry_safe(alias, tmp, &pmu->aliases, list) { +		list_del(&alias->list); +		perf_pmu_free_alias(alias); +	} +} +  /* Merge an alias, search in alias list. If this name is already   * present merge both of them to combine all information.   */ @@ -322,24 +346,23 @@ static bool perf_pmu_merge_alias(struct perf_pmu_alias *newalias,  	return false;  } -static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name, +static int __perf_pmu__new_alias(struct list_head *list, int dirfd, char *name,  				 char *desc, char *val, const struct pmu_event *pe)  {  	struct parse_events_term *term;  	struct perf_pmu_alias *alias;  	int ret; -	int num;  	char newval[256]; -	char *long_desc = NULL, *topic = NULL, *unit = NULL, *perpkg = NULL, -	     *deprecated = NULL, *pmu_name = NULL; +	const char *long_desc = NULL, *topic = NULL, *unit = NULL, *pmu_name = NULL; +	bool deprecated = false, perpkg = false;  	if (pe) { -		long_desc = (char *)pe->long_desc; -		topic = (char *)pe->topic; -		unit = (char *)pe->unit; -		perpkg = (char *)pe->perpkg; -		deprecated = (char *)pe->deprecated; -		pmu_name = (char *)pe->pmu; +		long_desc = pe->long_desc; +		topic = pe->topic; +		unit = pe->unit; +		perpkg = pe->perpkg; +		deprecated = pe->deprecated; +		pmu_name = pe->pmu;  	}  	alias = malloc(sizeof(*alias)); @@ -349,9 +372,9 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name,  	INIT_LIST_HEAD(&alias->terms);  	alias->scale = 1.0;  	alias->unit[0] = '\0'; -	alias->per_pkg = false; +	alias->per_pkg = perpkg;  	alias->snapshot = false; -	alias->deprecated = false; +	alias->deprecated = deprecated;  	ret = parse_events_terms(&alias->terms, val);  	if (ret) { @@ -382,14 +405,14 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name,  	}  	alias->name = strdup(name); -	if (dir) { +	if (dirfd >= 0) {  		/*  		 * load unit name and scale if available  		 */ -		perf_pmu__parse_unit(alias, dir, name); -		perf_pmu__parse_scale(alias, dir, name); -		perf_pmu__parse_per_pkg(alias, dir, name); -		perf_pmu__parse_snapshot(alias, dir, name); +		perf_pmu__parse_unit(alias, dirfd, name); +		perf_pmu__parse_scale(alias, dirfd, name); +		perf_pmu__parse_per_pkg(alias, dirfd, name); +		perf_pmu__parse_snapshot(alias, dirfd, name);  	}  	alias->desc = desc ? strdup(desc) : NULL; @@ -397,24 +420,20 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name,  				desc ? strdup(desc) : NULL;  	alias->topic = topic ? strdup(topic) : NULL;  	if (unit) { -		if (perf_pmu__convert_scale(unit, &unit, &alias->scale) < 0) +		if (perf_pmu__convert_scale(unit, (char **)&unit, &alias->scale) < 0)  			return -1;  		snprintf(alias->unit, sizeof(alias->unit), "%s", unit);  	} -	alias->per_pkg = perpkg && sscanf(perpkg, "%d", &num) == 1 && num == 1;  	alias->str = strdup(newval);  	alias->pmu_name = pmu_name ? strdup(pmu_name) : NULL; -	if (deprecated) -		alias->deprecated = true; -  	if (!perf_pmu_merge_alias(alias, list))  		list_add_tail(&alias->list, list);  	return 0;  } -static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FILE *file) +static int perf_pmu__new_alias(struct list_head *list, int dirfd, char *name, FILE *file)  {  	char buf[256];  	int ret; @@ -428,7 +447,7 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI  	/* Remove trailing newline from sysfs file */  	strim(buf); -	return __perf_pmu__new_alias(list, dir, name, NULL, buf, NULL); +	return __perf_pmu__new_alias(list, dirfd, name, NULL, buf, NULL);  }  static inline bool pmu_alias_info_file(char *name) @@ -452,17 +471,17 @@ static inline bool pmu_alias_info_file(char *name)   * Process all the sysfs attributes located under the directory   * specified in 'dir' parameter.   */ -static int pmu_aliases_parse(char *dir, struct list_head *head) +static int pmu_aliases_parse(int dirfd, struct list_head *head)  {  	struct dirent *evt_ent;  	DIR *event_dir; +	int fd; -	event_dir = opendir(dir); +	event_dir = fdopendir(dirfd);  	if (!event_dir)  		return -EINVAL;  	while ((evt_ent = readdir(event_dir))) { -		char path[PATH_MAX];  		char *name = evt_ent->d_name;  		FILE *file; @@ -475,15 +494,18 @@ static int pmu_aliases_parse(char *dir, struct list_head *head)  		if (pmu_alias_info_file(name))  			continue; -		scnprintf(path, PATH_MAX, "%s/%s", dir, name); - -		file = fopen(path, "r"); +		fd = openat(dirfd, name, O_RDONLY); +		if (fd == -1) { +			pr_debug("Cannot open %s\n", name); +			continue; +		} +		file = fdopen(fd, "r");  		if (!file) { -			pr_debug("Cannot open %s\n", path); +			close(fd);  			continue;  		} -		if (perf_pmu__new_alias(head, dir, name, file) < 0) +		if (perf_pmu__new_alias(head, dirfd, name, file) < 0)  			pr_debug("Cannot set up %s\n", name);  		fclose(file);  	} @@ -496,17 +518,16 @@ static int pmu_aliases_parse(char *dir, struct list_head *head)   * Reading the pmu event aliases definition, which should be located at:   * /sys/bus/event_source/devices/<dev>/events as sysfs group attributes.   */ -static int pmu_aliases(const char *name, struct list_head *head) +static int pmu_aliases(int dirfd, const char *name, struct list_head *head)  { -	char path[PATH_MAX]; - -	if (!perf_pmu__pathname_scnprintf(path, sizeof(path), name, "events")) -		return -1; +	int fd; -	if (!file_available(path)) +	fd = perf_pmu__pathname_fd(dirfd, name, "events", O_DIRECTORY); +	if (fd < 0)  		return 0; -	if (pmu_aliases_parse(path, head)) +	/* it'll close the fd */ +	if (pmu_aliases_parse(fd, head))  		return -1;  	return 0; @@ -539,14 +560,15 @@ static int pmu_alias_terms(struct perf_pmu_alias *alias,  /* Add all pmus in sysfs to pmu list: */  static void pmu_read_sysfs(void)  { -	char path[PATH_MAX]; +	int fd;  	DIR *dir;  	struct dirent *dent; -	if (!perf_pmu__event_source_devices_scnprintf(path, sizeof(path))) +	fd = perf_pmu__event_source_devices_fd(); +	if (fd < 0)  		return; -	dir = opendir(path); +	dir = fdopendir(fd);  	if (!dir)  		return; @@ -554,7 +576,7 @@ static void pmu_read_sysfs(void)  		if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))  			continue;  		/* add to static LIST_HEAD(pmus): */ -		perf_pmu__find(dent->d_name); +		perf_pmu__find2(fd, dent->d_name);  	}  	closedir(dir); @@ -564,7 +586,7 @@ static void pmu_read_sysfs(void)   * Uncore PMUs have a "cpumask" file under sysfs. CPU PMUs (e.g. on arm/arm64)   * may have a "cpus" file.   */ -static struct perf_cpu_map *pmu_cpumask(const char *name) +static struct perf_cpu_map *pmu_cpumask(int dirfd, const char *name)  {  	struct perf_cpu_map *cpus;  	const char *templates[] = { @@ -579,10 +601,11 @@ static struct perf_cpu_map *pmu_cpumask(const char *name)  	strlcpy(pmu_name, name, sizeof(pmu_name));  	for (template = templates; *template; template++) { -		file = perf_pmu__open_file(&pmu, *template); +		file = perf_pmu__open_file_at(&pmu, dirfd, *template);  		if (!file)  			continue;  		cpus = perf_cpu_map__read(file); +		fclose(file);  		if (cpus)  			return cpus;  	} @@ -590,15 +613,19 @@ static struct perf_cpu_map *pmu_cpumask(const char *name)  	return NULL;  } -static bool pmu_is_uncore(const char *name) +static bool pmu_is_uncore(int dirfd, const char *name)  { -	char path[PATH_MAX]; +	int fd;  	if (perf_pmu__hybrid_mounted(name))  		return false; -	perf_pmu__pathname_scnprintf(path, sizeof(path), name, "cpumask"); -	return file_available(path); +	fd = perf_pmu__pathname_fd(dirfd, name, "cpumask", O_PATH); +	if (fd < 0) +		return false; + +	close(fd); +	return true;  }  static char *pmu_id(const char *name) @@ -660,11 +687,14 @@ __weak const struct pmu_metrics_table *pmu_metrics_table__find(void)  	return perf_pmu__find_metrics_table(NULL);  } -/* - * Suffix must be in form tok_{digits}, or tok{digits}, or same as pmu_name - * to be valid. +/** + * perf_pmu__match_ignoring_suffix - Does the pmu_name match tok ignoring any + *                                   trailing suffix? The Suffix must be in form + *                                   tok_{digits}, or tok{digits}. + * @pmu_name: The pmu_name with possible suffix. + * @tok: The possible match to pmu_name without suffix.   */ -static bool perf_pmu__valid_suffix(const char *pmu_name, char *tok) +static bool perf_pmu__match_ignoring_suffix(const char *pmu_name, const char *tok)  {  	const char *p; @@ -689,11 +719,21 @@ static bool perf_pmu__valid_suffix(const char *pmu_name, char *tok)  	return true;  } -bool pmu_uncore_alias_match(const char *pmu_name, const char *name) +/** + * pmu_uncore_alias_match - does name match the PMU name? + * @pmu_name: the json struct pmu_event name. This may lack a suffix (which + *            matches) or be of the form "socket,pmuname" which will match + *            "socketX_pmunameY". + * @name: a real full PMU name as from sysfs. + */ +static bool pmu_uncore_alias_match(const char *pmu_name, const char *name)  {  	char *tmp = NULL, *tok, *str;  	bool res; +	if (strchr(pmu_name, ',') == NULL) +		return perf_pmu__match_ignoring_suffix(name, pmu_name); +  	str = strdup(pmu_name);  	if (!str)  		return false; @@ -720,7 +760,7 @@ bool pmu_uncore_alias_match(const char *pmu_name, const char *name)  		name = strstr(name, tok);  		if (!name || -		    (!next_tok && !perf_pmu__valid_suffix(name, tok))) { +		    (!next_tok && !perf_pmu__match_ignoring_suffix(name, tok))) {  			res = false;  			goto out;  		} @@ -758,7 +798,7 @@ static int pmu_add_cpu_aliases_map_callback(const struct pmu_event *pe,  new_alias:  	/* need type casts to override 'const' */ -	__perf_pmu__new_alias(data->head, NULL, (char *)pe->name, (char *)pe->desc, +	__perf_pmu__new_alias(data->head, -1, (char *)pe->name, (char *)pe->desc,  			      (char *)pe->event, pe);  	return 0;  } @@ -809,7 +849,7 @@ static int pmu_add_sys_aliases_iter_fn(const struct pmu_event *pe,  	if (!strcmp(pmu->id, pe->compat) &&  	    pmu_uncore_alias_match(pe->pmu, pmu->name)) { -		__perf_pmu__new_alias(idata->head, NULL, +		__perf_pmu__new_alias(idata->head, -1,  				      (char *)pe->name,  				      (char *)pe->desc,  				      (char *)pe->event, @@ -850,15 +890,15 @@ pmu_find_alias_name(const char *name __maybe_unused)  	return NULL;  } -static int pmu_max_precise(struct perf_pmu *pmu) +static int pmu_max_precise(int dirfd, struct perf_pmu *pmu)  {  	int max_precise = -1; -	perf_pmu__scan_file(pmu, "caps/max_precise", "%d", &max_precise); +	perf_pmu__scan_file_at(pmu, dirfd, "caps/max_precise", "%d", &max_precise);  	return max_precise;  } -static struct perf_pmu *pmu_lookup(const char *lookup_name) +static struct perf_pmu *pmu_lookup(int dirfd, const char *lookup_name)  {  	struct perf_pmu *pmu;  	LIST_HEAD(format); @@ -879,27 +919,27 @@ static struct perf_pmu *pmu_lookup(const char *lookup_name)  	 * type value and format definitions. Load both right  	 * now.  	 */ -	if (pmu_format(name, &format)) +	if (pmu_format(dirfd, name, &format))  		return NULL;  	/*  	 * Check the aliases first to avoid unnecessary work.  	 */ -	if (pmu_aliases(name, &aliases)) +	if (pmu_aliases(dirfd, name, &aliases))  		return NULL;  	pmu = zalloc(sizeof(*pmu));  	if (!pmu)  		return NULL; -	pmu->cpus = pmu_cpumask(name); +	pmu->cpus = pmu_cpumask(dirfd, name);  	pmu->name = strdup(name);  	if (!pmu->name)  		goto err;  	/* Read type, and ensure that type value is successfully assigned (return 1) */ -	if (perf_pmu__scan_file(pmu, "type", "%u", &type) != 1) +	if (perf_pmu__scan_file_at(pmu, dirfd, "type", "%u", &type) != 1)  		goto err;  	alias_name = pmu_find_alias_name(name); @@ -910,10 +950,10 @@ static struct perf_pmu *pmu_lookup(const char *lookup_name)  	}  	pmu->type = type; -	pmu->is_uncore = pmu_is_uncore(name); +	pmu->is_uncore = pmu_is_uncore(dirfd, name);  	if (pmu->is_uncore)  		pmu->id = pmu_id(name); -	pmu->max_precise = pmu_max_precise(pmu); +	pmu->max_precise = pmu_max_precise(dirfd, pmu);  	pmu_add_cpu_aliases(&aliases, pmu);  	pmu_add_sys_aliases(&aliases, pmu); @@ -926,13 +966,14 @@ static struct perf_pmu *pmu_lookup(const char *lookup_name)  	if (is_hybrid)  		list_add_tail(&pmu->hybrid_list, &perf_pmu__hybrid_pmus); +	else +		INIT_LIST_HEAD(&pmu->hybrid_list);  	pmu->default_config = perf_pmu__get_default_config(pmu);  	return pmu;  err: -	if (pmu->name) -		free(pmu->name); +	zfree(&pmu->name);  	free(pmu);  	return NULL;  } @@ -993,7 +1034,7 @@ struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu)  	return NULL;  } -struct perf_pmu *evsel__find_pmu(struct evsel *evsel) +struct perf_pmu *evsel__find_pmu(const struct evsel *evsel)  {  	struct perf_pmu *pmu = NULL; @@ -1005,20 +1046,50 @@ struct perf_pmu *evsel__find_pmu(struct evsel *evsel)  			break;  	} -	evsel->pmu = pmu; +	((struct evsel *)evsel)->pmu = pmu;  	return pmu;  } -bool evsel__is_aux_event(struct evsel *evsel) +bool evsel__is_aux_event(const struct evsel *evsel)  {  	struct perf_pmu *pmu = evsel__find_pmu(evsel);  	return pmu && pmu->auxtrace;  } +/* + * Set @config_name to @val as long as the user hasn't already set or cleared it + * by passing a config term on the command line. + * + * @val is the value to put into the bits specified by @config_name rather than + * the bit pattern. It is shifted into position by this function, so to set + * something to true, pass 1 for val rather than a pre shifted value. + */ +#define field_prep(_mask, _val) (((_val) << (ffsll(_mask) - 1)) & (_mask)) +void evsel__set_config_if_unset(struct perf_pmu *pmu, struct evsel *evsel, +				const char *config_name, u64 val) +{ +	u64 user_bits = 0, bits; +	struct evsel_config_term *term = evsel__get_config_term(evsel, CFG_CHG); + +	if (term) +		user_bits = term->val.cfg_chg; + +	bits = perf_pmu__format_bits(&pmu->format, config_name); + +	/* Do nothing if the user changed the value */ +	if (bits & user_bits) +		return; + +	/* Otherwise replace it */ +	evsel->core.attr.config &= ~bits; +	evsel->core.attr.config |= field_prep(bits, val); +} +  struct perf_pmu *perf_pmu__find(const char *name)  {  	struct perf_pmu *pmu; +	int dirfd;  	/*  	 * Once PMU is loaded it stays in the list, @@ -1029,7 +1100,27 @@ struct perf_pmu *perf_pmu__find(const char *name)  	if (pmu)  		return pmu; -	return pmu_lookup(name); +	dirfd = perf_pmu__event_source_devices_fd(); +	pmu = pmu_lookup(dirfd, name); +	close(dirfd); + +	return pmu; +} + +static struct perf_pmu *perf_pmu__find2(int dirfd, const char *name) +{ +	struct perf_pmu *pmu; + +	/* +	 * Once PMU is loaded it stays in the list, +	 * so we keep us from multiple reading/parsing +	 * the pmu format definitions. +	 */ +	pmu = pmu_find(name); +	if (pmu) +		return pmu; + +	return pmu_lookup(dirfd, name);  }  static struct perf_pmu_format * @@ -1214,6 +1305,9 @@ static int pmu_config_term(const char *pmu_name,  	case PERF_PMU_FORMAT_VALUE_CONFIG2:  		vp = &attr->config2;  		break; +	case PERF_PMU_FORMAT_VALUE_CONFIG3: +		vp = &attr->config3; +		break;  	default:  		return -EINVAL;  	} @@ -1454,7 +1548,7 @@ void perf_pmu__del_formats(struct list_head *formats)  	list_for_each_entry_safe(fmt, tmp, formats, list) {  		list_del(&fmt->list); -		free(fmt->name); +		zfree(&fmt->name);  		free(fmt);  	}  } @@ -1509,7 +1603,7 @@ static int cmp_sevent(const void *a, const void *b)  {  	const struct sevent *as = a;  	const struct sevent *bs = b; -	const char *a_pmu_name, *b_pmu_name; +	const char *a_pmu_name = NULL, *b_pmu_name = NULL;  	const char *a_name = "//", *a_desc = NULL, *a_topic = "";  	const char *b_name = "//", *b_desc = NULL, *b_topic = "";  	int ret; @@ -1518,11 +1612,13 @@ static int cmp_sevent(const void *a, const void *b)  		a_name = as->event->name;  		a_desc = as->event->desc;  		a_topic = as->event->topic ?: ""; +		a_pmu_name = as->event->pmu_name;  	}  	if (bs->event) {  		b_name = bs->event->name;  		b_desc = bs->event->desc;  		b_topic = bs->event->topic ?: ""; +		b_pmu_name = bs->event->pmu_name;  	}  	/* Put extra events last. */  	if (!!a_desc != !!b_desc) @@ -1538,11 +1634,13 @@ static int cmp_sevent(const void *a, const void *b)  		return as->is_cpu ? -1 : 1;  	/* Order by PMU name. */ -	a_pmu_name = as->pmu->name ?: ""; -	b_pmu_name = bs->pmu->name ?: ""; -	ret = strcmp(a_pmu_name, b_pmu_name); -	if (ret) -		return ret; +	if (as->pmu != bs->pmu) { +		a_pmu_name = a_pmu_name ?: (as->pmu->name ?: ""); +		b_pmu_name = b_pmu_name ?: (bs->pmu->name ?: ""); +		ret = strcmp(a_pmu_name, b_pmu_name); +		if (ret) +			return ret; +	}  	/* Order by event name. */  	return strcmp(a_name, b_name); @@ -1556,17 +1654,26 @@ bool is_pmu_core(const char *name)  static bool pmu_alias_is_duplicate(struct sevent *alias_a,  				   struct sevent *alias_b)  { -	const char *a_pmu_name, *b_pmu_name; -	const char *a_name = alias_a->event ? alias_a->event->name : "//"; -	const char *b_name = alias_b->event ? alias_b->event->name : "//"; +	const char *a_pmu_name = NULL, *b_pmu_name = NULL; +	const char *a_name = "//", *b_name = "//"; + + +	if (alias_a->event) { +		a_name = alias_a->event->name; +		a_pmu_name = alias_a->event->pmu_name; +	} +	if (alias_b->event) { +		b_name = alias_b->event->name; +		b_pmu_name = alias_b->event->pmu_name; +	}  	/* Different names -> never duplicates */  	if (strcmp(a_name, b_name))  		return false;  	/* Don't remove duplicates for different PMUs */ -	a_pmu_name = alias_a->pmu->name ?: ""; -	b_pmu_name = alias_b->pmu->name ?: ""; +	a_pmu_name = a_pmu_name ?: (alias_a->pmu->name ?: ""); +	b_pmu_name = b_pmu_name ?: (alias_b->pmu->name ?: "");  	return strcmp(a_pmu_name, b_pmu_name) == 0;  } @@ -1615,7 +1722,8 @@ void print_pmu_events(const struct print_callbacks *print_cb, void *print_state)  	for (j = 0; j < len; j++) {  		const char *name, *alias = NULL, *scale_unit = NULL,  			*desc = NULL, *long_desc = NULL, -			*encoding_desc = NULL, *topic = NULL; +			*encoding_desc = NULL, *topic = NULL, +			*pmu_name = NULL;  		bool deprecated = false;  		size_t buf_used; @@ -1625,7 +1733,8 @@ void print_pmu_events(const struct print_callbacks *print_cb, void *print_state)  		if (!aliases[j].event) {  			/* A selectable event. */ -			buf_used = snprintf(buf, sizeof(buf), "%s//", aliases[j].pmu->name) + 1; +			pmu_name = aliases[j].pmu->name; +			buf_used = snprintf(buf, sizeof(buf), "%s//", pmu_name) + 1;  			name = buf;  		} else {  			if (aliases[j].event->desc) { @@ -1640,6 +1749,7 @@ void print_pmu_events(const struct print_callbacks *print_cb, void *print_state)  				}  				buf_used = strlen(buf) + 1;  			} +			pmu_name = aliases[j].event->pmu_name ?: (aliases[j].pmu->name ?: "");  			if (strlen(aliases[j].event->unit) || aliases[j].event->scale != 1.0) {  				scale_unit = buf + buf_used;  				buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used, @@ -1651,12 +1761,11 @@ void print_pmu_events(const struct print_callbacks *print_cb, void *print_state)  			topic = aliases[j].event->topic;  			encoding_desc = buf + buf_used;  			buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used, -					"%s/%s/", aliases[j].pmu->name, -					aliases[j].event->str) + 1; +					"%s/%s/", pmu_name, aliases[j].event->str) + 1;  			deprecated = aliases[j].event->deprecated;  		}  		print_cb->print_event(print_state, -				aliases[j].pmu->name, +				pmu_name,  				topic,  				name,  				alias, @@ -1701,6 +1810,17 @@ FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name)  	return fopen(path, "r");  } +FILE *perf_pmu__open_file_at(struct perf_pmu *pmu, int dirfd, const char *name) +{ +	int fd; + +	fd = perf_pmu__pathname_fd(dirfd, pmu->name, name, O_RDONLY); +	if (fd < 0) +		return NULL; + +	return fdopen(fd, "r"); +} +  int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt,  			...)  { @@ -1718,6 +1838,23 @@ int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt,  	return ret;  } +int perf_pmu__scan_file_at(struct perf_pmu *pmu, int dirfd, const char *name, +			   const char *fmt, ...) +{ +	va_list args; +	FILE *file; +	int ret = EOF; + +	va_start(args, fmt); +	file = perf_pmu__open_file_at(pmu, dirfd, name); +	if (file) { +		ret = vfscanf(file, fmt, args); +		fclose(file); +	} +	va_end(args); +	return ret; +} +  bool perf_pmu__file_exists(struct perf_pmu *pmu, const char *name)  {  	char path[PATH_MAX]; @@ -1745,13 +1882,25 @@ static int perf_pmu__new_caps(struct list_head *list, char *name, char *value)  	return 0;  free_name: -	zfree(caps->name); +	zfree(&caps->name);  free_caps:  	free(caps);  	return -ENOMEM;  } +static void perf_pmu__del_caps(struct perf_pmu *pmu) +{ +	struct perf_pmu_caps *caps, *tmp; + +	list_for_each_entry_safe(caps, tmp, &pmu->caps, list) { +		list_del(&caps->list); +		zfree(&caps->name); +		zfree(&caps->value); +		free(caps); +	} +} +  /*   * Reading/parsing the given pmu capabilities, which should be located at:   * /sys/bus/event_source/devices/<dev>/caps as sysfs group attributes. @@ -1763,6 +1912,7 @@ int perf_pmu__caps_parse(struct perf_pmu *pmu)  	char caps_path[PATH_MAX];  	DIR *caps_dir;  	struct dirent *evt_ent; +	int caps_fd;  	if (pmu->caps_initialized)  		return pmu->nr_caps; @@ -1781,20 +1931,25 @@ int perf_pmu__caps_parse(struct perf_pmu *pmu)  	if (!caps_dir)  		return -EINVAL; +	caps_fd = dirfd(caps_dir); +  	while ((evt_ent = readdir(caps_dir)) != NULL) { -		char path[PATH_MAX + NAME_MAX + 1];  		char *name = evt_ent->d_name;  		char value[128];  		FILE *file; +		int fd;  		if (!strcmp(name, ".") || !strcmp(name, ".."))  			continue; -		snprintf(path, sizeof(path), "%s/%s", caps_path, name); - -		file = fopen(path, "r"); -		if (!file) +		fd = openat(caps_fd, name, O_RDONLY); +		if (fd == -1) +			continue; +		file = fdopen(fd, "r"); +		if (!file) { +			close(fd);  			continue; +		}  		if (!fgets(value, sizeof(value), file) ||  		    (perf_pmu__new_caps(&pmu->caps, name, value) < 0)) { @@ -1863,7 +2018,7 @@ int perf_pmu__match(char *pattern, char *name, char *tok)  	if (fnmatch(pattern, name, 0))  		return -1; -	if (tok && !perf_pmu__valid_suffix(name, tok)) +	if (tok && !perf_pmu__match_ignoring_suffix(name, tok))  		return -1;  	return 0; @@ -1890,13 +2045,13 @@ int perf_pmu__cpus_match(struct perf_pmu *pmu, struct perf_cpu_map *cpus,  	perf_cpu_map__for_each_cpu(cpu, i, cpus) {  		if (!perf_cpu_map__has(pmu_cpus, cpu)) -			unmatched_cpus->map[unmatched_nr++] = cpu; +			RC_CHK_ACCESS(unmatched_cpus)->map[unmatched_nr++] = cpu;  		else -			matched_cpus->map[matched_nr++] = cpu; +			RC_CHK_ACCESS(matched_cpus)->map[matched_nr++] = cpu;  	} -	unmatched_cpus->nr = unmatched_nr; -	matched_cpus->nr = matched_nr; +	perf_cpu_map__set_nr(unmatched_cpus, unmatched_nr); +	perf_cpu_map__set_nr(matched_cpus, matched_nr);  	*mcpus_ptr = matched_cpus;  	*ucpus_ptr = unmatched_cpus;  	return 0; @@ -1916,6 +2071,18 @@ int perf_pmu__event_source_devices_scnprintf(char *pathname, size_t size)  	return scnprintf(pathname, size, "%s/bus/event_source/devices/", sysfs);  } +int perf_pmu__event_source_devices_fd(void) +{ +	char path[PATH_MAX]; +	const char *sysfs = sysfs__mountpoint(); + +	if (!sysfs) +		return -1; + +	scnprintf(path, sizeof(path), "%s/bus/event_source/devices/", sysfs); +	return open(path, O_DIRECTORY); +} +  /*   * Fill 'buf' with the path to a file or folder in 'pmu_name' in   * sysfs. For example if pmu_name = "cs_etm" and 'filename' = "format" @@ -1934,3 +2101,37 @@ int perf_pmu__pathname_scnprintf(char *buf, size_t size,  		return 0;  	return scnprintf(buf, size, "%s%s/%s", base_path, pmu_name, filename);  } + +int perf_pmu__pathname_fd(int dirfd, const char *pmu_name, const char *filename, int flags) +{ +	char path[PATH_MAX]; + +	scnprintf(path, sizeof(path), "%s/%s", pmu_name, filename); +	return openat(dirfd, path, flags); +} + +static void perf_pmu__delete(struct perf_pmu *pmu) +{ +	perf_pmu__del_formats(&pmu->format); +	perf_pmu__del_aliases(pmu); +	perf_pmu__del_caps(pmu); + +	perf_cpu_map__put(pmu->cpus); + +	zfree(&pmu->default_config); +	zfree(&pmu->name); +	zfree(&pmu->alias_name); +	free(pmu); +} + +void perf_pmu__destroy(void) +{ +	struct perf_pmu *pmu, *tmp; + +	list_for_each_entry_safe(pmu, tmp, &pmus, list) { +		list_del(&pmu->list); +		list_del(&pmu->hybrid_list); + +		perf_pmu__delete(pmu); +	} +}  |