cregit-Linux how code gets into the kernel

Release 4.14 tools/perf/builtin-trace.c

Directory: tools/perf
/*
 * builtin-trace.c
 *
 * Builtin 'trace' command:
 *
 * Display a continuously updated trace of any workload, CPU, specific PID,
 * system wide, etc.  Default format is loosely strace like, but any other
 * event may be specified using --event.
 *
 * Copyright (C) 2012, 2013, 2014, 2015 Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
 *
 * Initially based on the 'trace' prototype by Thomas Gleixner:
 *
 * http://lwn.net/Articles/415728/ ("Announcing a new utility: 'trace'")
 *
 * Released under the GPL v2. (and only v2, not any later version)
 */

#include <traceevent/event-parse.h>
#include <api/fs/tracing_path.h>
#include "builtin.h"
#include "util/color.h"
#include "util/debug.h"
#include "util/event.h"
#include "util/evlist.h"
#include <subcmd/exec-cmd.h>
#include "util/machine.h"
#include "util/path.h"
#include "util/session.h"
#include "util/thread.h"
#include <subcmd/parse-options.h>
#include "util/strlist.h"
#include "util/intlist.h"
#include "util/thread_map.h"
#include "util/stat.h"
#include "trace/beauty/beauty.h"
#include "trace-event.h"
#include "util/parse-events.h"
#include "util/bpf-loader.h"
#include "callchain.h"
#include "print_binary.h"
#include "string2.h"
#include "syscalltbl.h"
#include "rb_resort.h"

#include <errno.h>
#include <inttypes.h>
#include <libaudit.h> /* FIXME: Still needed for audit_errno_to_name */
#include <poll.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <linux/err.h>
#include <linux/filter.h>
#include <linux/audit.h>
#include <linux/kernel.h>
#include <linux/random.h>
#include <linux/stringify.h>
#include <linux/time64.h>

#include "sane_ctype.h"

#ifndef O_CLOEXEC

# define O_CLOEXEC		02000000
#endif

#ifndef F_LINUX_SPECIFIC_BASE

# define F_LINUX_SPECIFIC_BASE	1024
#endif


struct trace {
	
struct perf_tool	tool;
	
struct syscalltbl	*sctbl;
	struct {
		
int		max;
		
struct syscall  *table;
		struct {
			
struct perf_evsel *sys_enter,
					  
*sys_exit;
		
}		events;
	
} syscalls;
	
struct record_opts	opts;
	
struct perf_evlist	*evlist;
	
struct machine		*host;
	
struct thread		*current;
	
u64			base_time;
	
FILE			*output;
	
unsigned long		nr_events;
	
struct strlist		*ev_qualifier;
	struct {
		
size_t		nr;
		
int		*entries;
	
}			ev_qualifier_ids;
	struct {
		
size_t		nr;
		
pid_t		*entries;
	
}			filter_pids;
	
double			duration_filter;
	
double			runtime_ms;
	struct {
		
u64		vfs_getname,
				
proc_getname;
	
} stats;
	
unsigned int		max_stack;
	
unsigned int		min_stack;
	
bool			not_ev_qualifier;
	
bool			live;
	
bool			full_time;
	
bool			sched;
	
bool			multiple_threads;
	
bool			summary;
	
bool			summary_only;
	
bool			show_comm;
	
bool			show_tool_stats;
	
bool			trace_syscalls;
	
bool			kernel_syscallchains;
	
bool			force;
	
bool			vfs_getname;
	
int			trace_pgfaults;
	
int			open_id;
};


struct tp_field {
	
int offset;
	union {
		
u64 (*integer)(struct tp_field *field, struct perf_sample *sample);
		
void *(*pointer)(struct tp_field *field, struct perf_sample *sample);
	};
};


#define TP_UINT_FIELD(bits) \
static u64 tp_field__u##bits(struct tp_field *field, struct perf_sample *sample) \
{ \
        u##bits value; \
        memcpy(&value, sample->raw_data + field->offset, sizeof(value)); \
        return value;  \
}

TP_UINT_FIELD(8);
TP_UINT_FIELD(16);
TP_UINT_FIELD(32);
TP_UINT_FIELD(64);


#define TP_UINT_FIELD__SWAPPED(bits) \
static u64 tp_field__swapped_u##bits(struct tp_field *field, struct perf_sample *sample) \
{ \
        u##bits value; \
        memcpy(&value, sample->raw_data + field->offset, sizeof(value)); \
        return bswap_##bits(value);\
}

TP_UINT_FIELD__SWAPPED(16);
TP_UINT_FIELD__SWAPPED(32);
TP_UINT_FIELD__SWAPPED(64);


static int tp_field__init_uint(struct tp_field *field, struct format_field *format_field, bool needs_swap) { field->offset = format_field->offset; switch (format_field->size) { case 1: field->integer = tp_field__u8; break; case 2: field->integer = needs_swap ? tp_field__swapped_u16 : tp_field__u16; break; case 4: field->integer = needs_swap ? tp_field__swapped_u32 : tp_field__u32; break; case 8: field->integer = needs_swap ? tp_field__swapped_u64 : tp_field__u64; break; default: return -1; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Arnaldo Carvalho de Melo95100.00%1100.00%
Total95100.00%1100.00%


static void *tp_field__ptr(struct tp_field *field, struct perf_sample *sample) { return sample->raw_data + field->offset; }

Contributors

PersonTokensPropCommitsCommitProp
Arnaldo Carvalho de Melo26100.00%1100.00%
Total26100.00%1100.00%


static int tp_field__init_ptr(struct tp_field *field, struct format_field *format_field) { field->offset = format_field->offset; field->pointer = tp_field__ptr; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Arnaldo Carvalho de Melo33100.00%1100.00%
Total33100.00%1100.00%

struct syscall_tp { struct tp_field id; union { struct tp_field args, ret; }; };
static int perf_evsel__init_tp_uint_field(struct perf_evsel *evsel, struct tp_field *field, const char *name) { struct format_field *format_field = perf_evsel__field(evsel, name); if (format_field == NULL) return -1; return tp_field__init_uint(field, format_field, evsel->needs_swap); }

Contributors

PersonTokensPropCommitsCommitProp
Arnaldo Carvalho de Melo55100.00%1100.00%
Total55100.00%1100.00%

#define perf_evsel__init_sc_tp_uint_field(evsel, name) \ ({ struct syscall_tp *sc = evsel->priv;\ perf_evsel__init_tp_uint_field(evsel, &sc->name, #name); })
static int perf_evsel__init_tp_ptr_field(struct perf_evsel *evsel, struct tp_field *field, const char *name) { struct format_field *format_field = perf_evsel__field(evsel, name); if (format_field == NULL) return -1; return tp_field__init_ptr(field, format_field); }

Contributors

PersonTokensPropCommitsCommitProp
Arnaldo Carvalho de Melo51100.00%1100.00%
Total51100.00%1100.00%

#define perf_evsel__init_sc_tp_ptr_field(evsel, name) \ ({ struct syscall_tp *sc = evsel->priv;\ perf_evsel__init_tp_ptr_field(evsel, &sc->name, #name); })
static void perf_evsel__delete_priv(struct perf_evsel *evsel) { zfree(&evsel->priv); perf_evsel__delete(evsel); }

Contributors

PersonTokensPropCommitsCommitProp
Arnaldo Carvalho de Melo24100.00%2100.00%
Total24100.00%2100.00%


static int perf_evsel__init_syscall_tp(struct perf_evsel *evsel, void *handler) { evsel->priv = malloc(sizeof(struct syscall_tp)); if (evsel->priv != NULL) { if (perf_evsel__init_sc_tp_uint_field(evsel, id)) goto out_delete; evsel->handler = handler; return 0; } return -ENOMEM; out_delete: zfree(&evsel->priv); return -ENOENT; }

Contributors

PersonTokensPropCommitsCommitProp
Arnaldo Carvalho de Melo5064.94%266.67%
Namhyung Kim2735.06%133.33%
Total77100.00%3100.00%


static struct perf_evsel *perf_evsel__syscall_newtp(const char *direction, void *handler) { struct perf_evsel *evsel = perf_evsel__newtp("raw_syscalls", direction); /* older kernel (e.g., RHEL6) use syscalls:{enter,exit} */ if (IS_ERR(evsel)) evsel = perf_evsel__newtp("syscalls", direction); if (IS_ERR(evsel)) return NULL; if (perf_evsel__init_syscall_tp(evsel, handler)) goto out_delete; return evsel; out_delete: perf_evsel__delete_priv(evsel); return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Namhyung Kim4454.32%125.00%
Arnaldo Carvalho de Melo1417.28%125.00%
David Ahern1417.28%125.00%
Jiri Olsa911.11%125.00%
Total81100.00%4100.00%

#define perf_evsel__sc_tp_uint(evsel, name, sample) \ ({ struct syscall_tp *fields = evsel->priv; \ fields->name.integer(&fields->name, sample); }) #define perf_evsel__sc_tp_ptr(evsel, name, sample) \ ({ struct syscall_tp *fields = evsel->priv; \ fields->name.pointer(&fields->name, sample); })
size_t strarray__scnprintf(struct strarray *sa, char *bf, size_t size, const char *intfmt, int val) { int idx = val - sa->offset; if (idx < 0 || idx >= sa->nr_entries) return scnprintf(bf, size, intfmt, val); return scnprintf(bf, size, "%s", sa->entries[idx]); }

Contributors

PersonTokensPropCommitsCommitProp
Arnaldo Carvalho de Melo75100.00%4100.00%
Total75100.00%4100.00%


static size_t __syscall_arg__scnprintf_strarray(char *bf, size_t size, const char *intfmt, struct syscall_arg *arg) { return strarray__scnprintf(arg->parm, bf, size, intfmt, arg->val); }

Contributors

PersonTokensPropCommitsCommitProp
Arnaldo Carvalho de Melo41100.00%1100.00%
Total41100.00%1100.00%


static size_t syscall_arg__scnprintf_strarray(char *bf, size_t size, struct syscall_arg *arg) { return __syscall_arg__scnprintf_strarray(bf, size, "%d", arg); }

Contributors

PersonTokensPropCommitsCommitProp
Arnaldo Carvalho de Melo30100.00%1100.00%
Total30100.00%1100.00%

#define SCA_STRARRAY syscall_arg__scnprintf_strarray struct strarrays { int nr_entries; struct strarray **entries; }; #define DEFINE_STRARRAYS(array) struct strarrays strarrays__##array = { \ .nr_entries = ARRAY_SIZE(array), \ .entries = array, \ }
size_t syscall_arg__scnprintf_strarrays(char *bf, size_t size, struct syscall_arg *arg) { struct strarrays *sas = arg->parm; int i; for (i = 0; i < sas->nr_entries; ++i) { struct strarray *sa = sas->entries[i]; int idx = arg->val - sa->offset; if (idx >= 0 && idx < sa->nr_entries) { if (sa->entries[idx] == NULL) break; return scnprintf(bf, size, "%s", sa->entries[idx]); } } return scnprintf(bf, size, "%d", arg->val); }

Contributors

PersonTokensPropCommitsCommitProp
Arnaldo Carvalho de Melo126100.00%1100.00%
Total126100.00%1100.00%

#ifndef AT_FDCWD #define AT_FDCWD -100 #endif
static size_t syscall_arg__scnprintf_fd_at(char *bf, size_t size, struct syscall_arg *arg) { int fd = arg->val; if (fd == AT_FDCWD) return scnprintf(bf, size, "CWD"); return syscall_arg__scnprintf_fd(bf, size, arg); }

Contributors

PersonTokensPropCommitsCommitProp
Alex Snast3670.59%150.00%
Arnaldo Carvalho de Melo1529.41%150.00%
Total51100.00%2100.00%

#define SCA_FDAT syscall_arg__scnprintf_fd_at static size_t syscall_arg__scnprintf_close_fd(char *bf, size_t size, struct syscall_arg *arg); #define SCA_CLOSE_FD syscall_arg__scnprintf_close_fd
size_t syscall_arg__scnprintf_hex(char *bf, size_t size, struct syscall_arg *arg) { return scnprintf(bf, size, "%#lx", arg->val); }

Contributors

PersonTokensPropCommitsCommitProp
Arnaldo Carvalho de Melo31100.00%4100.00%
Total31100.00%4100.00%


size_t syscall_arg__scnprintf_int(char *bf, size_t size, struct syscall_arg *arg) { return scnprintf(bf, size, "%d", arg->val); }

Contributors

PersonTokensPropCommitsCommitProp
Arnaldo Carvalho de Melo31100.00%2100.00%
Total31100.00%2100.00%


size_t syscall_arg__scnprintf_long(char *bf, size_t size, struct syscall_arg *arg) { return scnprintf(bf, size, "%ld", arg->val); }

Contributors

PersonTokensPropCommitsCommitProp
Arnaldo Carvalho de Melo31100.00%1100.00%
Total31100.00%1100.00%

static const char *bpf_cmd[] = { "MAP_CREATE", "MAP_LOOKUP_ELEM", "MAP_UPDATE_ELEM", "MAP_DELETE_ELEM", "MAP_GET_NEXT_KEY", "PROG_LOAD", }; static DEFINE_STRARRAY(bpf_cmd); static const char *epoll_ctl_ops[] = { "ADD", "DEL", "MOD", }; static DEFINE_STRARRAY_OFFSET(epoll_ctl_ops, 1); static const char *itimers[] = { "REAL", "VIRTUAL", "PROF", }; static DEFINE_STRARRAY(itimers); static const char *keyctl_options[] = { "GET_KEYRING_ID", "JOIN_SESSION_KEYRING", "UPDATE", "REVOKE", "CHOWN", "SETPERM", "DESCRIBE", "CLEAR", "LINK", "UNLINK", "SEARCH", "READ", "INSTANTIATE", "NEGATE", "SET_REQKEY_KEYRING", "SET_TIMEOUT", "ASSUME_AUTHORITY", "GET_SECURITY", "SESSION_TO_PARENT", "REJECT", "INSTANTIATE_IOV", "INVALIDATE", "GET_PERSISTENT", }; static DEFINE_STRARRAY(keyctl_options); static const char *whences[] = { "SET", "CUR", "END", #ifdef SEEK_DATA "DATA", #endif #ifdef SEEK_HOLE "HOLE", #endif }; static DEFINE_STRARRAY(whences); static const char *fcntl_cmds[] = { "DUPFD", "GETFD", "SETFD", "GETFL", "SETFL", "GETLK", "SETLK", "SETLKW", "SETOWN", "GETOWN", "SETSIG", "GETSIG", "GETLK64", "SETLK64", "SETLKW64", "SETOWN_EX", "GETOWN_EX", "GETOWNER_UIDS", }; static DEFINE_STRARRAY(fcntl_cmds); static const char *fcntl_linux_specific_cmds[] = { "SETLEASE", "GETLEASE", "NOTIFY", [5] = "CANCELLK", "DUPFD_CLOEXEC", "SETPIPE_SZ", "GETPIPE_SZ", "ADD_SEALS", "GET_SEALS", "GET_RW_HINT", "SET_RW_HINT", "GET_FILE_RW_HINT", "SET_FILE_RW_HINT", }; static DEFINE_STRARRAY_OFFSET(fcntl_linux_specific_cmds, F_LINUX_SPECIFIC_BASE); static struct strarray *fcntl_cmds_arrays[] = { &strarray__fcntl_cmds, &strarray__fcntl_linux_specific_cmds, }; static DEFINE_STRARRAYS(fcntl_cmds_arrays); static const char *rlimit_resources[] = { "CPU", "FSIZE", "DATA", "STACK", "CORE", "RSS", "NPROC", "NOFILE", "MEMLOCK", "AS", "LOCKS", "SIGPENDING", "MSGQUEUE", "NICE", "RTPRIO", "RTTIME", }; static DEFINE_STRARRAY(rlimit_resources); static const char *sighow[] = { "BLOCK", "UNBLOCK", "SETMASK", }; static DEFINE_STRARRAY(sighow); static const char *clockid[] = { "REALTIME", "MONOTONIC", "PROCESS_CPUTIME_ID", "THREAD_CPUTIME_ID", "MONOTONIC_RAW", "REALTIME_COARSE", "MONOTONIC_COARSE", "BOOTTIME", "REALTIME_ALARM", "BOOTTIME_ALARM", "SGI_CYCLE", "TAI" }; static DEFINE_STRARRAY(clockid); static const char *socket_families[] = { "UNSPEC", "LOCAL", "INET", "AX25", "IPX", "APPLETALK", "NETROM", "BRIDGE", "ATMPVC", "X25", "INET6", "ROSE", "DECnet", "NETBEUI", "SECURITY", "KEY", "NETLINK", "PACKET", "ASH", "ECONET", "ATMSVC", "RDS", "SNA", "IRDA", "PPPOX", "WANPIPE", "LLC", "IB", "CAN", "TIPC", "BLUETOOTH", "IUCV", "RXRPC", "ISDN", "PHONET", "IEEE802154", "CAIF", "ALG", "NFC", "VSOCK", }; static DEFINE_STRARRAY(socket_families);
static size_t syscall_arg__scnprintf_access_mode(char *bf, size_t size, struct syscall_arg *arg) { size_t printed = 0; int mode = arg->val; if (mode == F_OK) /* 0 */ return scnprintf(bf, size, "F"); #define P_MODE(n) \ if (mode & n##_OK) { \ printed += scnprintf(bf + printed, size - printed, "%s", #n); \ mode &= ~n##_OK; \ } P_MODE(R); P_MODE(W); P_MODE(X); #undef P_MODE if (mode) printed += scnprintf(bf + printed, size - printed, "|%#x", mode); return printed; }

Contributors

PersonTokensPropCommitsCommitProp
Arnaldo Carvalho de Melo97100.00%1100.00%
Total97100.00%1100.00%

#define SCA_ACCMODE syscall_arg__scnprintf_access_mode static size_t syscall_arg__scnprintf_filename(char *bf, size_t size, struct syscall_arg *arg); #define SCA_FILENAME syscall_arg__scnprintf_filename
static size_t syscall_arg__scnprintf_pipe_flags(char *bf, size_t size, struct syscall_arg *arg) { int printed = 0, flags = arg->val; #define P_FLAG(n) \ if (flags & O_##n) { \ printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \ flags &= ~O_##n; \ } P_FLAG(CLOEXEC); P_FLAG(NONBLOCK); #undef P_FLAG if (flags) printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags); return printed; }

Contributors

PersonTokensPropCommitsCommitProp
Arnaldo Carvalho de Melo80100.00%1100.00%
Total80100.00%1100.00%

#define SCA_PIPE_FLAGS syscall_arg__scnprintf_pipe_flags #ifndef GRND_NONBLOCK #define GRND_NONBLOCK 0x0001 #endif #ifndef GRND_RANDOM #define GRND_RANDOM 0x0002 #endif
static size_t syscall_arg__scnprintf_getrandom_flags(char *bf, size_t size, struct syscall_arg *arg) { int printed = 0, flags = arg->val; #define P_FLAG(n) \ if (flags & GRND_##n) { \ printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \ flags &= ~GRND_##n; \ } P_FLAG(RANDOM); P_FLAG(NONBLOCK); #undef P_FLAG if (flags) printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags); return printed; }

Contributors

PersonTokensPropCommitsCommitProp
Arnaldo Carvalho de Melo80100.00%1100.00%
Total80100.00%1100.00%

#define SCA_GETRANDOM_FLAGS syscall_arg__scnprintf_getrandom_flags #define STRARRAY(name, array) \ { .scnprintf = SCA_STRARRAY, \ .parm = &strarray__##array, } #include "trace/beauty/eventfd.c" #include "trace/beauty/flock.c" #include "trace/beauty/futex_op.c" #include "trace/beauty/mmap.c" #include "trace/beauty/mode_t.c" #include "trace/beauty/msg_flags.c" #include "trace/beauty/open_flags.c" #include "trace/beauty/perf_event_open.c" #include "trace/beauty/pid.c" #include "trace/beauty/sched_policy.c" #include "trace/beauty/seccomp.c" #include "trace/beauty/signum.c" #include "trace/beauty/socket_type.c" #include "trace/beauty/waitid_options.c" struct syscall_arg_fmt { size_t (*scnprintf)(char *bf, size_t size, struct syscall_arg *arg); void *parm; const char *name; bool show_zero; }; static struct syscall_fmt { const char *name; const char *alias; struct syscall_arg_fmt arg[6]; u8 nr_args; bool errpid; bool timeout; bool hexret; } syscall_fmts[] = { { .name = "access", .arg = { [1] = { .scnprintf = SCA_ACCMODE, /* mode */ }, }, }, { .name = "arch_prctl", .alias = "prctl", }, { .name = "bpf", .arg = { [0] = STRARRAY(cmd, bpf_cmd), }, }, { .name = "brk", .hexret = true, .arg = { [0] = { .scnprintf = SCA_HEX, /* brk */ }, }, }, { .name = "clock_gettime", .arg = { [0] = STRARRAY(clk_id, clockid), }, }, { .name = "clone", .errpid = true, .nr_args = 5, .arg = { [0] = { .name = "flags", .scnprintf = SCA_CLONE_FLAGS, }, [1] = { .name = "child_stack", .scnprintf = SCA_HEX, }, [2] = { .name = "parent_tidptr", .scnprintf = SCA_HEX, }, [3] = { .name = "child_tidptr", .scnprintf = SCA_HEX, }, [4] = { .name = "tls", .scnprintf = SCA_HEX, }, }, }, { .name = "close", .arg = { [0] = { .scnprintf = SCA_CLOSE_FD, /* fd */ }, }, }, { .name = "epoll_ctl", .arg = { [1] = STRARRAY(op, epoll_ctl_ops), }, }, { .name = "eventfd2", .arg = { [1] = { .scnprintf = SCA_EFD_FLAGS, /* flags */ }, }, }, { .name = "fchmodat", .arg = { [0] = { .scnprintf = SCA_FDAT, /* fd */ }, }, }, { .name = "fchownat", .arg = { [0] = { .scnprintf = SCA_FDAT, /* fd */ }, }, }, { .name = "fcntl", .arg = { [1] = { .scnprintf = SCA_FCNTL_CMD, /* cmd */ .parm = &strarrays__fcntl_cmds_arrays, .show_zero = true, }, [2] = { .scnprintf = SCA_FCNTL_ARG, /* arg */ }, }, }, { .name = "flock", .arg = { [1] = { .scnprintf = SCA_FLOCK, /* cmd */ }, }, }, { .name = "fstat", .alias = "newfstat", }, { .name = "fstatat", .alias = "newfstatat", }, { .name = "futex", .arg = { [1] = { .scnprintf = SCA_FUTEX_OP, /* op */ }, }, }, { .name = "futimesat", .arg = { [0] = { .scnprintf = SCA_FDAT, /* fd */ }, }, }, { .name = "getitimer", .arg = { [0] = STRARRAY(which, itimers), }, }, { .name