diff options
Diffstat (limited to 'tools/perf/builtin-trace.c')
| -rw-r--r-- | tools/perf/builtin-trace.c | 175 | 
1 files changed, 152 insertions, 23 deletions
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index e122970361f2..39ad4d0ca884 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -16,7 +16,6 @@  #include <libaudit.h>  #include <stdlib.h> -#include <sys/eventfd.h>  #include <sys/mman.h>  #include <linux/futex.h> @@ -41,6 +40,51 @@  # define EFD_SEMAPHORE		1  #endif +#ifndef EFD_NONBLOCK +# define EFD_NONBLOCK		00004000 +#endif + +#ifndef EFD_CLOEXEC +# define EFD_CLOEXEC		02000000 +#endif + +#ifndef O_CLOEXEC +# define O_CLOEXEC		02000000 +#endif + +#ifndef SOCK_DCCP +# define SOCK_DCCP		6 +#endif + +#ifndef SOCK_CLOEXEC +# define SOCK_CLOEXEC		02000000 +#endif + +#ifndef SOCK_NONBLOCK +# define SOCK_NONBLOCK		00004000 +#endif + +#ifndef MSG_CMSG_CLOEXEC +# define MSG_CMSG_CLOEXEC	0x40000000 +#endif + +#ifndef PERF_FLAG_FD_NO_GROUP +# define PERF_FLAG_FD_NO_GROUP		(1UL << 0) +#endif + +#ifndef PERF_FLAG_FD_OUTPUT +# define PERF_FLAG_FD_OUTPUT		(1UL << 1) +#endif + +#ifndef PERF_FLAG_PID_CGROUP +# define PERF_FLAG_PID_CGROUP		(1UL << 2) /* pid=cgroup id, per-cpu mode only */ +#endif + +#ifndef PERF_FLAG_FD_CLOEXEC +# define PERF_FLAG_FD_CLOEXEC		(1UL << 3) /* O_CLOEXEC */ +#endif + +  struct tp_field {  	int offset;  	union { @@ -331,6 +375,14 @@ static size_t syscall_arg__scnprintf_hex(char *bf, size_t size,  #define SCA_HEX syscall_arg__scnprintf_hex +static size_t syscall_arg__scnprintf_int(char *bf, size_t size, +					 struct syscall_arg *arg) +{ +	return scnprintf(bf, size, "%d", arg->val); +} + +#define SCA_INT syscall_arg__scnprintf_int +  static size_t syscall_arg__scnprintf_mmap_prot(char *bf, size_t size,  					       struct syscall_arg *arg)  { @@ -783,6 +835,34 @@ static size_t syscall_arg__scnprintf_open_flags(char *bf, size_t size,  #define SCA_OPEN_FLAGS syscall_arg__scnprintf_open_flags +static size_t syscall_arg__scnprintf_perf_flags(char *bf, size_t size, +						struct syscall_arg *arg) +{ +	int printed = 0, flags = arg->val; + +	if (flags == 0) +		return 0; + +#define	P_FLAG(n) \ +	if (flags & PERF_FLAG_##n) { \ +		printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \ +		flags &= ~PERF_FLAG_##n; \ +	} + +	P_FLAG(FD_NO_GROUP); +	P_FLAG(FD_OUTPUT); +	P_FLAG(PID_CGROUP); +	P_FLAG(FD_CLOEXEC); +#undef P_FLAG + +	if (flags) +		printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags); + +	return printed; +} + +#define SCA_PERF_FLAGS syscall_arg__scnprintf_perf_flags +  static size_t syscall_arg__scnprintf_eventfd_flags(char *bf, size_t size,  						   struct syscall_arg *arg)  { @@ -1050,6 +1130,11 @@ static struct syscall_fmt {  	{ .name	    = "openat",	    .errmsg = true,  	  .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */  			     [2] = SCA_OPEN_FLAGS, /* flags */ }, }, +	{ .name	    = "perf_event_open", .errmsg = true, +	  .arg_scnprintf = { [1] = SCA_INT, /* pid */ +			     [2] = SCA_INT, /* cpu */ +			     [3] = SCA_FD,  /* group_fd */ +			     [4] = SCA_PERF_FLAGS,  /* flags */ }, },  	{ .name	    = "pipe2",	    .errmsg = true,  	  .arg_scnprintf = { [1] = SCA_PIPE_FLAGS, /* flags */ }, },  	{ .name	    = "poll",	    .errmsg = true, .timeout = true, }, @@ -1433,7 +1518,8 @@ static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist)  		return -ENOMEM;  	err = __machine__synthesize_threads(trace->host, &trace->tool, &trace->opts.target, -					    evlist->threads, trace__tool_process, false); +					    evlist->threads, trace__tool_process, false, +					    trace->opts.proc_map_timeout);  	if (err)  		symbol__exit(); @@ -1531,6 +1617,34 @@ static int trace__read_syscall_info(struct trace *trace, int id)  	return syscall__set_arg_fmts(sc);  } +static int trace__validate_ev_qualifier(struct trace *trace) +{ +	int err = 0; +	struct str_node *pos; + +	strlist__for_each(pos, trace->ev_qualifier) { +		const char *sc = pos->s; + +		if (audit_name_to_syscall(sc, trace->audit.machine) < 0) { +			if (err == 0) { +				fputs("Error:\tInvalid syscall ", trace->output); +				err = -EINVAL; +			} else { +				fputs(", ", trace->output); +			} + +			fputs(sc, trace->output); +		} +	} + +	if (err < 0) { +		fputs("\nHint:\ttry 'perf list syscalls:sys_enter_*'" +		      "\nHint:\tand: 'man syscalls'\n", trace->output); +	} + +	return err; +} +  /*   * args is to be interpreted as a series of longs but we need to handle   * 8-byte unaligned accesses. args points to raw_data within the event @@ -1712,7 +1826,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,  	void *args;  	size_t printed = 0;  	struct thread *thread; -	int id = perf_evsel__sc_tp_uint(evsel, id, sample); +	int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1;  	struct syscall *sc = trace__syscall_info(trace, evsel, id);  	struct thread_trace *ttrace; @@ -1725,14 +1839,14 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,  	thread = machine__findnew_thread(trace->host, sample->pid, sample->tid);  	ttrace = thread__trace(thread, trace->output);  	if (ttrace == NULL) -		return -1; +		goto out_put;  	args = perf_evsel__sc_tp_ptr(evsel, args, sample);  	if (ttrace->entry_str == NULL) {  		ttrace->entry_str = malloc(1024);  		if (!ttrace->entry_str) -			return -1; +			goto out_put;  	}  	if (!trace->summary_only) @@ -1757,8 +1871,10 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,  		thread__put(trace->current);  		trace->current = thread__get(thread);  	} - -	return 0; +	err = 0; +out_put: +	thread__put(thread); +	return err;  }  static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, @@ -1768,7 +1884,7 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,  	long ret;  	u64 duration = 0;  	struct thread *thread; -	int id = perf_evsel__sc_tp_uint(evsel, id, sample); +	int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1;  	struct syscall *sc = trace__syscall_info(trace, evsel, id);  	struct thread_trace *ttrace; @@ -1781,7 +1897,7 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,  	thread = machine__findnew_thread(trace->host, sample->pid, sample->tid);  	ttrace = thread__trace(thread, trace->output);  	if (ttrace == NULL) -		return -1; +		goto out_put;  	if (trace->summary)  		thread__update_stats(ttrace, id, sample); @@ -1835,8 +1951,10 @@ signed_print:  	fputc('\n', trace->output);  out:  	ttrace->entry_pending = false; - -	return 0; +	err = 0; +out_put: +	thread__put(thread); +	return err;  }  static int trace__vfs_getname(struct trace *trace, struct perf_evsel *evsel, @@ -1863,6 +1981,7 @@ static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evs  	ttrace->runtime_ms += runtime_ms;  	trace->runtime_ms += runtime_ms; +	thread__put(thread);  	return 0;  out_dump: @@ -1872,6 +1991,7 @@ out_dump:  	       (pid_t)perf_evsel__intval(evsel, sample, "pid"),  	       runtime,  	       perf_evsel__intval(evsel, sample, "vruntime")); +	thread__put(thread);  	return 0;  } @@ -1924,11 +2044,12 @@ static int trace__pgfault(struct trace *trace,  	struct addr_location al;  	char map_type = 'd';  	struct thread_trace *ttrace; +	int err = -1;  	thread = machine__findnew_thread(trace->host, sample->pid, sample->tid);  	ttrace = thread__trace(thread, trace->output);  	if (ttrace == NULL) -		return -1; +		goto out_put;  	if (evsel->attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ)  		ttrace->pfmaj++; @@ -1936,7 +2057,7 @@ static int trace__pgfault(struct trace *trace,  		ttrace->pfmin++;  	if (trace->summary_only) -		return 0; +		goto out;  	thread__find_addr_location(thread, cpumode, MAP__FUNCTION,  			      sample->ip, &al); @@ -1967,8 +2088,11 @@ static int trace__pgfault(struct trace *trace,  	print_location(trace->output, sample, &al, true, false);  	fprintf(trace->output, " (%c%c)\n", map_type, al.level); - -	return 0; +out: +	err = 0; +out_put: +	thread__put(thread); +	return err;  }  static bool skip_sample(struct trace *trace, struct perf_sample *sample) @@ -2229,7 +2353,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv)  	 */  	if (trace->filter_pids.nr > 0)  		err = perf_evlist__set_filter_pids(evlist, trace->filter_pids.nr, trace->filter_pids.entries); -	else if (evlist->threads->map[0] == -1) +	else if (thread_map__pid(evlist->threads, 0) == -1)  		err = perf_evlist__set_filter_pid(evlist, getpid());  	if (err < 0) { @@ -2247,7 +2371,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv)  	if (forks)  		perf_evlist__start_workload(evlist); -	trace->multiple_threads = evlist->threads->map[0] == -1 || +	trace->multiple_threads = thread_map__pid(evlist->threads, 0) == -1 ||  				  evlist->threads->nr > 1 ||  				  perf_evlist__first(evlist)->attr.inherit;  again: @@ -2652,6 +2776,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)  			.user_interval = ULLONG_MAX,  			.no_buffering  = true,  			.mmap_pages    = UINT_MAX, +			.proc_map_timeout  = 500,  		},  		.output = stdout,  		.show_comm = true, @@ -2666,16 +2791,15 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)  	OPT_BOOLEAN(0, "comm", &trace.show_comm,  		    "show the thread COMM next to its id"),  	OPT_BOOLEAN(0, "tool_stats", &trace.show_tool_stats, "show tool stats"), -	OPT_STRING('e', "expr", &ev_qualifier_str, "expr", -		    "list of events to trace"), +	OPT_STRING('e', "expr", &ev_qualifier_str, "expr", "list of syscalls to trace"),  	OPT_STRING('o', "output", &output_name, "file", "output file name"),  	OPT_STRING('i', "input", &input_name, "file", "Analyze events in file"),  	OPT_STRING('p', "pid", &trace.opts.target.pid, "pid",  		    "trace events on existing process id"),  	OPT_STRING('t', "tid", &trace.opts.target.tid, "tid",  		    "trace events on existing thread id"), -	OPT_CALLBACK(0, "filter-pids", &trace, "float", -		     "show only events with duration > N.M ms", trace__set_filter_pids), +	OPT_CALLBACK(0, "filter-pids", &trace, "CSV list of pids", +		     "pids to filter (by the kernel)", trace__set_filter_pids),  	OPT_BOOLEAN('a', "all-cpus", &trace.opts.target.system_wide,  		    "system-wide collection from all CPUs"),  	OPT_STRING('C', "cpu", &trace.opts.target.cpu_list, "cpu", @@ -2702,6 +2826,8 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)  		     "Trace pagefaults", parse_pagefaults, "maj"),  	OPT_BOOLEAN(0, "syscalls", &trace.trace_syscalls, "Trace syscalls"),  	OPT_BOOLEAN('f', "force", &trace.force, "don't complain, do it"), +	OPT_UINTEGER(0, "proc-map-timeout", &trace.opts.proc_map_timeout, +			"per thread proc mmap processing timeout in ms"),  	OPT_END()  	};  	const char * const trace_subcommands[] = { "record", NULL }; @@ -2712,11 +2838,10 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)  	signal(SIGFPE, sighandler_dump_stack);  	trace.evlist = perf_evlist__new(); -	if (trace.evlist == NULL) -		return -ENOMEM;  	if (trace.evlist == NULL) {  		pr_err("Not enough memory to run!\n"); +		err = -ENOMEM;  		goto out;  	} @@ -2765,6 +2890,10 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)  			err = -ENOMEM;  			goto out_close;  		} + +		err = trace__validate_ev_qualifier(&trace); +		if (err) +			goto out_close;  	}  	err = target__validate(&trace.opts.target);  |