diff options
Diffstat (limited to 'tools/perf/builtin-script.c')
| -rw-r--r-- | tools/perf/builtin-script.c | 245 | 
1 files changed, 199 insertions, 46 deletions
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 72b5deb4bd79..c691214d820f 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -3,9 +3,9 @@  #include "perf.h"  #include "util/cache.h"  #include "util/debug.h" -#include "util/exec_cmd.h" +#include <subcmd/exec-cmd.h>  #include "util/header.h" -#include "util/parse-options.h" +#include <subcmd/parse-options.h>  #include "util/perf_regs.h"  #include "util/session.h"  #include "util/tool.h" @@ -18,7 +18,11 @@  #include "util/sort.h"  #include "util/data.h"  #include "util/auxtrace.h" +#include "util/cpumap.h" +#include "util/thread_map.h" +#include "util/stat.h"  #include <linux/bitmap.h> +#include "asm/bug.h"  static char const		*script_name;  static char const		*generate_script_lang; @@ -32,6 +36,7 @@ static bool			print_flags;  static bool			nanosecs;  static const char		*cpu_list;  static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); +static struct perf_stat_config	stat_config;  unsigned int scripting_max_stack = PERF_MAX_STACK_DEPTH; @@ -130,6 +135,18 @@ static struct {  		.invalid_fields = PERF_OUTPUT_TRACE,  	}, + +	[PERF_TYPE_BREAKPOINT] = { +		.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_PERIOD, + +		.invalid_fields = PERF_OUTPUT_TRACE, +	},  };  static bool output_set_by_user(void) @@ -204,6 +221,9 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,  	struct perf_event_attr *attr = &evsel->attr;  	bool allow_user_set; +	if (perf_header__has_feat(&session->header, HEADER_STAT)) +		return 0; +  	allow_user_set = perf_header__has_feat(&session->header,  					       HEADER_AUXTRACE); @@ -588,8 +608,35 @@ static void print_sample_flags(u32 flags)  	printf("  %-4s ", str);  } -static void process_event(union perf_event *event, struct perf_sample *sample, -			  struct perf_evsel *evsel, struct addr_location *al) +struct perf_script { +	struct perf_tool	tool; +	struct perf_session	*session; +	bool			show_task_events; +	bool			show_mmap_events; +	bool			show_switch_events; +	bool			allocated; +	struct cpu_map		*cpus; +	struct thread_map	*threads; +	int			name_width; +}; + +static int perf_evlist__max_name_len(struct perf_evlist *evlist) +{ +	struct perf_evsel *evsel; +	int max = 0; + +	evlist__for_each(evlist, evsel) { +		int len = strlen(perf_evsel__name(evsel)); + +		max = MAX(len, max); +	} + +	return max; +} + +static void process_event(struct perf_script *script, union perf_event *event, +			  struct perf_sample *sample, struct perf_evsel *evsel, +			  struct addr_location *al)  {  	struct thread *thread = al->thread;  	struct perf_event_attr *attr = &evsel->attr; @@ -604,7 +651,12 @@ static void process_event(union perf_event *event, struct perf_sample *sample,  	if (PRINT_FIELD(EVNAME)) {  		const char *evname = perf_evsel__name(evsel); -		printf("%s: ", evname ? evname : "[unknown]"); + +		if (!script->name_width) +			script->name_width = perf_evlist__max_name_len(script->session->evlist); + +		printf("%*s: ", script->name_width, +		       evname ? evname : "[unknown]");  	}  	if (print_flags) @@ -643,65 +695,81 @@ static void process_event(union perf_event *event, struct perf_sample *sample,  	printf("\n");  } -static int default_start_script(const char *script __maybe_unused, -				int argc __maybe_unused, -				const char **argv __maybe_unused) -{ -	return 0; -} +static struct scripting_ops	*scripting_ops; -static int default_flush_script(void) +static void __process_stat(struct perf_evsel *counter, u64 tstamp)  { -	return 0; +	int nthreads = thread_map__nr(counter->threads); +	int ncpus = perf_evsel__nr_cpus(counter); +	int cpu, thread; +	static int header_printed; + +	if (counter->system_wide) +		nthreads = 1; + +	if (!header_printed) { +		printf("%3s %8s %15s %15s %15s %15s %s\n", +		       "CPU", "THREAD", "VAL", "ENA", "RUN", "TIME", "EVENT"); +		header_printed = 1; +	} + +	for (thread = 0; thread < nthreads; thread++) { +		for (cpu = 0; cpu < ncpus; cpu++) { +			struct perf_counts_values *counts; + +			counts = perf_counts(counter->counts, cpu, thread); + +			printf("%3d %8d %15" PRIu64 " %15" PRIu64 " %15" PRIu64 " %15" PRIu64 " %s\n", +				counter->cpus->map[cpu], +				thread_map__pid(counter->threads, thread), +				counts->val, +				counts->ena, +				counts->run, +				tstamp, +				perf_evsel__name(counter)); +		} +	}  } -static int default_stop_script(void) +static void process_stat(struct perf_evsel *counter, u64 tstamp)  { -	return 0; +	if (scripting_ops && scripting_ops->process_stat) +		scripting_ops->process_stat(&stat_config, counter, tstamp); +	else +		__process_stat(counter, tstamp);  } -static int default_generate_script(struct pevent *pevent __maybe_unused, -				   const char *outfile __maybe_unused) +static void process_stat_interval(u64 tstamp)  { -	return 0; +	if (scripting_ops && scripting_ops->process_stat_interval) +		scripting_ops->process_stat_interval(tstamp);  } -static struct scripting_ops default_scripting_ops = { -	.start_script		= default_start_script, -	.flush_script		= default_flush_script, -	.stop_script		= default_stop_script, -	.process_event		= process_event, -	.generate_script	= default_generate_script, -}; - -static struct scripting_ops	*scripting_ops; -  static void setup_scripting(void)  {  	setup_perl_scripting();  	setup_python_scripting(); - -	scripting_ops = &default_scripting_ops;  }  static int flush_scripting(void)  { -	return scripting_ops->flush_script(); +	return scripting_ops ? scripting_ops->flush_script() : 0;  }  static int cleanup_scripting(void)  {  	pr_debug("\nperf script stopped\n"); -	return scripting_ops->stop_script(); +	return scripting_ops ? scripting_ops->stop_script() : 0;  } -static int process_sample_event(struct perf_tool *tool __maybe_unused, +static int process_sample_event(struct perf_tool *tool,  				union perf_event *event,  				struct perf_sample *sample,  				struct perf_evsel *evsel,  				struct machine *machine)  { +	struct perf_script *scr = container_of(tool, struct perf_script, tool);  	struct addr_location al;  	if (debug_mode) { @@ -727,20 +795,16 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,  	if (cpu_list && !test_bit(sample->cpu, cpu_bitmap))  		goto out_put; -	scripting_ops->process_event(event, sample, evsel, &al); +	if (scripting_ops) +		scripting_ops->process_event(event, sample, evsel, &al); +	else +		process_event(scr, event, sample, evsel, &al); +  out_put:  	addr_location__put(&al);  	return 0;  } -struct perf_script { -	struct perf_tool	tool; -	struct perf_session	*session; -	bool			show_task_events; -	bool			show_mmap_events; -	bool			show_switch_events; -}; -  static int process_attr(struct perf_tool *tool, union perf_event *event,  			struct perf_evlist **pevlist)  { @@ -1156,6 +1220,8 @@ static int parse_output_fields(const struct option *opt __maybe_unused,  			type = PERF_TYPE_TRACEPOINT;  		else if (!strcmp(str, "raw"))  			type = PERF_TYPE_RAW; +		else if (!strcmp(str, "break")) +			type = PERF_TYPE_BREAKPOINT;  		else {  			fprintf(stderr, "Invalid event type in field string.\n");  			rc = -EINVAL; @@ -1421,7 +1487,7 @@ static int list_available_scripts(const struct option *opt __maybe_unused,  	char first_half[BUFSIZ];  	char *script_root; -	snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path()); +	snprintf(scripts_path, MAXPATHLEN, "%s/scripts", get_argv_exec_path());  	scripts_dir = opendir(scripts_path);  	if (!scripts_dir) @@ -1542,7 +1608,7 @@ int find_scripts(char **scripts_array, char **scripts_path_array)  	if (!session)  		return -1; -	snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path()); +	snprintf(scripts_path, MAXPATHLEN, "%s/scripts", get_argv_exec_path());  	scripts_dir = opendir(scripts_path);  	if (!scripts_dir) { @@ -1600,7 +1666,7 @@ static char *get_script_path(const char *script_root, const char *suffix)  	char lang_path[MAXPATHLEN];  	char *__script_root; -	snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path()); +	snprintf(scripts_path, MAXPATHLEN, "%s/scripts", get_argv_exec_path());  	scripts_dir = opendir(scripts_path);  	if (!scripts_dir) @@ -1695,6 +1761,87 @@ static void script__setup_sample_type(struct perf_script *script)  	}  } +static int process_stat_round_event(struct perf_tool *tool __maybe_unused, +				    union perf_event *event, +				    struct perf_session *session) +{ +	struct stat_round_event *round = &event->stat_round; +	struct perf_evsel *counter; + +	evlist__for_each(session->evlist, counter) { +		perf_stat_process_counter(&stat_config, counter); +		process_stat(counter, round->time); +	} + +	process_stat_interval(round->time); +	return 0; +} + +static int process_stat_config_event(struct perf_tool *tool __maybe_unused, +				     union perf_event *event, +				     struct perf_session *session __maybe_unused) +{ +	perf_event__read_stat_config(&stat_config, &event->stat_config); +	return 0; +} + +static int set_maps(struct perf_script *script) +{ +	struct perf_evlist *evlist = script->session->evlist; + +	if (!script->cpus || !script->threads) +		return 0; + +	if (WARN_ONCE(script->allocated, "stats double allocation\n")) +		return -EINVAL; + +	perf_evlist__set_maps(evlist, script->cpus, script->threads); + +	if (perf_evlist__alloc_stats(evlist, true)) +		return -ENOMEM; + +	script->allocated = true; +	return 0; +} + +static +int process_thread_map_event(struct perf_tool *tool, +			     union perf_event *event, +			     struct perf_session *session __maybe_unused) +{ +	struct perf_script *script = container_of(tool, struct perf_script, tool); + +	if (script->threads) { +		pr_warning("Extra thread map event, ignoring.\n"); +		return 0; +	} + +	script->threads = thread_map__new_event(&event->thread_map); +	if (!script->threads) +		return -ENOMEM; + +	return set_maps(script); +} + +static +int process_cpu_map_event(struct perf_tool *tool __maybe_unused, +			  union perf_event *event, +			  struct perf_session *session __maybe_unused) +{ +	struct perf_script *script = container_of(tool, struct perf_script, tool); + +	if (script->cpus) { +		pr_warning("Extra cpu map event, ignoring.\n"); +		return 0; +	} + +	script->cpus = cpu_map__new_data(&event->cpu_map.data); +	if (!script->cpus) +		return -ENOMEM; + +	return set_maps(script); +} +  int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)  {  	bool show_full_info = false; @@ -1723,6 +1870,11 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)  			.auxtrace_info	 = perf_event__process_auxtrace_info,  			.auxtrace	 = perf_event__process_auxtrace,  			.auxtrace_error	 = perf_event__process_auxtrace_error, +			.stat		 = perf_event__process_stat_event, +			.stat_round	 = process_stat_round_event, +			.stat_config	 = process_stat_config_event, +			.thread_map	 = process_thread_map_event, +			.cpu_map	 = process_cpu_map_event,  			.ordered_events	 = true,  			.ordering_requires_timestamps = true,  		}, @@ -1836,7 +1988,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)  		scripting_max_stack = itrace_synth_opts.callchain_sz;  	/* make sure PERF_EXEC_PATH is set for scripts */ -	perf_set_argv_exec_path(perf_exec_path()); +	set_argv_exec_path(get_argv_exec_path());  	if (argc && !script_name && !rec_script_path && !rep_script_path) {  		int live_pipe[2]; @@ -2076,6 +2228,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)  	flush_scripting();  out_delete: +	perf_evlist__free_stats(session->evlist);  	perf_session__delete(session);  	if (script_started)  | 
