X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=strace.c;h=aae7505f4e47c4d3f71b7030feb2f6bdb5fcf33e;hb=8224758b339b23f7b4388d84b68dd032045af5e6;hp=a489db43e52c3dcc370ce39b514e83a3a7ad89cc;hpb=6162a3f34fc8d15095a3b71dc6e4cbdfe1b14ac1;p=strace diff --git a/strace.c b/strace.c index a489db43..aae7505f 100644 --- a/strace.c +++ b/strace.c @@ -50,6 +50,10 @@ extern char **environ; extern int optind; extern char *optarg; +#ifdef USE_LIBUNWIND +/* if this is true do the stack trace for every system call */ +bool stack_trace_enabled = false; +#endif #if defined __NR_tkill # define my_tkill(tid, sig) syscall(__NR_tkill, (tid), (sig)) @@ -77,11 +81,12 @@ unsigned int xflag = 0; bool need_fork_exec_workarounds = 0; bool debug_flag = 0; bool Tflag = 0; +bool iflag = 0; +bool count_wallclock = 0; unsigned int qflag = 0; /* Which WSTOPSIG(status) value marks syscall traps? */ static unsigned int syscall_trap_sig = SIGTRAP; static unsigned int tflag = 0; -static bool iflag = 0; static bool rflag = 0; static bool print_pid_pfx = 0; @@ -124,7 +129,7 @@ static int post_attach_sigstop = TCB_IGNORE_ONE_SIGSTOP; bool not_failing_only = 0; /* Show path associated with fd arguments */ -bool show_fd_path = 0; +unsigned int show_fd_path = 0; static bool detach_on_execve = 0; /* Are we "strace PROG" and need to skip detach on first execve? */ @@ -200,6 +205,7 @@ usage: strace [-CdffhiqrtttTvVxxy] [-I n] [-e expr]...\n\ -p pid... / [-D] [-E var=val]... [-u username] PROG [ARGS]\n\ -c -- count time, calls, and errors for each syscall and report summary\n\ -C -- like -c but also print regular output\n\ +-w -- summarise syscall latency (default is system time)\n\ -d -- enable debug output to stderr\n\ -D -- run tracer process as a detached grandchild, not as parent\n\ -f -- follow forks, -ff -- with output into separate files\n\ @@ -210,6 +216,7 @@ usage: strace [-CdffhiqrtttTvVxxy] [-I n] [-e expr]...\n\ -v -- verbose mode: print unabbreviated argv, stat, termios, etc. args\n\ -x -- print non-ascii strings in hex, -xx -- print all strings in hex\n\ -y -- print paths associated with file descriptor arguments\n\ +-yy -- print ip:port pairs associated with socket file descriptors\n\ -h -- print help message, -V -- print version\n\ -a column -- alignment COLUMN for printing syscall results (default %d)\n\ -b execve -- detach on this syscall\n\ @@ -231,6 +238,10 @@ usage: strace [-CdffhiqrtttTvVxxy] [-I n] [-e expr]...\n\ -E var -- remove var from the environment for command\n\ -P path -- trace accesses to path\n\ " +#ifdef USE_LIBUNWIND +"-k obtain stack trace between each syscall (experimental)\n\ +" +#endif /* ancient, no one should use it -F -- attempt to follow vforks (deprecated, use -f)\n\ */ @@ -335,11 +346,11 @@ ptrace_attach_or_seize(int pid) { int r; if (!use_seize) - return ptrace(PTRACE_ATTACH, pid, 0, 0); - r = ptrace(PTRACE_SEIZE, pid, 0, 0); + return ptrace(PTRACE_ATTACH, pid, 0L, 0L); + r = ptrace(PTRACE_SEIZE, pid, 0L, (unsigned long)ptrace_setoptions); if (r) return r; - r = ptrace(PTRACE_INTERRUPT, pid, 0, 0); + r = ptrace(PTRACE_INTERRUPT, pid, 0L, 0L); return r; } #else @@ -438,8 +449,12 @@ swap_uid(void) } } -#if _LFS64_LARGEFILE -# define fopen_for_output fopen64 +#ifdef _LARGEFILE64_SOURCE +# ifdef HAVE_FOPEN64 +# define fopen_for_output fopen64 +# else +# define fopen_for_output fopen +# endif # define struct_stat struct stat64 # define stat_file stat64 # define struct_dirent struct dirent64 @@ -537,6 +552,10 @@ tprintf(const char *fmt, ...) va_end(args); } +#ifndef HAVE_FPUTS_UNLOCKED +# define fputs_unlocked fputs +#endif + void tprints(const char *str) { @@ -657,7 +676,7 @@ expand_tcbtab(void) callers have pointers and it would be a pain. So tcbtab is a table of pointers. Since we never free the TCBs, we allocate a single chunk of many. */ - int i = tcbtabsize; + unsigned int i = tcbtabsize; struct tcb *newtcbs = calloc(tcbtabsize, sizeof(newtcbs[0])); struct tcb **newtab = realloc(tcbtab, tcbtabsize * 2 * sizeof(tcbtab[0])); if (!newtab || !newtcbs) @@ -671,7 +690,7 @@ expand_tcbtab(void) static struct tcb * alloctcb(int pid) { - int i; + unsigned int i; struct tcb *tcp; if (nprocs == tcbtabsize) @@ -685,6 +704,12 @@ alloctcb(int pid) #if SUPPORTED_PERSONALITIES > 1 tcp->currpers = current_personality; #endif + +#ifdef USE_LIBUNWIND + if (stack_trace_enabled) + unwind_tcb_init(tcp); +#endif + nprocs++; if (debug_flag) fprintf(stderr, "new tcb for pid %d, active tcbs:%d\n", tcp->pid, nprocs); @@ -700,6 +725,12 @@ droptcb(struct tcb *tcp) if (tcp->pid == 0) return; +#ifdef USE_LIBUNWIND + if (stack_trace_enabled) { + unwind_tcb_fin(tcp); + } +#endif + nprocs--; if (debug_flag) fprintf(stderr, "dropped tcb for pid %d, %d remain\n", tcp->pid, nprocs); @@ -807,7 +838,7 @@ detach(struct tcb *tcp) * 3. Attach SIGSTOP was already pending (TCB_IGNORE_ONE_SIGSTOP set) */ for (;;) { - int sig; + unsigned int sig; if (waitpid(tcp->pid, &status, __WALL) < 0) { if (errno == EINTR) continue; @@ -924,7 +955,7 @@ process_opt_p_list(char *opt) static void startup_attach(void) { - int tcbi; + unsigned int tcbi; struct tcb *tcp; /* @@ -1130,7 +1161,7 @@ startup_child(char **argv) { struct_stat statbuf; const char *filename; - char pathname[MAXPATHLEN]; + char pathname[PATH_MAX]; int pid; struct tcb *tcp; @@ -1153,7 +1184,7 @@ startup_child(char **argv) #endif /* USE_DEBUGGING_EXEC */ else { const char *path; - int m, n, len; + size_t m, n, len; for (path = getenv("PATH"); path && *path; path += m) { const char *colon = strchr(path, ':'); @@ -1164,7 +1195,7 @@ startup_child(char **argv) else m = n = strlen(path); if (n == 0) { - if (!getcwd(pathname, MAXPATHLEN)) + if (!getcwd(pathname, PATH_MAX)) continue; len = strlen(pathname); } @@ -1277,7 +1308,7 @@ startup_child(char **argv) * instead of call (won't push anything to stack), * (2) by trying very hard in exec_or_die() * to not use any stack, - * (3) having a really big (MAXPATHLEN) stack object + * (3) having a really big (PATH_MAX) stack object * in this function, which creates a "buffer" between * child's and parent's stack pointers. * This may save us if (1) and (2) failed @@ -1407,12 +1438,13 @@ test_ptrace_setoptions_followfork(void) * * Use of this option enables correct handling of user-generated SIGTRAPs, * and SIGTRAPs generated by special instructions such as int3 on x86: - * _start: .globl _start - * int3 - * movl $42, %ebx - * movl $1, %eax - * int $0x80 - * (compile with: "gcc -nostartfiles -nostdlib -o int3 int3.S") + +# compile with: gcc -nostartfiles -nostdlib -o int3 int3.S +_start: .globl _start + int3 + movl $42, %ebx + movl $1, %eax + int $0x80 */ static int test_ptrace_setoptions_for_all(void) @@ -1604,6 +1636,7 @@ init(int argc, char *argv[]) struct tcb *tcp; int c, i; int optF = 0; + unsigned int tcbi; struct sigaction sa; progname = argv[0] ? argv[0] : "strace"; @@ -1627,8 +1660,8 @@ init(int argc, char *argv[]) tcp = calloc(tcbtabsize, sizeof(*tcp)); if (!tcp) die_out_of_memory(); - for (c = 0; c < tcbtabsize; c++) - tcbtab[c] = tcp++; + for (tcbi = 0; tcbi < tcbtabsize; tcbi++) + tcbtab[tcbi] = tcp++; shared_log = stderr; set_sortby(DEFAULT_SORTBY); @@ -1641,7 +1674,10 @@ init(int argc, char *argv[]) #endif qualify("signal=all"); while ((c = getopt(argc, argv, - "+b:cCdfFhiqrtTvVxyz" + "+b:cCdfFhiqrtTvVwxyz" +#ifdef USE_LIBUNWIND + "k" +#endif "D" "a:e:o:O:p:s:S:u:E:P:I:")) != EOF) { switch (c) { @@ -1693,11 +1729,14 @@ init(int argc, char *argv[]) case 'T': Tflag = 1; break; + case 'w': + count_wallclock = 1; + break; case 'x': xflag++; break; case 'y': - show_fd_path = 1; + show_fd_path++; break; case 'v': qualify("abbrev=none"); @@ -1744,6 +1783,11 @@ init(int argc, char *argv[]) case 'u': username = strdup(optarg); break; +#ifdef USE_LIBUNWIND + case 'k': + stack_trace_enabled = true; + break; +#endif case 'E': if (putenv(optarg) < 0) die_out_of_memory(); @@ -1782,6 +1826,32 @@ init(int argc, char *argv[]) error_msg_and_die("(-c or -C) and -ff are mutually exclusive"); } + if (count_wallclock && !cflag) { + error_msg_and_die("-w must be given with (-c or -C)"); + } + + if (cflag == CFLAG_ONLY_STATS) { + if (iflag) + error_msg("-%c has no effect with -c", 'i'); +#ifdef USE_LIBUNWIND + if (stack_trace_enabled) + error_msg("-%c has no effect with -c", 'k'); +#endif + if (rflag) + error_msg("-%c has no effect with -c", 'r'); + if (tflag) + error_msg("-%c has no effect with -c", 't'); + if (Tflag) + error_msg("-%c has no effect with -c", 'T'); + if (show_fd_path) + error_msg("-%c has no effect with -c", 'y'); + } + +#ifdef USE_LIBUNWIND + if (stack_trace_enabled) + unwind_init(); +#endif + /* See if they want to run as another user. */ if (username != NULL) { struct passwd *pent; @@ -1910,7 +1980,7 @@ init(int argc, char *argv[]) static struct tcb * pid2tcb(int pid) { - int i; + unsigned int i; if (pid <= 0) return NULL; @@ -1927,7 +1997,7 @@ pid2tcb(int pid) static void cleanup(void) { - int i; + unsigned int i; struct tcb *tcp; int fatal_sig; @@ -1964,17 +2034,35 @@ trace(void) { struct rusage ru; - while (nprocs != 0) { + /* Used to be "while (nprocs != 0)", but in this testcase: + * int main() { _exit(!!fork()); } + * under strace -f, parent sometimes (rarely) manages + * to exit before we see the first stop of the child, + * and we are losing track of it: + * 19923 clone(...) = 19924 + * 19923 exit_group(1) = ? + * 19923 +++ exited with 1 +++ + * Waiting for ECHILD works better. + * (However, if -o|logger is in use, we can't do that. + * Can work around that by double-forking the logger, + * but that loses the ability to wait for its completion on exit. + * Oh well...) + */ + while (1) { int pid; int wait_errno; - int status, sig; + int status; int stopped; - struct tcb *tcp; + unsigned int sig; unsigned event; + struct tcb *tcp; if (interrupted) return; + if (popen_pid != 0 && nprocs == 0) + return; + if (interactive) sigprocmask(SIG_SETMASK, &empty_set, NULL); pid = wait4(-1, &status, __WALL, (cflag ? &ru : NULL)); @@ -1985,9 +2073,11 @@ trace(void) if (pid < 0) { if (wait_errno == EINTR) continue; - if (wait_errno == ECHILD) - /* Should not happen since nprocs > 0 */ + if (nprocs == 0 && wait_errno == ECHILD) return; + /* If nprocs > 0, ECHILD is not expected, + * treat it as any other error here: + */ errno = wait_errno; perror_msg_and_die("wait4(__WALL)"); } @@ -2098,10 +2188,10 @@ trace(void) if (ptrace(PTRACE_GETEVENTMSG, pid, NULL, (long) &old_pid) < 0) goto dont_switch_tcbs; /* Avoid truncation in pid2tcb() param passing */ - if (old_pid > UINT_MAX) - goto dont_switch_tcbs; if (old_pid <= 0 || old_pid == pid) goto dont_switch_tcbs; + if ((unsigned long) old_pid > UINT_MAX) + goto dont_switch_tcbs; execve_thread = pid2tcb(old_pid); /* It should be !NULL, but I feel paranoid */ if (!execve_thread) @@ -2205,9 +2295,9 @@ trace(void) return; } } - if (ptrace_setoptions) { + if (!use_seize && ptrace_setoptions) { if (debug_flag) - fprintf(stderr, "setting opts %x on pid %d\n", ptrace_setoptions, tcp->pid); + fprintf(stderr, "setting opts 0x%x on pid %d\n", ptrace_setoptions, tcp->pid); if (ptrace(PTRACE_SETOPTIONS, tcp->pid, NULL, ptrace_setoptions) < 0) { if (errno != ESRCH) { /* Should never happen, really */ @@ -2331,7 +2421,7 @@ trace(void) exit_code = 1; return; } - } /* while (nprocs != 0) */ + } /* while (1) */ } int