diff options
Diffstat (limited to 'tools/perf/builtin-script.c')
| -rw-r--r-- | tools/perf/builtin-script.c | 355 | 
1 files changed, 322 insertions, 33 deletions
| diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index d05aec491cff..83cdc0a61fd6 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -85,6 +85,8 @@ enum perf_output_field {  	PERF_OUTPUT_INSN	    = 1U << 21,  	PERF_OUTPUT_INSNLEN	    = 1U << 22,  	PERF_OUTPUT_BRSTACKINSN	    = 1U << 23, +	PERF_OUTPUT_BRSTACKOFF	    = 1U << 24, +	PERF_OUTPUT_SYNTH           = 1U << 25,  };  struct output_option { @@ -115,6 +117,13 @@ struct output_option {  	{.str = "insn", .field = PERF_OUTPUT_INSN},  	{.str = "insnlen", .field = PERF_OUTPUT_INSNLEN},  	{.str = "brstackinsn", .field = PERF_OUTPUT_BRSTACKINSN}, +	{.str = "brstackoff", .field = PERF_OUTPUT_BRSTACKOFF}, +	{.str = "synth", .field = PERF_OUTPUT_SYNTH}, +}; + +enum { +	OUTPUT_TYPE_SYNTH = PERF_TYPE_MAX, +	OUTPUT_TYPE_MAX  };  /* default set to maintain compatibility with current format */ @@ -124,7 +133,7 @@ static struct {  	unsigned int print_ip_opts;  	u64 fields;  	u64 invalid_fields; -} output[PERF_TYPE_MAX] = { +} output[OUTPUT_TYPE_MAX] = {  	[PERF_TYPE_HARDWARE] = {  		.user_set = false, @@ -182,12 +191,44 @@ static struct {  		.invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT,  	}, + +	[OUTPUT_TYPE_SYNTH] = { +		.user_set = false, + +		.fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | +			      PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | +			      PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | +			      PERF_OUTPUT_SYM | PERF_OUTPUT_DSO | +			      PERF_OUTPUT_SYNTH, + +		.invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT, +	},  }; +static inline int output_type(unsigned int type) +{ +	switch (type) { +	case PERF_TYPE_SYNTH: +		return OUTPUT_TYPE_SYNTH; +	default: +		return type; +	} +} + +static inline unsigned int attr_type(unsigned int type) +{ +	switch (type) { +	case OUTPUT_TYPE_SYNTH: +		return PERF_TYPE_SYNTH; +	default: +		return type; +	} +} +  static bool output_set_by_user(void)  {  	int j; -	for (j = 0; j < PERF_TYPE_MAX; ++j) { +	for (j = 0; j < OUTPUT_TYPE_MAX; ++j) {  		if (output[j].user_set)  			return true;  	} @@ -208,7 +249,7 @@ static const char *output_field2str(enum perf_output_field field)  	return str;  } -#define PRINT_FIELD(x)  (output[attr->type].fields & PERF_OUTPUT_##x) +#define PRINT_FIELD(x)  (output[output_type(attr->type)].fields & PERF_OUTPUT_##x)  static int perf_evsel__do_check_stype(struct perf_evsel *evsel,  				      u64 sample_type, const char *sample_msg, @@ -216,7 +257,7 @@ static int perf_evsel__do_check_stype(struct perf_evsel *evsel,  				      bool allow_user_set)  {  	struct perf_event_attr *attr = &evsel->attr; -	int type = attr->type; +	int type = output_type(attr->type);  	const char *evname;  	if (attr->sample_type & sample_type) @@ -298,10 +339,10 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,  		       "selected.\n");  		return -EINVAL;  	} -	if (PRINT_FIELD(DSO) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR)) { -		pr_err("Display of DSO requested but neither sample IP nor " -			   "sample address\nis selected. Hence, no addresses to convert " -		       "to DSO.\n"); +	if (PRINT_FIELD(DSO) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR) && +	    !PRINT_FIELD(BRSTACK) && !PRINT_FIELD(BRSTACKSYM) && !PRINT_FIELD(BRSTACKOFF)) { +		pr_err("Display of DSO requested but no address to convert.  Select\n" +		       "sample IP, sample address, brstack, brstacksym, or brstackoff.\n");  		return -EINVAL;  	}  	if (PRINT_FIELD(SRCLINE) && !PRINT_FIELD(IP)) { @@ -346,7 +387,7 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,  static void set_print_ip_opts(struct perf_event_attr *attr)  { -	unsigned int type = attr->type; +	unsigned int type = output_type(attr->type);  	output[type].print_ip_opts = 0;  	if (PRINT_FIELD(IP)) @@ -374,16 +415,17 @@ static int perf_session__check_output_opt(struct perf_session *session)  	unsigned int j;  	struct perf_evsel *evsel; -	for (j = 0; j < PERF_TYPE_MAX; ++j) { -		evsel = perf_session__find_first_evtype(session, j); +	for (j = 0; j < OUTPUT_TYPE_MAX; ++j) { +		evsel = perf_session__find_first_evtype(session, attr_type(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) { +		if (!evsel && output[j].user_set && !output[j].wildcard_set && +		    j != OUTPUT_TYPE_SYNTH) {  			pr_err("%s events do not exist. " -			       "Remove corresponding -f option to proceed.\n", +			       "Remove corresponding -F option to proceed.\n",  			       event_type(j));  			return -1;  		} @@ -514,18 +556,43 @@ mispred_str(struct branch_entry *br)  	return br->flags.predicted ? 'P' : 'M';  } -static void print_sample_brstack(struct perf_sample *sample) +static void print_sample_brstack(struct perf_sample *sample, +				 struct thread *thread, +				 struct perf_event_attr *attr)  {  	struct branch_stack *br = sample->branch_stack; -	u64 i; +	struct addr_location alf, alt; +	u64 i, from, to;  	if (!(br && br->nr))  		return;  	for (i = 0; i < br->nr; i++) { -		printf(" 0x%"PRIx64"/0x%"PRIx64"/%c/%c/%c/%d ", -			br->entries[i].from, -			br->entries[i].to, +		from = br->entries[i].from; +		to   = br->entries[i].to; + +		if (PRINT_FIELD(DSO)) { +			memset(&alf, 0, sizeof(alf)); +			memset(&alt, 0, sizeof(alt)); +			thread__find_addr_map(thread, sample->cpumode, MAP__FUNCTION, from, &alf); +			thread__find_addr_map(thread, sample->cpumode, MAP__FUNCTION, to, &alt); +		} + +		printf("0x%"PRIx64, from); +		if (PRINT_FIELD(DSO)) { +			printf("("); +			map__fprintf_dsoname(alf.map, stdout); +			printf(")"); +		} + +		printf("/0x%"PRIx64, to); +		if (PRINT_FIELD(DSO)) { +			printf("("); +			map__fprintf_dsoname(alt.map, stdout); +			printf(")"); +		} + +		printf("/%c/%c/%c/%d ",  			mispred_str( br->entries + i),  			br->entries[i].flags.in_tx? 'X' : '-',  			br->entries[i].flags.abort? 'A' : '-', @@ -534,7 +601,8 @@ static void print_sample_brstack(struct perf_sample *sample)  }  static void print_sample_brstacksym(struct perf_sample *sample, -				    struct thread *thread) +				    struct thread *thread, +				    struct perf_event_attr *attr)  {  	struct branch_stack *br = sample->branch_stack;  	struct addr_location alf, alt; @@ -559,8 +627,18 @@ static void print_sample_brstacksym(struct perf_sample *sample,  			alt.sym = map__find_symbol(alt.map, alt.addr);  		symbol__fprintf_symname_offs(alf.sym, &alf, stdout); +		if (PRINT_FIELD(DSO)) { +			printf("("); +			map__fprintf_dsoname(alf.map, stdout); +			printf(")"); +		}  		putchar('/');  		symbol__fprintf_symname_offs(alt.sym, &alt, stdout); +		if (PRINT_FIELD(DSO)) { +			printf("("); +			map__fprintf_dsoname(alt.map, stdout); +			printf(")"); +		}  		printf("/%c/%c/%c/%d ",  			mispred_str( br->entries + i),  			br->entries[i].flags.in_tx? 'X' : '-', @@ -569,6 +647,51 @@ static void print_sample_brstacksym(struct perf_sample *sample,  	}  } +static void print_sample_brstackoff(struct perf_sample *sample, +				    struct thread *thread, +				    struct perf_event_attr *attr) +{ +	struct branch_stack *br = sample->branch_stack; +	struct addr_location alf, alt; +	u64 i, from, to; + +	if (!(br && br->nr)) +		return; + +	for (i = 0; i < br->nr; i++) { + +		memset(&alf, 0, sizeof(alf)); +		memset(&alt, 0, sizeof(alt)); +		from = br->entries[i].from; +		to   = br->entries[i].to; + +		thread__find_addr_map(thread, sample->cpumode, MAP__FUNCTION, from, &alf); +		if (alf.map && !alf.map->dso->adjust_symbols) +			from = map__map_ip(alf.map, from); + +		thread__find_addr_map(thread, sample->cpumode, MAP__FUNCTION, to, &alt); +		if (alt.map && !alt.map->dso->adjust_symbols) +			to = map__map_ip(alt.map, to); + +		printf("0x%"PRIx64, from); +		if (PRINT_FIELD(DSO)) { +			printf("("); +			map__fprintf_dsoname(alf.map, stdout); +			printf(")"); +		} +		printf("/0x%"PRIx64, to); +		if (PRINT_FIELD(DSO)) { +			printf("("); +			map__fprintf_dsoname(alt.map, stdout); +			printf(")"); +		} +		printf("/%c/%c/%c/%d ", +			mispred_str(br->entries + i), +			br->entries[i].flags.in_tx ? 'X' : '-', +			br->entries[i].flags.abort ? 'A' : '-', +			br->entries[i].flags.cycles); +	} +}  #define MAXBB 16384UL  static int grab_bb(u8 *buffer, u64 start, u64 end, @@ -906,6 +1029,7 @@ static void print_sample_bts(struct perf_sample *sample,  			     struct machine *machine)  {  	struct perf_event_attr *attr = &evsel->attr; +	unsigned int type = output_type(attr->type);  	bool print_srcline_last = false;  	if (PRINT_FIELD(CALLINDENT)) @@ -913,7 +1037,7 @@ static void print_sample_bts(struct perf_sample *sample,  	/* print branch_from information */  	if (PRINT_FIELD(IP)) { -		unsigned int print_opts = output[attr->type].print_ip_opts; +		unsigned int print_opts = output[type].print_ip_opts;  		struct callchain_cursor *cursor = NULL;  		if (symbol_conf.use_callchain && sample->callchain && @@ -936,7 +1060,7 @@ static void print_sample_bts(struct perf_sample *sample,  	/* print branch_to information */  	if (PRINT_FIELD(ADDR) ||  	    ((evsel->attr.sample_type & PERF_SAMPLE_ADDR) && -	     !output[attr->type].user_set)) { +	     !output[type].user_set)) {  		printf(" => ");  		print_sample_addr(sample, thread, attr);  	} @@ -1079,6 +1203,127 @@ static void print_sample_bpf_output(struct perf_sample *sample)  		       (char *)(sample->raw_data));  } +static void print_sample_spacing(int len, int spacing) +{ +	if (len > 0 && len < spacing) +		printf("%*s", spacing - len, ""); +} + +static void print_sample_pt_spacing(int len) +{ +	print_sample_spacing(len, 34); +} + +static void print_sample_synth_ptwrite(struct perf_sample *sample) +{ +	struct perf_synth_intel_ptwrite *data = perf_sample__synth_ptr(sample); +	int len; + +	if (perf_sample__bad_synth_size(sample, *data)) +		return; + +	len = printf(" IP: %u payload: %#" PRIx64 " ", +		     data->ip, le64_to_cpu(data->payload)); +	print_sample_pt_spacing(len); +} + +static void print_sample_synth_mwait(struct perf_sample *sample) +{ +	struct perf_synth_intel_mwait *data = perf_sample__synth_ptr(sample); +	int len; + +	if (perf_sample__bad_synth_size(sample, *data)) +		return; + +	len = printf(" hints: %#x extensions: %#x ", +		     data->hints, data->extensions); +	print_sample_pt_spacing(len); +} + +static void print_sample_synth_pwre(struct perf_sample *sample) +{ +	struct perf_synth_intel_pwre *data = perf_sample__synth_ptr(sample); +	int len; + +	if (perf_sample__bad_synth_size(sample, *data)) +		return; + +	len = printf(" hw: %u cstate: %u sub-cstate: %u ", +		     data->hw, data->cstate, data->subcstate); +	print_sample_pt_spacing(len); +} + +static void print_sample_synth_exstop(struct perf_sample *sample) +{ +	struct perf_synth_intel_exstop *data = perf_sample__synth_ptr(sample); +	int len; + +	if (perf_sample__bad_synth_size(sample, *data)) +		return; + +	len = printf(" IP: %u ", data->ip); +	print_sample_pt_spacing(len); +} + +static void print_sample_synth_pwrx(struct perf_sample *sample) +{ +	struct perf_synth_intel_pwrx *data = perf_sample__synth_ptr(sample); +	int len; + +	if (perf_sample__bad_synth_size(sample, *data)) +		return; + +	len = printf(" deepest cstate: %u last cstate: %u wake reason: %#x ", +		     data->deepest_cstate, data->last_cstate, +		     data->wake_reason); +	print_sample_pt_spacing(len); +} + +static void print_sample_synth_cbr(struct perf_sample *sample) +{ +	struct perf_synth_intel_cbr *data = perf_sample__synth_ptr(sample); +	unsigned int percent, freq; +	int len; + +	if (perf_sample__bad_synth_size(sample, *data)) +		return; + +	freq = (le32_to_cpu(data->freq) + 500) / 1000; +	len = printf(" cbr: %2u freq: %4u MHz ", data->cbr, freq); +	if (data->max_nonturbo) { +		percent = (5 + (1000 * data->cbr) / data->max_nonturbo) / 10; +		len += printf("(%3u%%) ", percent); +	} +	print_sample_pt_spacing(len); +} + +static void print_sample_synth(struct perf_sample *sample, +			       struct perf_evsel *evsel) +{ +	switch (evsel->attr.config) { +	case PERF_SYNTH_INTEL_PTWRITE: +		print_sample_synth_ptwrite(sample); +		break; +	case PERF_SYNTH_INTEL_MWAIT: +		print_sample_synth_mwait(sample); +		break; +	case PERF_SYNTH_INTEL_PWRE: +		print_sample_synth_pwre(sample); +		break; +	case PERF_SYNTH_INTEL_EXSTOP: +		print_sample_synth_exstop(sample); +		break; +	case PERF_SYNTH_INTEL_PWRX: +		print_sample_synth_pwrx(sample); +		break; +	case PERF_SYNTH_INTEL_CBR: +		print_sample_synth_cbr(sample); +		break; +	default: +		break; +	} +} +  struct perf_script {  	struct perf_tool	tool;  	struct perf_session	*session; @@ -1132,8 +1377,9 @@ static void process_event(struct perf_script *script,  {  	struct thread *thread = al->thread;  	struct perf_event_attr *attr = &evsel->attr; +	unsigned int type = output_type(attr->type); -	if (output[attr->type].fields == 0) +	if (output[type].fields == 0)  		return;  	print_sample_start(sample, thread, evsel); @@ -1162,6 +1408,10 @@ static void process_event(struct perf_script *script,  	if (PRINT_FIELD(TRACE))  		event_format__print(evsel->tp_format, sample->cpu,  				    sample->raw_data, sample->raw_size); + +	if (attr->type == PERF_TYPE_SYNTH && PRINT_FIELD(SYNTH)) +		print_sample_synth(sample, evsel); +  	if (PRINT_FIELD(ADDR))  		print_sample_addr(sample, thread, attr); @@ -1180,16 +1430,18 @@ static void process_event(struct perf_script *script,  			cursor = &callchain_cursor;  		putchar(cursor ? '\n' : ' '); -		sample__fprintf_sym(sample, al, 0, output[attr->type].print_ip_opts, cursor, stdout); +		sample__fprintf_sym(sample, al, 0, output[type].print_ip_opts, cursor, stdout);  	}  	if (PRINT_FIELD(IREGS))  		print_sample_iregs(sample, attr);  	if (PRINT_FIELD(BRSTACK)) -		print_sample_brstack(sample); +		print_sample_brstack(sample, thread, attr);  	else if (PRINT_FIELD(BRSTACKSYM)) -		print_sample_brstacksym(sample, thread); +		print_sample_brstacksym(sample, thread, attr); +	else if (PRINT_FIELD(BRSTACKOFF)) +		print_sample_brstackoff(sample, thread, attr);  	if (perf_evsel__is_bpf_output(evsel) && PRINT_FIELD(BPF_OUTPUT))  		print_sample_bpf_output(sample); @@ -1325,7 +1577,8 @@ static int process_attr(struct perf_tool *tool, union perf_event *event,  	evlist = *pevlist;  	evsel = perf_evlist__last(*pevlist); -	if (evsel->attr.type >= PERF_TYPE_MAX) +	if (evsel->attr.type >= PERF_TYPE_MAX && +	    evsel->attr.type != PERF_TYPE_SYNTH)  		return 0;  	evlist__for_each_entry(evlist, pos) { @@ -1727,6 +1980,7 @@ static int parse_output_fields(const struct option *opt __maybe_unused,  	int rc = 0;  	char *str = strdup(arg);  	int type = -1; +	enum { DEFAULT, SET, ADD, REMOVE } change = DEFAULT;  	if (!str)  		return -ENOMEM; @@ -1749,6 +2003,8 @@ static int parse_output_fields(const struct option *opt __maybe_unused,  			type = PERF_TYPE_RAW;  		else if (!strcmp(str, "break"))  			type = PERF_TYPE_BREAKPOINT; +		else if (!strcmp(str, "synth")) +			type = OUTPUT_TYPE_SYNTH;  		else {  			fprintf(stderr, "Invalid event type in field string.\n");  			rc = -EINVAL; @@ -1772,23 +2028,44 @@ static int parse_output_fields(const struct option *opt __maybe_unused,  			goto out;  		} +		/* Don't override defaults for +- */ +		if (strchr(str, '+') || strchr(str, '-')) +			goto parse; +  		if (output_set_by_user())  			pr_warning("Overriding previous field request for all events.\n"); -		for (j = 0; j < PERF_TYPE_MAX; ++j) { +		for (j = 0; j < OUTPUT_TYPE_MAX; ++j) {  			output[j].fields = 0;  			output[j].user_set = true;  			output[j].wildcard_set = true;  		}  	} +parse:  	for (tok = strtok_r(tok, ",", &strtok_saveptr); tok; tok = strtok_r(NULL, ",", &strtok_saveptr)) { +		if (*tok == '+') { +			if (change == SET) +				goto out_badmix; +			change = ADD; +			tok++; +		} else if (*tok == '-') { +			if (change == SET) +				goto out_badmix; +			change = REMOVE; +			tok++; +		} else { +			if (change != SET && change != DEFAULT) +				goto out_badmix; +			change = SET; +		} +  		for (i = 0; i < imax; ++i) {  			if (strcmp(tok, all_output_options[i].str) == 0)  				break;  		}  		if (i == imax && strcmp(tok, "flags") == 0) { -			print_flags = true; +			print_flags = change == REMOVE ? false : true;  			continue;  		}  		if (i == imax) { @@ -1801,12 +2078,16 @@ static int parse_output_fields(const struct option *opt __maybe_unused,  			/* add user option to all events types for  			 * which it is valid  			 */ -			for (j = 0; j < PERF_TYPE_MAX; ++j) { +			for (j = 0; j < OUTPUT_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 (change == REMOVE) +						output[j].fields &= ~all_output_options[i].field; +					else +						output[j].fields |= all_output_options[i].field; +				}  			}  		} else {  			if (output[type].invalid_fields & all_output_options[i].field) { @@ -1826,7 +2107,11 @@ static int parse_output_fields(const struct option *opt __maybe_unused,  				 "Events will not be displayed.\n", event_type(type));  		}  	} +	goto out; +out_badmix: +	fprintf(stderr, "Cannot mix +-field with overridden fields\n"); +	rc = -EINVAL;  out:  	free(str);  	return rc; @@ -2444,10 +2729,11 @@ int cmd_script(int argc, const char **argv)  		     symbol__config_symfs),  	OPT_CALLBACK('F', "fields", NULL, "str",  		     "comma separated output fields prepend with 'type:'. " -		     "Valid types: hw,sw,trace,raw. " +		     "+field to add and -field to remove." +		     "Valid types: hw,sw,trace,raw,synth. "  		     "Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,"  		     "addr,symoff,period,iregs,brstack,brstacksym,flags," -		     "bpf-output,callindent,insn,insnlen,brstackinsn", +		     "bpf-output,callindent,insn,insnlen,brstackinsn,synth",  		     parse_output_fields),  	OPT_BOOLEAN('a', "all-cpus", &system_wide,  		    "system-wide collection from all CPUs"), @@ -2494,6 +2780,8 @@ int cmd_script(int argc, const char **argv)  			"Enable kernel symbol demangling"),  	OPT_STRING(0, "time", &script.time_str, "str",  		   "Time span of interest (start,stop)"), +	OPT_BOOLEAN(0, "inline", &symbol_conf.inline_name, +		    "Show inline function"),  	OPT_END()  	};  	const char * const script_subcommands[] = { "record", "report", NULL }; @@ -2704,6 +2992,7 @@ int cmd_script(int argc, const char **argv)  		err = perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap);  		if (err < 0)  			goto out_delete; +		itrace_synth_opts.cpu_bitmap = cpu_bitmap;  	}  	if (!no_callchain) |