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))
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;
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? */
unsigned os_release; /* generated from uname()'s u.release */
-static int detach(struct tcb *tcp);
-static int trace(void);
+static void detach(struct tcb *tcp);
static void cleanup(void);
static void interrupt(int sig);
static sigset_t empty_set, blocked_set;
-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\
-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\
-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\
*/
{
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
}
}
-#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
strace_popen(const char *command)
{
FILE *fp;
+ int pid;
int fds[2];
swap_uid();
set_cloexec_flag(fds[1]); /* never fails */
- popen_pid = vfork();
- if (popen_pid == -1)
+ pid = vfork();
+ if (pid < 0)
perror_msg_and_die("vfork");
- if (popen_pid == 0) {
+ if (pid == 0) {
/* child */
close(fds[1]);
if (fds[0] != 0) {
}
/* parent */
+ popen_pid = pid;
close(fds[0]);
swap_uid();
fp = fdopen(fds[1], "w");
va_end(args);
}
+#ifndef HAVE_FPUTS_UNLOCKED
+# define fputs_unlocked fputs
+#endif
+
void
tprints(const char *str)
{
}
}
if (iflag)
- printcall(tcp);
+ print_pc(tcp);
}
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)
static struct tcb *
alloctcb(int pid)
{
- int i;
+ unsigned int i;
struct tcb *tcp;
if (nprocs == tcbtabsize)
for (i = 0; i < tcbtabsize; i++) {
tcp = tcbtab[i];
- if ((tcp->flags & TCB_INUSE) == 0) {
+ if (!tcp->pid) {
memset(tcp, 0, sizeof(*tcp));
tcp->pid = pid;
- tcp->flags = TCB_INUSE;
#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);
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);
* attached-unstopped processes give the same ESRCH. For unattached process we
* would SIGSTOP it and wait for its SIGSTOP notification forever.
*/
-static int
+static void
detach(struct tcb *tcp)
{
int error;
- int status, sigstop_expected, interrupt_done;
+ int status;
if (tcp->flags & TCB_BPTSET)
clearbpt(tcp);
# define PTRACE_DETACH PTRACE_SUNDETACH
#endif
- error = 0;
- sigstop_expected = 0;
- interrupt_done = 0;
- if (tcp->flags & TCB_ATTACHED) {
+ if (!(tcp->flags & TCB_ATTACHED))
+ goto drop;
+
+ /* We attached but possibly didn't see the expected SIGSTOP.
+ * We must catch exactly one as otherwise the detached process
+ * would be left stopped (process state T).
+ */
+ if (tcp->flags & TCB_IGNORE_ONE_SIGSTOP)
+ goto wait_loop;
+
+ error = ptrace(PTRACE_DETACH, tcp->pid, 0, 0);
+ if (!error) {
+ /* On a clear day, you can see forever. */
+ goto drop;
+ }
+ if (errno != ESRCH) {
+ /* Shouldn't happen. */
+ perror_msg("detach: ptrace(PTRACE_DETACH,%u)", tcp->pid);
+ goto drop;
+ }
+ /* ESRCH: process is either not stopped or doesn't exist. */
+ if (my_tkill(tcp->pid, 0) < 0) {
+ if (errno != ESRCH)
+ /* Shouldn't happen. */
+ perror_msg("detach: tkill(%u,0)", tcp->pid);
+ /* else: process doesn't exist. */
+ goto drop;
+ }
+ /* Process is not stopped, need to stop it. */
+ if (use_seize) {
/*
- * We attached but possibly didn't see the expected SIGSTOP.
- * We must catch exactly one as otherwise the detached process
- * would be left stopped (process state T).
+ * With SEIZE, tracee can be in group-stop already.
+ * In this state sending it another SIGSTOP does nothing.
+ * Need to use INTERRUPT.
+ * Testcase: trying to ^C a "strace -p <stopped_process>".
*/
- sigstop_expected = (tcp->flags & TCB_IGNORE_ONE_SIGSTOP);
- error = ptrace(PTRACE_DETACH, tcp->pid, 0, 0);
- if (error == 0) {
- /* On a clear day, you can see forever. */
- }
- else if (errno != ESRCH) {
- /* Shouldn't happen. */
- perror_msg("detach: ptrace(PTRACE_DETACH, ...)");
- }
- else
- /* ESRCH: process is either not stopped or doesn't exist. */
- if (my_tkill(tcp->pid, 0) < 0) {
- if (errno != ESRCH)
- /* Shouldn't happen. */
- perror_msg("detach: checking sanity");
- /* else: process doesn't exist. */
+ error = ptrace(PTRACE_INTERRUPT, tcp->pid, 0, 0);
+ if (!error)
+ goto wait_loop;
+ if (errno != ESRCH)
+ perror_msg("detach: ptrace(PTRACE_INTERRUPT,%u)", tcp->pid);
+ }
+ else {
+ error = my_tkill(tcp->pid, SIGSTOP);
+ if (!error)
+ goto wait_loop;
+ if (errno != ESRCH)
+ perror_msg("detach: tkill(%u,SIGSTOP)", tcp->pid);
+ }
+ /* Either process doesn't exist, or some weird error. */
+ goto drop;
+
+ wait_loop:
+ /* We end up here in three cases:
+ * 1. We sent PTRACE_INTERRUPT (use_seize case)
+ * 2. We sent SIGSTOP (!use_seize)
+ * 3. Attach SIGSTOP was already pending (TCB_IGNORE_ONE_SIGSTOP set)
+ */
+ for (;;) {
+ unsigned int sig;
+ if (waitpid(tcp->pid, &status, __WALL) < 0) {
+ if (errno == EINTR)
+ continue;
+ /*
+ * if (errno == ECHILD) break;
+ * ^^^ WRONG! We expect this PID to exist,
+ * and want to emit a message otherwise:
+ */
+ perror_msg("detach: waitpid(%u)", tcp->pid);
+ break;
}
- else
- /* Process is not stopped. */
- if (!sigstop_expected) {
- /* We need to stop it. */
- if (use_seize) {
- /*
- * With SEIZE, tracee can be in group-stop already.
- * In this state sending it another SIGSTOP does nothing.
- * Need to use INTERRUPT.
- * Testcase: trying to ^C a "strace -p <stopped_process>".
- */
- error = ptrace(PTRACE_INTERRUPT, tcp->pid, 0, 0);
- if (!error)
- interrupt_done = 1;
- }
- else {
- error = my_tkill(tcp->pid, SIGSTOP);
- if (!error)
- sigstop_expected = 1;
- }
- if (error && errno != ESRCH) {
- if (use_seize)
- perror_msg("detach: ptrace(PTRACE_INTERRUPT, ...)");
- else
- perror_msg("detach: stopping child");
- }
+ if (!WIFSTOPPED(status)) {
+ /*
+ * Tracee exited or was killed by signal.
+ * We shouldn't normally reach this place:
+ * we don't want to consume exit status.
+ * Consider "strace -p PID" being ^C-ed:
+ * we want merely to detach from PID.
+ *
+ * However, we _can_ end up here if tracee
+ * was SIGKILLed.
+ */
+ break;
}
- }
-
- if (sigstop_expected || interrupt_done) {
- for (;;) {
- int sig;
- if (waitpid(tcp->pid, &status, __WALL) < 0) {
- if (errno == EINTR)
- continue;
- if (errno == ECHILD) /* Already gone. */
- break;
- perror_msg("detach: waiting");
- break;
- }
- if (!WIFSTOPPED(status)) {
- /* Au revoir, mon ami. */
- break;
- }
- sig = WSTOPSIG(status);
- if (debug_flag)
- fprintf(stderr, "detach wait: event:%d sig:%d\n",
- (unsigned)status >> 16, sig);
- if (sigstop_expected && sig == SIGSTOP) {
- /* Detach, suppressing SIGSTOP */
- ptrace_restart(PTRACE_DETACH, tcp, 0);
- break;
- }
- if (interrupt_done) {
- unsigned event = (unsigned)status >> 16;
- if (event == PTRACE_EVENT_STOP /*&& sig == SIGTRAP*/) {
- /*
- * sig == SIGTRAP: PTRACE_INTERRUPT stop.
- * sig == other: process was already stopped
- * with this stopping sig (see tests/detach-stopped).
- * Looks like re-injecting this sig is not necessary
- * in DETACH for the tracee to remain stopped.
- */
- sig = 0;
- }
+ sig = WSTOPSIG(status);
+ if (debug_flag)
+ fprintf(stderr, "detach wait: event:%d sig:%d\n",
+ (unsigned)status >> 16, sig);
+ if (use_seize) {
+ unsigned event = (unsigned)status >> 16;
+ if (event == PTRACE_EVENT_STOP /*&& sig == SIGTRAP*/) {
/*
- * PTRACE_INTERRUPT is not guaranteed to produce
- * the above event if other ptrace-stop is pending.
- * See tests/detach-sleeping testcase:
- * strace got SIGINT while tracee is sleeping.
- * We sent PTRACE_INTERRUPT.
- * We see syscall exit, not PTRACE_INTERRUPT stop.
- * We won't get PTRACE_INTERRUPT stop
- * if we would CONT now. Need to DETACH.
+ * sig == SIGTRAP: PTRACE_INTERRUPT stop.
+ * sig == other: process was already stopped
+ * with this stopping sig (see tests/detach-stopped).
+ * Looks like re-injecting this sig is not necessary
+ * in DETACH for the tracee to remain stopped.
*/
- if (sig == syscall_trap_sig)
- sig = 0;
- /* else: not sure in which case we can be here.
- * Signal stop? Inject it while detaching.
- */
- ptrace_restart(PTRACE_DETACH, tcp, sig);
- break;
+ sig = 0;
}
+ /*
+ * PTRACE_INTERRUPT is not guaranteed to produce
+ * the above event if other ptrace-stop is pending.
+ * See tests/detach-sleeping testcase:
+ * strace got SIGINT while tracee is sleeping.
+ * We sent PTRACE_INTERRUPT.
+ * We see syscall exit, not PTRACE_INTERRUPT stop.
+ * We won't get PTRACE_INTERRUPT stop
+ * if we would CONT now. Need to DETACH.
+ */
if (sig == syscall_trap_sig)
sig = 0;
- /* Can't detach just yet, may need to wait for SIGSTOP */
- error = ptrace_restart(PTRACE_CONT, tcp, sig);
- if (error < 0)
- break;
+ /* else: not sure in which case we can be here.
+ * Signal stop? Inject it while detaching.
+ */
+ ptrace_restart(PTRACE_DETACH, tcp, sig);
+ break;
+ }
+ /* Note: this check has to be after use_seize check */
+ /* (else, in use_seize case SIGSTOP will be mistreated) */
+ if (sig == SIGSTOP) {
+ /* Detach, suppressing SIGSTOP */
+ ptrace_restart(PTRACE_DETACH, tcp, 0);
+ break;
+ }
+ if (sig == syscall_trap_sig)
+ sig = 0;
+ /* Can't detach just yet, may need to wait for SIGSTOP */
+ error = ptrace_restart(PTRACE_CONT, tcp, sig);
+ if (error < 0) {
+ /* Should not happen.
+ * Note: ptrace_restart returns 0 on ESRCH, so it's not it.
+ * ptrace_restart already emitted error message.
+ */
+ break;
}
}
+ drop:
if (!qflag && (tcp->flags & TCB_ATTACHED))
fprintf(stderr, "Process %u detached\n", tcp->pid);
droptcb(tcp);
-
- return error;
}
static void
static void
startup_attach(void)
{
- int tcbi;
+ unsigned int tcbi;
struct tcb *tcp;
/*
for (tcbi = 0; tcbi < tcbtabsize; tcbi++) {
tcp = tcbtab[tcbi];
- if (!(tcp->flags & TCB_INUSE))
+ if (!tcp->pid)
continue;
/* Is this a process we should attach to, but not yet attached? */
{
struct_stat statbuf;
const char *filename;
- char pathname[MAXPATHLEN];
+ char pathname[PATH_MAX];
int pid;
struct tcb *tcp;
#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, ':');
else
m = n = strlen(path);
if (n == 0) {
- if (!getcwd(pathname, MAXPATHLEN))
+ if (!getcwd(pathname, PATH_MAX))
continue;
len = strlen(pathname);
}
prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY);
#endif
- strace_child = pid = fork();
+ pid = fork();
if (pid < 0) {
perror_msg_and_die("fork");
}
/* We are the tracer */
if (!daemonized_tracer) {
+ strace_child = pid;
if (!use_seize) {
/* child did PTRACE_TRACEME, nothing to do in parent */
} else {
}
tcp = alloctcb(pid);
if (!NOMMU_SYSTEM)
- tcp->flags |= TCB_ATTACHED | TCB_STRACE_CHILD | TCB_STARTUP | post_attach_sigstop;
+ tcp->flags |= TCB_ATTACHED | TCB_STARTUP | post_attach_sigstop;
else
- tcp->flags |= TCB_ATTACHED | TCB_STRACE_CHILD | TCB_STARTUP;
+ tcp->flags |= TCB_ATTACHED | TCB_STARTUP;
newoutf(tcp);
}
else {
* 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
*
* 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)
struct tcb *tcp;
int c, i;
int optF = 0;
+ unsigned int tcbi;
struct sigaction sa;
progname = argv[0] ? argv[0] : "strace";
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);
#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) {
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");
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();
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;
static struct tcb *
pid2tcb(int pid)
{
- int i;
+ unsigned int i;
if (pid <= 0)
return NULL;
for (i = 0; i < tcbtabsize; i++) {
struct tcb *tcp = tcbtab[i];
- if (tcp->pid == pid && (tcp->flags & TCB_INUSE))
+ if (tcp->pid == pid)
return tcp;
}
static void
cleanup(void)
{
- int i;
+ unsigned int i;
struct tcb *tcp;
int fatal_sig;
for (i = 0; i < tcbtabsize; i++) {
tcp = tcbtab[i];
- if (!(tcp->flags & TCB_INUSE))
+ if (!tcp->pid)
continue;
if (debug_flag)
fprintf(stderr,
"cleanup: looking at pid %u\n", tcp->pid);
- if (tcp->flags & TCB_STRACE_CHILD) {
+ if (tcp->pid == strace_child) {
kill(tcp->pid, SIGCONT);
kill(tcp->pid, fatal_sig);
}
interrupted = sig;
}
-static int
+static void
trace(void)
{
struct rusage ru;
- while (nprocs != 0) {
+ /* Used to be "while (nprocs != 0)", but in this testcase:
+ * int main() { _exit(!!fork()); }
+ * under strace -f, parent sometimes (rarely) manages
+ * to exit before we see the first stop of the child,
+ * and we are losing track of it:
+ * 19923 clone(...) = 19924
+ * 19923 exit_group(1) = ?
+ * 19923 +++ exited with 1 +++
+ * Waiting for ECHILD works better.
+ * (However, if -o|logger is in use, we can't do that.
+ * Can work around that by double-forking the logger,
+ * but that loses the ability to wait for its completion on exit.
+ * Oh well...)
+ */
+ while (1) {
int pid;
int wait_errno;
- int status, sig;
+ int status;
int stopped;
- struct tcb *tcp;
+ unsigned int sig;
unsigned event;
+ struct tcb *tcp;
if (interrupted)
- return 0;
+ return;
+
+ if (popen_pid != 0 && nprocs == 0)
+ return;
if (interactive)
sigprocmask(SIG_SETMASK, &empty_set, NULL);
if (pid < 0) {
if (wait_errno == EINTR)
continue;
- if (wait_errno == ECHILD)
- /* Should not happen since nprocs > 0 */
- return 0;
+ if (nprocs == 0 && wait_errno == ECHILD)
+ return;
+ /* If nprocs > 0, ECHILD is not expected,
+ * treat it as any other error here:
+ */
errno = wait_errno;
- perror_msg("wait4(__WALL)");
- return -1;
+ perror_msg_and_die("wait4(__WALL)");
}
if (pid == popen_pid) {
- if (WIFEXITED(status) || WIFSIGNALED(status))
+ if (!WIFSTOPPED(status))
popen_pid = 0;
continue;
}
event = ((unsigned)status >> 16);
if (debug_flag) {
char buf[sizeof("WIFEXITED,exitcode=%u") + sizeof(int)*3 /*paranoia:*/ + 16];
- char evbuf[sizeof(",PTRACE_EVENT_?? (%u)") + sizeof(int)*3 /*paranoia:*/ + 16];
+ char evbuf[sizeof(",EVENT_VFORK_DONE (%u)") + sizeof(int)*3 /*paranoia:*/ + 16];
strcpy(buf, "???");
if (WIFSIGNALED(status))
#ifdef WCOREDUMP
if (WIFSTOPPED(status))
sprintf(buf, "WIFSTOPPED,sig=%s", signame(WSTOPSIG(status)));
#ifdef WIFCONTINUED
+ /* Should never be seen */
if (WIFCONTINUED(status))
strcpy(buf, "WIFCONTINUED");
#endif
[PTRACE_EVENT_VFORK_DONE] = "VFORK_DONE",
[PTRACE_EVENT_EXEC] = "EXEC",
[PTRACE_EVENT_EXIT] = "EXIT",
+ /* [PTRACE_EVENT_STOP (=128)] would make biggish array */
};
- const char *e;
+ const char *e = "??";
if (event < ARRAY_SIZE(event_names))
e = event_names[event];
- else {
- sprintf(buf, "?? (%u)", event);
- e = buf;
- }
- sprintf(evbuf, ",PTRACE_EVENT_%s", e);
+ else if (event == PTRACE_EVENT_STOP)
+ e = "STOP";
+ sprintf(evbuf, ",EVENT_%s (%u)", e, event);
}
- fprintf(stderr, " [wait(0x%04x) = %u] %s%s\n", status, pid, buf, evbuf);
+ fprintf(stderr, " [wait(0x%06x) = %u] %s%s\n", status, pid, buf, evbuf);
}
/* Look up 'pid' in our table. */
tcp = pid2tcb(pid);
if (!tcp) {
+ if (!WIFSTOPPED(status)) {
+ /* This can happen if we inherited
+ * an unknown child. Example:
+ * (sleep 1 & exec strace sleep 2)
+ */
+ error_msg("Exit of unknown pid %u seen", pid);
+ continue;
+ }
if (followfork) {
+ /* We assume it's a fork/vfork/clone child */
tcp = alloctcb(pid);
tcp->flags |= TCB_ATTACHED | TCB_STARTUP | post_attach_sigstop;
newoutf(tcp);
pid);
} else {
/* This can happen if a clone call used
- CLONE_PTRACE itself. */
- if (WIFSTOPPED(status))
- ptrace(PTRACE_CONT, pid, (char *) 0, 0);
- error_msg_and_die("Unknown pid: %u", pid);
+ * CLONE_PTRACE itself.
+ */
+ ptrace(PTRACE_CONT, pid, (char *) 0, 0);
+ error_msg("Stop of unknown pid %u seen, PTRACE_CONTed it", pid);
+ continue;
}
}
if (ptrace(PTRACE_GETEVENTMSG, pid, NULL, (long) &old_pid) < 0)
goto dont_switch_tcbs;
+ /* Avoid truncation in pid2tcb() param passing */
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)
if (clearbpt(tcp) < 0) {
/* Pretty fatal */
droptcb(tcp);
- cleanup();
- return -1;
+ exit_code = 1;
+ 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 */
&& !hide_log_until_execve
&& (qual_flags[sig] & QUAL_SIGNAL)
) {
-#if defined(PT_CR_IPSR) && defined(PT_CR_IIP)
- long pc = 0;
- long psr = 0;
-
- upeek(tcp, PT_CR_IPSR, &psr);
- upeek(tcp, PT_CR_IIP, &pc);
-
-# define PSR_RI 41
- pc += (psr >> PSR_RI) & 0x3;
-# define PC_FORMAT_STR " @ %lx"
-# define PC_FORMAT_ARG , pc
-#else
-# define PC_FORMAT_STR ""
-# define PC_FORMAT_ARG /* nothing */
-#endif
printleader(tcp);
if (!stopped) {
tprintf("--- %s ", signame(sig));
printsiginfo(&si, verbose(tcp));
- tprintf(PC_FORMAT_STR " ---\n"
- PC_FORMAT_ARG);
+ tprints(" ---\n");
} else
- tprintf("--- stopped by %s" PC_FORMAT_STR " ---\n",
- signame(sig)
- PC_FORMAT_ARG);
+ tprintf("--- stopped by %s ---\n",
+ signame(sig));
line_ended();
}
* (that is, process really stops. It used to continue to run).
*/
if (ptrace_restart(PTRACE_LISTEN, tcp, 0) < 0) {
- cleanup();
- return -1;
+ /* Note: ptrace_restart emitted error message */
+ exit_code = 1;
+ return;
}
continue;
}
/* We handled quick cases, we are permitted to interrupt now. */
if (interrupted)
- return 0;
+ return;
/* This should be syscall entry or exit.
* (Or it still can be that pesky post-execve SIGTRAP!)
sig = 0;
restart_tracee:
if (ptrace_restart(PTRACE_SYSCALL, tcp, sig) < 0) {
- cleanup();
- return -1;
+ /* Note: ptrace_restart emitted error message */
+ exit_code = 1;
+ return;
}
- }
- return 0;
+ } /* while (1) */
}
int
init(argc, argv);
/* Run main tracing loop */
- if (trace() < 0)
- return 1;
+ trace();
cleanup();
fflush(NULL);