diff options
Diffstat (limited to 'tools/perf/builtin-script.c')
| -rw-r--r-- | tools/perf/builtin-script.c | 296 | 
1 files changed, 229 insertions, 67 deletions
| diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index ac574ea23917..974f6d3f4e53 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -49,57 +49,169 @@ struct output_option {  };  /* default set to maintain compatibility with current format */ -static u64 output_fields[PERF_TYPE_MAX] = { -	[PERF_TYPE_HARDWARE] = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | \ -			       PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | \ -			       PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM, - -	[PERF_TYPE_SOFTWARE] = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | \ -			       PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | \ -			       PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM, - -	[PERF_TYPE_TRACEPOINT] = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | \ -				 PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | \ -				 PERF_OUTPUT_EVNAME | PERF_OUTPUT_TRACE, +static struct { +	bool user_set; +	bool wildcard_set; +	u64 fields; +	u64 invalid_fields; +} output[PERF_TYPE_MAX] = { + +	[PERF_TYPE_HARDWARE] = { +		.user_set = false, + +		.fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | +			      PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | +			      PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM, + +		.invalid_fields = PERF_OUTPUT_TRACE, +	}, + +	[PERF_TYPE_SOFTWARE] = { +		.user_set = false, + +		.fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | +			      PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | +			      PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM, + +		.invalid_fields = PERF_OUTPUT_TRACE, +	}, + +	[PERF_TYPE_TRACEPOINT] = { +		.user_set = false, + +		.fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | +				  PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | +				  PERF_OUTPUT_EVNAME | PERF_OUTPUT_TRACE, +	}, + +	[PERF_TYPE_RAW] = { +		.user_set = false, + +		.fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | +			      PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | +			      PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM, + +		.invalid_fields = PERF_OUTPUT_TRACE, +	},  }; -static bool output_set_by_user; +static bool output_set_by_user(void) +{ +	int j; +	for (j = 0; j < PERF_TYPE_MAX; ++j) { +		if (output[j].user_set) +			return true; +	} +	return false; +} + +static const char *output_field2str(enum perf_output_field field) +{ +	int i, imax = ARRAY_SIZE(all_output_options); +	const char *str = ""; + +	for (i = 0; i < imax; ++i) { +		if (all_output_options[i].field == field) { +			str = all_output_options[i].str; +			break; +		} +	} +	return str; +} -#define PRINT_FIELD(x)  (output_fields[attr->type] & PERF_OUTPUT_##x) +#define PRINT_FIELD(x)  (output[attr->type].fields & PERF_OUTPUT_##x) -static int perf_session__check_attr(struct perf_session *session, -				    struct perf_event_attr *attr) +static int perf_event_attr__check_stype(struct perf_event_attr *attr, +				  u64 sample_type, const char *sample_msg, +				  enum perf_output_field field)  { +	int type = attr->type; +	const char *evname; + +	if (attr->sample_type & sample_type) +		return 0; + +	if (output[type].user_set) { +		evname = __event_name(attr->type, attr->config); +		pr_err("Samples for '%s' event do not have %s attribute set. " +		       "Cannot print '%s' field.\n", +		       evname, sample_msg, output_field2str(field)); +		return -1; +	} + +	/* user did not ask for it explicitly so remove from the default list */ +	output[type].fields &= ~field; +	evname = __event_name(attr->type, attr->config); +	pr_debug("Samples for '%s' event do not have %s attribute set. " +		 "Skipping '%s' field.\n", +		 evname, sample_msg, output_field2str(field)); + +	return 0; +} + +static int perf_evsel__check_attr(struct perf_evsel *evsel, +				  struct perf_session *session) +{ +	struct perf_event_attr *attr = &evsel->attr; +  	if (PRINT_FIELD(TRACE) &&  		!perf_session__has_traces(session, "record -R"))  		return -EINVAL;  	if (PRINT_FIELD(SYM)) { -		if (!(session->sample_type & PERF_SAMPLE_IP)) { -			pr_err("Samples do not contain IP data.\n"); +		if (perf_event_attr__check_stype(attr, PERF_SAMPLE_IP, "IP", +					   PERF_OUTPUT_SYM))  			return -EINVAL; -		} +  		if (!no_callchain && -		    !(session->sample_type & PERF_SAMPLE_CALLCHAIN)) +		    !(attr->sample_type & PERF_SAMPLE_CALLCHAIN))  			symbol_conf.use_callchain = false;  	}  	if ((PRINT_FIELD(PID) || PRINT_FIELD(TID)) && -		!(session->sample_type & PERF_SAMPLE_TID)) { -		pr_err("Samples do not contain TID/PID data.\n"); +		perf_event_attr__check_stype(attr, PERF_SAMPLE_TID, "TID", +				       PERF_OUTPUT_TID|PERF_OUTPUT_PID))  		return -EINVAL; -	}  	if (PRINT_FIELD(TIME) && -		!(session->sample_type & PERF_SAMPLE_TIME)) { -		pr_err("Samples do not contain timestamps.\n"); +		perf_event_attr__check_stype(attr, PERF_SAMPLE_TIME, "TIME", +				       PERF_OUTPUT_TIME))  		return -EINVAL; -	}  	if (PRINT_FIELD(CPU) && -		!(session->sample_type & PERF_SAMPLE_CPU)) { -		pr_err("Samples do not contain cpu.\n"); +		perf_event_attr__check_stype(attr, PERF_SAMPLE_CPU, "CPU", +				       PERF_OUTPUT_CPU))  		return -EINVAL; + +	return 0; +} + +/* + * verify all user requested events exist and the samples + * have the expected data + */ +static int perf_session__check_output_opt(struct perf_session *session) +{ +	int j; +	struct perf_evsel *evsel; + +	for (j = 0; j < PERF_TYPE_MAX; ++j) { +		evsel = perf_session__find_first_evtype(session, j); + +		/* +		 * even if fields is set to 0 (ie., show nothing) event must +		 * exist if user explicitly includes it on the command line +		 */ +		if (!evsel && output[j].user_set && !output[j].wildcard_set) { +			pr_err("%s events do not exist. " +			       "Remove corresponding -f option to proceed.\n", +			       event_type(j)); +			return -1; +		} + +		if (evsel && output[j].fields && +			perf_evsel__check_attr(evsel, session)) +			return -1;  	}  	return 0; @@ -168,10 +280,7 @@ static void process_event(union perf_event *event __unused,  {  	struct perf_event_attr *attr = &evsel->attr; -	if (output_fields[attr->type] == 0) -		return; - -	if (perf_session__check_attr(session, attr) < 0) +	if (output[attr->type].fields == 0)  		return;  	print_sample_start(sample, thread, attr); @@ -451,6 +560,7 @@ static int parse_output_fields(const struct option *opt __used,  {  	char *tok;  	int i, imax = sizeof(all_output_options) / sizeof(struct output_option); +	int j;  	int rc = 0;  	char *str = strdup(arg);  	int type = -1; @@ -458,52 +568,99 @@ static int parse_output_fields(const struct option *opt __used,  	if (!str)  		return -ENOMEM; -	tok = strtok(str, ":"); -	if (!tok) { -		fprintf(stderr, -			"Invalid field string - not prepended with type."); -		return -EINVAL; -	} - -	/* first word should state which event type user -	 * is specifying the fields +	/* first word can state for which event type the user is specifying +	 * the fields. If no type exists, the specified fields apply to all +	 * event types found in the file minus the invalid fields for a type.  	 */ -	if (!strcmp(tok, "hw")) -		type = PERF_TYPE_HARDWARE; -	else if (!strcmp(tok, "sw")) -		type = PERF_TYPE_SOFTWARE; -	else if (!strcmp(tok, "trace")) -		type = PERF_TYPE_TRACEPOINT; -	else { -		fprintf(stderr, "Invalid event type in field string."); -		return -EINVAL; +	tok = strchr(str, ':'); +	if (tok) { +		*tok = '\0'; +		tok++; +		if (!strcmp(str, "hw")) +			type = PERF_TYPE_HARDWARE; +		else if (!strcmp(str, "sw")) +			type = PERF_TYPE_SOFTWARE; +		else if (!strcmp(str, "trace")) +			type = PERF_TYPE_TRACEPOINT; +		else if (!strcmp(str, "raw")) +			type = PERF_TYPE_RAW; +		else { +			fprintf(stderr, "Invalid event type in field string.\n"); +			return -EINVAL; +		} + +		if (output[type].user_set) +			pr_warning("Overriding previous field request for %s events.\n", +				   event_type(type)); + +		output[type].fields = 0; +		output[type].user_set = true; +		output[type].wildcard_set = false; + +	} else { +		tok = str; +		if (strlen(str) == 0) { +			fprintf(stderr, +				"Cannot set fields to 'none' for all event types.\n"); +			rc = -EINVAL; +			goto out; +		} + +		if (output_set_by_user()) +			pr_warning("Overriding previous field request for all events.\n"); + +		for (j = 0; j < PERF_TYPE_MAX; ++j) { +			output[j].fields = 0; +			output[j].user_set = true; +			output[j].wildcard_set = true; +		}  	} -	output_fields[type] = 0; -	while (1) { -		tok = strtok(NULL, ","); -		if (!tok) -			break; +	tok = strtok(tok, ","); +	while (tok) {  		for (i = 0; i < imax; ++i) { -			if (strcmp(tok, all_output_options[i].str) == 0) { -				output_fields[type] |= all_output_options[i].field; +			if (strcmp(tok, all_output_options[i].str) == 0)  				break; -			}  		}  		if (i == imax) { -			fprintf(stderr, "Invalid field requested."); +			fprintf(stderr, "Invalid field requested.\n");  			rc = -EINVAL; -			break; +			goto out;  		} -	} -	if (output_fields[type] == 0) { -		pr_debug("No fields requested for %s type. " -			 "Events will not be displayed\n", event_type(type)); +		if (type == -1) { +			/* add user option to all events types for +			 * which it is valid +			 */ +			for (j = 0; j < PERF_TYPE_MAX; ++j) { +				if (output[j].invalid_fields & all_output_options[i].field) { +					pr_warning("\'%s\' not valid for %s events. Ignoring.\n", +						   all_output_options[i].str, event_type(j)); +				} else +					output[j].fields |= all_output_options[i].field; +			} +		} else { +			if (output[type].invalid_fields & all_output_options[i].field) { +				fprintf(stderr, "\'%s\' not valid for %s events.\n", +					 all_output_options[i].str, event_type(type)); + +				rc = -EINVAL; +				goto out; +			} +			output[type].fields |= all_output_options[i].field; +		} + +		tok = strtok(NULL, ",");  	} -	output_set_by_user = true; +	if (type >= 0) { +		if (output[type].fields == 0) { +			pr_debug("No fields requested for %s type. " +				 "Events will not be displayed.\n", event_type(type)); +		} +	} +out:  	free(str);  	return rc;  } @@ -829,7 +986,7 @@ static const struct option options[] = {  	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",  		    "Look for files with symbols relative to this directory"),  	OPT_CALLBACK('f', "fields", NULL, "str", -		     "comma separated output fields prepend with 'type:'. Valid types: hw,sw,trace. Fields: comm,tid,pid,time,cpu,event,trace,sym", +		     "comma separated output fields prepend with 'type:'. Valid types: hw,sw,trace,raw. Fields: comm,tid,pid,time,cpu,event,trace,sym",  		     parse_output_fields),  	OPT_END() @@ -1020,7 +1177,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __used)  		struct stat perf_stat;  		int input; -		if (output_set_by_user) { +		if (output_set_by_user()) {  			fprintf(stderr,  				"custom fields not supported for generated scripts");  			return -1; @@ -1060,6 +1217,11 @@ int cmd_script(int argc, const char **argv, const char *prefix __used)  		pr_debug("perf script started with script %s\n\n", script_name);  	} + +	err = perf_session__check_output_opt(session); +	if (err < 0) +		goto out; +  	err = __cmd_script(session);  	perf_session__delete(session); |