X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=strace.c;h=9c633887265228a6c6c0f00783dba7b5c99e2367;hb=5940e6593911dcace424c668a1c0934c71fccb9e;hp=d83cbf63cfaf233b565efdee0755a7114c6b90b5;hpb=35aba6a8dd87a8d1fd69110d80f98224d1f5b18d;p=strace diff --git a/strace.c b/strace.c index d83cbf63..9c633887 100644 --- a/strace.c +++ b/strace.c @@ -44,21 +44,18 @@ #include #include #include -#include #include #ifdef LINUX # include -# if defined __NR_tgkill -# define my_tgkill(pid, tid, sig) syscall (__NR_tgkill, (pid), (tid), (sig)) -# elif defined __NR_tkill -# define my_tgkill(pid, tid, sig) syscall (__NR_tkill, (tid), (sig)) +# if defined __NR_tkill +# define my_tkill(tid, sig) syscall(__NR_tkill, (tid), (sig)) # else /* kill() may choose arbitrarily the target task of the process group while we later wait on a that specific TID. PID process waits become TID task specific waits for a process under ptrace(2). */ # warning "Neither tkill(2) nor tgkill(2) available, risk of strace hangs!" -# define my_tgkill(pid, tid, sig) kill ((tid), (sig)) +# define my_tkill(tid, sig) kill((tid), (sig)) # endif #endif @@ -84,10 +81,9 @@ extern char *optarg; int debug = 0, followfork = 0; -unsigned int ptrace_setoptions_followfork = 0; -unsigned int ptrace_setoptions_for_all = 0; +unsigned int ptrace_setoptions = 0; /* Which WSTOPSIG(status) value marks syscall traps? */ -static unsigned int SYSCALLTRAP = SIGTRAP; +static unsigned int syscall_trap_sig = SIGTRAP; int dtime = 0, xflag = 0, qflag = 0; cflag_t cflag = CFLAG_NONE; static int iflag = 0, interactive = 0, pflag_seen = 0, rflag = 0, tflag = 0; @@ -116,20 +112,21 @@ int tracing_paths = 0; static int exit_code = 0; static int strace_child = 0; +static int strace_tracer_pid = 0; static char *username = NULL; -uid_t run_uid; -gid_t run_gid; +static uid_t run_uid; +static gid_t run_gid; -int acolumn = DEFAULT_ACOLUMN; int max_strlen = DEFAULT_STRLEN; +static int acolumn = DEFAULT_ACOLUMN; +static char *acolumn_spaces; static char *outfname = NULL; -FILE *outf; +static FILE *outf; static int curcol; -struct tcb **tcbtab; -unsigned int nprocs, tcbtabsize; -const char *progname; -extern char **environ; +static struct tcb **tcbtab; +static unsigned int nprocs, tcbtabsize; +static const char *progname; static int detach(struct tcb *tcp, int sig); static int trace(void); @@ -174,9 +171,7 @@ static int proc_poll_pipe[2] = { -1, -1 }; #endif /* USE_PROCFS */ static void -usage(ofp, exitval) -FILE *ofp; -int exitval; +usage(FILE *ofp, int exitval) { fprintf(ofp, "\ usage: strace [-CdDffhiqrtttTvVxxy] [-a column] [-e expr] ... [-o file]\n\ @@ -215,29 +210,67 @@ usage: strace [-CdDffhiqrtttTvVxxy] [-a column] [-e expr] ... [-o file]\n\ exit(exitval); } -static void error_msg_and_die(const char *fmt, ...) -#if defined __GNUC__ - __attribute__ ((noreturn, format(printf, 1, 2))) -#endif -; -static void error_msg_and_die(const char *fmt, ...) +static void die(void) __attribute__ ((noreturn)); +static void die(void) +{ + if (strace_tracer_pid == getpid()) { + cflag = 0; + cleanup(); + } + exit(1); +} + +static void verror_msg(int err_no, const char *fmt, va_list p) +{ + fflush(NULL); + fprintf(stderr, "%s: ", progname); + vfprintf(stderr, fmt, p); + if (err_no) + fprintf(stderr, ": %s\n", strerror(err_no)); + else + putc('\n', stderr); + fflush(stderr); +} + +void error_msg(const char *fmt, ...) { - char *msg; va_list p; + va_start(p, fmt); + verror_msg(0, fmt, p); + va_end(p); +} +void error_msg_and_die(const char *fmt, ...) +{ + va_list p; va_start(p, fmt); - msg = NULL; - vasprintf(&msg, fmt, p); - if (msg) { - fprintf(stderr, "%s: %s\n", progname, msg); - free(msg); - } + verror_msg(0, fmt, p); + die(); +} + +void perror_msg(const char *fmt, ...) +{ + va_list p; + va_start(p, fmt); + verror_msg(errno, fmt, p); va_end(p); +} - /* TODO? cflag = 0; -- or else cleanup() may print summary */ - cleanup(); - fflush(NULL); - _exit(1); +void perror_msg_and_die(const char *fmt, ...) +{ + va_list p; + va_start(p, fmt); + verror_msg(errno, fmt, p); + die(); +} + +void die_out_of_memory(void) +{ + static bool recursed = 0; + if (recursed) + exit(1); + recursed = 1; + error_msg_and_die("Out of memory"); } #ifdef SVR4 @@ -257,30 +290,26 @@ foobar() # define fork() vfork() #endif -static int +static void set_cloexec_flag(int fd) { - int flags, newflags; - - if ((flags = fcntl(fd, F_GETFD, 0)) < 0) - { - fprintf(stderr, "%s: fcntl F_GETFD: %s\n", - progname, strerror(errno)); - return -1; + int flags, newflags; + + flags = fcntl(fd, F_GETFD); + if (flags < 0) { + /* Can happen only if fd is bad. + * Should never happen: if it does, we have a bug + * in the caller. Therefore we just abort + * instead of propagating the error. + */ + perror_msg_and_die("fcntl(%d, F_GETFD)", fd); } newflags = flags | FD_CLOEXEC; if (flags == newflags) - return 0; - - if (fcntl(fd, F_SETFD, newflags) < 0) - { - fprintf(stderr, "%s: fcntl F_SETFD: %s\n", - progname, strerror(errno)); - return -1; - } + return; - return 0; + fcntl(fd, F_SETFD, newflags); /* never fails */ } /* @@ -293,11 +322,8 @@ swap_uid(void) #ifndef SVR4 int euid = geteuid(), uid = getuid(); - if (euid != uid && setreuid(euid, uid) < 0) - { - fprintf(stderr, "%s: setreuid: %s\n", - progname, strerror(errno)); - exit(1); + if (euid != uid && setreuid(euid, uid) < 0) { + perror_msg_and_die("setreuid"); } #endif } @@ -309,24 +335,20 @@ swap_uid(void) #endif static FILE * -strace_fopen(const char *path, const char *mode) +strace_fopen(const char *path) { FILE *fp; swap_uid(); - if ((fp = fopen_for_output(path, mode)) == NULL) - fprintf(stderr, "%s: can't fopen '%s': %s\n", - progname, path, strerror(errno)); + fp = fopen_for_output(path, "w"); + if (!fp) + perror_msg_and_die("Can't fopen '%s'", path); swap_uid(); - if (fp && set_cloexec_flag(fileno(fp)) < 0) - { - fclose(fp); - fp = NULL; - } + set_cloexec_flag(fileno(fp)); return fp; } -static int popen_pid = -1; +static int popen_pid = 0; #ifndef _PATH_BSHELL # define _PATH_BSHELL "/bin/sh" @@ -340,71 +362,48 @@ static int popen_pid = -1; static FILE * strace_popen(const char *command) { - int fds[2]; + FILE *fp; + int fds[2]; swap_uid(); if (pipe(fds) < 0) - { - fprintf(stderr, "%s: pipe: %s\n", - progname, strerror(errno)); - swap_uid(); - return NULL; - } + perror_msg_and_die("pipe"); - if (set_cloexec_flag(fds[1]) < 0) - { - close(fds[0]); - close(fds[1]); - swap_uid(); - return NULL; - } + set_cloexec_flag(fds[1]); /* never fails */ - if ((popen_pid = fork()) == -1) - { - fprintf(stderr, "%s: fork: %s\n", - progname, strerror(errno)); - close(fds[0]); - close(fds[1]); - swap_uid(); - return NULL; - } + popen_pid = vfork(); + if (popen_pid == -1) + perror_msg_and_die("vfork"); - if (popen_pid) - { - /* parent */ - close(fds[0]); - swap_uid(); - return fdopen(fds[1], "w"); - } else - { + if (popen_pid == 0) { /* child */ close(fds[1]); - if (fds[0] && (dup2(fds[0], 0) || close(fds[0]))) - { - fprintf(stderr, "%s: dup2: %s\n", - progname, strerror(errno)); - _exit(1); + if (fds[0] != 0) { + if (dup2(fds[0], 0)) + perror_msg_and_die("dup2"); + close(fds[0]); } execl(_PATH_BSHELL, "sh", "-c", command, NULL); - fprintf(stderr, "%s: execl: %s: %s\n", - progname, _PATH_BSHELL, strerror(errno)); - _exit(1); + perror_msg_and_die("Can't execute '%s'", _PATH_BSHELL); } + + /* parent */ + close(fds[0]); + swap_uid(); + fp = fdopen(fds[1], "w"); + if (!fp) + die_out_of_memory(); + return fp; } -static int +static void newoutf(struct tcb *tcp) { if (outfname && followfork > 1) { char name[520 + sizeof(int) * 3]; - FILE *fp; - sprintf(name, "%.512s.%u", outfname, tcp->pid); - if ((fp = strace_fopen(name, "w")) == NULL) - return -1; - tcp->outf = fp; + tcp->outf = strace_fopen(name); } - return 0; } static void @@ -417,7 +416,7 @@ startup_attach(void) * Block user interruptions as we would leave the traced * process stopped (process state T) if we would terminate in * between PTRACE_ATTACH and wait4 () on SIGSTOP. - * We rely on cleanup () from this point on. + * We rely on cleanup() from this point on. */ if (interactive) sigprocmask(SIG_BLOCK, &blocked_set, NULL); @@ -429,28 +428,31 @@ startup_attach(void) } if (pid) { /* parent */ /* - * Wait for child to attach to straced process - * (our parent). Child SIGKILLs us after it attached. - * Parent's wait() is unblocked by our death, + * Wait for grandchild to attach to straced process + * (grandparent). Grandchild SIGKILLs us after it attached. + * Grandparent's wait() is unblocked by our death, * it proceeds to exec the straced program. */ pause(); _exit(0); /* paranoia */ } + /* grandchild */ + /* We will be the tracer process. Remember our new pid: */ + strace_tracer_pid = getpid(); } for (tcbi = 0; tcbi < tcbtabsize; tcbi++) { tcp = tcbtab[tcbi]; + if (!(tcp->flags & TCB_INUSE) || !(tcp->flags & TCB_ATTACHED)) continue; #ifdef LINUX - if (tcp->flags & TCB_CLONE_THREAD) + if (tcp->flags & TCB_ATTACH_DONE) continue; #endif /* Reinitialize the output since it may have changed. */ tcp->outf = outf; - if (newoutf(tcp) < 0) - exit(1); + newoutf(tcp); #ifdef USE_PROCFS if (proc_open(tcp, 1) < 0) { @@ -477,19 +479,23 @@ startup_attach(void) if (tid <= 0) continue; ++ntid; - if (ptrace(PTRACE_ATTACH, tid, (char *) 1, 0) < 0) + if (ptrace(PTRACE_ATTACH, tid, (char *) 1, 0) < 0) { ++nerr; - else if (tid != tcbtab[tcbi]->pid) { - tcp = alloctcb(tid); - tcp->flags |= TCB_ATTACHED|TCB_CLONE_THREAD|TCB_FOLLOWFORK; - tcbtab[tcbi]->nchildren++; - tcbtab[tcbi]->nclone_threads++; - tcp->parent = tcbtab[tcbi]; + if (debug) + fprintf(stderr, "attach to pid %d failed\n", tid); + } + else { + if (debug) + fprintf(stderr, "attach to pid %d succeeded\n", tid); + if (tid != tcp->pid) { + struct tcb *new_tcp = alloctcb(tid); + new_tcp->flags |= TCB_ATTACHED|TCB_ATTACH_DONE; + } } if (interactive) { sigprocmask(SIG_SETMASK, &empty_set, NULL); if (interrupted) - return; + goto ret; sigprocmask(SIG_BLOCK, &blocked_set, NULL); } } @@ -504,18 +510,19 @@ startup_attach(void) fprintf(stderr, ntid > 1 ? "Process %u attached with %u threads - interrupt to quit\n" : "Process %u attached - interrupt to quit\n", - tcbtab[tcbi]->pid, ntid); + tcp->pid, ntid); } continue; } /* if (opendir worked) */ } /* if (-f) */ -# endif +# endif /* LINUX */ if (ptrace(PTRACE_ATTACH, tcp->pid, (char *) 1, 0) < 0) { perror("attach: ptrace(PTRACE_ATTACH, ...)"); droptcb(tcp); continue; } - /* INTERRUPTED is going to be checked at the top of TRACE. */ + if (debug) + fprintf(stderr, "attach to pid %d (main) succeeded\n", tcp->pid); if (daemonized_tracer) { /* @@ -535,14 +542,23 @@ startup_attach(void) fprintf(stderr, "Process %u attached - interrupt to quit\n", tcp->pid); + } /* for each tcbtab[] */ + + ret: +#ifdef LINUX + /* TCB_ATTACH_DONE flag is used only in this function */ + for (tcbi = 0; tcbi < tcbtabsize; tcbi++) { + tcp = tcbtab[tcbi]; + tcp->flags &= ~TCB_ATTACH_DONE; } +#endif if (interactive) sigprocmask(SIG_SETMASK, &empty_set, NULL); } static void -startup_child (char **argv) +startup_child(char **argv) { struct stat statbuf; const char *filename; @@ -554,8 +570,7 @@ startup_child (char **argv) if (strchr(filename, '/')) { if (strlen(filename) > sizeof pathname - 1) { errno = ENAMETOOLONG; - perror("strace: exec"); - exit(1); + perror_msg_and_die("exec"); } strcpy(pathname, filename); } @@ -603,22 +618,18 @@ startup_child (char **argv) } } if (stat(pathname, &statbuf) < 0) { - fprintf(stderr, "%s: %s: command not found\n", - progname, filename); - exit(1); + perror_msg_and_die("Can't stat '%s'", filename); } strace_child = pid = fork(); if (pid < 0) { - perror("strace: fork"); - cleanup(); - exit(1); + perror_msg_and_die("fork"); } - if ((pid != 0 && daemonized_tracer) /* parent: to become a traced process */ - || (pid == 0 && !daemonized_tracer) /* child: to become a traced process */ + if ((pid != 0 && daemonized_tracer) /* -D: parent to become a traced process */ + || (pid == 0 && !daemonized_tracer) /* not -D: child to become a traced process */ ) { pid = getpid(); #ifdef USE_PROCFS - if (outf != stderr) close (fileno (outf)); + if (outf != stderr) close(fileno(outf)); #ifdef MIPS /* Kludge for SGI, see proc_open for details. */ sa.sa_handler = foobar; @@ -632,13 +643,12 @@ startup_child (char **argv) kill(pid, SIGSTOP); /* stop HERE */ #endif /* FREEBSD */ #else /* !USE_PROCFS */ - if (outf!=stderr) - close(fileno (outf)); + if (outf != stderr) + close(fileno(outf)); if (!daemonized_tracer) { if (ptrace(PTRACE_TRACEME, 0, (char *) 1, 0) < 0) { - perror("strace: ptrace(PTRACE_TRACEME, ...)"); - exit(1); + perror_msg_and_die("ptrace(PTRACE_TRACEME, ...)"); } if (debug) kill(pid, SIGSTOP); @@ -659,16 +669,13 @@ startup_child (char **argv) */ if (username != NULL) { if (initgroups(username, run_gid) < 0) { - perror("initgroups"); - exit(1); + perror_msg_and_die("initgroups"); } if (setregid(run_gid, run_egid) < 0) { - perror("setregid"); - exit(1); + perror_msg_and_die("setregid"); } if (setreuid(run_uid, run_euid) < 0) { - perror("setreuid"); - exit(1); + perror_msg_and_die("setreuid"); } } } @@ -705,11 +712,13 @@ startup_child (char **argv) #endif /* !USE_PROCFS */ execv(pathname, argv); - perror("strace: exec"); - _exit(1); + perror_msg_and_die("exec"); } /* We are the tracer. */ + /* With -D, we are *child* here, IOW: different pid. Fetch it. */ + strace_tracer_pid = getpid(); + tcp = alloctcb(daemonized_tracer ? getppid() : pid); if (daemonized_tracer) { /* We want subsequent startup_attach() to attach to it. */ @@ -717,20 +726,26 @@ startup_child (char **argv) } #ifdef USE_PROCFS if (proc_open(tcp, 0) < 0) { - fprintf(stderr, "trouble opening proc file\n"); - cleanup(); - exit(1); + perror_msg_and_die("trouble opening proc file"); } #endif /* USE_PROCFS */ } #ifdef LINUX +static void kill_save_errno(pid_t pid, int sig) +{ + int saved_errno = errno; + + (void) kill(pid, sig); + errno = saved_errno; +} + /* * Test whether the kernel support PTRACE_O_TRACECLONE et al options. * First fork a new child, call ptrace with PTRACE_SETOPTIONS on it, * and then see which options are supported by the kernel. */ -static int +static void test_ptrace_setoptions_followfork(void) { int pid, expected_grandchild = 0, found_grandchild = 0; @@ -738,60 +753,95 @@ test_ptrace_setoptions_followfork(void) PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK; - if ((pid = fork()) < 0) - return -1; - else if (pid == 0) { - if (ptrace(PTRACE_TRACEME, 0, (char *)1, 0) < 0) - _exit(1); - kill(getpid(), SIGSTOP); - _exit(fork() < 0); + pid = fork(); + if (pid < 0) + perror_msg_and_die("fork"); + if (pid == 0) { + pid = getpid(); + if (ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) + perror_msg_and_die("%s: PTRACE_TRACEME doesn't work", + __func__); + kill(pid, SIGSTOP); + if (fork() < 0) + perror_msg_and_die("fork"); + _exit(0); } while (1) { int status, tracee_pid; + errno = 0; tracee_pid = wait(&status); - if (tracee_pid == -1) { + if (tracee_pid <= 0) { if (errno == EINTR) continue; else if (errno == ECHILD) break; - perror("test_ptrace_setoptions_followfork"); - return -1; + kill_save_errno(pid, SIGKILL); + perror_msg_and_die("%s: unexpected wait result %d", + __func__, tracee_pid); + } + if (WIFEXITED(status)) { + if (WEXITSTATUS(status)) { + if (tracee_pid != pid) + kill_save_errno(pid, SIGKILL); + error_msg_and_die("%s: unexpected exit status %u", + __func__, WEXITSTATUS(status)); + } + continue; + } + if (WIFSIGNALED(status)) { + if (tracee_pid != pid) + kill_save_errno(pid, SIGKILL); + error_msg_and_die("%s: unexpected signal %u", + __func__, WTERMSIG(status)); + } + if (!WIFSTOPPED(status)) { + if (tracee_pid != pid) + kill_save_errno(tracee_pid, SIGKILL); + kill(pid, SIGKILL); + error_msg_and_die("%s: unexpected wait status %x", + __func__, status); } if (tracee_pid != pid) { found_grandchild = tracee_pid; - if (ptrace(PTRACE_CONT, tracee_pid, 0, 0) < 0 && - errno != ESRCH) - kill(tracee_pid, SIGKILL); - } - else if (WIFSTOPPED(status)) { - switch (WSTOPSIG(status)) { - case SIGSTOP: - if (ptrace(PTRACE_SETOPTIONS, pid, - NULL, test_options) < 0) { - kill(pid, SIGKILL); - return -1; - } - break; - case SIGTRAP: - if (status >> 16 == PTRACE_EVENT_FORK) { - long msg = 0; + if (ptrace(PTRACE_CONT, tracee_pid, 0, 0) < 0) { + kill_save_errno(tracee_pid, SIGKILL); + kill_save_errno(pid, SIGKILL); + perror_msg_and_die("PTRACE_CONT doesn't work"); + } + continue; + } + switch (WSTOPSIG(status)) { + case SIGSTOP: + if (ptrace(PTRACE_SETOPTIONS, pid, 0, test_options) < 0 + && errno != EINVAL && errno != EIO) + perror_msg("PTRACE_SETOPTIONS"); + break; + case SIGTRAP: + if (status >> 16 == PTRACE_EVENT_FORK) { + long msg = 0; - if (ptrace(PTRACE_GETEVENTMSG, pid, - NULL, (long) &msg) == 0) - expected_grandchild = msg; - } - break; + if (ptrace(PTRACE_GETEVENTMSG, pid, + NULL, (long) &msg) == 0) + expected_grandchild = msg; } - if (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0 && - errno != ESRCH) - kill(pid, SIGKILL); + break; + } + if (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) { + kill_save_errno(pid, SIGKILL); + perror_msg_and_die("PTRACE_SYSCALL doesn't work"); } } - if (expected_grandchild && expected_grandchild == found_grandchild) - ptrace_setoptions_followfork |= test_options; - return 0; + if (expected_grandchild && expected_grandchild == found_grandchild) { + ptrace_setoptions |= test_options; + if (debug) + fprintf(stderr, "ptrace_setoptions = %#x\n", + ptrace_setoptions); + return; + } + error_msg("Test for PTRACE_O_TRACECLONE failed, " + "giving up using this feature."); } /* @@ -811,19 +861,21 @@ test_ptrace_setoptions_followfork(void) static void test_ptrace_setoptions_for_all(void) { - const unsigned int test_options = PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXEC; + const unsigned int test_options = PTRACE_O_TRACESYSGOOD | + PTRACE_O_TRACEEXEC; int pid; int it_worked = 0; pid = fork(); if (pid < 0) - error_msg_and_die("fork failed"); + perror_msg_and_die("fork"); if (pid == 0) { pid = getpid(); if (ptrace(PTRACE_TRACEME, 0L, 0L, 0L) < 0) - /* "parent, something is deeply wrong!" */ - kill(pid, SIGKILL); + /* Note: exits with exitcode 1 */ + perror_msg_and_die("%s: PTRACE_TRACEME doesn't work", + __func__); kill(pid, SIGSTOP); _exit(0); /* parent should see entry into this syscall */ } @@ -836,14 +888,24 @@ test_ptrace_setoptions_for_all(void) if (tracee_pid <= 0) { if (errno == EINTR) continue; - kill(pid, SIGKILL); - error_msg_and_die("%s: unexpected wait result %d", __func__, tracee_pid); + kill_save_errno(pid, SIGKILL); + perror_msg_and_die("%s: unexpected wait result %d", + __func__, tracee_pid); + } + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) == 0) + break; + error_msg_and_die("%s: unexpected exit status %u", + __func__, WEXITSTATUS(status)); + } + if (WIFSIGNALED(status)) { + error_msg_and_die("%s: unexpected signal %u", + __func__, WTERMSIG(status)); } - if (WIFEXITED(status)) - break; if (!WIFSTOPPED(status)) { kill(pid, SIGKILL); - error_msg_and_die("%s: unexpected wait status %x", __func__, status); + error_msg_and_die("%s: unexpected wait status %x", + __func__, status); } if (WSTOPSIG(status) == SIGSTOP) { /* @@ -852,28 +914,30 @@ test_ptrace_setoptions_for_all(void) * and thus will decide to not use the option. * IOW: the outcome of the test will be correct. */ - ptrace(PTRACE_SETOPTIONS, pid, 0L, test_options); + if (ptrace(PTRACE_SETOPTIONS, pid, 0L, test_options) < 0 + && errno != EINVAL && errno != EIO) + perror_msg("PTRACE_SETOPTIONS"); } if (WSTOPSIG(status) == (SIGTRAP | 0x80)) { it_worked = 1; } if (ptrace(PTRACE_SYSCALL, pid, 0L, 0L) < 0) { - kill(pid, SIGKILL); - error_msg_and_die("PTRACE_SYSCALL doesn't work"); + kill_save_errno(pid, SIGKILL); + perror_msg_and_die("PTRACE_SYSCALL doesn't work"); } } if (it_worked) { - SYSCALLTRAP = (SIGTRAP | 0x80); - ptrace_setoptions_for_all = test_options; + syscall_trap_sig = (SIGTRAP | 0x80); + ptrace_setoptions |= test_options; if (debug) - fprintf(stderr, "ptrace_setoptions_for_all = %#x\n", - ptrace_setoptions_for_all); + fprintf(stderr, "ptrace_setoptions = %#x\n", + ptrace_setoptions); return; } - fprintf(stderr, - "Test for PTRACE_O_TRACESYSGOOD failed, giving up using this feature.\n"); + error_msg("Test for PTRACE_O_TRACESYSGOOD failed, " + "giving up using this feature."); } #endif @@ -885,22 +949,20 @@ main(int argc, char *argv[]) int optF = 0; struct sigaction sa; - static char buf[BUFSIZ]; - progname = argv[0] ? argv[0] : "strace"; + strace_tracer_pid = getpid(); + /* Allocate the initial tcbtab. */ tcbtabsize = argc; /* Surely enough for all -p args. */ - if ((tcbtab = calloc(tcbtabsize, sizeof tcbtab[0])) == NULL) { - fprintf(stderr, "%s: out of memory\n", progname); - exit(1); - } - if ((tcbtab[0] = calloc(tcbtabsize, sizeof tcbtab[0][0])) == NULL) { - fprintf(stderr, "%s: out of memory\n", progname); - exit(1); - } - for (tcp = tcbtab[0]; tcp < &tcbtab[0][tcbtabsize]; ++tcp) - tcbtab[tcp - tcbtab[0]] = &tcbtab[0][tcp - tcbtab[0]]; + tcbtab = calloc(tcbtabsize, sizeof(tcbtab[0])); + if (!tcbtab) + die_out_of_memory(); + tcp = calloc(tcbtabsize, sizeof(*tcp)); + if (!tcp) + die_out_of_memory(); + for (c = 0; c < tcbtabsize; c++) + tcbtab[c] = tcp++; outf = stderr; interactive = 1; @@ -919,17 +981,13 @@ main(int argc, char *argv[]) switch (c) { case 'c': if (cflag == CFLAG_BOTH) { - fprintf(stderr, "%s: -c and -C are mutually exclusive options\n", - progname); - exit(1); + error_msg_and_die("-c and -C are mutually exclusive options"); } cflag = CFLAG_ONLY_STATS; break; case 'C': if (cflag == CFLAG_ONLY_STATS) { - fprintf(stderr, "%s: -c and -C are mutually exclusive options\n", - progname); - exit(1); + error_msg_and_die("-c and -C are mutually exclusive options"); } cflag = CFLAG_BOTH; break; @@ -984,6 +1042,8 @@ main(int argc, char *argv[]) break; case 'a': acolumn = atoi(optarg); + if (acolumn < 0) + error_msg_and_die("Bad column width '%s'", optarg); break; case 'e': qualify(optarg); @@ -995,13 +1055,13 @@ main(int argc, char *argv[]) set_overhead(atoi(optarg)); break; case 'p': - if ((pid = atoi(optarg)) <= 0) { - fprintf(stderr, "%s: Invalid process id: %s\n", - progname, optarg); + pid = atoi(optarg); + if (pid <= 0) { + error_msg("Invalid process id: '%s'", optarg); break; } - if (pid == getpid()) { - fprintf(stderr, "%s: I'm sorry, I can't let you do that, Dave.\n", progname); + if (pid == strace_tracer_pid) { + error_msg("I'm sorry, I can't let you do that, Dave."); break; } tcp = alloc_tcb(pid, 0); @@ -1011,17 +1071,13 @@ main(int argc, char *argv[]) case 'P': tracing_paths = 1; if (pathtrace_select(optarg)) { - fprintf(stderr,"%s : failed to select path '%s'\n", progname, optarg); - exit(1); + error_msg_and_die("Failed to select path '%s'", optarg); } break; case 's': max_strlen = atoi(optarg); if (max_strlen < 0) { - fprintf(stderr, - "%s: invalid -s argument: %s\n", - progname, optarg); - exit(1); + error_msg_and_die("Invalid -s argument: '%s'", optarg); } break; case 'S': @@ -1031,11 +1087,8 @@ main(int argc, char *argv[]) username = strdup(optarg); break; case 'E': - if (putenv(optarg) < 0) { - fprintf(stderr, "%s: out of memory\n", - progname); - exit(1); - } + if (putenv(optarg) < 0) + die_out_of_memory(); break; default: usage(stderr, 1); @@ -1043,24 +1096,24 @@ main(int argc, char *argv[]) } } + acolumn_spaces = malloc(acolumn + 1); + if (!acolumn_spaces) + die_out_of_memory(); + memset(acolumn_spaces, ' ', acolumn); + acolumn_spaces[acolumn] = '\0'; + if ((optind == argc) == !pflag_seen) usage(stderr, 1); if (pflag_seen && daemonized_tracer) { - fprintf(stderr, - "%s: -D and -p are mutually exclusive options\n", - progname); - exit(1); + error_msg_and_die("-D and -p are mutually exclusive options"); } if (!followfork) followfork = optF; if (followfork > 1 && cflag) { - fprintf(stderr, - "%s: (-c or -C) and -ff are mutually exclusive options\n", - progname); - exit(1); + error_msg_and_die("(-c or -C) and -ff are mutually exclusive options"); } /* See if they want to run as another user. */ @@ -1068,15 +1121,11 @@ main(int argc, char *argv[]) struct passwd *pent; if (getuid() != 0 || geteuid() != 0) { - fprintf(stderr, - "%s: you must be root to use the -u option\n", - progname); - exit(1); + error_msg_and_die("You must be root to use the -u option"); } - if ((pent = getpwnam(username)) == NULL) { - fprintf(stderr, "%s: cannot find user `%s'\n", - progname, username); - exit(1); + pent = getpwnam(username); + if (pent == NULL) { + error_msg_and_die("Cannot find user '%s'", username); } run_uid = pent->pw_uid; run_gid = pent->pw_gid; @@ -1087,17 +1136,8 @@ main(int argc, char *argv[]) } #ifdef LINUX - if (followfork) { - if (test_ptrace_setoptions_followfork() < 0) { - fprintf(stderr, - "Test for options supported by PTRACE_SETOPTIONS " - "failed, giving up using this feature.\n"); - ptrace_setoptions_followfork = 0; - } - if (debug) - fprintf(stderr, "ptrace_setoptions_followfork = %#x\n", - ptrace_setoptions_followfork); - } + if (followfork) + test_ptrace_setoptions_followfork(); test_ptrace_setoptions_for_all(); #endif @@ -1109,23 +1149,18 @@ main(int argc, char *argv[]) * We can't do the .PID funny business * when using popen, so prohibit it. */ - if (followfork > 1) { - fprintf(stderr, "\ -%s: piping the output and -ff are mutually exclusive options\n", - progname); - exit(1); - } - - if ((outf = strace_popen(outfname + 1)) == NULL) - exit(1); + if (followfork > 1) + error_msg_and_die("Piping the output and -ff are mutually exclusive"); + outf = strace_popen(outfname + 1); } - else if (followfork <= 1 && - (outf = strace_fopen(outfname, "w")) == NULL) - exit(1); + else if (followfork <= 1) + outf = strace_fopen(outfname); } - if (!outfname || outfname[0] == '|' || outfname[0] == '!') + if (!outfname || outfname[0] == '|' || outfname[0] == '!') { + static char buf[BUFSIZ]; setvbuf(outf, buf, _IOLBF, BUFSIZ); + } if (outfname && optind < argc) { interactive = 0; qflag = 1; @@ -1202,7 +1237,7 @@ main(int argc, char *argv[]) exit(exit_code); } -void +static void expand_tcbtab(void) { /* Allocate some more TCBs and expand the table. @@ -1210,21 +1245,15 @@ 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. */ - struct tcb **newtab = (struct tcb **) - realloc(tcbtab, 2 * tcbtabsize * sizeof tcbtab[0]); - struct tcb *newtcbs = (struct tcb *) calloc(tcbtabsize, - sizeof *newtcbs); - int i; - if (newtab == NULL || newtcbs == NULL) { - fprintf(stderr, "%s: expand_tcbtab: out of memory\n", - progname); - cleanup(); - exit(1); - } - for (i = tcbtabsize; i < 2 * tcbtabsize; ++i) - newtab[i] = &newtcbs[i - tcbtabsize]; + int i = tcbtabsize; + struct tcb *newtcbs = calloc(tcbtabsize, sizeof(newtcbs[0])); + struct tcb **newtab = realloc(tcbtab, tcbtabsize * 2 * sizeof(tcbtab[0])); + if (!newtab || !newtcbs) + die_out_of_memory(); tcbtabsize *= 2; tcbtab = newtab; + while (i < tcbtabsize) + tcbtab[i++] = newtcbs++; } struct tcb * @@ -1239,29 +1268,22 @@ alloc_tcb(int pid, int command_options_parsed) for (i = 0; i < tcbtabsize; i++) { tcp = tcbtab[i]; if ((tcp->flags & TCB_INUSE) == 0) { + memset(tcp, 0, sizeof(*tcp)); tcp->pid = pid; - tcp->parent = NULL; - tcp->nchildren = 0; - tcp->nzombies = 0; -#ifdef TCB_CLONE_THREAD - tcp->nclone_threads = 0; - tcp->nclone_waiting = 0; -#endif tcp->flags = TCB_INUSE | TCB_STARTUP; tcp->outf = outf; /* Initialise to current out file */ - tcp->curcol = 0; - tcp->stime.tv_sec = 0; - tcp->stime.tv_usec = 0; +#ifdef USE_PROCFS tcp->pfd = -1; +#endif nprocs++; + if (debug) + fprintf(stderr, "new tcb for pid %d, active tcbs:%d\n", tcp->pid, nprocs); if (command_options_parsed) newoutf(tcp); return tcp; } } - fprintf(stderr, "%s: bug in alloc_tcb\n", progname); - cleanup(); - exit(1); + error_msg_and_die("bug in alloc_tcb"); } #ifdef USE_PROCFS @@ -1283,55 +1305,52 @@ proc_open(struct tcb *tcp, int attaching) #ifdef HAVE_MP_PROCFS /* Open the process pseudo-files in /proc. */ sprintf(proc, "/proc/%d/ctl", tcp->pid); - if ((tcp->pfd = open(proc, O_WRONLY|O_EXCL)) < 0) { + tcp->pfd = open(proc, O_WRONLY|O_EXCL); + if (tcp->pfd < 0) { perror("strace: open(\"/proc/...\", ...)"); return -1; } - if (set_cloexec_flag(tcp->pfd) < 0) { - return -1; - } + set_cloexec_flag(tcp->pfd); sprintf(proc, "/proc/%d/status", tcp->pid); - if ((tcp->pfd_stat = open(proc, O_RDONLY|O_EXCL)) < 0) { + tcp->pfd_stat = open(proc, O_RDONLY|O_EXCL); + if (tcp->pfd_stat < 0) { perror("strace: open(\"/proc/...\", ...)"); return -1; } - if (set_cloexec_flag(tcp->pfd_stat) < 0) { - return -1; - } + set_cloexec_flag(tcp->pfd_stat); sprintf(proc, "/proc/%d/as", tcp->pid); - if ((tcp->pfd_as = open(proc, O_RDONLY|O_EXCL)) < 0) { + tcp->pfd_as = open(proc, O_RDONLY|O_EXCL); + if (tcp->pfd_as < 0) { perror("strace: open(\"/proc/...\", ...)"); return -1; } - if (set_cloexec_flag(tcp->pfd_as) < 0) { - return -1; - } + set_cloexec_flag(tcp->pfd_as); #else /* Open the process pseudo-file in /proc. */ -#ifndef FREEBSD +# ifndef FREEBSD sprintf(proc, "/proc/%d", tcp->pid); tcp->pfd = open(proc, O_RDWR|O_EXCL); -#else /* FREEBSD */ +# else sprintf(proc, "/proc/%d/mem", tcp->pid); tcp->pfd = open(proc, O_RDWR); -#endif /* FREEBSD */ +# endif if (tcp->pfd < 0) { perror("strace: open(\"/proc/...\", ...)"); return -1; } - if (set_cloexec_flag(tcp->pfd) < 0) { - return -1; - } + set_cloexec_flag(tcp->pfd); #endif #ifdef FREEBSD sprintf(proc, "/proc/%d/regs", tcp->pid); - if ((tcp->pfd_reg = open(proc, O_RDONLY)) < 0) { + tcp->pfd_reg = open(proc, O_RDONLY); + if (tcp->pfd_reg < 0) { perror("strace: open(\"/proc/.../regs\", ...)"); return -1; } if (cflag) { sprintf(proc, "/proc/%d/status", tcp->pid); - if ((tcp->pfd_status = open(proc, O_RDONLY)) < 0) { + tcp->pfd_status = open(proc, O_RDONLY); + if (tcp->pfd_status < 0) { perror("strace: open(\"/proc/.../status\", ...)"); return -1; } @@ -1345,12 +1364,12 @@ proc_open(struct tcb *tcp, int attaching) * condition we have to poll for the event. */ for (;;) { - if (IOCTL_STATUS (tcp) < 0) { + if (IOCTL_STATUS(tcp) < 0) { perror("strace: PIOCSTATUS"); return -1; } if (tcp->status.PR_FLAGS & PR_ASLEEP) - break; + break; } } #ifndef FREEBSD @@ -1401,22 +1420,22 @@ proc_open(struct tcb *tcp, int attaching) premptyset(&syscalls); for (i = 1; i < MAX_QUALS; ++i) { if (i > (sizeof syscalls) * CHAR_BIT) break; - if (qual_flags [i] & QUAL_TRACE) praddset (&syscalls, i); + if (qual_flags[i] & QUAL_TRACE) praddset(&syscalls, i); } - praddset (&syscalls, SYS_execve); + praddset(&syscalls, SYS_execve); if (followfork) { - praddset (&syscalls, SYS_fork); + praddset(&syscalls, SYS_fork); #ifdef SYS_forkall - praddset (&syscalls, SYS_forkall); + praddset(&syscalls, SYS_forkall); #endif #ifdef SYS_fork1 - praddset (&syscalls, SYS_fork1); + praddset(&syscalls, SYS_fork1); #endif #ifdef SYS_rfork1 - praddset (&syscalls, SYS_rfork1); + praddset(&syscalls, SYS_rfork1); #endif #ifdef SYS_rforkall - praddset (&syscalls, SYS_rforkall); + praddset(&syscalls, SYS_rforkall); #endif } if (IOCTL(tcp->pfd, PIOCSENTRY, &syscalls) < 0) { @@ -1432,7 +1451,7 @@ proc_open(struct tcb *tcp, int attaching) premptyset(&signals); for (i = 1; i < MAX_QUALS; ++i) { if (i > (sizeof signals) * CHAR_BIT) break; - if (qual_flags [i] & QUAL_SIGNAL) praddset (&signals, i); + if (qual_flags[i] & QUAL_SIGNAL) praddset(&signals, i); } if (IOCTL(tcp->pfd, PIOCSTRACE, &signals) < 0) { perror("PIOCSTRACE"); @@ -1442,7 +1461,7 @@ proc_open(struct tcb *tcp, int attaching) premptyset(&faults); for (i = 1; i < MAX_QUALS; ++i) { if (i > (sizeof faults) * CHAR_BIT) break; - if (qual_flags [i] & QUAL_FAULT) praddset (&faults, i); + if (qual_flags[i] & QUAL_FAULT) praddset(&faults, i); } if (IOCTL(tcp->pfd, PIOCSFAULT, &faults) < 0) { perror("PIOCSFAULT"); @@ -1450,8 +1469,8 @@ proc_open(struct tcb *tcp, int attaching) } #else /* FREEBSD */ /* set events flags. */ - arg = S_SIG | S_SCE | S_SCX ; - if(ioctl(tcp->pfd, PIOCBIS, arg) < 0) { + arg = S_SIG | S_SCE | S_SCX; + if (ioctl(tcp->pfd, PIOCBIS, arg) < 0) { perror("PIOCBIS"); return -1; } @@ -1467,7 +1486,7 @@ proc_open(struct tcb *tcp, int attaching) #ifdef PRSABORT /* The child is in a pause(), abort it. */ arg = PRSABORT; - if (IOCTL (tcp->pfd, PIOCRUN, &arg) < 0) { + if (IOCTL(tcp->pfd, PIOCRUN, &arg) < 0) { perror("PIOCRUN"); return -1; } @@ -1479,7 +1498,7 @@ proc_open(struct tcb *tcp, int attaching) #endif for (;;) { /* Wait for the child to do something. */ - if (IOCTL_WSTOP (tcp) < 0) { + if (IOCTL_WSTOP(tcp) < 0) { perror("PIOCWSTOP"); return -1; } @@ -1492,10 +1511,11 @@ proc_open(struct tcb *tcp, int attaching) /* Set it running: maybe execve will be next. */ #ifndef FREEBSD arg = 0; - if (IOCTL(tcp->pfd, PIOCRUN, &arg) < 0) { -#else /* FREEBSD */ - if (IOCTL(tcp->pfd, PIOCRUN, 0) < 0) { -#endif /* FREEBSD */ + if (IOCTL(tcp->pfd, PIOCRUN, &arg) < 0) +#else + if (IOCTL(tcp->pfd, PIOCRUN, 0) < 0) +#endif + { perror("PIOCRUN"); return -1; } @@ -1507,10 +1527,9 @@ proc_open(struct tcb *tcp, int attaching) kill(tcp->pid, SIGCONT); #endif } -#ifndef FREEBSD } -#else /* FREEBSD */ - } else { +#ifdef FREEBSD + else { if (attaching < 2) { /* We are attaching to an already running process. * Try to figure out the state of the process in syscalls, @@ -1586,8 +1605,7 @@ first_used_tcb(void) } static struct tcb * -pfd2tcb(pfd) -int pfd; +pfd2tcb(int pfd) { int i; @@ -1604,49 +1622,20 @@ int pfd; #endif /* USE_PROCFS */ void -droptcb(tcp) -struct tcb *tcp; +droptcb(struct tcb *tcp) { if (tcp->pid == 0) return; -#ifdef TCB_CLONE_THREAD - if (tcp->nclone_threads > 0) { - /* There are other threads left in this process, but this - is the one whose PID represents the whole process. - We need to keep this record around as a zombie until - all the threads die. */ - tcp->flags |= TCB_EXITING; - return; - } -#endif - nprocs--; - tcp->pid = 0; - if (tcp->parent != NULL) { - tcp->parent->nchildren--; -#ifdef TCB_CLONE_THREAD - if (tcp->flags & TCB_CLONE_THREAD) - tcp->parent->nclone_threads--; -#endif - tcp->parent->nzombies++; -#ifdef LINUX - /* Update `tcp->parent->parent->nchildren' and the other fields - like NCLONE_DETACHED, only for zombie group leader that has - already reported and been short-circuited at the top of this - function. The same condition as at the top of DETACH. */ - if ((tcp->flags & TCB_CLONE_THREAD) && - tcp->parent->nclone_threads == 0 && - (tcp->parent->flags & TCB_EXITING)) - droptcb(tcp->parent); -#endif - tcp->parent = NULL; - } + nprocs--; + if (debug) + fprintf(stderr, "dropped tcb for pid %d, %d remain\n", tcp->pid, nprocs); - tcp->flags = 0; +#ifdef USE_PROCFS if (tcp->pfd != -1) { close(tcp->pfd); tcp->pfd = -1; -#ifdef FREEBSD +# ifdef FREEBSD if (tcp->pfd_reg != -1) { close(tcp->pfd_reg); tcp->pfd_reg = -1; @@ -1655,133 +1644,29 @@ struct tcb *tcp; close(tcp->pfd_status); tcp->pfd_status = -1; } -#endif /* !FREEBSD */ -#ifdef USE_PROCFS - rebuild_pollv(); /* Note, flags needs to be cleared by now. */ -#endif +# endif + tcp->flags = 0; /* rebuild_pollv needs it */ + rebuild_pollv(); } +#endif if (outfname && followfork > 1 && tcp->outf) fclose(tcp->outf); - tcp->outf = 0; + memset(tcp, 0, sizeof(*tcp)); } -#ifndef USE_PROCFS - -static int -resume(tcp) -struct tcb *tcp; -{ - if (tcp == NULL) - return -1; - - if (!(tcp->flags & TCB_SUSPENDED)) { - fprintf(stderr, "PANIC: pid %u not suspended\n", tcp->pid); - return -1; - } - tcp->flags &= ~TCB_SUSPENDED; -#ifdef TCB_CLONE_THREAD - if (tcp->flags & TCB_CLONE_THREAD) - tcp->parent->nclone_waiting--; -#endif - - if (ptrace_restart(PTRACE_SYSCALL, tcp, 0) < 0) - return -1; - - if (!qflag) - fprintf(stderr, "Process %u resumed\n", tcp->pid); - return 0; -} - -static int -resume_from_tcp (struct tcb *tcp) -{ - int error = 0; - int resumed = 0; - - /* XXX This won't always be quite right (but it never was). - A waiter with argument 0 or < -1 is waiting for any pid in - a particular pgrp, which this child might or might not be - in. The waiter will only wake up if it's argument is -1 - or if it's waiting for tcp->pid's pgrp. It makes a - difference to wake up a waiter when there might be more - traced children, because it could get a false ECHILD - error. OTOH, if this was the last child in the pgrp, then - it ought to wake up and get ECHILD. We would have to - search the system for all pid's in the pgrp to be sure. - - && (t->waitpid == -1 || - (t->waitpid == 0 && getpgid (tcp->pid) == getpgid (t->pid)) - || (t->waitpid < 0 && t->waitpid == -getpid (t->pid))) - */ - - if (tcp->parent && - (tcp->parent->flags & TCB_SUSPENDED) && - (tcp->parent->waitpid <= 0 || tcp->parent->waitpid == tcp->pid)) { - error = resume(tcp->parent); - ++resumed; - } -#ifdef TCB_CLONE_THREAD - if (tcp->parent && tcp->parent->nclone_waiting > 0) { - /* Some other threads of our parent are waiting too. */ - unsigned int i; - - /* Resume all the threads that were waiting for this PID. */ - for (i = 0; i < tcbtabsize; i++) { - struct tcb *t = tcbtab[i]; - if (t->parent == tcp->parent && t != tcp - && ((t->flags & (TCB_CLONE_THREAD|TCB_SUSPENDED)) - == (TCB_CLONE_THREAD|TCB_SUSPENDED)) - && t->waitpid == tcp->pid) { - error |= resume (t); - ++resumed; - } - } - if (resumed == 0) - /* Noone was waiting for this PID in particular, - so now we might need to resume some wildcarders. */ - for (i = 0; i < tcbtabsize; i++) { - struct tcb *t = tcbtab[i]; - if (t->parent == tcp->parent && t != tcp - && ((t->flags - & (TCB_CLONE_THREAD|TCB_SUSPENDED)) - == (TCB_CLONE_THREAD|TCB_SUSPENDED)) - && t->waitpid <= 0 - ) { - error |= resume (t); - break; - } - } - } -#endif - - return error; -} - -#endif /* !USE_PROCFS */ - /* detach traced process; continue with sig Never call DETACH twice on the same process as both unattached and attached-unstopped processes give the same ESRCH. For unattached process we would SIGSTOP it and wait for its SIGSTOP notification forever. */ static int -detach(tcp, sig) -struct tcb *tcp; -int sig; +detach(struct tcb *tcp, int sig) { int error = 0; #ifdef LINUX int status, catch_sigstop; - struct tcb *zombie = NULL; - - /* If the group leader is lingering only because of this other - thread now dying, then detach the leader as well. */ - if ((tcp->flags & TCB_CLONE_THREAD) && - tcp->parent->nclone_threads == 1 && - (tcp->parent->flags & TCB_EXITING)) - zombie = tcp->parent; #endif if (tcp->flags & TCB_BPTSET) @@ -1803,22 +1688,19 @@ int sig; * detached process would be left stopped (process state T). */ catch_sigstop = (tcp->flags & TCB_STARTUP); - if ((error = ptrace(PTRACE_DETACH, tcp->pid, (char *) 1, sig)) == 0) { + error = ptrace(PTRACE_DETACH, tcp->pid, (char *) 1, sig); + if (error == 0) { /* On a clear day, you can see forever. */ } else if (errno != ESRCH) { /* Shouldn't happen. */ perror("detach: ptrace(PTRACE_DETACH, ...)"); } - else if (my_tgkill((tcp->flags & TCB_CLONE_THREAD ? tcp->parent->pid - : tcp->pid), - tcp->pid, 0) < 0) { + else if (my_tkill(tcp->pid, 0) < 0) { if (errno != ESRCH) perror("detach: checking sanity"); } - else if (!catch_sigstop && my_tgkill((tcp->flags & TCB_CLONE_THREAD - ? tcp->parent->pid : tcp->pid), - tcp->pid, SIGSTOP) < 0) { + else if (!catch_sigstop && my_tkill(tcp->pid, SIGSTOP) < 0) { if (errno != ESRCH) perror("detach: stopping child"); } @@ -1863,7 +1745,7 @@ int sig; break; } error = ptrace_restart(PTRACE_CONT, tcp, - WSTOPSIG(status) == SYSCALLTRAP ? 0 + WSTOPSIG(status) == syscall_trap_sig ? 0 : WSTOPSIG(status)); if (error < 0) break; @@ -1879,22 +1761,11 @@ int sig; error = ptrace_restart(PTRACE_DETACH, tcp, sig); #endif /* SUNOS4 */ -#ifndef USE_PROCFS - error |= resume_from_tcp (tcp); -#endif - if (!qflag) fprintf(stderr, "Process %u detached\n", tcp->pid); droptcb(tcp); -#ifdef LINUX - if (zombie != NULL) { - /* TCP no longer exists therefore you must not detach () it. */ - droptcb(zombie); - } -#endif - return error; } @@ -1912,7 +1783,7 @@ static void reaper(int sig) #endif /* USE_PROCFS */ static void -cleanup() +cleanup(void) { int i; struct tcb *tcp; @@ -1941,8 +1812,7 @@ cleanup() } static void -interrupt(sig) -int sig; +interrupt(int sig) { interrupted = 1; } @@ -1955,8 +1825,7 @@ extern char *sys_errlist[]; #endif /* HAVE_DECL_SYS_ERRLIST */ const char * -strerror(err_no) -int err_no; +strerror(int err_no) { static char buf[64]; @@ -1979,8 +1848,7 @@ extern char *_sys_siglist[]; #endif const char * -strsignal(sig) -int sig; +strsignal(int sig) { static char buf[64]; @@ -2000,17 +1868,14 @@ int sig; #ifdef USE_PROCFS static void -rebuild_pollv() +rebuild_pollv(void) { int i, j; - if (pollv != NULL) - free (pollv); - pollv = (struct pollfd *) malloc(nprocs * sizeof pollv[0]); - if (pollv == NULL) { - fprintf(stderr, "%s: out of memory\n", progname); - exit(1); - } + free(pollv); + pollv = malloc(nprocs * sizeof(pollv[0])); + if (!pollv) + die_out_of_memory(); for (i = j = 0; i < tcbtabsize; i++) { struct tcb *tcp = tcbtab[i]; @@ -2021,44 +1886,37 @@ rebuild_pollv() j++; } if (j != nprocs) { - fprintf(stderr, "strace: proc miscount\n"); - exit(1); + error_msg_and_die("proc miscount"); } } #ifndef HAVE_POLLABLE_PROCFS static void -proc_poll_open() +proc_poll_open(void) { int i; if (pipe(proc_poll_pipe) < 0) { - perror("pipe"); - exit(1); + perror_msg_and_die("pipe"); } for (i = 0; i < 2; i++) { - if (set_cloexec_flag(proc_poll_pipe[i]) < 0) { - exit(1); - } + set_cloexec_flag(proc_poll_pipe[i]); } } static int -proc_poll(pollv, nfds, timeout) -struct pollfd *pollv; -int nfds; -int timeout; +proc_poll(struct pollfd *pollv, int nfds, int timeout) { int i; int n; struct proc_pollfd pollinfo; - if ((n = read(proc_poll_pipe[0], &pollinfo, sizeof(pollinfo))) < 0) + n = read(proc_poll_pipe[0], &pollinfo, sizeof(pollinfo)); + if (n < 0) return n; if (n != sizeof(struct proc_pollfd)) { - fprintf(stderr, "panic: short read: %d\n", n); - exit(1); + error_msg_and_die("panic: short read: %d", n); } for (i = 0; i < nprocs; i++) { if (pollv[i].fd == pollinfo.fd) @@ -2071,14 +1929,12 @@ int timeout; } static void -wakeup_handler(sig) -int sig; +wakeup_handler(int sig) { } static void -proc_poller(pfd) -int pfd; +proc_poller(int pfd) { struct proc_pollfd pollinfo; struct sigaction sa; @@ -2092,8 +1948,7 @@ int pfd; switch (fork()) { case -1: - perror("fork"); - _exit(1); + perror_msg_and_die("fork"); case 0: break; default: @@ -2116,8 +1971,7 @@ int pfd; sigemptyset(&empty_set); if (getrlimit(RLIMIT_NOFILE, &rl) < 0) { - perror("getrlimit(RLIMIT_NOFILE, ...)"); - _exit(1); + perror_msg_and_die("getrlimit(RLIMIT_NOFILE, ...)"); } n = rl.rlim_cur; for (i = 0; i < n; i++) { @@ -2173,7 +2027,7 @@ choose_pfd() * heuristic improves the readability of the trace. */ tcp = pfd2tcb(pollv[last].fd); - if (tcp && (tcp->flags & TCB_INSYSCALL)) + if (tcp && exiting(tcp)) return pollv[last].fd; } @@ -2183,8 +2037,7 @@ choose_pfd() if (pollv[j].revents & (POLLHUP | POLLERR)) { tcp = pfd2tcb(pollv[j].fd); if (!tcp) { - fprintf(stderr, "strace: lost proc\n"); - exit(1); + error_msg_and_die("lost proc"); } droptcb(tcp); return -1; @@ -2194,12 +2047,11 @@ choose_pfd() return pollv[j].fd; } } - fprintf(stderr, "strace: nothing ready\n"); - exit(1); + error_msg_and_die("nothing ready"); } static int -trace() +trace(void) { #ifdef POLL_HACK struct tcb *in_syscall = NULL; @@ -2246,7 +2098,8 @@ trace() in_syscall = NULL; pv.fd = tcp->pfd; pv.events = POLLWANT; - if ((what = poll (&pv, 1, 1)) < 0) { + what = poll(&pv, 1, 1); + if (what < 0) { if (interrupted) return 0; continue; @@ -2276,9 +2129,9 @@ trace() } /* Look up `pfd' in our table. */ - if ((tcp = pfd2tcb(pfd)) == NULL) { - fprintf(stderr, "unknown pfd: %u\n", pfd); - exit(1); + tcp = pfd2tcb(pfd); + if (tcp == NULL) { + error_msg_and_die("unknown pfd: %u", pfd); } #ifdef POLL_HACK FOUND: @@ -2286,15 +2139,15 @@ trace() /* Get the status of the process. */ if (!interrupted) { #ifndef FREEBSD - ioctl_result = IOCTL_WSTOP (tcp); + ioctl_result = IOCTL_WSTOP(tcp); #else /* FREEBSD */ /* Thanks to some scheduling mystery, the first poller sometimes waits for the already processed end of fork event. Doing a non blocking poll here solves the problem. */ if (proc_poll_pipe[0] != -1) - ioctl_result = IOCTL_STATUS (tcp); + ioctl_result = IOCTL_STATUS(tcp); else - ioctl_result = IOCTL_WSTOP (tcp); + ioctl_result = IOCTL_WSTOP(tcp); #endif /* FREEBSD */ ioctl_errno = errno; #ifndef HAVE_POLLABLE_PROCFS @@ -2325,15 +2178,14 @@ trace() droptcb(tcp); continue; default: - perror("PIOCWSTOP"); - exit(1); + perror_msg_and_die("PIOCWSTOP"); } } #ifdef FREEBSD if ((tcp->flags & TCB_STARTUP) && (tcp->status.PR_WHY == PR_SYSEXIT)) { /* discard first event for a syscall we never entered */ - IOCTL (tcp->pfd, PIOCRUN, 0); + IOCTL(tcp->pfd, PIOCRUN, 0); continue; } #endif @@ -2351,7 +2203,8 @@ trace() char buf[1024]; int len; - if ((len = pread(tcp->pfd_status, buf, sizeof(buf) - 1, 0)) > 0) { + len = pread(tcp->pfd_status, buf, sizeof(buf) - 1, 0); + if (len > 0) { buf[len] = '\0'; sscanf(buf, "%*s %*d %*d %*d %*d %*d,%*d %*s %*d,%*d %*d,%*d %ld,%ld", @@ -2372,8 +2225,7 @@ trace() if (tcp->status.PR_FLAGS & PR_ASLEEP) { tcp->status.PR_WHY = PR_SYSENTRY; if (trace_syscall(tcp) < 0) { - fprintf(stderr, "syscall trouble\n"); - exit(1); + error_msg_and_die("syscall trouble"); } } break; @@ -2384,8 +2236,7 @@ trace() #endif case PR_SYSEXIT: if (trace_syscall(tcp) < 0) { - fprintf(stderr, "syscall trouble\n"); - exit(1); + error_msg_and_die("syscall trouble"); } break; case PR_SIGNALLED: @@ -2418,21 +2269,19 @@ trace() continue; #endif default: - fprintf(stderr, "odd stop %d\n", tcp->status.PR_WHY); - exit(1); + error_msg_and_die("odd stop %d", tcp->status.PR_WHY); break; } /* Remember current print column before continuing. */ tcp->curcol = curcol; arg = 0; #ifndef FREEBSD - if (IOCTL (tcp->pfd, PIOCRUN, &arg) < 0) + if (IOCTL(tcp->pfd, PIOCRUN, &arg) < 0) #else - if (IOCTL (tcp->pfd, PIOCRUN, 0) < 0) + if (IOCTL(tcp->pfd, PIOCRUN, 0) < 0) #endif { - perror("PIOCRUN"); - exit(1); + perror_msg_and_die("PIOCRUN"); } } return 0; @@ -2440,95 +2289,6 @@ trace() #else /* !USE_PROCFS */ -#ifdef TCB_GROUP_EXITING -/* Handle an exit detach or death signal that is taking all the - related clone threads with it. This is called in three circumstances: - SIG == -1 TCP has already died (TCB_ATTACHED is clear, strace is parent). - SIG == 0 Continuing TCP will perform an exit_group syscall. - SIG == other Continuing TCP with SIG will kill the process. -*/ -static int -handle_group_exit(struct tcb *tcp, int sig) -{ - /* We need to locate our records of all the clone threads - related to TCP, either its children or siblings. */ - struct tcb *leader = NULL; - - if (tcp->flags & TCB_CLONE_THREAD) - leader = tcp->parent; - - if (sig < 0) { - if (leader != NULL && leader != tcp - && !(leader->flags & TCB_GROUP_EXITING) - && !(tcp->flags & TCB_STARTUP) - ) { - fprintf(stderr, - "PANIC: handle_group_exit: %d leader %d\n", - tcp->pid, leader ? leader->pid : -1); - } - /* TCP no longer exists therefore you must not detach() it. */ -#ifndef USE_PROCFS - resume_from_tcp(tcp); -#endif - droptcb(tcp); /* Already died. */ - } - else { - /* Mark that we are taking the process down. */ - tcp->flags |= TCB_EXITING | TCB_GROUP_EXITING; - if (tcp->flags & TCB_ATTACHED) { - detach(tcp, sig); - if (leader != NULL && leader != tcp) - leader->flags |= TCB_GROUP_EXITING; - } else { - if (ptrace_restart(PTRACE_CONT, tcp, sig) < 0) { - cleanup(); - return -1; - } - if (leader != NULL) { - leader->flags |= TCB_GROUP_EXITING; - if (leader != tcp) - droptcb(tcp); - } - /* The leader will report to us as parent now, - and then we'll get to the SIG==-1 case. */ - return 0; - } - } - - return 0; -} -#endif - -#ifdef LINUX -static int -handle_ptrace_event(int status, struct tcb *tcp) -{ - if (status >> 16 == PTRACE_EVENT_VFORK || - status >> 16 == PTRACE_EVENT_CLONE || - status >> 16 == PTRACE_EVENT_FORK) { - long childpid; - - if (do_ptrace(PTRACE_GETEVENTMSG, tcp, NULL, &childpid) < 0) { - if (errno != ESRCH) { - fprintf(stderr, "\ -%s: handle_ptrace_event: ptrace cannot get new child's pid\n", - progname); - cleanup(); - exit(1); - } - return -1; - } - return handle_new_child(tcp, childpid, 0); - } - if (status >> 16 == PTRACE_EVENT_EXEC) { - if (debug) - fprintf(stderr, "PTRACE_EVENT_EXEC on pid %d (ignored)\n", tcp->pid); - return 0; - } - return 1; -} -#endif - static int trace() { @@ -2538,9 +2298,10 @@ trace() struct tcb *tcp; #ifdef LINUX struct rusage ru; -#ifdef __WALL + struct rusage *rup = cflag ? &ru : NULL; +# ifdef __WALL static int wait4_options = __WALL; -#endif +# endif #endif /* LINUX */ while (nprocs != 0) { @@ -2549,36 +2310,32 @@ trace() if (interactive) sigprocmask(SIG_SETMASK, &empty_set, NULL); #ifdef LINUX -#ifdef __WALL - pid = wait4(-1, &status, wait4_options, cflag ? &ru : NULL); +# ifdef __WALL + pid = wait4(-1, &status, wait4_options, rup); if (pid < 0 && (wait4_options & __WALL) && errno == EINVAL) { /* this kernel does not support __WALL */ wait4_options &= ~__WALL; - errno = 0; - pid = wait4(-1, &status, wait4_options, - cflag ? &ru : NULL); + pid = wait4(-1, &status, wait4_options, rup); } if (pid < 0 && !(wait4_options & __WALL) && errno == ECHILD) { /* most likely a "cloned" process */ - pid = wait4(-1, &status, __WCLONE, - cflag ? &ru : NULL); - if (pid == -1) { - fprintf(stderr, "strace: clone wait4 " - "failed: %s\n", strerror(errno)); + pid = wait4(-1, &status, __WCLONE, rup); + if (pid < 0) { + perror_msg("wait4(__WCLONE) failed"); } } -#else - pid = wait4(-1, &status, 0, cflag ? &ru : NULL); -#endif /* __WALL */ +# else + pid = wait4(-1, &status, 0, rup); +# endif /* __WALL */ #endif /* LINUX */ #ifdef SUNOS4 pid = wait(&status); -#endif /* SUNOS4 */ +#endif wait_errno = errno; if (interactive) sigprocmask(SIG_BLOCK, &blocked_set, NULL); - if (pid == -1) { + if (pid < 0) { switch (wait_errno) { case EINTR: continue; @@ -2598,14 +2355,56 @@ trace() } if (pid == popen_pid) { if (WIFEXITED(status) || WIFSIGNALED(status)) - popen_pid = -1; + popen_pid = 0; continue; } - if (debug) - fprintf(stderr, " [wait(%#x) = %u]\n", status, pid); + if (debug) { + char buf[sizeof("WIFEXITED,exitcode=%u") + sizeof(int)*3 /*paranoia:*/ + 16]; +#ifdef LINUX + unsigned ev = (unsigned)status >> 16; + if (ev) { + static const char *const event_names[] = { + [PTRACE_EVENT_CLONE] = "CLONE", + [PTRACE_EVENT_FORK] = "FORK", + [PTRACE_EVENT_VFORK] = "VFORK", + [PTRACE_EVENT_VFORK_DONE] = "VFORK_DONE", + [PTRACE_EVENT_EXEC] = "EXEC", + [PTRACE_EVENT_EXIT] = "EXIT", + }; + const char *e; + if (ev < ARRAY_SIZE(event_names)) + e = event_names[ev]; + else { + sprintf(buf, "?? (%u)", ev); + e = buf; + } + fprintf(stderr, " PTRACE_EVENT_%s", e); + } +#endif + strcpy(buf, "???"); + if (WIFSIGNALED(status)) +#ifdef WCOREDUMP + sprintf(buf, "WIFSIGNALED,%ssig=%s", + WCOREDUMP(status) ? "core," : "", + signame(WTERMSIG(status))); +#else + sprintf(buf, "WIFSIGNALED,sig=%s", + signame(WTERMSIG(status))); +#endif + if (WIFEXITED(status)) + sprintf(buf, "WIFEXITED,exitcode=%u", WEXITSTATUS(status)); + if (WIFSTOPPED(status)) + sprintf(buf, "WIFSTOPPED,sig=%s", signame(WSTOPSIG(status))); +#ifdef WIFCONTINUED + if (WIFCONTINUED(status)) + strcpy(buf, "WIFCONTINUED"); +#endif + fprintf(stderr, " [wait(0x%04x) = %u] %s\n", status, pid, buf); + } /* Look up `pid' in our table. */ - if ((tcp = pid2tcb(pid)) == NULL) { + tcp = pid2tcb(pid); + if (tcp == NULL) { #ifdef LINUX if (followfork) { /* This is needed to go with the CLONE_PTRACE @@ -2618,10 +2417,9 @@ trace() child so that we know how to do clearbpt in the child. */ tcp = alloctcb(pid); - tcp->flags |= TCB_ATTACHED | TCB_SUSPENDED; + tcp->flags |= TCB_ATTACHED; if (!qflag) - fprintf(stderr, "\ -Process %d attached (waiting for parent)\n", + fprintf(stderr, "Process %d attached\n", pid); } else @@ -2629,80 +2427,55 @@ Process %d attached (waiting for parent)\n", CLONE_PTRACE itself. */ #endif { - fprintf(stderr, "unknown pid: %u\n", pid); if (WIFSTOPPED(status)) ptrace(PTRACE_CONT, pid, (char *) 1, 0); - exit(1); + error_msg_and_die("Unknown pid: %u", pid); } } /* set current output file */ outf = tcp->outf; curcol = tcp->curcol; - if (cflag) { #ifdef LINUX + if (cflag) { tv_sub(&tcp->dtime, &ru.ru_stime, &tcp->stime); tcp->stime = ru.ru_stime; -#endif /* !LINUX */ } +#endif - if (tcp->flags & TCB_SUSPENDED) { - /* - * Apparently, doing any ptrace() call on a stopped - * process, provokes the kernel to report the process - * status again on a subsequent wait(), even if the - * process has not been actually restarted. - * Since we have inspected the arguments of suspended - * processes we end up here testing for this case. - */ - continue; - } if (WIFSIGNALED(status)) { if (pid == strace_child) exit_code = 0x100 | WTERMSIG(status); if (cflag != CFLAG_ONLY_STATS && (qual_flags[WTERMSIG(status)] & QUAL_SIGNAL)) { printleader(tcp); +#ifdef WCOREDUMP tprintf("+++ killed by %s %s+++", signame(WTERMSIG(status)), -#ifdef WCOREDUMP - WCOREDUMP(status) ? "(core dumped) " : + WCOREDUMP(status) ? "(core dumped) " : ""); +#else + tprintf("+++ killed by %s +++", + signame(WTERMSIG(status))); #endif - ""); printtrailer(); } -#ifdef TCB_GROUP_EXITING - handle_group_exit(tcp, -1); -#else droptcb(tcp); -#endif continue; } if (WIFEXITED(status)) { if (pid == strace_child) exit_code = WEXITSTATUS(status); - if (debug) - fprintf(stderr, "pid %u exited with %d\n", pid, WEXITSTATUS(status)); - if ((tcp->flags & (TCB_ATTACHED|TCB_STARTUP)) == TCB_ATTACHED -#ifdef TCB_GROUP_EXITING - && !(tcp->parent && (tcp->parent->flags & TCB_GROUP_EXITING)) - && !(tcp->flags & TCB_GROUP_EXITING) -#endif - ) { - fprintf(stderr, - "PANIC: attached pid %u exited with %d\n", - pid, WEXITSTATUS(status)); - } if (tcp == tcp_last) { if ((tcp->flags & (TCB_INSYSCALL|TCB_REPRINT)) == TCB_INSYSCALL) tprintf(" \n", WEXITSTATUS(status)); tcp_last = NULL; } -#ifdef TCB_GROUP_EXITING - handle_group_exit(tcp, -1); -#else + if (!cflag /* && (qual_flags[WTERMSIG(status)] & QUAL_SIGNAL) */ ) { + printleader(tcp); + tprintf("+++ exited with %d +++", WEXITSTATUS(status)); + printtrailer(); + } droptcb(tcp); -#endif continue; } if (!WIFSTOPPED(status)) { @@ -2710,13 +2483,10 @@ Process %d attached (waiting for parent)\n", droptcb(tcp); continue; } - if (debug) - fprintf(stderr, "pid %u stopped, [%s]\n", - pid, signame(WSTOPSIG(status))); if (status >> 16) { - if (handle_ptrace_event(status, tcp) != 1) - goto tracing; + /* Ptrace event (we ignore all of them for now) */ + goto tracing; } /* @@ -2738,7 +2508,7 @@ Process %d attached (waiting for parent)\n", if (tcp->flags & TCB_BPTSET) { /* * One example is a breakpoint inherited from - * parent through fork (). + * parent through fork(). */ if (clearbpt(tcp) < 0) /* Pretty fatal */ { droptcb(tcp); @@ -2747,16 +2517,13 @@ Process %d attached (waiting for parent)\n", } } #ifdef LINUX - int options = ptrace_setoptions_for_all; - if (followfork && (tcp->parent == NULL)) - options |= ptrace_setoptions_followfork; - if (options) { + if (ptrace_setoptions) { if (debug) - fprintf(stderr, "setting opts %x on pid %d\n", options, tcp->pid); - if (ptrace(PTRACE_SETOPTIONS, tcp->pid, NULL, options) < 0) { + fprintf(stderr, "setting opts %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 */ - error_msg_and_die("PTRACE_SETOPTIONS"); + perror_msg_and_die("PTRACE_SETOPTIONS"); } } } @@ -2764,7 +2531,7 @@ Process %d attached (waiting for parent)\n", goto tracing; } - if (WSTOPSIG(status) != SYSCALLTRAP) { + if (WSTOPSIG(status) != syscall_trap_sig) { if (WSTOPSIG(status) == SIGSTOP && (tcp->flags & TCB_SIGTRAPPED)) { /* @@ -2791,45 +2558,40 @@ Process %d attached (waiting for parent)\n", # define PSR_RI 41 pc += (psr >> PSR_RI) & 0x3; # define PC_FORMAT_STR " @ %lx" -# define PC_FORMAT_ARG pc +# define PC_FORMAT_ARG , pc #else -# define PC_FORMAT_STR "%s" -# define PC_FORMAT_ARG "" +# define PC_FORMAT_STR "" +# define PC_FORMAT_ARG /* nothing */ #endif printleader(tcp); if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) { tprintf("--- "); printsiginfo(&si, verbose(tcp)); tprintf(" (%s)" PC_FORMAT_STR " ---", - strsignal(WSTOPSIG(status)), + strsignal(WSTOPSIG(status)) PC_FORMAT_ARG); } else tprintf("--- %s by %s" PC_FORMAT_STR " ---", strsignal(WSTOPSIG(status)), - signame(WSTOPSIG(status)), + signame(WSTOPSIG(status)) PC_FORMAT_ARG); printtrailer(); } - if (((tcp->flags & TCB_ATTACHED) || - tcp->nclone_threads > 0) && - !sigishandled(tcp, WSTOPSIG(status))) { -#ifdef TCB_GROUP_EXITING - handle_group_exit(tcp, WSTOPSIG(status)); -#else - detach(tcp, WSTOPSIG(status)); -#endif - continue; - } if (ptrace_restart(PTRACE_SYSCALL, tcp, WSTOPSIG(status)) < 0) { cleanup(); return -1; } - tcp->flags &= ~TCB_SUSPENDED; continue; } - /* we handled the STATUS, we are permitted to interrupt now. */ + + /* We handled quick cases, we are permitted to interrupt now. */ if (interrupted) return 0; + + /* This should be syscall entry or exit. + * (Or it still can be that pesky post-execve SIGTRAP!) + * Handle it. + */ if (trace_syscall(tcp) < 0 && !tcp->ptrace_errno) { /* ptrace() failed in trace_syscall() with ESRCH. * Likely a result of process disappearing mid-flight. @@ -2841,7 +2603,7 @@ Process %d attached (waiting for parent)\n", if (tcp->flags & TCB_ATTACHED) { if (tcp_last) { /* Do we have dangling line "syscall(param, param"? - * Finish the line then. We cannot + * Finish the line then. */ tcp_last->flags |= TCB_REPRINT; tprintf(" "); @@ -2855,27 +2617,6 @@ Process %d attached (waiting for parent)\n", } continue; } - if (tcp->flags & TCB_EXITING) { -#ifdef TCB_GROUP_EXITING - if (tcp->flags & TCB_GROUP_EXITING) { - if (handle_group_exit(tcp, 0) < 0) - return -1; - continue; - } -#endif - if (tcp->flags & TCB_ATTACHED) - detach(tcp, 0); - else if (ptrace_restart(PTRACE_CONT, tcp, 0) < 0) { - cleanup(); - return -1; - } - continue; - } - if (tcp->flags & TCB_SUSPENDED) { - if (!qflag) - fprintf(stderr, "Process %u suspended\n", pid); - continue; - } tracing: /* Remember current print column before continuing. */ tcp->curcol = curcol; @@ -2889,8 +2630,6 @@ Process %d attached (waiting for parent)\n", #endif /* !USE_PROCFS */ -#include - void tprintf(const char *fmt, ...) { @@ -2907,18 +2646,31 @@ tprintf(const char *fmt, ...) curcol += n; } va_end(args); - return; } void -printleader(tcp) -struct tcb *tcp; +tprints(const char *str) +{ + if (outf) { + int n = fputs(str, outf); + if (n >= 0) { + curcol += strlen(str); + return; + } + if (outf != stderr) + perror(outfname == NULL + ? "" : outfname); + } +} + +void +printleader(struct tcb *tcp) { if (tcp_last) { if (tcp_last->ptrace_errno) { if (tcp_last->flags & TCB_INSYSCALL) { - tprintf(" )"); - tabto(acolumn); + tprintf(" ) "); + tabto(); } tprintf("= ? \n"); tcp_last->ptrace_errno = 0; @@ -2964,11 +2716,10 @@ struct tcb *tcp; } void -tabto(col) -int col; +tabto(void) { - if (curcol < col) - tprintf("%*s", col - curcol, ""); + if (curcol < acolumn) + tprints(acolumn_spaces + curcol); } void