]> granicus.if.org Git - strace/blobdiff - strace.c
Always compile sys_prctl parser
[strace] / strace.c
index 42cfcb99c546d290a83474ae01dc79b21b9ae48f..aae7505f4e47c4d3f71b7030feb2f6bdb5fcf33e 100644 (file)
--- 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;
 
@@ -1981,10 +2051,11 @@ trace(void)
        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;
@@ -2117,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)
@@ -2224,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 */