]> granicus.if.org Git - strace/blobdiff - strace.c
io.c: use printaddr and umove_or_printaddr
[strace] / strace.c
index 250f49c2a094a8fe86b3ded4c26ad1db76796335..9b93e7968dbb632294328a4abdf6acb867d4529a 100644 (file)
--- a/strace.c
+++ b/strace.c
@@ -32,6 +32,7 @@
 #include <stdarg.h>
 #include <sys/param.h>
 #include <fcntl.h>
+#include <signal.h>
 #include <sys/resource.h>
 #include <sys/wait.h>
 #include <sys/stat.h>
@@ -44,6 +45,7 @@
 #endif
 
 #include "ptrace.h"
+#include "printsiginfo.h"
 
 /* In some libc, these aren't declared. Do it ourself: */
 extern char **environ;
@@ -74,18 +76,17 @@ bool stack_trace_enabled = false;
 # define fork() vfork()
 #endif
 
+const unsigned int syscall_trap_sig = SIGTRAP | 0x80;
+
 cflag_t cflag = CFLAG_NONE;
 unsigned int followfork = 0;
-unsigned int ptrace_setoptions = 0;
+unsigned int ptrace_setoptions = PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXEC;
 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 rflag = 0;
 static bool print_pid_pfx = 0;
@@ -252,8 +253,8 @@ usage: strace [-CdffhiqrtttTvVxxy] [-I n] [-e expr]...\n\
        exit(exitval);
 }
 
-static void die(void) __attribute__ ((noreturn));
-static void die(void)
+static void ATTRIBUTE_NORETURN
+die(void)
 {
        if (strace_tracer_pid == getpid()) {
                cflag = 0;
@@ -325,15 +326,6 @@ void perror_msg_and_die(const char *fmt, ...)
        die();
 }
 
-void die_out_of_memory(void)
-{
-       static bool recursed = 0;
-       if (recursed)
-               exit(1);
-       recursed = 1;
-       error_msg_and_die("Out of memory");
-}
-
 static void
 error_opt_arg(int opt, const char *arg)
 {
@@ -347,7 +339,7 @@ ptrace_attach_or_seize(int pid)
        int r;
        if (!use_seize)
                return ptrace(PTRACE_ATTACH, pid, 0L, 0L);
-       r = ptrace(PTRACE_SEIZE, pid, 0L, (unsigned long)ptrace_setoptions);
+       r = ptrace(PTRACE_SEIZE, pid, 0L, (unsigned long) ptrace_setoptions);
        if (r)
                return r;
        r = ptrace(PTRACE_INTERRUPT, pid, 0L, 0L);
@@ -677,10 +669,9 @@ expand_tcbtab(void)
           So tcbtab is a table of pointers.  Since we never
           free the TCBs, we allocate a single chunk of many.  */
        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)
-               die_out_of_memory();
+       struct tcb *newtcbs = xcalloc(tcbtabsize, sizeof(newtcbs[0]));
+       struct tcb **newtab = xreallocarray(tcbtab, tcbtabsize * 2,
+                                           sizeof(tcbtab[0]));
        tcbtabsize *= 2;
        tcbtab = newtab;
        while (i < tcbtabsize)
@@ -712,7 +703,8 @@ alloctcb(int pid)
 
                        nprocs++;
                        if (debug_flag)
-                               fprintf(stderr, "new tcb for pid %d, active tcbs:%d\n", tcp->pid, nprocs);
+                               error_msg("new tcb for pid %d, active tcbs:%d",
+                                         tcp->pid, nprocs);
                        return tcp;
                }
        }
@@ -733,7 +725,8 @@ droptcb(struct tcb *tcp)
 
        nprocs--;
        if (debug_flag)
-               fprintf(stderr, "dropped tcb for pid %d, %d remain\n", tcp->pid, nprocs);
+               error_msg("dropped tcb for pid %d, %d remain",
+                         tcp->pid, nprocs);
 
        if (tcp->outf) {
                if (followfork >= 2) {
@@ -766,9 +759,6 @@ detach(struct tcb *tcp)
        int error;
        int status;
 
-       if (tcp->flags & TCB_BPTSET)
-               clearbpt(tcp);
-
        /*
         * Linux wrongly insists the child be stopped
         * before detaching.  Arghh.  We go through hoops
@@ -865,8 +855,8 @@ detach(struct tcb *tcp)
                }
                sig = WSTOPSIG(status);
                if (debug_flag)
-                       fprintf(stderr, "detach wait: event:%d sig:%d\n",
-                                       (unsigned)status >> 16, sig);
+                       error_msg("detach wait: event:%d sig:%d",
+                                 (unsigned)status >> 16, sig);
                if (use_seize) {
                        unsigned event = (unsigned)status >> 16;
                        if (event == PTRACE_EVENT_STOP /*&& sig == SIGTRAP*/) {
@@ -919,7 +909,7 @@ detach(struct tcb *tcp)
 
  drop:
        if (!qflag && (tcp->flags & TCB_ATTACHED))
-               fprintf(stderr, "Process %u detached\n", tcp->pid);
+               error_msg("Process %u detached", tcp->pid);
 
        droptcb(tcp);
 }
@@ -1021,11 +1011,11 @@ startup_attach(void)
                                        if (ptrace_attach_or_seize(tid) < 0) {
                                                ++nerr;
                                                if (debug_flag)
-                                                       fprintf(stderr, "attach to pid %d failed\n", tid);
+                                                       error_msg("attach to pid %d failed", tid);
                                                continue;
                                        }
                                        if (debug_flag)
-                                               fprintf(stderr, "attach to pid %d succeeded\n", tid);
+                                               error_msg("attach to pid %d succeeded", tid);
                                        cur_tcp = tcp;
                                        if (tid != tcp->pid)
                                                cur_tcp = alloctcb(tid);
@@ -1046,9 +1036,9 @@ startup_attach(void)
                                        continue;
                                }
                                if (!qflag) {
-                                       fprintf(stderr, ntid > 1
-? "Process %u attached with %u threads\n"
-: "Process %u attached\n",
+                                       error_msg(ntid > 1
+? "Process %u attached with %u threads"
+: "Process %u attached",
                                                tcp->pid, ntid);
                                }
                                if (!(tcp->flags & TCB_ATTACHED)) {
@@ -1069,7 +1059,7 @@ startup_attach(void)
                tcp->flags |= TCB_ATTACHED | TCB_STARTUP | post_attach_sigstop;
                newoutf(tcp);
                if (debug_flag)
-                       fprintf(stderr, "attach to pid %d (main) succeeded\n", tcp->pid);
+                       error_msg("attach to pid %d (main) succeeded", tcp->pid);
 
                if (daemonized_tracer) {
                        /*
@@ -1080,9 +1070,7 @@ startup_attach(void)
                }
 
                if (!qflag)
-                       fprintf(stderr,
-                               "Process %u attached\n",
-                               tcp->pid);
+                       error_msg("Process %u attached", tcp->pid);
        } /* for each tcbtab[] */
 
  ret:
@@ -1101,7 +1089,8 @@ struct exec_params {
        char *pathname;
 };
 static struct exec_params params_for_tracee;
-static void __attribute__ ((noinline, noreturn))
+
+static void ATTRIBUTE_NOINLINE ATTRIBUTE_NORETURN
 exec_or_die(void)
 {
        struct exec_params *params = &params_for_tracee;
@@ -1161,16 +1150,19 @@ startup_child(char **argv)
 {
        struct_stat statbuf;
        const char *filename;
+       size_t filename_len;
        char pathname[PATH_MAX];
        int pid;
        struct tcb *tcp;
 
        filename = argv[0];
+       filename_len = strlen(filename);
+
+       if (filename_len > sizeof(pathname) - 1) {
+               errno = ENAMETOOLONG;
+               perror_msg_and_die("exec");
+       }
        if (strchr(filename, '/')) {
-               if (strlen(filename) > sizeof pathname - 1) {
-                       errno = ENAMETOOLONG;
-                       perror_msg_and_die("exec");
-               }
                strcpy(pathname, filename);
        }
 #ifdef USE_DEBUGGING_EXEC
@@ -1207,6 +1199,8 @@ startup_child(char **argv)
                        }
                        if (len && pathname[len - 1] != '/')
                                pathname[len++] = '/';
+                       if (filename_len + len > sizeof(pathname) - 1)
+                               continue;
                        strcpy(pathname + len, filename);
                        if (stat_file(pathname, &statbuf) == 0 &&
                            /* Accept only regular files
@@ -1216,6 +1210,8 @@ startup_child(char **argv)
                            (statbuf.st_mode & 0111))
                                break;
                }
+               if (!path || !*path)
+                       pathname[0] = '\0';
        }
        if (stat_file(pathname, &statbuf) < 0) {
                perror_msg_and_die("Can't stat '%s'", filename);
@@ -1229,7 +1225,7 @@ startup_child(char **argv)
         * On NOMMU, can be safely freed only after execve in tracee.
         * It's hard to know when that happens, so we just leak it.
         */
-       params_for_tracee.pathname = NOMMU_SYSTEM ? strdup(pathname) : pathname;
+       params_for_tracee.pathname = NOMMU_SYSTEM ? xstrdup(pathname) : pathname;
 
 #if defined HAVE_PRCTL && defined PR_SET_PTRACER && defined PR_SET_PTRACER_ANY
        if (daemonized_tracer)
@@ -1321,219 +1317,6 @@ startup_child(char **argv)
        }
 }
 
-/*
- * 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
-test_ptrace_setoptions_followfork(void)
-{
-       int pid, expected_grandchild = 0, found_grandchild = 0;
-       const unsigned int test_options = PTRACE_O_TRACECLONE |
-                                         PTRACE_O_TRACEFORK |
-                                         PTRACE_O_TRACEVFORK;
-
-       /* Need fork for test. NOMMU has no forks */
-       if (NOMMU_SYSTEM)
-               goto worked; /* be bold, and pretend that test succeeded */
-
-       pid = fork();
-       if (pid < 0)
-               perror_msg_and_die("fork");
-       if (pid == 0) {
-               pid = getpid();
-               if (ptrace(PTRACE_TRACEME, 0L, 0L, 0L) < 0)
-                       perror_msg_and_die("%s: PTRACE_TRACEME doesn't work",
-                                          __func__);
-               kill_save_errno(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 <= 0) {
-                       if (errno == EINTR)
-                               continue;
-                       if (errno == ECHILD)
-                               break;
-                       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_save_errno(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) {
-                               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_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) {
- worked:
-               ptrace_setoptions |= test_options;
-               if (debug_flag)
-                       fprintf(stderr, "ptrace_setoptions = %#x\n",
-                               ptrace_setoptions);
-               return 0;
-       }
-       error_msg("Test for PTRACE_O_TRACECLONE failed, "
-                 "giving up using this feature.");
-       return 1;
-}
-
-/*
- * Test whether the kernel support PTRACE_O_TRACESYSGOOD.
- * First fork a new child, call ptrace(PTRACE_SETOPTIONS) on it,
- * and then see whether it will stop with (SIGTRAP | 0x80).
- *
- * Use of this option enables correct handling of user-generated SIGTRAPs,
- * and SIGTRAPs generated by special instructions such as int3 on x86:
-
-# 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)
-{
-       const unsigned int test_options = PTRACE_O_TRACESYSGOOD |
-                                         PTRACE_O_TRACEEXEC;
-       int pid;
-       int it_worked = 0;
-
-       /* Need fork for test. NOMMU has no forks */
-       if (NOMMU_SYSTEM)
-               goto worked; /* be bold, and pretend that test succeeded */
-
-       pid = fork();
-       if (pid < 0)
-               perror_msg_and_die("fork");
-
-       if (pid == 0) {
-               pid = getpid();
-               if (ptrace(PTRACE_TRACEME, 0L, 0L, 0L) < 0)
-                       /* 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 */
-       }
-
-       while (1) {
-               int status, tracee_pid;
-
-               errno = 0;
-               tracee_pid = wait(&status);
-               if (tracee_pid <= 0) {
-                       if (errno == EINTR)
-                               continue;
-                       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 (!WIFSTOPPED(status)) {
-                       kill(pid, SIGKILL);
-                       error_msg_and_die("%s: unexpected wait status %x",
-                                         __func__, status);
-               }
-               if (WSTOPSIG(status) == SIGSTOP) {
-                       /*
-                        * We don't check "options aren't accepted" error.
-                        * If it happens, we'll never get (SIGTRAP | 0x80),
-                        * and thus will decide to not use the option.
-                        * IOW: the outcome of the test will be correct.
-                        */
-                       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_save_errno(pid, SIGKILL);
-                       perror_msg_and_die("PTRACE_SYSCALL doesn't work");
-               }
-       }
-
-       if (it_worked) {
- worked:
-               syscall_trap_sig = (SIGTRAP | 0x80);
-               ptrace_setoptions |= test_options;
-               if (debug_flag)
-                       fprintf(stderr, "ptrace_setoptions = %#x\n",
-                               ptrace_setoptions);
-               return 0;
-       }
-
-       error_msg("Test for PTRACE_O_TRACESYSGOOD failed, "
-                 "giving up using this feature.");
-       return 1;
-}
-
 #if USE_SEIZE
 static void
 test_ptrace_seize(void)
@@ -1562,7 +1345,7 @@ test_ptrace_seize(void)
        if (ptrace(PTRACE_SEIZE, pid, 0, 0) == 0) {
                post_attach_sigstop = 0; /* this sets use_seize to 1 */
        } else if (debug_flag) {
-               fprintf(stderr, "PTRACE_SEIZE doesn't work\n");
+               error_msg("PTRACE_SEIZE doesn't work");
        }
 
        kill(pid, SIGKILL);
@@ -1630,7 +1413,7 @@ get_os_release(void)
  * Don't want main() to inline us and defeat the reason
  * we have a separate function.
  */
-static void __attribute__ ((noinline))
+static void ATTRIBUTE_NOINLINE
 init(int argc, char *argv[])
 {
        struct tcb *tcp;
@@ -1654,12 +1437,8 @@ init(int argc, char *argv[])
 
        /* Allocate the initial tcbtab.  */
        tcbtabsize = argc;      /* Surely enough for all -p args.  */
-       tcbtab = calloc(tcbtabsize, sizeof(tcbtab[0]));
-       if (!tcbtab)
-               die_out_of_memory();
-       tcp = calloc(tcbtabsize, sizeof(*tcp));
-       if (!tcp)
-               die_out_of_memory();
+       tcbtab = xcalloc(tcbtabsize, sizeof(tcbtab[0]));
+       tcp = xcalloc(tcbtabsize, sizeof(*tcp));
        for (tcbi = 0; tcbi < tcbtabsize; tcbi++)
                tcbtab[tcbi] = tcp++;
 
@@ -1757,7 +1536,7 @@ init(int argc, char *argv[])
                        qualify(optarg);
                        break;
                case 'o':
-                       outfname = strdup(optarg);
+                       outfname = xstrdup(optarg);
                        break;
                case 'O':
                        i = string_to_uint(optarg);
@@ -1781,7 +1560,7 @@ init(int argc, char *argv[])
                        set_sortby(optarg);
                        break;
                case 'u':
-                       username = strdup(optarg);
+                       username = xstrdup(optarg);
                        break;
 #ifdef USE_LIBUNWIND
                case 'k':
@@ -1805,9 +1584,7 @@ init(int argc, char *argv[])
        argv += optind;
        /* argc -= optind; - no need, argc is not used below */
 
-       acolumn_spaces = malloc(acolumn + 1);
-       if (!acolumn_spaces)
-               die_out_of_memory();
+       acolumn_spaces = xmalloc(acolumn + 1);
        memset(acolumn_spaces, ' ', acolumn);
        acolumn_spaces[acolumn] = '\0';
 
@@ -1871,14 +1648,12 @@ init(int argc, char *argv[])
                run_gid = getgid();
        }
 
-       /*
-        * On any reasonably recent Linux kernel (circa about 2.5.46)
-        * need_fork_exec_workarounds should stay 0 after these tests:
-        */
-       /*need_fork_exec_workarounds = 0; - already is */
        if (followfork)
-               need_fork_exec_workarounds = test_ptrace_setoptions_followfork();
-       need_fork_exec_workarounds |= test_ptrace_setoptions_for_all();
+               ptrace_setoptions |= PTRACE_O_TRACECLONE |
+                                    PTRACE_O_TRACEFORK |
+                                    PTRACE_O_TRACEVFORK;
+       if (debug_flag)
+               error_msg("ptrace_setoptions = %#x", ptrace_setoptions);
        test_ptrace_seize();
 
        /* Check if they want to redirect the output. */
@@ -1902,15 +1677,14 @@ init(int argc, char *argv[])
        }
 
        if (!outfname || outfname[0] == '|' || outfname[0] == '!') {
-               char *buf = malloc(BUFSIZ);
-               if (!buf)
-                       die_out_of_memory();
+               char *buf = xmalloc(BUFSIZ);
                setvbuf(shared_log, buf, _IOLBF, BUFSIZ);
        }
        if (outfname && argv[0]) {
                if (!opt_intr)
                        opt_intr = INTR_NEVER;
-               qflag = 1;
+               if (!qflag)
+                       qflag = 1;
        }
        if (!opt_intr)
                opt_intr = INTR_WHILE_WAIT;
@@ -2011,8 +1785,7 @@ cleanup(void)
                if (!tcp->pid)
                        continue;
                if (debug_flag)
-                       fprintf(stderr,
-                               "cleanup: looking at pid %u\n", tcp->pid);
+                       error_msg("cleanup: looking at pid %u", tcp->pid);
                if (tcp->pid == strace_child) {
                        kill(tcp->pid, SIGCONT);
                        kill(tcp->pid, fatal_sig);
@@ -2030,7 +1803,7 @@ interrupt(int sig)
 }
 
 static void
-print_debug_info(const int pid, const int status)
+print_debug_info(const int pid, int status)
 {
        const unsigned int event = (unsigned int) status >> 16;
        char buf[sizeof("WIFEXITED,exitcode=%u") + sizeof(int)*3 /*paranoia:*/ + 16];
@@ -2073,11 +1846,11 @@ print_debug_info(const int pid, const int status)
                        e = "STOP";
                sprintf(evbuf, ",EVENT_%s (%u)", e, event);
        }
-       fprintf(stderr, " [wait(0x%06x) = %u] %s%s\n", status, pid, buf, evbuf);
+       error_msg("[wait(0x%06x) = %u] %s%s", status, pid, buf, evbuf);
 }
 
 static struct tcb *
-maybe_allocate_tcb(const int pid, const int status)
+maybe_allocate_tcb(const int pid, int status)
 {
        if (!WIFSTOPPED(status)) {
                if (detach_on_execve && pid == strace_child) {
@@ -2098,7 +1871,7 @@ maybe_allocate_tcb(const int pid, const int status)
                tcp->flags |= TCB_ATTACHED | TCB_STARTUP | post_attach_sigstop;
                newoutf(tcp);
                if (!qflag)
-                       fprintf(stderr, "Process %d attached\n", pid);
+                       error_msg("Process %d attached", pid);
                return tcp;
        } else {
                /* This can happen if a clone call used
@@ -2160,7 +1933,7 @@ maybe_switch_tcbs(struct tcb *tcp, const int pid)
 }
 
 static void
-print_signalled(struct tcb *tcp, const int pid, const int status)
+print_signalled(struct tcb *tcp, const int pid, int status)
 {
        if (pid == strace_child) {
                exit_code = 0x100 | WTERMSIG(status);
@@ -2184,7 +1957,7 @@ print_signalled(struct tcb *tcp, const int pid, const int status)
 }
 
 static void
-print_exited(struct tcb *tcp, const int pid, const int status)
+print_exited(struct tcb *tcp, const int pid, int status)
 {
        if (pid == strace_child) {
                exit_code = WEXITSTATUS(status);
@@ -2217,32 +1990,18 @@ print_stopped(struct tcb *tcp, const siginfo_t *si, const unsigned int sig)
        }
 }
 
-static bool
+static void
 startup_tcb(struct tcb *tcp)
 {
        if (debug_flag)
-               fprintf(stderr, "pid %d has TCB_STARTUP, initializing it\n",
-                       tcp->pid);
+               error_msg("pid %d has TCB_STARTUP, initializing it", tcp->pid);
 
        tcp->flags &= ~TCB_STARTUP;
 
-       if (tcp->flags & TCB_BPTSET) {
-               /*
-                * One example is a breakpoint inherited from
-                * parent through fork().
-                */
-               if (clearbpt(tcp) < 0) {
-                       /* Pretty fatal */
-                       droptcb(tcp);
-                       exit_code = 1;
-                       return false;
-               }
-       }
-
-       if (!use_seize && ptrace_setoptions) {
+       if (!use_seize) {
                if (debug_flag)
-                       fprintf(stderr, "setting opts 0x%x on pid %d\n",
-                               ptrace_setoptions, tcp->pid);
+                       error_msg("setting opts 0x%x on pid %d",
+                                 ptrace_setoptions, tcp->pid);
                if (ptrace(PTRACE_SETOPTIONS, tcp->pid, NULL, ptrace_setoptions) < 0) {
                        if (errno != ESRCH) {
                                /* Should never happen, really */
@@ -2250,8 +2009,6 @@ startup_tcb(struct tcb *tcp)
                        }
                }
        }
-
-       return true;
 }
 
 /* Returns true iff the main trace loop has to continue. */
@@ -2270,8 +2027,26 @@ trace(void)
        if (interrupted)
                return false;
 
-       if (popen_pid != 0 && nprocs == 0)
-               return false;
+       /*
+        * Used to exit simply when nprocs hits zero, 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 +++
+        * Exiting only when wait() returns ECHILD works better.
+        */
+       if (popen_pid != 0) {
+               /* 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...
+                */
+               if (nprocs == 0)
+                       return false;
+       }
 
        if (interactive)
                sigprocmask(SIG_SETMASK, &empty_set, NULL);
@@ -2311,9 +2086,10 @@ trace(void)
                        return true;
        }
 
-       clear_regs();
        if (WIFSTOPPED(status))
                get_regs(pid);
+       else
+               clear_regs();
 
        event = (unsigned int) status >> 16;
 
@@ -2375,8 +2151,9 @@ trace(void)
 
        /* Is this the very first time we see this tracee stopped? */
        if (tcp->flags & TCB_STARTUP) {
-               if (!startup_tcb(tcp))
-                       return false;
+               startup_tcb(tcp);
+               if (get_scno(tcp) == 1)
+                       tcp->s_prev_ent = tcp->s_ent;
        }
 
        sig = WSTOPSIG(status);
@@ -2411,7 +2188,7 @@ trace(void)
         */
        if (sig == SIGSTOP && (tcp->flags & TCB_IGNORE_ONE_SIGSTOP)) {
                if (debug_flag)
-                       fprintf(stderr, "ignored SIGSTOP on pid %d\n", tcp->pid);
+                       error_msg("ignored SIGSTOP on pid %d", tcp->pid);
                tcp->flags &= ~TCB_IGNORE_ONE_SIGSTOP;
                goto restart_tracee_with_sig_0;
        }
@@ -2459,7 +2236,6 @@ show_stopsig:
 
        /*
         * This should be syscall entry or exit.
-        * (Or it still can be that pesky post-execve SIGTRAP!)
         * Handle it.
         */
        if (trace_syscall(tcp) < 0) {
@@ -2495,23 +2271,6 @@ main(int argc, char *argv[])
 {
        init(argc, argv);
 
-       /*
-        * Run main tracing loop.
-        *
-        * 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 (trace())
                ;