diff options
Diffstat (limited to 'tools/perf/util/parse-events.c')
| -rw-r--r-- | tools/perf/util/parse-events.c | 1475 | 
1 files changed, 932 insertions, 543 deletions
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 4af5bd59cfd..1e15df10a88 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -1,130 +1,128 @@ -#include "../../../include/linux/hw_breakpoint.h" +#include <linux/hw_breakpoint.h>  #include "util.h"  #include "../perf.h" +#include "evlist.h" +#include "evsel.h"  #include "parse-options.h"  #include "parse-events.h"  #include "exec_cmd.h" -#include "string.h" +#include "linux/string.h"  #include "symbol.h"  #include "cache.h"  #include "header.h" -#include "debugfs.h" +#include <api/fs/debugfs.h> +#include "parse-events-bison.h" +#define YY_EXTRA_TYPE int +#include "parse-events-flex.h" +#include "pmu.h" +#include "thread_map.h" -int				nr_counters; - -struct perf_event_attr		attrs[MAX_COUNTERS]; -char				*filters[MAX_COUNTERS]; +#define MAX_NAME_LEN 100  struct event_symbol { -	u8		type; -	u64		config;  	const char	*symbol;  	const char	*alias;  }; -enum event_result { -	EVT_FAILED, -	EVT_HANDLED, -	EVT_HANDLED_ALL +#ifdef PARSER_DEBUG +extern int parse_events_debug; +#endif +int parse_events_parse(void *data, void *scanner); + +static struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = { +	[PERF_COUNT_HW_CPU_CYCLES] = { +		.symbol = "cpu-cycles", +		.alias  = "cycles", +	}, +	[PERF_COUNT_HW_INSTRUCTIONS] = { +		.symbol = "instructions", +		.alias  = "", +	}, +	[PERF_COUNT_HW_CACHE_REFERENCES] = { +		.symbol = "cache-references", +		.alias  = "", +	}, +	[PERF_COUNT_HW_CACHE_MISSES] = { +		.symbol = "cache-misses", +		.alias  = "", +	}, +	[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = { +		.symbol = "branch-instructions", +		.alias  = "branches", +	}, +	[PERF_COUNT_HW_BRANCH_MISSES] = { +		.symbol = "branch-misses", +		.alias  = "", +	}, +	[PERF_COUNT_HW_BUS_CYCLES] = { +		.symbol = "bus-cycles", +		.alias  = "", +	}, +	[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = { +		.symbol = "stalled-cycles-frontend", +		.alias  = "idle-cycles-frontend", +	}, +	[PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = { +		.symbol = "stalled-cycles-backend", +		.alias  = "idle-cycles-backend", +	}, +	[PERF_COUNT_HW_REF_CPU_CYCLES] = { +		.symbol = "ref-cycles", +		.alias  = "", +	},  }; -char debugfs_path[MAXPATHLEN]; - -#define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x -#define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x - -static struct event_symbol event_symbols[] = { -  { CHW(CPU_CYCLES),		"cpu-cycles",		"cycles"	}, -  { CHW(INSTRUCTIONS),		"instructions",		""		}, -  { CHW(CACHE_REFERENCES),	"cache-references",	""		}, -  { CHW(CACHE_MISSES),		"cache-misses",		""		}, -  { CHW(BRANCH_INSTRUCTIONS),	"branch-instructions",	"branches"	}, -  { CHW(BRANCH_MISSES),		"branch-misses",	""		}, -  { CHW(BUS_CYCLES),		"bus-cycles",		""		}, - -  { CSW(CPU_CLOCK),		"cpu-clock",		""		}, -  { CSW(TASK_CLOCK),		"task-clock",		""		}, -  { CSW(PAGE_FAULTS),		"page-faults",		"faults"	}, -  { CSW(PAGE_FAULTS_MIN),	"minor-faults",		""		}, -  { CSW(PAGE_FAULTS_MAJ),	"major-faults",		""		}, -  { CSW(CONTEXT_SWITCHES),	"context-switches",	"cs"		}, -  { CSW(CPU_MIGRATIONS),	"cpu-migrations",	"migrations"	}, -  { CSW(ALIGNMENT_FAULTS),	"alignment-faults",	""		}, -  { CSW(EMULATION_FAULTS),	"emulation-faults",	""		}, +static struct event_symbol event_symbols_sw[PERF_COUNT_SW_MAX] = { +	[PERF_COUNT_SW_CPU_CLOCK] = { +		.symbol = "cpu-clock", +		.alias  = "", +	}, +	[PERF_COUNT_SW_TASK_CLOCK] = { +		.symbol = "task-clock", +		.alias  = "", +	}, +	[PERF_COUNT_SW_PAGE_FAULTS] = { +		.symbol = "page-faults", +		.alias  = "faults", +	}, +	[PERF_COUNT_SW_CONTEXT_SWITCHES] = { +		.symbol = "context-switches", +		.alias  = "cs", +	}, +	[PERF_COUNT_SW_CPU_MIGRATIONS] = { +		.symbol = "cpu-migrations", +		.alias  = "migrations", +	}, +	[PERF_COUNT_SW_PAGE_FAULTS_MIN] = { +		.symbol = "minor-faults", +		.alias  = "", +	}, +	[PERF_COUNT_SW_PAGE_FAULTS_MAJ] = { +		.symbol = "major-faults", +		.alias  = "", +	}, +	[PERF_COUNT_SW_ALIGNMENT_FAULTS] = { +		.symbol = "alignment-faults", +		.alias  = "", +	}, +	[PERF_COUNT_SW_EMULATION_FAULTS] = { +		.symbol = "emulation-faults", +		.alias  = "", +	}, +	[PERF_COUNT_SW_DUMMY] = { +		.symbol = "dummy", +		.alias  = "", +	},  };  #define __PERF_EVENT_FIELD(config, name) \  	((config & PERF_EVENT_##name##_MASK) >> PERF_EVENT_##name##_SHIFT) -#define PERF_EVENT_RAW(config)	__PERF_EVENT_FIELD(config, RAW) +#define PERF_EVENT_RAW(config)		__PERF_EVENT_FIELD(config, RAW)  #define PERF_EVENT_CONFIG(config)	__PERF_EVENT_FIELD(config, CONFIG) -#define PERF_EVENT_TYPE(config)	__PERF_EVENT_FIELD(config, TYPE) +#define PERF_EVENT_TYPE(config)		__PERF_EVENT_FIELD(config, TYPE)  #define PERF_EVENT_ID(config)		__PERF_EVENT_FIELD(config, EVENT) -static const char *hw_event_names[] = { -	"cycles", -	"instructions", -	"cache-references", -	"cache-misses", -	"branches", -	"branch-misses", -	"bus-cycles", -}; - -static const char *sw_event_names[] = { -	"cpu-clock-msecs", -	"task-clock-msecs", -	"page-faults", -	"context-switches", -	"CPU-migrations", -	"minor-faults", -	"major-faults", -	"alignment-faults", -	"emulation-faults", -}; - -#define MAX_ALIASES 8 - -static const char *hw_cache[][MAX_ALIASES] = { - { "L1-dcache",	"l1-d",		"l1d",		"L1-data",		}, - { "L1-icache",	"l1-i",		"l1i",		"L1-instruction",	}, - { "LLC",	"L2"							}, - { "dTLB",	"d-tlb",	"Data-TLB",				}, - { "iTLB",	"i-tlb",	"Instruction-TLB",			}, - { "branch",	"branches",	"bpu",		"btb",		"bpc",	}, -}; - -static const char *hw_cache_op[][MAX_ALIASES] = { - { "load",	"loads",	"read",					}, - { "store",	"stores",	"write",				}, - { "prefetch",	"prefetches",	"speculative-read", "speculative-load",	}, -}; - -static const char *hw_cache_result[][MAX_ALIASES] = { - { "refs",	"Reference",	"ops",		"access",		}, - { "misses",	"miss",							}, -}; - -#define C(x)		PERF_COUNT_HW_CACHE_##x -#define CACHE_READ	(1 << C(OP_READ)) -#define CACHE_WRITE	(1 << C(OP_WRITE)) -#define CACHE_PREFETCH	(1 << C(OP_PREFETCH)) -#define COP(x)		(1 << x) - -/* - * cache operartion stat - * L1I : Read and prefetch only - * ITLB and BPU : Read-only - */ -static unsigned long hw_cache_stat[C(MAX)] = { - [C(L1D)]	= (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), - [C(L1I)]	= (CACHE_READ | CACHE_PREFETCH), - [C(LL)]	= (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), - [C(DTLB)]	= (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), - [C(ITLB)]	= (CACHE_READ), - [C(BPU)]	= (CACHE_READ), -}; -  #define for_each_subsystem(sys_dir, sys_dirent, sys_next)	       \  	while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next)	       \  	if (sys_dirent.d_type == DT_DIR &&				       \ @@ -136,7 +134,7 @@ static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir)  	char evt_path[MAXPATHLEN];  	int fd; -	snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path, +	snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", tracing_events_path,  			sys_dir->d_name, evt_dir->d_name);  	fd = open(evt_path, O_RDONLY);  	if (fd < 0) @@ -161,22 +159,22 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config)  	struct tracepoint_path *path = NULL;  	DIR *sys_dir, *evt_dir;  	struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; -	char id_buf[4]; +	char id_buf[24];  	int fd;  	u64 id;  	char evt_path[MAXPATHLEN];  	char dir_path[MAXPATHLEN]; -	if (debugfs_valid_mountpoint(debugfs_path)) +	if (debugfs_valid_mountpoint(tracing_events_path))  		return NULL; -	sys_dir = opendir(debugfs_path); +	sys_dir = opendir(tracing_events_path);  	if (!sys_dir)  		return NULL;  	for_each_subsystem(sys_dir, sys_dirent, sys_next) { -		snprintf(dir_path, MAXPATHLEN, "%s/%s", debugfs_path, +		snprintf(dir_path, MAXPATHLEN, "%s/%s", tracing_events_path,  			 sys_dirent.d_name);  		evt_dir = opendir(dir_path);  		if (!evt_dir) @@ -206,7 +204,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config)  				}  				path->name = malloc(MAX_EVENT_LENGTH);  				if (!path->name) { -					free(path->system); +					zfree(&path->system);  					free(path);  					return NULL;  				} @@ -224,99 +222,43 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config)  	return NULL;  } -#define TP_PATH_LEN (MAX_EVENT_LENGTH * 2 + 1) -static const char *tracepoint_id_to_name(u64 config) +struct tracepoint_path *tracepoint_name_to_path(const char *name)  { -	static char buf[TP_PATH_LEN]; -	struct tracepoint_path *path; +	struct tracepoint_path *path = zalloc(sizeof(*path)); +	char *str = strchr(name, ':'); -	path = tracepoint_id_to_path(config); -	if (path) { -		snprintf(buf, TP_PATH_LEN, "%s:%s", path->system, path->name); -		free(path->name); -		free(path->system); +	if (path == NULL || str == NULL) {  		free(path); -	} else -		snprintf(buf, TP_PATH_LEN, "%s:%s", "unknown", "unknown"); - -	return buf; -} - -static int is_cache_op_valid(u8 cache_type, u8 cache_op) -{ -	if (hw_cache_stat[cache_type] & COP(cache_op)) -		return 1;	/* valid */ -	else -		return 0;	/* invalid */ -} - -static char *event_cache_name(u8 cache_type, u8 cache_op, u8 cache_result) -{ -	static char name[50]; - -	if (cache_result) { -		sprintf(name, "%s-%s-%s", hw_cache[cache_type][0], -			hw_cache_op[cache_op][0], -			hw_cache_result[cache_result][0]); -	} else { -		sprintf(name, "%s-%s", hw_cache[cache_type][0], -			hw_cache_op[cache_op][1]); +		return NULL;  	} -	return name; -} +	path->system = strndup(name, str - name); +	path->name = strdup(str+1); -const char *event_name(int counter) -{ -	u64 config = attrs[counter].config; -	int type = attrs[counter].type; +	if (path->system == NULL || path->name == NULL) { +		zfree(&path->system); +		zfree(&path->name); +		free(path); +		path = NULL; +	} -	return __event_name(type, config); +	return path;  } -const char *__event_name(int type, u64 config) +const char *event_type(int type)  { -	static char buf[32]; - -	if (type == PERF_TYPE_RAW) { -		sprintf(buf, "raw 0x%llx", config); -		return buf; -	} -  	switch (type) {  	case PERF_TYPE_HARDWARE: -		if (config < PERF_COUNT_HW_MAX) -			return hw_event_names[config]; -		return "unknown-hardware"; - -	case PERF_TYPE_HW_CACHE: { -		u8 cache_type, cache_op, cache_result; - -		cache_type   = (config >>  0) & 0xff; -		if (cache_type > PERF_COUNT_HW_CACHE_MAX) -			return "unknown-ext-hardware-cache-type"; - -		cache_op     = (config >>  8) & 0xff; -		if (cache_op > PERF_COUNT_HW_CACHE_OP_MAX) -			return "unknown-ext-hardware-cache-op"; - -		cache_result = (config >> 16) & 0xff; -		if (cache_result > PERF_COUNT_HW_CACHE_RESULT_MAX) -			return "unknown-ext-hardware-cache-result"; - -		if (!is_cache_op_valid(cache_type, cache_op)) -			return "invalid-cache"; - -		return event_cache_name(cache_type, cache_op, cache_result); -	} +		return "hardware";  	case PERF_TYPE_SOFTWARE: -		if (config < PERF_COUNT_SW_MAX) -			return sw_event_names[config]; -		return "unknown-software"; +		return "software";  	case PERF_TYPE_TRACEPOINT: -		return tracepoint_id_to_name(config); +		return "tracepoint"; + +	case PERF_TYPE_HW_CACHE: +		return "hardware-cache";  	default:  		break; @@ -325,66 +267,93 @@ const char *__event_name(int type, u64 config)  	return "unknown";  } -static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int size) + + +static struct perf_evsel * +__add_event(struct list_head *list, int *idx, +	    struct perf_event_attr *attr, +	    char *name, struct cpu_map *cpus) +{ +	struct perf_evsel *evsel; + +	event_attr_init(attr); + +	evsel = perf_evsel__new_idx(attr, (*idx)++); +	if (!evsel) +		return NULL; + +	evsel->cpus = cpus; +	if (name) +		evsel->name = strdup(name); +	list_add_tail(&evsel->node, list); +	return evsel; +} + +static int add_event(struct list_head *list, int *idx, +		     struct perf_event_attr *attr, char *name) +{ +	return __add_event(list, idx, attr, name, NULL) ? 0 : -ENOMEM; +} + +static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES], int size)  {  	int i, j;  	int n, longest = -1;  	for (i = 0; i < size; i++) { -		for (j = 0; j < MAX_ALIASES && names[i][j]; j++) { +		for (j = 0; j < PERF_EVSEL__MAX_ALIASES && names[i][j]; j++) {  			n = strlen(names[i][j]); -			if (n > longest && !strncasecmp(*str, names[i][j], n)) +			if (n > longest && !strncasecmp(str, names[i][j], n))  				longest = n;  		} -		if (longest > 0) { -			*str += longest; +		if (longest > 0)  			return i; -		}  	}  	return -1;  } -static enum event_result -parse_generic_hw_event(const char **str, struct perf_event_attr *attr) +int parse_events_add_cache(struct list_head *list, int *idx, +			   char *type, char *op_result1, char *op_result2)  { -	const char *s = *str; +	struct perf_event_attr attr; +	char name[MAX_NAME_LEN];  	int cache_type = -1, cache_op = -1, cache_result = -1; +	char *op_result[2] = { op_result1, op_result2 }; +	int i, n; -	cache_type = parse_aliases(&s, hw_cache, PERF_COUNT_HW_CACHE_MAX);  	/*  	 * No fallback - if we cannot get a clear cache type  	 * then bail out:  	 */ +	cache_type = parse_aliases(type, perf_evsel__hw_cache, +				   PERF_COUNT_HW_CACHE_MAX);  	if (cache_type == -1) -		return EVT_FAILED; +		return -EINVAL; -	while ((cache_op == -1 || cache_result == -1) && *s == '-') { -		++s; +	n = snprintf(name, MAX_NAME_LEN, "%s", type); + +	for (i = 0; (i < 2) && (op_result[i]); i++) { +		char *str = op_result[i]; + +		n += snprintf(name + n, MAX_NAME_LEN - n, "-%s", str);  		if (cache_op == -1) { -			cache_op = parse_aliases(&s, hw_cache_op, -						PERF_COUNT_HW_CACHE_OP_MAX); +			cache_op = parse_aliases(str, perf_evsel__hw_cache_op, +						 PERF_COUNT_HW_CACHE_OP_MAX);  			if (cache_op >= 0) { -				if (!is_cache_op_valid(cache_type, cache_op)) -					return 0; +				if (!perf_evsel__is_cache_op_valid(cache_type, cache_op)) +					return -EINVAL;  				continue;  			}  		}  		if (cache_result == -1) { -			cache_result = parse_aliases(&s, hw_cache_result, -						PERF_COUNT_HW_CACHE_RESULT_MAX); +			cache_result = parse_aliases(str, perf_evsel__hw_cache_result, +						     PERF_COUNT_HW_CACHE_RESULT_MAX);  			if (cache_result >= 0)  				continue;  		} - -		/* -		 * Can't parse this as a cache op or result, so back up -		 * to the '-'. -		 */ -		--s; -		break;  	}  	/* @@ -399,307 +368,361 @@ parse_generic_hw_event(const char **str, struct perf_event_attr *attr)  	if (cache_result == -1)  		cache_result = PERF_COUNT_HW_CACHE_RESULT_ACCESS; -	attr->config = cache_type | (cache_op << 8) | (cache_result << 16); -	attr->type = PERF_TYPE_HW_CACHE; - -	*str = s; -	return EVT_HANDLED; +	memset(&attr, 0, sizeof(attr)); +	attr.config = cache_type | (cache_op << 8) | (cache_result << 16); +	attr.type = PERF_TYPE_HW_CACHE; +	return add_event(list, idx, &attr, name);  } -static enum event_result -parse_single_tracepoint_event(char *sys_name, -			      const char *evt_name, -			      unsigned int evt_length, -			      struct perf_event_attr *attr, -			      const char **strp) +static int add_tracepoint(struct list_head *list, int *idx, +			  char *sys_name, char *evt_name)  { -	char evt_path[MAXPATHLEN]; -	char id_buf[4]; -	u64 id; -	int fd; - -	snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path, -		 sys_name, evt_name); - -	fd = open(evt_path, O_RDONLY); -	if (fd < 0) -		return EVT_FAILED; - -	if (read(fd, id_buf, sizeof(id_buf)) < 0) { -		close(fd); -		return EVT_FAILED; -	} - -	close(fd); -	id = atoll(id_buf); -	attr->config = id; -	attr->type = PERF_TYPE_TRACEPOINT; -	*strp = evt_name + evt_length; - -	attr->sample_type |= PERF_SAMPLE_RAW; -	attr->sample_type |= PERF_SAMPLE_TIME; -	attr->sample_type |= PERF_SAMPLE_CPU; +	struct perf_evsel *evsel; -	attr->sample_period = 1; +	evsel = perf_evsel__newtp_idx(sys_name, evt_name, (*idx)++); +	if (!evsel) +		return -ENOMEM; +	list_add_tail(&evsel->node, list); -	return EVT_HANDLED; +	return 0;  } -/* sys + ':' + event + ':' + flags*/ -#define MAX_EVOPT_LEN	(MAX_EVENT_LENGTH * 2 + 2 + 128) -static enum event_result -parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp, -				char *flags) +static int add_tracepoint_multi_event(struct list_head *list, int *idx, +				      char *sys_name, char *evt_name)  {  	char evt_path[MAXPATHLEN];  	struct dirent *evt_ent;  	DIR *evt_dir; +	int ret = 0; -	snprintf(evt_path, MAXPATHLEN, "%s/%s", debugfs_path, sys_name); +	snprintf(evt_path, MAXPATHLEN, "%s/%s", tracing_events_path, sys_name);  	evt_dir = opendir(evt_path); -  	if (!evt_dir) {  		perror("Can't open event dir"); -		return EVT_FAILED; +		return -1;  	} -	while ((evt_ent = readdir(evt_dir))) { -		char event_opt[MAX_EVOPT_LEN + 1]; -		int len; - +	while (!ret && (evt_ent = readdir(evt_dir))) {  		if (!strcmp(evt_ent->d_name, ".")  		    || !strcmp(evt_ent->d_name, "..")  		    || !strcmp(evt_ent->d_name, "enable")  		    || !strcmp(evt_ent->d_name, "filter"))  			continue; -		if (!strglobmatch(evt_ent->d_name, evt_exp)) +		if (!strglobmatch(evt_ent->d_name, evt_name))  			continue; -		len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s%s%s", sys_name, -			       evt_ent->d_name, flags ? ":" : "", -			       flags ?: ""); -		if (len < 0) -			return EVT_FAILED; - -		if (parse_events(NULL, event_opt, 0)) -			return EVT_FAILED; +		ret = add_tracepoint(list, idx, sys_name, evt_ent->d_name);  	} -	return EVT_HANDLED_ALL; +	closedir(evt_dir); +	return ret;  } - -static enum event_result parse_tracepoint_event(const char **strp, -				    struct perf_event_attr *attr) +static int add_tracepoint_event(struct list_head *list, int *idx, +				char *sys_name, char *evt_name)  { -	const char *evt_name; -	char *flags; -	char sys_name[MAX_EVENT_LENGTH]; -	unsigned int sys_length, evt_length; +	return strpbrk(evt_name, "*?") ? +	       add_tracepoint_multi_event(list, idx, sys_name, evt_name) : +	       add_tracepoint(list, idx, sys_name, evt_name); +} -	if (debugfs_valid_mountpoint(debugfs_path)) -		return 0; +static int add_tracepoint_multi_sys(struct list_head *list, int *idx, +				    char *sys_name, char *evt_name) +{ +	struct dirent *events_ent; +	DIR *events_dir; +	int ret = 0; -	evt_name = strchr(*strp, ':'); -	if (!evt_name) -		return EVT_FAILED; +	events_dir = opendir(tracing_events_path); +	if (!events_dir) { +		perror("Can't open event dir"); +		return -1; +	} -	sys_length = evt_name - *strp; -	if (sys_length >= MAX_EVENT_LENGTH) -		return 0; +	while (!ret && (events_ent = readdir(events_dir))) { +		if (!strcmp(events_ent->d_name, ".") +		    || !strcmp(events_ent->d_name, "..") +		    || !strcmp(events_ent->d_name, "enable") +		    || !strcmp(events_ent->d_name, "header_event") +		    || !strcmp(events_ent->d_name, "header_page")) +			continue; -	strncpy(sys_name, *strp, sys_length); -	sys_name[sys_length] = '\0'; -	evt_name = evt_name + 1; +		if (!strglobmatch(events_ent->d_name, sys_name)) +			continue; -	flags = strchr(evt_name, ':'); -	if (flags) { -		/* split it out: */ -		evt_name = strndup(evt_name, flags - evt_name); -		flags++; +		ret = add_tracepoint_event(list, idx, events_ent->d_name, +					   evt_name);  	} -	evt_length = strlen(evt_name); -	if (evt_length >= MAX_EVENT_LENGTH) -		return EVT_FAILED; +	closedir(events_dir); +	return ret; +} + +int parse_events_add_tracepoint(struct list_head *list, int *idx, +				char *sys, char *event) +{ +	int ret; + +	ret = debugfs_valid_mountpoint(tracing_events_path); +	if (ret) +		return ret; -	if (strpbrk(evt_name, "*?")) { -		*strp = evt_name + evt_length; -		return parse_multiple_tracepoint_event(sys_name, evt_name, -						       flags); -	} else -		return parse_single_tracepoint_event(sys_name, evt_name, -						     evt_length, attr, strp); +	if (strpbrk(sys, "*?")) +		return add_tracepoint_multi_sys(list, idx, sys, event); +	else +		return add_tracepoint_event(list, idx, sys, event);  } -static enum event_result -parse_breakpoint_type(const char *type, const char **strp, -		      struct perf_event_attr *attr) +static int +parse_breakpoint_type(const char *type, struct perf_event_attr *attr)  {  	int i;  	for (i = 0; i < 3; i++) { -		if (!type[i]) +		if (!type || !type[i])  			break; +#define CHECK_SET_TYPE(bit)		\ +do {					\ +	if (attr->bp_type & bit)	\ +		return -EINVAL;		\ +	else				\ +		attr->bp_type |= bit;	\ +} while (0) +  		switch (type[i]) {  		case 'r': -			attr->bp_type |= HW_BREAKPOINT_R; +			CHECK_SET_TYPE(HW_BREAKPOINT_R);  			break;  		case 'w': -			attr->bp_type |= HW_BREAKPOINT_W; +			CHECK_SET_TYPE(HW_BREAKPOINT_W);  			break;  		case 'x': -			attr->bp_type |= HW_BREAKPOINT_X; +			CHECK_SET_TYPE(HW_BREAKPOINT_X);  			break;  		default: -			return EVT_FAILED; +			return -EINVAL;  		}  	} + +#undef CHECK_SET_TYPE +  	if (!attr->bp_type) /* Default */  		attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W; -	*strp = type + i; - -	return EVT_HANDLED; +	return 0;  } -static enum event_result -parse_breakpoint_event(const char **strp, struct perf_event_attr *attr) +int parse_events_add_breakpoint(struct list_head *list, int *idx, +				void *ptr, char *type)  { -	const char *target; -	const char *type; -	char *endaddr; -	u64 addr; -	enum event_result err; - -	target = strchr(*strp, ':'); -	if (!target) -		return EVT_FAILED; - -	if (strncmp(*strp, "mem", target - *strp) != 0) -		return EVT_FAILED; - -	target++; - -	addr = strtoull(target, &endaddr, 0); -	if (target == endaddr) -		return EVT_FAILED; - -	attr->bp_addr = addr; -	*strp = endaddr; +	struct perf_event_attr attr; -	type = strchr(target, ':'); +	memset(&attr, 0, sizeof(attr)); +	attr.bp_addr = (unsigned long) ptr; -	/* If no type is defined, just rw as default */ -	if (!type) { -		attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W; -	} else { -		err = parse_breakpoint_type(++type, strp, attr); -		if (err == EVT_FAILED) -			return EVT_FAILED; -	} +	if (parse_breakpoint_type(type, &attr)) +		return -EINVAL;  	/*  	 * We should find a nice way to override the access length  	 * Provide some defaults for now  	 */ -	if (attr->bp_type == HW_BREAKPOINT_X) -		attr->bp_len = sizeof(long); +	if (attr.bp_type == HW_BREAKPOINT_X) +		attr.bp_len = sizeof(long);  	else -		attr->bp_len = HW_BREAKPOINT_LEN_4; +		attr.bp_len = HW_BREAKPOINT_LEN_4; -	attr->type = PERF_TYPE_BREAKPOINT; +	attr.type = PERF_TYPE_BREAKPOINT; +	attr.sample_period = 1; -	return EVT_HANDLED; +	return add_event(list, idx, &attr, NULL);  } -static int check_events(const char *str, unsigned int i) +static int config_term(struct perf_event_attr *attr, +		       struct parse_events_term *term)  { -	int n; +#define CHECK_TYPE_VAL(type)					\ +do {								\ +	if (PARSE_EVENTS__TERM_TYPE_ ## type != term->type_val)	\ +		return -EINVAL;					\ +} while (0) + +	switch (term->type_term) { +	case PARSE_EVENTS__TERM_TYPE_CONFIG: +		CHECK_TYPE_VAL(NUM); +		attr->config = term->val.num; +		break; +	case PARSE_EVENTS__TERM_TYPE_CONFIG1: +		CHECK_TYPE_VAL(NUM); +		attr->config1 = term->val.num; +		break; +	case PARSE_EVENTS__TERM_TYPE_CONFIG2: +		CHECK_TYPE_VAL(NUM); +		attr->config2 = term->val.num; +		break; +	case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD: +		CHECK_TYPE_VAL(NUM); +		attr->sample_period = term->val.num; +		break; +	case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE: +		/* +		 * TODO uncomment when the field is available +		 * attr->branch_sample_type = term->val.num; +		 */ +		break; +	case PARSE_EVENTS__TERM_TYPE_NAME: +		CHECK_TYPE_VAL(STR); +		break; +	default: +		return -EINVAL; +	} + +	return 0; +#undef CHECK_TYPE_VAL +} -	n = strlen(event_symbols[i].symbol); -	if (!strncmp(str, event_symbols[i].symbol, n)) -		return n; +static int config_attr(struct perf_event_attr *attr, +		       struct list_head *head, int fail) +{ +	struct parse_events_term *term; + +	list_for_each_entry(term, head, list) +		if (config_term(attr, term) && fail) +			return -EINVAL; -	n = strlen(event_symbols[i].alias); -	if (n) -		if (!strncmp(str, event_symbols[i].alias, n)) -			return n;  	return 0;  } -static enum event_result -parse_symbolic_event(const char **strp, struct perf_event_attr *attr) +int parse_events_add_numeric(struct list_head *list, int *idx, +			     u32 type, u64 config, +			     struct list_head *head_config) +{ +	struct perf_event_attr attr; + +	memset(&attr, 0, sizeof(attr)); +	attr.type = type; +	attr.config = config; + +	if (head_config && +	    config_attr(&attr, head_config, 1)) +		return -EINVAL; + +	return add_event(list, idx, &attr, NULL); +} + +static int parse_events__is_name_term(struct parse_events_term *term)  { -	const char *str = *strp; -	unsigned int i; -	int n; +	return term->type_term == PARSE_EVENTS__TERM_TYPE_NAME; +} -	for (i = 0; i < ARRAY_SIZE(event_symbols); i++) { -		n = check_events(str, i); -		if (n > 0) { -			attr->type = event_symbols[i].type; -			attr->config = event_symbols[i].config; -			*strp = str + n; -			return EVT_HANDLED; -		} -	} -	return EVT_FAILED; +static char *pmu_event_name(struct list_head *head_terms) +{ +	struct parse_events_term *term; + +	list_for_each_entry(term, head_terms, list) +		if (parse_events__is_name_term(term)) +			return term->val.str; + +	return NULL;  } -static enum event_result -parse_raw_event(const char **strp, struct perf_event_attr *attr) +int parse_events_add_pmu(struct list_head *list, int *idx, +			 char *name, struct list_head *head_config)  { -	const char *str = *strp; -	u64 config; -	int n; +	struct perf_event_attr attr; +	struct perf_pmu *pmu; +	struct perf_evsel *evsel; +	const char *unit; +	double scale; + +	pmu = perf_pmu__find(name); +	if (!pmu) +		return -EINVAL; -	if (*str != 'r') -		return EVT_FAILED; -	n = hex2u64(str + 1, &config); -	if (n > 0) { -		*strp = str + n + 1; -		attr->type = PERF_TYPE_RAW; -		attr->config = config; -		return EVT_HANDLED; +	memset(&attr, 0, sizeof(attr)); + +	if (perf_pmu__check_alias(pmu, head_config, &unit, &scale)) +		return -EINVAL; + +	/* +	 * Configure hardcoded terms first, no need to check +	 * return value when called with fail == 0 ;) +	 */ +	config_attr(&attr, head_config, 0); + +	if (perf_pmu__config(pmu, &attr, head_config)) +		return -EINVAL; + +	evsel = __add_event(list, idx, &attr, pmu_event_name(head_config), +			    pmu->cpus); +	if (evsel) { +		evsel->unit = unit; +		evsel->scale = scale;  	} -	return EVT_FAILED; + +	return evsel ? 0 : -ENOMEM;  } -static enum event_result -parse_numeric_event(const char **strp, struct perf_event_attr *attr) +int parse_events__modifier_group(struct list_head *list, +				 char *event_mod)  { -	const char *str = *strp; -	char *endp; -	unsigned long type; -	u64 config; +	return parse_events__modifier_event(list, event_mod, true); +} -	type = strtoul(str, &endp, 0); -	if (endp > str && type < PERF_TYPE_MAX && *endp == ':') { -		str = endp + 1; -		config = strtoul(str, &endp, 0); -		if (endp > str) { -			attr->type = type; -			attr->config = config; -			*strp = endp; -			return EVT_HANDLED; -		} -	} -	return EVT_FAILED; +void parse_events__set_leader(char *name, struct list_head *list) +{ +	struct perf_evsel *leader; + +	__perf_evlist__set_leader(list); +	leader = list_entry(list->next, struct perf_evsel, node); +	leader->group_name = name ? strdup(name) : NULL;  } -static enum event_result -parse_event_modifier(const char **strp, struct perf_event_attr *attr) +/* list_event is assumed to point to malloc'ed memory */ +void parse_events_update_lists(struct list_head *list_event, +			       struct list_head *list_all)  { -	const char *str = *strp; -	int exclude = 0; -	int eu = 0, ek = 0, eh = 0, precise = 0; +	/* +	 * Called for single event definition. Update the +	 * 'all event' list, and reinit the 'single event' +	 * list, for next event definition. +	 */ +	list_splice_tail(list_event, list_all); +	free(list_event); +} + +struct event_modifier { +	int eu; +	int ek; +	int eh; +	int eH; +	int eG; +	int precise; +	int exclude_GH; +	int sample_read; +	int pinned; +}; + +static int get_event_modifier(struct event_modifier *mod, char *str, +			       struct perf_evsel *evsel) +{ +	int eu = evsel ? evsel->attr.exclude_user : 0; +	int ek = evsel ? evsel->attr.exclude_kernel : 0; +	int eh = evsel ? evsel->attr.exclude_hv : 0; +	int eH = evsel ? evsel->attr.exclude_host : 0; +	int eG = evsel ? evsel->attr.exclude_guest : 0; +	int precise = evsel ? evsel->attr.precise_ip : 0; +	int sample_read = 0; +	int pinned = evsel ? evsel->attr.pinned : 0; + +	int exclude = eu | ek | eh; +	int exclude_GH = evsel ? evsel->exclude_GH : 0; + +	memset(mod, 0, sizeof(*mod)); -	if (*str++ != ':') -		return 0;  	while (*str) {  		if (*str == 'u') {  			if (!exclude) @@ -713,148 +736,252 @@ parse_event_modifier(const char **strp, struct perf_event_attr *attr)  			if (!exclude)  				exclude = eu = ek = eh = 1;  			eh = 0; +		} else if (*str == 'G') { +			if (!exclude_GH) +				exclude_GH = eG = eH = 1; +			eG = 0; +		} else if (*str == 'H') { +			if (!exclude_GH) +				exclude_GH = eG = eH = 1; +			eH = 0;  		} else if (*str == 'p') {  			precise++; +			/* use of precise requires exclude_guest */ +			if (!exclude_GH) +				eG = 1; +		} else if (*str == 'S') { +			sample_read = 1; +		} else if (*str == 'D') { +			pinned = 1;  		} else  			break;  		++str;  	} -	if (str >= *strp + 2) { -		*strp = str; -		attr->exclude_user   = eu; -		attr->exclude_kernel = ek; -		attr->exclude_hv     = eh; -		attr->precise_ip     = precise; -		return 1; -	} + +	/* +	 * precise ip: +	 * +	 *  0 - SAMPLE_IP can have arbitrary skid +	 *  1 - SAMPLE_IP must have constant skid +	 *  2 - SAMPLE_IP requested to have 0 skid +	 *  3 - SAMPLE_IP must have 0 skid +	 * +	 *  See also PERF_RECORD_MISC_EXACT_IP +	 */ +	if (precise > 3) +		return -EINVAL; + +	mod->eu = eu; +	mod->ek = ek; +	mod->eh = eh; +	mod->eH = eH; +	mod->eG = eG; +	mod->precise = precise; +	mod->exclude_GH = exclude_GH; +	mod->sample_read = sample_read; +	mod->pinned = pinned; +  	return 0;  }  /* - * Each event can have multiple symbolic names. - * Symbolic names are (almost) exactly matched. + * Basic modifier sanity check to validate it contains only one + * instance of any modifier (apart from 'p') present.   */ -static enum event_result -parse_event_symbols(const char **str, struct perf_event_attr *attr) +static int check_modifier(char *str)  { -	enum event_result ret; +	char *p = str; -	ret = parse_tracepoint_event(str, attr); -	if (ret != EVT_FAILED) -		goto modifier; +	/* The sizeof includes 0 byte as well. */ +	if (strlen(str) > (sizeof("ukhGHpppSD") - 1)) +		return -1; -	ret = parse_raw_event(str, attr); -	if (ret != EVT_FAILED) -		goto modifier; +	while (*p) { +		if (*p != 'p' && strchr(p + 1, *p)) +			return -1; +		p++; +	} -	ret = parse_numeric_event(str, attr); -	if (ret != EVT_FAILED) -		goto modifier; +	return 0; +} -	ret = parse_symbolic_event(str, attr); -	if (ret != EVT_FAILED) -		goto modifier; +int parse_events__modifier_event(struct list_head *list, char *str, bool add) +{ +	struct perf_evsel *evsel; +	struct event_modifier mod; -	ret = parse_generic_hw_event(str, attr); -	if (ret != EVT_FAILED) -		goto modifier; +	if (str == NULL) +		return 0; -	ret = parse_breakpoint_event(str, attr); -	if (ret != EVT_FAILED) -		goto modifier; +	if (check_modifier(str)) +		return -EINVAL; -	fprintf(stderr, "invalid or unsupported event: '%s'\n", *str); -	fprintf(stderr, "Run 'perf list' for a list of valid events\n"); -	return EVT_FAILED; +	if (!add && get_event_modifier(&mod, str, NULL)) +		return -EINVAL; -modifier: -	parse_event_modifier(str, attr); +	__evlist__for_each(list, evsel) { +		if (add && get_event_modifier(&mod, str, evsel)) +			return -EINVAL; + +		evsel->attr.exclude_user   = mod.eu; +		evsel->attr.exclude_kernel = mod.ek; +		evsel->attr.exclude_hv     = mod.eh; +		evsel->attr.precise_ip     = mod.precise; +		evsel->attr.exclude_host   = mod.eH; +		evsel->attr.exclude_guest  = mod.eG; +		evsel->exclude_GH          = mod.exclude_GH; +		evsel->sample_read         = mod.sample_read; + +		if (perf_evsel__is_group_leader(evsel)) +			evsel->attr.pinned = mod.pinned; +	} -	return ret; +	return 0;  } -static int store_event_type(const char *orgname) +int parse_events_name(struct list_head *list, char *name)  { -	char filename[PATH_MAX], *c; -	FILE *file; -	int id, n; +	struct perf_evsel *evsel; -	sprintf(filename, "%s/", debugfs_path); -	strncat(filename, orgname, strlen(orgname)); -	strcat(filename, "/id"); +	__evlist__for_each(list, evsel) { +		if (!evsel->name) +			evsel->name = strdup(name); +	} -	c = strchr(filename, ':'); -	if (c) -		*c = '/'; +	return 0; +} -	file = fopen(filename, "r"); -	if (!file) -		return 0; -	n = fscanf(file, "%i", &id); -	fclose(file); -	if (n < 1) { -		pr_err("cannot store event ID\n"); -		return -EINVAL; +static int parse_events__scanner(const char *str, void *data, int start_token); + +static int parse_events_fixup(int ret, const char *str, void *data, +			      int start_token) +{ +	char *o = strdup(str); +	char *s = NULL; +	char *t = o; +	char *p; +	int len = 0; + +	if (!o) +		return ret; +	while ((p = strsep(&t, ",")) != NULL) { +		if (s) +			str_append(&s, &len, ","); +		str_append(&s, &len, "cpu/"); +		str_append(&s, &len, p); +		str_append(&s, &len, "/");  	} -	return perf_header__push_event(id, orgname); +	free(o); +	if (!s) +		return -ENOMEM; +	return parse_events__scanner(s, data, start_token);  } -int parse_events(const struct option *opt __used, const char *str, int unset __used) +static int parse_events__scanner(const char *str, void *data, int start_token)  { -	struct perf_event_attr attr; -	enum event_result ret; +	YY_BUFFER_STATE buffer; +	void *scanner; +	int ret; + +	ret = parse_events_lex_init_extra(start_token, &scanner); +	if (ret) +		return ret; + +	buffer = parse_events__scan_string(str, scanner); + +#ifdef PARSER_DEBUG +	parse_events_debug = 1; +#endif +	ret = parse_events_parse(data, scanner); + +	parse_events__flush_buffer(buffer, scanner); +	parse_events__delete_buffer(buffer, scanner); +	parse_events_lex_destroy(scanner); +	if (ret && !strchr(str, '/')) +		ret = parse_events_fixup(ret, str, data, start_token); +	return ret; +} -	if (strchr(str, ':')) -		if (store_event_type(str) < 0) -			return -1; +/* + * parse event config string, return a list of event terms. + */ +int parse_events_terms(struct list_head *terms, const char *str) +{ +	struct parse_events_terms data = { +		.terms = NULL, +	}; +	int ret; + +	ret = parse_events__scanner(str, &data, PE_START_TERMS); +	if (!ret) { +		list_splice(data.terms, terms); +		zfree(&data.terms); +		return 0; +	} -	for (;;) { -		if (nr_counters == MAX_COUNTERS) -			return -1; +	if (data.terms) +		parse_events__free_terms(data.terms); +	return ret; +} -		memset(&attr, 0, sizeof(attr)); -		ret = parse_event_symbols(&str, &attr); -		if (ret == EVT_FAILED) -			return -1; +int parse_events(struct perf_evlist *evlist, const char *str) +{ +	struct parse_events_evlist data = { +		.list = LIST_HEAD_INIT(data.list), +		.idx  = evlist->nr_entries, +	}; +	int ret; + +	ret = parse_events__scanner(str, &data, PE_START_EVENTS); +	if (!ret) { +		int entries = data.idx - evlist->nr_entries; +		perf_evlist__splice_list_tail(evlist, &data.list, entries); +		evlist->nr_groups += data.nr_groups; +		return 0; +	} -		if (!(*str == 0 || *str == ',' || isspace(*str))) -			return -1; +	/* +	 * There are 2 users - builtin-record and builtin-test objects. +	 * Both call perf_evlist__delete in case of error, so we dont +	 * need to bother. +	 */ +	return ret; +} -		if (ret != EVT_HANDLED_ALL) { -			attrs[nr_counters] = attr; -			nr_counters++; -		} +int parse_events_option(const struct option *opt, const char *str, +			int unset __maybe_unused) +{ +	struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; +	int ret = parse_events(evlist, str); -		if (*str == 0) -			break; -		if (*str == ',') -			++str; -		while (isspace(*str)) -			++str; +	if (ret) { +		fprintf(stderr, "invalid or unsupported event: '%s'\n", str); +		fprintf(stderr, "Run 'perf list' for a list of valid events\n");  	} - -	return 0; +	return ret;  } -int parse_filter(const struct option *opt __used, const char *str, -		 int unset __used) +int parse_filter(const struct option *opt, const char *str, +		 int unset __maybe_unused)  { -	int i = nr_counters - 1; -	int len = strlen(str); +	struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; +	struct perf_evsel *last = NULL; + +	if (evlist->nr_entries > 0) +		last = perf_evlist__last(evlist); -	if (i < 0 || attrs[i].type != PERF_TYPE_TRACEPOINT) { +	if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) {  		fprintf(stderr,  			"-F option should follow a -e tracepoint option\n");  		return -1;  	} -	filters[i] = malloc(len + 1); -	if (!filters[i]) { +	last->filter = strdup(str); +	if (last->filter == NULL) {  		fprintf(stderr, "not enough memory to hold filter string\n");  		return -1;  	} -	strcpy(filters[i], str);  	return 0;  } @@ -872,32 +999,47 @@ static const char * const event_type_descriptors[] = {   * Print the events from <debugfs_mount_point>/tracing/events   */ -static void print_tracepoint_events(void) +void print_tracepoint_events(const char *subsys_glob, const char *event_glob, +			     bool name_only)  {  	DIR *sys_dir, *evt_dir;  	struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;  	char evt_path[MAXPATHLEN];  	char dir_path[MAXPATHLEN]; -	if (debugfs_valid_mountpoint(debugfs_path)) +	if (debugfs_valid_mountpoint(tracing_events_path)) { +		printf("  [ Tracepoints not available: %s ]\n", strerror(errno));  		return; +	} -	sys_dir = opendir(debugfs_path); +	sys_dir = opendir(tracing_events_path);  	if (!sys_dir)  		return;  	for_each_subsystem(sys_dir, sys_dirent, sys_next) { +		if (subsys_glob != NULL &&  +		    !strglobmatch(sys_dirent.d_name, subsys_glob)) +			continue; -		snprintf(dir_path, MAXPATHLEN, "%s/%s", debugfs_path, +		snprintf(dir_path, MAXPATHLEN, "%s/%s", tracing_events_path,  			 sys_dirent.d_name);  		evt_dir = opendir(dir_path);  		if (!evt_dir)  			continue;  		for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { +			if (event_glob != NULL &&  +			    !strglobmatch(evt_dirent.d_name, event_glob)) +				continue; + +			if (name_only) { +				printf("%s:%s ", sys_dirent.d_name, evt_dirent.d_name); +				continue; +			} +  			snprintf(evt_path, MAXPATHLEN, "%s:%s",  				 sys_dirent.d_name, evt_dirent.d_name); -			printf("  %-42s [%s]\n", evt_path, +			printf("  %-50s [%s]\n", evt_path,  				event_type_descriptors[PERF_TYPE_TRACEPOINT]);  		}  		closedir(evt_dir); @@ -906,60 +1048,307 @@ static void print_tracepoint_events(void)  }  /* - * Print the help text for the event symbols: + * Check whether event is in <debugfs_mount_point>/tracing/events   */ -void print_events(void) + +int is_valid_tracepoint(const char *event_string)  { -	struct event_symbol *syms = event_symbols; -	unsigned int i, type, op, prev_type = -1; -	char name[40]; +	DIR *sys_dir, *evt_dir; +	struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; +	char evt_path[MAXPATHLEN]; +	char dir_path[MAXPATHLEN]; -	printf("\n"); -	printf("List of pre-defined events (to be used in -e):\n"); +	if (debugfs_valid_mountpoint(tracing_events_path)) +		return 0; -	for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { -		type = syms->type; +	sys_dir = opendir(tracing_events_path); +	if (!sys_dir) +		return 0; -		if (type != prev_type) -			printf("\n"); +	for_each_subsystem(sys_dir, sys_dirent, sys_next) { + +		snprintf(dir_path, MAXPATHLEN, "%s/%s", tracing_events_path, +			 sys_dirent.d_name); +		evt_dir = opendir(dir_path); +		if (!evt_dir) +			continue; + +		for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { +			snprintf(evt_path, MAXPATHLEN, "%s:%s", +				 sys_dirent.d_name, evt_dirent.d_name); +			if (!strcmp(evt_path, event_string)) { +				closedir(evt_dir); +				closedir(sys_dir); +				return 1; +			} +		} +		closedir(evt_dir); +	} +	closedir(sys_dir); +	return 0; +} + +static bool is_event_supported(u8 type, unsigned config) +{ +	bool ret = true; +	int open_return; +	struct perf_evsel *evsel; +	struct perf_event_attr attr = { +		.type = type, +		.config = config, +		.disabled = 1, +	}; +	struct { +		struct thread_map map; +		int threads[1]; +	} tmap = { +		.map.nr	 = 1, +		.threads = { 0 }, +	}; + +	evsel = perf_evsel__new(&attr); +	if (evsel) { +		open_return = perf_evsel__open(evsel, NULL, &tmap.map); +		ret = open_return >= 0; + +		if (open_return == -EACCES) { +			/* +			 * This happens if the paranoid value +			 * /proc/sys/kernel/perf_event_paranoid is set to 2 +			 * Re-run with exclude_kernel set; we don't do that +			 * by default as some ARM machines do not support it. +			 * +			 */ +			evsel->attr.exclude_kernel = 1; +			ret = perf_evsel__open(evsel, NULL, &tmap.map) >= 0; +		} +		perf_evsel__delete(evsel); +	} + +	return ret; +} + +static void __print_events_type(u8 type, struct event_symbol *syms, +				unsigned max) +{ +	char name[64]; +	unsigned i; + +	for (i = 0; i < max ; i++, syms++) { +		if (!is_event_supported(type, i)) +			continue;  		if (strlen(syms->alias)) -			sprintf(name, "%s OR %s", syms->symbol, syms->alias); +			snprintf(name, sizeof(name),  "%s OR %s", +				 syms->symbol, syms->alias);  		else -			strcpy(name, syms->symbol); -		printf("  %-42s [%s]\n", name, -			event_type_descriptors[type]); +			snprintf(name, sizeof(name), "%s", syms->symbol); -		prev_type = type; +		printf("  %-50s [%s]\n", name, event_type_descriptors[type]);  	} +} + +void print_events_type(u8 type) +{ +	if (type == PERF_TYPE_SOFTWARE) +		__print_events_type(type, event_symbols_sw, PERF_COUNT_SW_MAX); +	else +		__print_events_type(type, event_symbols_hw, PERF_COUNT_HW_MAX); +} + +int print_hwcache_events(const char *event_glob, bool name_only) +{ +	unsigned int type, op, i, printed = 0; +	char name[64]; -	printf("\n");  	for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) {  		for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) {  			/* skip invalid cache type */ -			if (!is_cache_op_valid(type, op)) +			if (!perf_evsel__is_cache_op_valid(type, op))  				continue;  			for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { -				printf("  %-42s [%s]\n", -					event_cache_name(type, op, i), -					event_type_descriptors[PERF_TYPE_HW_CACHE]); +				__perf_evsel__hw_cache_type_op_res_name(type, op, i, +									name, sizeof(name)); +				if (event_glob != NULL && !strglobmatch(name, event_glob)) +					continue; + +				if (!is_event_supported(PERF_TYPE_HW_CACHE, +							type | (op << 8) | (i << 16))) +					continue; + +				if (name_only) +					printf("%s ", name); +				else +					printf("  %-50s [%s]\n", name, +					       event_type_descriptors[PERF_TYPE_HW_CACHE]); +				++printed;  			}  		}  	} -	printf("\n"); -	printf("  %-42s [%s]\n", -		"rNNN (see 'perf list --help' on how to encode it)", -	       event_type_descriptors[PERF_TYPE_RAW]); -	printf("\n"); +	if (printed) +		printf("\n"); +	return printed; +} + +static void print_symbol_events(const char *event_glob, unsigned type, +				struct event_symbol *syms, unsigned max, +				bool name_only) +{ +	unsigned i, printed = 0; +	char name[MAX_NAME_LEN]; -	printf("  %-42s [%s]\n", -			"mem:<addr>[:access]", +	for (i = 0; i < max; i++, syms++) { + +		if (event_glob != NULL &&  +		    !(strglobmatch(syms->symbol, event_glob) || +		      (syms->alias && strglobmatch(syms->alias, event_glob)))) +			continue; + +		if (!is_event_supported(type, i)) +			continue; + +		if (name_only) { +			printf("%s ", syms->symbol); +			continue; +		} + +		if (strlen(syms->alias)) +			snprintf(name, MAX_NAME_LEN, "%s OR %s", syms->symbol, syms->alias); +		else +			strncpy(name, syms->symbol, MAX_NAME_LEN); + +		printf("  %-50s [%s]\n", name, event_type_descriptors[type]); + +		printed++; +	} + +	if (printed) +		printf("\n"); +} + +/* + * Print the help text for the event symbols: + */ +void print_events(const char *event_glob, bool name_only) +{ +	if (!name_only) { +		printf("\n"); +		printf("List of pre-defined events (to be used in -e):\n"); +	} + +	print_symbol_events(event_glob, PERF_TYPE_HARDWARE, +			    event_symbols_hw, PERF_COUNT_HW_MAX, name_only); + +	print_symbol_events(event_glob, PERF_TYPE_SOFTWARE, +			    event_symbols_sw, PERF_COUNT_SW_MAX, name_only); + +	print_hwcache_events(event_glob, name_only); + +	print_pmu_events(event_glob, name_only); + +	if (event_glob != NULL) +		return; + +	if (!name_only) { +		printf("  %-50s [%s]\n", +		       "rNNN", +		       event_type_descriptors[PERF_TYPE_RAW]); +		printf("  %-50s [%s]\n", +		       "cpu/t1=v1[,t2=v2,t3 ...]/modifier", +		       event_type_descriptors[PERF_TYPE_RAW]); +		printf("   (see 'man perf-list' on how to encode it)\n"); +		printf("\n"); + +		printf("  %-50s [%s]\n", +		       "mem:<addr>[:access]",  			event_type_descriptors[PERF_TYPE_BREAKPOINT]); -	printf("\n"); +		printf("\n"); +	} + +	print_tracepoint_events(NULL, NULL, name_only); +} + +int parse_events__is_hardcoded_term(struct parse_events_term *term) +{ +	return term->type_term != PARSE_EVENTS__TERM_TYPE_USER; +} + +static int new_term(struct parse_events_term **_term, int type_val, +		    int type_term, char *config, +		    char *str, u64 num) +{ +	struct parse_events_term *term; + +	term = zalloc(sizeof(*term)); +	if (!term) +		return -ENOMEM; + +	INIT_LIST_HEAD(&term->list); +	term->type_val  = type_val; +	term->type_term = type_term; +	term->config = config; + +	switch (type_val) { +	case PARSE_EVENTS__TERM_TYPE_NUM: +		term->val.num = num; +		break; +	case PARSE_EVENTS__TERM_TYPE_STR: +		term->val.str = str; +		break; +	default: +		free(term); +		return -EINVAL; +	} + +	*_term = term; +	return 0; +} + +int parse_events_term__num(struct parse_events_term **term, +			   int type_term, char *config, u64 num) +{ +	return new_term(term, PARSE_EVENTS__TERM_TYPE_NUM, type_term, +			config, NULL, num); +} + +int parse_events_term__str(struct parse_events_term **term, +			   int type_term, char *config, char *str) +{ +	return new_term(term, PARSE_EVENTS__TERM_TYPE_STR, type_term, +			config, str, 0); +} -	print_tracepoint_events(); +int parse_events_term__sym_hw(struct parse_events_term **term, +			      char *config, unsigned idx) +{ +	struct event_symbol *sym; + +	BUG_ON(idx >= PERF_COUNT_HW_MAX); +	sym = &event_symbols_hw[idx]; + +	if (config) +		return new_term(term, PARSE_EVENTS__TERM_TYPE_STR, +				PARSE_EVENTS__TERM_TYPE_USER, config, +				(char *) sym->symbol, 0); +	else +		return new_term(term, PARSE_EVENTS__TERM_TYPE_STR, +				PARSE_EVENTS__TERM_TYPE_USER, +				(char *) "event", (char *) sym->symbol, 0); +} + +int parse_events_term__clone(struct parse_events_term **new, +			     struct parse_events_term *term) +{ +	return new_term(new, term->type_val, term->type_term, term->config, +			term->val.str, term->val.num); +} + +void parse_events__free_terms(struct list_head *terms) +{ +	struct parse_events_term *term, *h; -	exit(129); +	list_for_each_entry_safe(term, h, terms, list) +		free(term);  }  | 
