diff options
Diffstat (limited to 'tools/perf/util/parse-events.c')
| -rw-r--r-- | tools/perf/util/parse-events.c | 308 | 
1 files changed, 237 insertions, 71 deletions
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index b5e2adef49de..ed7c008b9c8b 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -182,6 +182,37 @@ static int tp_event_has_id(const char *dir_path, struct dirent *evt_dir)  #define MAX_EVENT_LENGTH 512 +void parse_events__handle_error(struct parse_events_error *err, int idx, +				char *str, char *help) +{ +	if (WARN(!str, "WARNING: failed to provide error string\n")) { +		free(help); +		return; +	} +	switch (err->num_errors) { +	case 0: +		err->idx = idx; +		err->str = str; +		err->help = help; +		break; +	case 1: +		err->first_idx = err->idx; +		err->idx = idx; +		err->first_str = err->str; +		err->str = str; +		err->first_help = err->help; +		err->help = help; +		break; +	default: +		WARN_ONCE(1, "WARNING: multiple event parsing errors\n"); +		free(err->str); +		err->str = str; +		free(err->help); +		err->help = help; +		break; +	} +	err->num_errors++; +}  struct tracepoint_path *tracepoint_id_to_path(u64 config)  { @@ -480,6 +511,7 @@ int parse_events_add_cache(struct list_head *list, int *idx,  static void tracepoint_error(struct parse_events_error *e, int err,  			     const char *sys, const char *name)  { +	const char *str;  	char help[BUFSIZ];  	if (!e) @@ -493,18 +525,18 @@ static void tracepoint_error(struct parse_events_error *e, int err,  	switch (err) {  	case EACCES: -		e->str = strdup("can't access trace events"); +		str = "can't access trace events";  		break;  	case ENOENT: -		e->str = strdup("unknown tracepoint"); +		str = "unknown tracepoint";  		break;  	default: -		e->str = strdup("failed to add tracepoint"); +		str = "failed to add tracepoint";  		break;  	}  	tracing_path__strerror_open_tp(err, help, sizeof(help), sys, name); -	e->help = strdup(help); +	parse_events__handle_error(e, 0, strdup(str), strdup(help));  }  static int add_tracepoint(struct list_head *list, int *idx, @@ -932,11 +964,11 @@ static int check_type_val(struct parse_events_term *term,  		return 0;  	if (err) { -		err->idx = term->err_val; -		if (type == PARSE_EVENTS__TERM_TYPE_NUM) -			err->str = strdup("expected numeric value"); -		else -			err->str = strdup("expected string value"); +		parse_events__handle_error(err, term->err_val, +					type == PARSE_EVENTS__TERM_TYPE_NUM +					? strdup("expected numeric value") +					: strdup("expected string value"), +					NULL);  	}  	return -EINVAL;  } @@ -965,6 +997,7 @@ static const char *config_term_names[__PARSE_EVENTS__TERM_TYPE_NR] = {  	[PARSE_EVENTS__TERM_TYPE_DRV_CFG]		= "driver-config",  	[PARSE_EVENTS__TERM_TYPE_PERCORE]		= "percore",  	[PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT]		= "aux-output", +	[PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE]	= "aux-sample-size",  };  static bool config_term_shrinked; @@ -972,8 +1005,11 @@ static bool config_term_shrinked;  static bool  config_term_avail(int term_type, struct parse_events_error *err)  { +	char *err_str; +  	if (term_type < 0 || term_type >= __PARSE_EVENTS__TERM_TYPE_NR) { -		err->str = strdup("Invalid term_type"); +		parse_events__handle_error(err, -1, +					strdup("Invalid term_type"), NULL);  		return false;  	}  	if (!config_term_shrinked) @@ -992,9 +1028,9 @@ config_term_avail(int term_type, struct parse_events_error *err)  			return false;  		/* term_type is validated so indexing is safe */ -		if (asprintf(&err->str, "'%s' is not usable in 'perf stat'", -			     config_term_names[term_type]) < 0) -			err->str = NULL; +		if (asprintf(&err_str, "'%s' is not usable in 'perf stat'", +				config_term_names[term_type]) >= 0) +			parse_events__handle_error(err, -1, err_str, NULL);  		return false;  	}  } @@ -1036,17 +1072,20 @@ do {									   \  	case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE:  		CHECK_TYPE_VAL(STR);  		if (strcmp(term->val.str, "no") && -		    parse_branch_str(term->val.str, &attr->branch_sample_type)) { -			err->str = strdup("invalid branch sample type"); -			err->idx = term->err_val; +		    parse_branch_str(term->val.str, +				    &attr->branch_sample_type)) { +			parse_events__handle_error(err, term->err_val, +					strdup("invalid branch sample type"), +					NULL);  			return -EINVAL;  		}  		break;  	case PARSE_EVENTS__TERM_TYPE_TIME:  		CHECK_TYPE_VAL(NUM);  		if (term->val.num > 1) { -			err->str = strdup("expected 0 or 1"); -			err->idx = term->err_val; +			parse_events__handle_error(err, term->err_val, +						strdup("expected 0 or 1"), +						NULL);  			return -EINVAL;  		}  		break; @@ -1080,18 +1119,28 @@ do {									   \  	case PARSE_EVENTS__TERM_TYPE_PERCORE:  		CHECK_TYPE_VAL(NUM);  		if ((unsigned int)term->val.num > 1) { -			err->str = strdup("expected 0 or 1"); -			err->idx = term->err_val; +			parse_events__handle_error(err, term->err_val, +						strdup("expected 0 or 1"), +						NULL);  			return -EINVAL;  		}  		break;  	case PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT:  		CHECK_TYPE_VAL(NUM);  		break; +	case PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE: +		CHECK_TYPE_VAL(NUM); +		if (term->val.num > UINT_MAX) { +			parse_events__handle_error(err, term->err_val, +						strdup("too big"), +						NULL); +			return -EINVAL; +		} +		break;  	default: -		err->str = strdup("unknown term"); -		err->idx = term->err_term; -		err->help = parse_events_formats_error_string(NULL); +		parse_events__handle_error(err, term->err_term, +				strdup("unknown term"), +				parse_events_formats_error_string(NULL));  		return -EINVAL;  	} @@ -1139,12 +1188,13 @@ static int config_term_tracepoint(struct perf_event_attr *attr,  	case PARSE_EVENTS__TERM_TYPE_OVERWRITE:  	case PARSE_EVENTS__TERM_TYPE_NOOVERWRITE:  	case PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT: +	case PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE:  		return config_term_common(attr, term, err);  	default:  		if (err) { -			err->idx = term->err_term; -			err->str = strdup("unknown term"); -			err->help = strdup("valid terms: call-graph,stack-size\n"); +			parse_events__handle_error(err, term->err_term, +				strdup("unknown term"), +				strdup("valid terms: call-graph,stack-size\n"));  		}  		return -EINVAL;  	} @@ -1234,11 +1284,47 @@ do {								\  		case PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT:  			ADD_CONFIG_TERM(AUX_OUTPUT, aux_output, term->val.num ? 1 : 0);  			break; +		case PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE: +			ADD_CONFIG_TERM(AUX_SAMPLE_SIZE, aux_sample_size, term->val.num); +			break; +		default: +			break; +		} +	} +	return 0; +} + +/* + * Add PERF_EVSEL__CONFIG_TERM_CFG_CHG where cfg_chg will have a bit set for + * each bit of attr->config that the user has changed. + */ +static int get_config_chgs(struct perf_pmu *pmu, struct list_head *head_config, +			   struct list_head *head_terms) +{ +	struct parse_events_term *term; +	u64 bits = 0; +	int type; + +	list_for_each_entry(term, head_config, list) { +		switch (term->type_term) { +		case PARSE_EVENTS__TERM_TYPE_USER: +			type = perf_pmu__format_type(&pmu->format, term->config); +			if (type != PERF_PMU_FORMAT_VALUE_CONFIG) +				continue; +			bits |= perf_pmu__format_bits(&pmu->format, term->config); +			break; +		case PARSE_EVENTS__TERM_TYPE_CONFIG: +			bits = ~(u64)0; +			break;  		default:  			break;  		}  	} -#undef ADD_EVSEL_CONFIG + +	if (bits) +		ADD_CONFIG_TERM(CFG_CHG, cfg_chg, bits); + +#undef ADD_CONFIG_TERM  	return 0;  } @@ -1323,10 +1409,12 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,  	pmu = perf_pmu__find(name);  	if (!pmu) { -		if (asprintf(&err->str, +		char *err_str; + +		if (asprintf(&err_str,  				"Cannot find PMU `%s'. Missing kernel support?", -				name) < 0) -			err->str = NULL; +				name) >= 0) +			parse_events__handle_error(err, 0, err_str, NULL);  		return -EINVAL;  	} @@ -1365,8 +1453,22 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,  	if (get_config_terms(head_config, &config_terms))  		return -ENOMEM; -	if (perf_pmu__config(pmu, &attr, head_config, parse_state->error)) +	/* +	 * When using default config, record which bits of attr->config were +	 * changed by the user. +	 */ +	if (pmu->default_config && get_config_chgs(pmu, head_config, &config_terms)) +		return -ENOMEM; + +	if (perf_pmu__config(pmu, &attr, head_config, parse_state->error)) { +		struct perf_evsel_config_term *pos, *tmp; + +		list_for_each_entry_safe(pos, tmp, &config_terms, list) { +			list_del_init(&pos->list); +			free(pos); +		}  		return -EINVAL; +	}  	evsel = __add_event(list, &parse_state->idx, &attr,  			    get_config_name(head_config), pmu, @@ -1389,7 +1491,6 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,  int parse_events_multi_pmu_add(struct parse_events_state *parse_state,  			       char *str, struct list_head **listp)  { -	struct list_head *head;  	struct parse_events_term *term;  	struct list_head *list;  	struct perf_pmu *pmu = NULL; @@ -1406,19 +1507,30 @@ int parse_events_multi_pmu_add(struct parse_events_state *parse_state,  		list_for_each_entry(alias, &pmu->aliases, list) {  			if (!strcasecmp(alias->name, str)) { +				struct list_head *head; +				char *config; +  				head = malloc(sizeof(struct list_head));  				if (!head)  					return -1;  				INIT_LIST_HEAD(head); -				if (parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, -							   str, 1, false, &str, NULL) < 0) +				config = strdup(str); +				if (!config)  					return -1; +				if (parse_events_term__num(&term, +						   PARSE_EVENTS__TERM_TYPE_USER, +						   config, 1, false, &config, +						   NULL) < 0) { +					free(list); +					free(config); +					return -1; +				}  				list_add_tail(&term->list, head);  				if (!parse_events_add_pmu(parse_state, list,  							  pmu->name, head,  							  true, true)) { -					pr_debug("%s -> %s/%s/\n", str, +					pr_debug("%s -> %s/%s/\n", config,  						 pmu->name, alias->str);  					ok++;  				} @@ -1427,8 +1539,10 @@ int parse_events_multi_pmu_add(struct parse_events_state *parse_state,  			}  		}  	} -	if (!ok) +	if (!ok) { +		free(list);  		return -1; +	}  	*listp = list;  	return 0;  } @@ -1927,15 +2041,20 @@ int parse_events(struct evlist *evlist, const char *str,  	ret = parse_events__scanner(str, &parse_state, PE_START_EVENTS);  	perf_pmu__parse_cleanup(); + +	if (!ret && list_empty(&parse_state.list)) { +		WARN_ONCE(true, "WARNING: event parser found nothing\n"); +		return -1; +	} + +	/* +	 * Add list to the evlist even with errors to allow callers to clean up. +	 */ +	perf_evlist__splice_list_tail(evlist, &parse_state.list); +  	if (!ret) {  		struct evsel *last; -		if (list_empty(&parse_state.list)) { -			WARN_ONCE(true, "WARNING: event parser found nothing\n"); -			return -1; -		} - -		perf_evlist__splice_list_tail(evlist, &parse_state.list);  		evlist->nr_groups += parse_state.nr_groups;  		last = evlist__last(evlist);  		last->cmdline_group_boundary = true; @@ -1960,15 +2079,14 @@ static int get_term_width(void)  	return ws.ws_col > MAX_WIDTH ? MAX_WIDTH : ws.ws_col;  } -void parse_events_print_error(struct parse_events_error *err, -			      const char *event) +static void __parse_events_print_error(int err_idx, const char *err_str, +				const char *err_help, const char *event)  {  	const char *str = "invalid or unsupported event: ";  	char _buf[MAX_WIDTH];  	char *buf = (char *) event;  	int idx = 0; - -	if (err->str) { +	if (err_str) {  		/* -2 for extra '' in the final fprintf */  		int width       = get_term_width() - 2;  		int len_event   = strlen(event); @@ -1991,8 +2109,8 @@ void parse_events_print_error(struct parse_events_error *err,  		buf = _buf;  		/* We're cutting from the beginning. */ -		if (err->idx > max_err_idx) -			cut = err->idx - max_err_idx; +		if (err_idx > max_err_idx) +			cut = err_idx - max_err_idx;  		strncpy(buf, event + cut, max_len); @@ -2005,16 +2123,33 @@ void parse_events_print_error(struct parse_events_error *err,  			buf[max_len] = 0;  		} -		idx = len_str + err->idx - cut; +		idx = len_str + err_idx - cut;  	}  	fprintf(stderr, "%s'%s'\n", str, buf);  	if (idx) { -		fprintf(stderr, "%*s\\___ %s\n", idx + 1, "", err->str); -		if (err->help) -			fprintf(stderr, "\n%s\n", err->help); -		zfree(&err->str); -		zfree(&err->help); +		fprintf(stderr, "%*s\\___ %s\n", idx + 1, "", err_str); +		if (err_help) +			fprintf(stderr, "\n%s\n", err_help); +	} +} + +void parse_events_print_error(struct parse_events_error *err, +			      const char *event) +{ +	if (!err->num_errors) +		return; + +	__parse_events_print_error(err->idx, err->str, err->help, event); +	zfree(&err->str); +	zfree(&err->help); + +	if (err->num_errors > 1) { +		fputs("\nInitial error:\n", stderr); +		__parse_events_print_error(err->first_idx, err->first_str, +					err->first_help, event); +		zfree(&err->first_str); +		zfree(&err->first_help);  	}  } @@ -2024,8 +2159,11 @@ int parse_events_option(const struct option *opt, const char *str,  			int unset __maybe_unused)  {  	struct evlist *evlist = *(struct evlist **)opt->value; -	struct parse_events_error err = { .idx = 0, }; -	int ret = parse_events(evlist, str, &err); +	struct parse_events_error err; +	int ret; + +	bzero(&err, sizeof(err)); +	ret = parse_events(evlist, str, &err);  	if (ret) {  		parse_events_print_error(&err, str); @@ -2600,7 +2738,7 @@ out_enomem:   * Print the help text for the event symbols:   */  void print_events(const char *event_glob, bool name_only, bool quiet_flag, -			bool long_desc, bool details_flag) +			bool long_desc, bool details_flag, bool deprecated)  {  	print_symbol_events(event_glob, PERF_TYPE_HARDWARE,  			    event_symbols_hw, PERF_COUNT_HW_MAX, name_only); @@ -2612,7 +2750,7 @@ void print_events(const char *event_glob, bool name_only, bool quiet_flag,  	print_hwcache_events(event_glob, name_only);  	print_pmu_events(event_glob, name_only, quiet_flag, long_desc, -			details_flag); +			details_flag, deprecated);  	if (event_glob != NULL)  		return; @@ -2718,30 +2856,63 @@ int parse_events_term__sym_hw(struct parse_events_term **term,  			      char *config, unsigned idx)  {  	struct event_symbol *sym; +	char *str;  	struct parse_events_term temp = {  		.type_val  = PARSE_EVENTS__TERM_TYPE_STR,  		.type_term = PARSE_EVENTS__TERM_TYPE_USER, -		.config    = config ?: (char *) "event", +		.config    = config,  	}; +	if (!temp.config) { +		temp.config = strdup("event"); +		if (!temp.config) +			return -ENOMEM; +	}  	BUG_ON(idx >= PERF_COUNT_HW_MAX);  	sym = &event_symbols_hw[idx]; -	return new_term(term, &temp, (char *) sym->symbol, 0); +	str = strdup(sym->symbol); +	if (!str) +		return -ENOMEM; +	return new_term(term, &temp, str, 0);  }  int parse_events_term__clone(struct parse_events_term **new,  			     struct parse_events_term *term)  { +	char *str;  	struct parse_events_term temp = {  		.type_val  = term->type_val,  		.type_term = term->type_term, -		.config    = term->config, +		.config    = NULL,  		.err_term  = term->err_term,  		.err_val   = term->err_val,  	}; -	return new_term(new, &temp, term->val.str, term->val.num); +	if (term->config) { +		temp.config = strdup(term->config); +		if (!temp.config) +			return -ENOMEM; +	} +	if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM) +		return new_term(new, &temp, NULL, term->val.num); + +	str = strdup(term->val.str); +	if (!str) +		return -ENOMEM; +	return new_term(new, &temp, str, 0); +} + +void parse_events_term__delete(struct parse_events_term *term) +{ +	if (term->array.nr_ranges) +		zfree(&term->array.ranges); + +	if (term->type_val != PARSE_EVENTS__TERM_TYPE_NUM) +		zfree(&term->val.str); + +	zfree(&term->config); +	free(term);  }  int parse_events_copy_term_list(struct list_head *old, @@ -2774,10 +2945,8 @@ void parse_events_terms__purge(struct list_head *terms)  	struct parse_events_term *term, *h;  	list_for_each_entry_safe(term, h, terms, list) { -		if (term->array.nr_ranges) -			zfree(&term->array.ranges);  		list_del_init(&term->list); -		free(term); +		parse_events_term__delete(term);  	}  } @@ -2797,13 +2966,10 @@ void parse_events__clear_array(struct parse_events_array *a)  void parse_events_evlist_error(struct parse_events_state *parse_state,  			       int idx, const char *str)  { -	struct parse_events_error *err = parse_state->error; - -	if (!err) +	if (!parse_state->error)  		return; -	err->idx = idx; -	err->str = strdup(str); -	WARN_ONCE(!err->str, "WARNING: failed to allocate error string"); + +	parse_events__handle_error(parse_state->error, idx, strdup(str), NULL);  }  static void config_terms_list(char *buf, size_t buf_sz)  | 
