diff options
Diffstat (limited to 'tools/perf/builtin-script.c')
| -rw-r--r-- | tools/perf/builtin-script.c | 383 | 
1 files changed, 322 insertions, 61 deletions
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 7f31a3ded1b..9e9c91f5b7f 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -15,6 +15,7 @@  #include "util/evlist.h"  #include "util/evsel.h"  #include "util/sort.h" +#include "util/data.h"  #include <linux/bitmap.h>  static char const		*script_name; @@ -42,6 +43,7 @@ enum perf_output_field {  	PERF_OUTPUT_DSO             = 1U << 9,  	PERF_OUTPUT_ADDR            = 1U << 10,  	PERF_OUTPUT_SYMOFFSET       = 1U << 11, +	PERF_OUTPUT_SRCLINE         = 1U << 12,  };  struct output_option { @@ -60,6 +62,7 @@ struct output_option {  	{.str = "dso",   .field = PERF_OUTPUT_DSO},  	{.str = "addr",  .field = PERF_OUTPUT_ADDR},  	{.str = "symoff", .field = PERF_OUTPUT_SYMOFFSET}, +	{.str = "srcline", .field = PERF_OUTPUT_SRCLINE},  };  /* default set to maintain compatibility with current format */ @@ -209,6 +212,11 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,  		       "to DSO.\n");  		return -EINVAL;  	} +	if (PRINT_FIELD(SRCLINE) && !PRINT_FIELD(IP)) { +		pr_err("Display of source line number requested but sample IP is not\n" +		       "selected. Hence, no address to lookup the source line number.\n"); +		return -EINVAL; +	}  	if ((PRINT_FIELD(PID) || PRINT_FIELD(TID)) &&  		perf_evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID", @@ -228,6 +236,27 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,  	return 0;  } +static void set_print_ip_opts(struct perf_event_attr *attr) +{ +	unsigned int type = attr->type; + +	output[type].print_ip_opts = 0; +	if (PRINT_FIELD(IP)) +		output[type].print_ip_opts |= PRINT_IP_OPT_IP; + +	if (PRINT_FIELD(SYM)) +		output[type].print_ip_opts |= PRINT_IP_OPT_SYM; + +	if (PRINT_FIELD(DSO)) +		output[type].print_ip_opts |= PRINT_IP_OPT_DSO; + +	if (PRINT_FIELD(SYMOFFSET)) +		output[type].print_ip_opts |= PRINT_IP_OPT_SYMOFFSET; + +	if (PRINT_FIELD(SRCLINE)) +		output[type].print_ip_opts |= PRINT_IP_OPT_SRCLINE; +} +  /*   * verify all user requested events exist and the samples   * have the expected data @@ -236,7 +265,6 @@ static int perf_session__check_output_opt(struct perf_session *session)  {  	int j;  	struct perf_evsel *evsel; -	struct perf_event_attr *attr;  	for (j = 0; j < PERF_TYPE_MAX; ++j) {  		evsel = perf_session__find_first_evtype(session, j); @@ -259,22 +287,33 @@ static int perf_session__check_output_opt(struct perf_session *session)  		if (evsel == NULL)  			continue; -		attr = &evsel->attr; +		set_print_ip_opts(&evsel->attr); +	} -		output[j].print_ip_opts = 0; -		if (PRINT_FIELD(IP)) -			output[j].print_ip_opts |= PRINT_IP_OPT_IP; +	/* +	 * set default for tracepoints to print symbols only +	 * if callchains are present +	 */ +	if (symbol_conf.use_callchain && +	    !output[PERF_TYPE_TRACEPOINT].user_set) { +		struct perf_event_attr *attr; -		if (PRINT_FIELD(SYM)) -			output[j].print_ip_opts |= PRINT_IP_OPT_SYM; +		j = PERF_TYPE_TRACEPOINT; +		evsel = perf_session__find_first_evtype(session, j); +		if (evsel == NULL) +			goto out; -		if (PRINT_FIELD(DSO)) -			output[j].print_ip_opts |= PRINT_IP_OPT_DSO; +		attr = &evsel->attr; -		if (PRINT_FIELD(SYMOFFSET)) -			output[j].print_ip_opts |= PRINT_IP_OPT_SYMOFFSET; +		if (attr->sample_type & PERF_SAMPLE_CALLCHAIN) { +			output[j].fields |= PERF_OUTPUT_IP; +			output[j].fields |= PERF_OUTPUT_SYM; +			output[j].fields |= PERF_OUTPUT_DSO; +			set_print_ip_opts(attr); +		}  	} +out:  	return 0;  } @@ -283,18 +322,17 @@ static void print_sample_start(struct perf_sample *sample,  			       struct perf_evsel *evsel)  {  	struct perf_event_attr *attr = &evsel->attr; -	const char *evname = NULL;  	unsigned long secs;  	unsigned long usecs;  	unsigned long long nsecs;  	if (PRINT_FIELD(COMM)) {  		if (latency_format) -			printf("%8.8s ", thread->comm); +			printf("%8.8s ", thread__comm_str(thread));  		else if (PRINT_FIELD(IP) && symbol_conf.use_callchain) -			printf("%s ", thread->comm); +			printf("%s ", thread__comm_str(thread));  		else -			printf("%16s ", thread->comm); +			printf("%16s ", thread__comm_str(thread));  	}  	if (PRINT_FIELD(PID) && PRINT_FIELD(TID)) @@ -318,11 +356,6 @@ static void print_sample_start(struct perf_sample *sample,  		usecs = nsecs / NSECS_PER_USEC;  		printf("%5lu.%06lu: ", secs, usecs);  	} - -	if (PRINT_FIELD(EVNAME)) { -		evname = perf_evsel__name(evsel); -		printf("%s: ", evname ? evname : "[unknown]"); -	}  }  static bool is_bts_event(struct perf_event_attr *attr) @@ -390,8 +423,8 @@ static void print_sample_addr(union perf_event *event,  static void print_sample_bts(union perf_event *event,  			     struct perf_sample *sample,  			     struct perf_evsel *evsel, -			     struct machine *machine, -			     struct thread *thread) +			     struct thread *thread, +			     struct addr_location *al)  {  	struct perf_event_attr *attr = &evsel->attr; @@ -401,7 +434,7 @@ static void print_sample_bts(union perf_event *event,  			printf(" ");  		else  			printf("\n"); -		perf_evsel__print_ip(evsel, event, sample, machine, +		perf_evsel__print_ip(evsel, sample, al,  				     output[attr->type].print_ip_opts,  				     PERF_MAX_STACK_DEPTH);  	} @@ -409,16 +442,17 @@ static void print_sample_bts(union perf_event *event,  	printf(" => ");  	/* print branch_to information */ -	if (PRINT_FIELD(ADDR)) -		print_sample_addr(event, sample, machine, thread, attr); +	if (PRINT_FIELD(ADDR) || +	    ((evsel->attr.sample_type & PERF_SAMPLE_ADDR) && +	     !output[attr->type].user_set)) +		print_sample_addr(event, sample, al->machine, thread, attr);  	printf("\n");  }  static void process_event(union perf_event *event, struct perf_sample *sample, -			  struct perf_evsel *evsel, struct machine *machine, -			  struct thread *thread, -			  struct addr_location *al __maybe_unused) +			  struct perf_evsel *evsel, struct thread *thread, +			  struct addr_location *al)  {  	struct perf_event_attr *attr = &evsel->attr; @@ -427,8 +461,13 @@ static void process_event(union perf_event *event, struct perf_sample *sample,  	print_sample_start(sample, thread, evsel); +	if (PRINT_FIELD(EVNAME)) { +		const char *evname = perf_evsel__name(evsel); +		printf("%s: ", evname ? evname : "[unknown]"); +	} +  	if (is_bts_event(attr)) { -		print_sample_bts(event, sample, evsel, machine, thread); +		print_sample_bts(event, sample, evsel, thread, al);  		return;  	} @@ -436,7 +475,7 @@ static void process_event(union perf_event *event, struct perf_sample *sample,  		event_format__print(evsel->tp_format, sample->cpu,  				    sample->raw_data, sample->raw_size);  	if (PRINT_FIELD(ADDR)) -		print_sample_addr(event, sample, machine, thread, attr); +		print_sample_addr(event, sample, al->machine, thread, attr);  	if (PRINT_FIELD(IP)) {  		if (!symbol_conf.use_callchain) @@ -444,7 +483,7 @@ static void process_event(union perf_event *event, struct perf_sample *sample,  		else  			printf("\n"); -		perf_evsel__print_ip(evsel, event, sample, machine, +		perf_evsel__print_ip(evsel, sample, al,  				     output[attr->type].print_ip_opts,  				     PERF_MAX_STACK_DEPTH);  	} @@ -533,40 +572,227 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,  	if (cpu_list && !test_bit(sample->cpu, cpu_bitmap))  		return 0; -	scripting_ops->process_event(event, sample, evsel, machine, thread, &al); +	scripting_ops->process_event(event, sample, evsel, thread, &al);  	evsel->hists.stats.total_period += sample->period;  	return 0;  } -static struct perf_tool perf_script = { -	.sample		 = process_sample_event, -	.mmap		 = perf_event__process_mmap, -	.mmap2		 = perf_event__process_mmap2, -	.comm		 = perf_event__process_comm, -	.exit		 = perf_event__process_exit, -	.fork		 = perf_event__process_fork, -	.attr		 = perf_event__process_attr, -	.tracing_data	 = perf_event__process_tracing_data, -	.build_id	 = perf_event__process_build_id, -	.ordered_samples = true, -	.ordering_requires_timestamps = true, +struct perf_script { +	struct perf_tool	tool; +	struct perf_session	*session; +	bool			show_task_events; +	bool			show_mmap_events;  }; -extern volatile int session_done; +static int process_attr(struct perf_tool *tool, union perf_event *event, +			struct perf_evlist **pevlist) +{ +	struct perf_script *scr = container_of(tool, struct perf_script, tool); +	struct perf_evlist *evlist; +	struct perf_evsel *evsel, *pos; +	int err; + +	err = perf_event__process_attr(tool, event, pevlist); +	if (err) +		return err; + +	evlist = *pevlist; +	evsel = perf_evlist__last(*pevlist); + +	if (evsel->attr.type >= PERF_TYPE_MAX) +		return 0; + +	evlist__for_each(evlist, pos) { +		if (pos->attr.type == evsel->attr.type && pos != evsel) +			return 0; +	} + +	set_print_ip_opts(&evsel->attr); + +	return perf_evsel__check_attr(evsel, scr->session); +} + +static int process_comm_event(struct perf_tool *tool, +			      union perf_event *event, +			      struct perf_sample *sample, +			      struct machine *machine) +{ +	struct thread *thread; +	struct perf_script *script = container_of(tool, struct perf_script, tool); +	struct perf_session *session = script->session; +	struct perf_evsel *evsel = perf_evlist__first(session->evlist); +	int ret = -1; + +	thread = machine__findnew_thread(machine, event->comm.pid, event->comm.tid); +	if (thread == NULL) { +		pr_debug("problem processing COMM event, skipping it.\n"); +		return -1; +	} + +	if (perf_event__process_comm(tool, event, sample, machine) < 0) +		goto out; + +	if (!evsel->attr.sample_id_all) { +		sample->cpu = 0; +		sample->time = 0; +		sample->tid = event->comm.tid; +		sample->pid = event->comm.pid; +	} +	print_sample_start(sample, thread, evsel); +	perf_event__fprintf(event, stdout); +	ret = 0; + +out: +	return ret; +} + +static int process_fork_event(struct perf_tool *tool, +			      union perf_event *event, +			      struct perf_sample *sample, +			      struct machine *machine) +{ +	struct thread *thread; +	struct perf_script *script = container_of(tool, struct perf_script, tool); +	struct perf_session *session = script->session; +	struct perf_evsel *evsel = perf_evlist__first(session->evlist); + +	if (perf_event__process_fork(tool, event, sample, machine) < 0) +		return -1; + +	thread = machine__findnew_thread(machine, event->fork.pid, event->fork.tid); +	if (thread == NULL) { +		pr_debug("problem processing FORK event, skipping it.\n"); +		return -1; +	} + +	if (!evsel->attr.sample_id_all) { +		sample->cpu = 0; +		sample->time = event->fork.time; +		sample->tid = event->fork.tid; +		sample->pid = event->fork.pid; +	} +	print_sample_start(sample, thread, evsel); +	perf_event__fprintf(event, stdout); + +	return 0; +} +static int process_exit_event(struct perf_tool *tool, +			      union perf_event *event, +			      struct perf_sample *sample, +			      struct machine *machine) +{ +	struct thread *thread; +	struct perf_script *script = container_of(tool, struct perf_script, tool); +	struct perf_session *session = script->session; +	struct perf_evsel *evsel = perf_evlist__first(session->evlist); + +	thread = machine__findnew_thread(machine, event->fork.pid, event->fork.tid); +	if (thread == NULL) { +		pr_debug("problem processing EXIT event, skipping it.\n"); +		return -1; +	} + +	if (!evsel->attr.sample_id_all) { +		sample->cpu = 0; +		sample->time = 0; +		sample->tid = event->comm.tid; +		sample->pid = event->comm.pid; +	} +	print_sample_start(sample, thread, evsel); +	perf_event__fprintf(event, stdout); + +	if (perf_event__process_exit(tool, event, sample, machine) < 0) +		return -1; + +	return 0; +} + +static int process_mmap_event(struct perf_tool *tool, +			      union perf_event *event, +			      struct perf_sample *sample, +			      struct machine *machine) +{ +	struct thread *thread; +	struct perf_script *script = container_of(tool, struct perf_script, tool); +	struct perf_session *session = script->session; +	struct perf_evsel *evsel = perf_evlist__first(session->evlist); + +	if (perf_event__process_mmap(tool, event, sample, machine) < 0) +		return -1; + +	thread = machine__findnew_thread(machine, event->mmap.pid, event->mmap.tid); +	if (thread == NULL) { +		pr_debug("problem processing MMAP event, skipping it.\n"); +		return -1; +	} + +	if (!evsel->attr.sample_id_all) { +		sample->cpu = 0; +		sample->time = 0; +		sample->tid = event->mmap.tid; +		sample->pid = event->mmap.pid; +	} +	print_sample_start(sample, thread, evsel); +	perf_event__fprintf(event, stdout); + +	return 0; +} + +static int process_mmap2_event(struct perf_tool *tool, +			      union perf_event *event, +			      struct perf_sample *sample, +			      struct machine *machine) +{ +	struct thread *thread; +	struct perf_script *script = container_of(tool, struct perf_script, tool); +	struct perf_session *session = script->session; +	struct perf_evsel *evsel = perf_evlist__first(session->evlist); + +	if (perf_event__process_mmap2(tool, event, sample, machine) < 0) +		return -1; + +	thread = machine__findnew_thread(machine, event->mmap2.pid, event->mmap2.tid); +	if (thread == NULL) { +		pr_debug("problem processing MMAP2 event, skipping it.\n"); +		return -1; +	} + +	if (!evsel->attr.sample_id_all) { +		sample->cpu = 0; +		sample->time = 0; +		sample->tid = event->mmap2.tid; +		sample->pid = event->mmap2.pid; +	} +	print_sample_start(sample, thread, evsel); +	perf_event__fprintf(event, stdout); + +	return 0; +}  static void sig_handler(int sig __maybe_unused)  {  	session_done = 1;  } -static int __cmd_script(struct perf_session *session) +static int __cmd_script(struct perf_script *script)  {  	int ret;  	signal(SIGINT, sig_handler); -	ret = perf_session__process_events(session, &perf_script); +	/* override event processing functions */ +	if (script->show_task_events) { +		script->tool.comm = process_comm_event; +		script->tool.fork = process_fork_event; +		script->tool.exit = process_exit_event; +	} +	if (script->show_mmap_events) { +		script->tool.mmap = process_mmap_event; +		script->tool.mmap2 = process_mmap2_event; +	} + +	ret = perf_session__process_events(script->session, &script->tool);  	if (debug_mode)  		pr_err("Misordered timestamps: %" PRIu64 "\n", nr_unordered); @@ -876,9 +1102,9 @@ static struct script_desc *script_desc__new(const char *name)  static void script_desc__delete(struct script_desc *s)  { -	free(s->name); -	free(s->half_liner); -	free(s->args); +	zfree(&s->name); +	zfree(&s->half_liner); +	zfree(&s->args);  	free(s);  } @@ -1083,8 +1309,7 @@ static int check_ev_match(char *dir_name, char *scriptname,  			snprintf(evname, len + 1, "%s", p);  			match = 0; -			list_for_each_entry(pos, -					&session->evlist->entries, node) { +			evlist__for_each(session->evlist, pos) {  				if (!strcmp(perf_evsel__name(pos), evname)) {  					match = 1;  					break; @@ -1115,10 +1340,14 @@ int find_scripts(char **scripts_array, char **scripts_path_array)  	char scripts_path[MAXPATHLEN], lang_path[MAXPATHLEN];  	DIR *scripts_dir, *lang_dir;  	struct perf_session *session; +	struct perf_data_file file = { +		.path = input_name, +		.mode = PERF_DATA_MODE_READ, +	};  	char *temp;  	int i = 0; -	session = perf_session__new(input_name, O_RDONLY, 0, false, NULL); +	session = perf_session__new(&file, false, NULL);  	if (!session)  		return -1; @@ -1262,12 +1491,29 @@ static int have_cmd(int argc, const char **argv)  int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)  {  	bool show_full_info = false; +	bool header = false; +	bool header_only = false;  	char *rec_script_path = NULL;  	char *rep_script_path = NULL;  	struct perf_session *session;  	char *script_path = NULL;  	const char **__argv;  	int i, j, err; +	struct perf_script script = { +		.tool = { +			.sample		 = process_sample_event, +			.mmap		 = perf_event__process_mmap, +			.mmap2		 = perf_event__process_mmap2, +			.comm		 = perf_event__process_comm, +			.exit		 = perf_event__process_exit, +			.fork		 = perf_event__process_fork, +			.attr		 = process_attr, +			.tracing_data	 = perf_event__process_tracing_data, +			.build_id	 = perf_event__process_build_id, +			.ordered_samples = true, +			.ordering_requires_timestamps = true, +		}, +	};  	const struct option options[] = {  	OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,  		    "dump raw trace in ASCII"), @@ -1285,6 +1531,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)  	OPT_STRING('i', "input", &input_name, "file", "input file name"),  	OPT_BOOLEAN('d', "debug-mode", &debug_mode,  		   "do various checks like samples ordering and lost events"), +	OPT_BOOLEAN(0, "header", &header, "Show data header."), +	OPT_BOOLEAN(0, "header-only", &header_only, "Show only data header."),  	OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,  		   "file", "vmlinux pathname"),  	OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, @@ -1309,6 +1557,10 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)  		    "display extended information from perf.data file"),  	OPT_BOOLEAN('\0', "show-kernel-path", &symbol_conf.show_kernel_path,  		    "Show the path of [kernel.kallsyms]"), +	OPT_BOOLEAN('\0', "show-task-events", &script.show_task_events, +		    "Show the fork/comm/exit events"), +	OPT_BOOLEAN('\0', "show-mmap-events", &script.show_mmap_events, +		    "Show the mmap events"),  	OPT_END()  	};  	const char * const script_usage[] = { @@ -1319,12 +1571,17 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)  		"perf script [<options>] <top-script> [script-args]",  		NULL  	}; +	struct perf_data_file file = { +		.mode = PERF_DATA_MODE_READ, +	};  	setup_scripting();  	argc = parse_options(argc, argv, options, script_usage,  			     PARSE_OPT_STOP_AT_NON_OPTION); +	file.path = input_name; +  	if (argc > 1 && !strncmp(argv[0], "rec", strlen("rec"))) {  		rec_script_path = get_script_path(argv[1], RECORD_SUFFIX);  		if (!rec_script_path) @@ -1488,19 +1745,23 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)  	if (!script_name)  		setup_pager(); -	session = perf_session__new(input_name, O_RDONLY, 0, false, -				    &perf_script); +	session = perf_session__new(&file, false, &script.tool);  	if (session == NULL)  		return -ENOMEM; +	if (header || header_only) { +		perf_session__fprintf_info(session, stdout, show_full_info); +		if (header_only) +			return 0; +	} + +	script.session = session; +  	if (cpu_list) {  		if (perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap))  			return -1;  	} -	if (!script_name && !generate_script_lang) -		perf_session__fprintf_info(session, stdout, show_full_info); -  	if (!no_callchain)  		symbol_conf.use_callchain = true;  	else @@ -1516,7 +1777,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)  			return -1;  		} -		input = open(session->filename, O_RDONLY);	/* input_name */ +		input = open(file.path, O_RDONLY);	/* input_name */  		if (input < 0) {  			perror("failed to open file");  			return -1; @@ -1539,7 +1800,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)  			return -1;  		} -		err = scripting_ops->generate_script(session->pevent, +		err = scripting_ops->generate_script(session->tevent.pevent,  						     "perf-script");  		goto out;  	} @@ -1556,7 +1817,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)  	if (err < 0)  		goto out; -	err = __cmd_script(session); +	err = __cmd_script(&script);  	perf_session__delete(session);  	cleanup_scripting();  | 
