cregit-Linux how code gets into the kernel

Release 4.10 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/evlist.h"
#include <subcmd/exec-cmd.h>
#include "util/machine.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-event.h"
#include "util/parse-events.h"
#include "util/bpf-loader.h"
#include "callchain.h"
#include "syscalltbl.h"
#include "rb_resort.h"

#include <libaudit.h> /* FIXME: Still needed for audit_errno_to_name */
#include <stdlib.h>
#include <linux/err.h>
#include <linux/filter.h>
#include <linux/audit.h>
#include <linux/random.h>
#include <linux/stringify.h>
#include <linux/time64.h>

#ifndef O_CLOEXEC

# define O_CLOEXEC		02000000
#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 meloarnaldo 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 meloarnaldo 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 meloarnaldo 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 meloarnaldo 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 meloarnaldo 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 meloarnaldo 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 meloarnaldo carvalho de melo5064.94%266.67%
namhyung kimnamhyung 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 kimnamhyung kim4454.32%125.00%
arnaldo carvalho de meloarnaldo carvalho de melo1417.28%125.00%
david aherndavid ahern1417.28%125.00%
jiri olsajiri 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); }) struct syscall_arg { unsigned long val; struct thread *thread; struct trace *trace; void *parm; u8 idx; u8 mask; }; struct strarray { int offset; int nr_entries; const char **entries; }; #define DEFINE_STRARRAY(array) struct strarray strarray__##array = { \ .nr_entries = ARRAY_SIZE(array), \ .entries = array, \ } #define DEFINE_STRARRAY_OFFSET(array, off) struct strarray strarray__##array = { \ .offset = off, \ .nr_entries = ARRAY_SIZE(array), \ .entries = array, \ }
static size_t __syscall_arg__scnprintf_strarray(char *bf, size_t size, const char *intfmt, struct syscall_arg *arg) { struct strarray *sa = arg->parm; int idx = arg->val - sa->offset; if (idx < 0 || idx >= sa->nr_entries) return scnprintf(bf, size, intfmt, arg->val); return scnprintf(bf, size, "%s", sa->entries[idx]); }

Contributors

PersonTokensPropCommitsCommitProp
arnaldo carvalho de meloarnaldo carvalho de melo86100.00%3100.00%
Total86100.00%3100.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 meloarnaldo carvalho de melo30100.00%1100.00%
Total30100.00%1100.00%

#define SCA_STRARRAY syscall_arg__scnprintf_strarray #if defined(__i386__) || defined(__x86_64__) /* * FIXME: Make this available to all arches as soon as the ioctl beautifier * gets rewritten to support all arches. */
static size_t syscall_arg__scnprintf_strhexarray(char *bf, size_t size, struct syscall_arg *arg) { return __syscall_arg__scnprintf_strarray(bf, size, "%#x", arg); }

Contributors

PersonTokensPropCommitsCommitProp
arnaldo carvalho de meloarnaldo carvalho de melo30100.00%1100.00%
Total30100.00%1100.00%

#define SCA_STRHEXARRAY syscall_arg__scnprintf_strhexarray #endif /* defined(__i386__) || defined(__x86_64__) */ static size_t syscall_arg__scnprintf_fd(char *bf, size_t size, struct syscall_arg *arg); #define SCA_FD syscall_arg__scnprintf_fd #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
arnaldo carvalho de meloarnaldo carvalho de melo51100.00%1100.00%
Total51100.00%1100.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
static 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 meloarnaldo carvalho de melo32100.00%3100.00%
Total32100.00%3100.00%

#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); }

Contributors

PersonTokensPropCommitsCommitProp
arnaldo carvalho de meloarnaldo carvalho de melo32100.00%1100.00%
Total32100.00%1100.00%

#define SCA_INT syscall_arg__scnprintf_int 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", "F_GETLK64", "F_SETLK64", "F_SETLKW64", "F_SETOWN_EX", "F_GETOWN_EX", "F_GETOWNER_UIDS", }; static DEFINE_STRARRAY(fcntl_cmds); 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 meloarnaldo 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 meloarnaldo carvalho de melo80100.00%1100.00%
Total80100.00%1100.00%

#define SCA_PIPE_FLAGS syscall_arg__scnprintf_pipe_flags #if defined(__i386__) || defined(__x86_64__) /* * FIXME: Make this available to all arches. */ #define TCGETS 0x5401 static const char *tioctls[] = { "TCGETS", "TCSETS", "TCSETSW", "TCSETSF", "TCGETA", "TCSETA", "TCSETAW", "TCSETAF", "TCSBRK", "TCXONC", "TCFLSH", "TIOCEXCL", "TIOCNXCL", "TIOCSCTTY", "TIOCGPGRP", "TIOCSPGRP", "TIOCOUTQ", "TIOCSTI", "TIOCGWINSZ", "TIOCSWINSZ", "TIOCMGET", "TIOCMBIS", "TIOCMBIC", "TIOCMSET", "TIOCGSOFTCAR", "TIOCSSOFTCAR", "FIONREAD", "TIOCLINUX", "TIOCCONS", "TIOCGSERIAL", "TIOCSSERIAL", "TIOCPKT", "FIONBIO", "TIOCNOTTY", "TIOCSETD", "TIOCGETD", "TCSBRKP", [0x27] = "TIOCSBRK", "TIOCCBRK", "TIOCGSID", "TCGETS2", "TCSETS2", "TCSETSW2", "TCSETSF2", "TIOCGRS485", "TIOCSRS485", "TIOCGPTN", "TIOCSPTLCK", "TIOCGDEV||TCGETX", "TCSETX", "TCSETXF", "TCSETXW", "TIOCSIG", "TIOCVHANGUP", "TIOCGPKT", "TIOCGPTLCK", "TIOCGEXCL", [0x50] = "FIONCLEX", "FIOCLEX", "FIOASYNC", "TIOCSERCONFIG", "TIOCSERGWILD", "TIOCSERSWILD", "TIOCGLCKTRMIOS", "TIOCSLCKTRMIOS", "TIOCSERGSTRUCT", "TIOCSERGETLSR", "TIOCSERGETMULTI", "TIOCSERSETMULTI", "TIOCMIWAIT", "TIOCGICOUNT", [0x60] = "FIOQSIZE", }; static DEFINE_STRARRAY_OFFSET(tioctls, 0x5401); #endif /* defined(__i386__) || defined(__x86_64__) */ #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 meloarnaldo carvalho de melo80100.00%1100.00%
Total80100.00%1100.00%

#define SCA_GETRANDOM_FLAGS syscall_arg__scnprintf_getrandom_flags #define STRARRAY(arg, name, array) \ .arg_scnprintf = { [arg] = SCA_STRARRAY, }, \ .arg_parm = { [arg] = &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" static struct syscall_fmt { const char *name; const char *alias; size_t (*arg_scnprintf[6])(char *bf, size_t size, struct syscall_arg *arg); void *arg_parm[6]; bool errmsg; bool errpid; bool timeout; bool hexret; } syscall_fmts[] = { { .name = "access", .errmsg = true, .arg_scnprintf = { [1] = SCA_ACCMODE, /* mode */ }, }, { .name = "arch_prctl", .errmsg = true, .alias = "prctl", }, { .name = "bpf", .errmsg = true, STRARRAY(0, cmd, bpf_cmd), }, { .name = "brk", .hexret = true, .arg_scnprintf = { [0] = SCA_HEX, /* brk */ }, }, { .name = "chdir", .errmsg = true, }, { .name = "chmod", .errmsg = true, }, { .name = "chroot", .errmsg = true, }, { .name = "clock_gettime", .errmsg = true, STRARRAY(0, clk_id, clockid), }, { .name = "clone", .errpid = true, }, { .name = "close", .errmsg = true, .arg_scnprintf = { [0] = SCA_CLOSE_FD, /* fd */ }, }, { .name = "connect", .errmsg = true, }, { .name = "creat", .errmsg = true, }, { .name = "dup", .errmsg = true, }, { .name = "dup2", .errmsg = true, }, { .name = "dup3", .errmsg = true, }, { .name = "epoll_ctl", .errmsg = true, STRARRAY(1, op, epoll_ctl_ops), }, { .name = "eventfd2", .errmsg = true, .arg_scnprintf = { [1] = SCA_EFD_FLAGS, /* flags */ }, }, { .name = "faccessat", .errmsg = true, }, { .name = "fadvise64", .errmsg = true, }, { .name = "fallocate", .errmsg = true, }, { .name = "fchdir", .errmsg = true, }, { .name = "fchmod", .errmsg = true, }, { .name = "fchmodat", .errmsg = true, .arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, }, { .name = "fchown", .errmsg = true, }, { .name = "fchownat", .errmsg = true, .arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, }, { .name = "fcntl", .errmsg = true, .arg_scnprintf = { [1] =