#include <pwd.h>
#include <grp.h>
#include <string.h>
-#include <limits.h>
#include <dirent.h>
#ifdef LINUX
# include <asm/unistd.h>
-# 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
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;
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);
#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\
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
# 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 */
}
/*
#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
}
#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"
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
* 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);
}
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) {
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);
}
}
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) {
/*
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;
if (strchr(filename, '/')) {
if (strlen(filename) > sizeof pathname - 1) {
errno = ENAMETOOLONG;
- perror("strace: exec");
- exit(1);
+ perror_msg_and_die("exec");
}
strcpy(pathname, filename);
}
}
}
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;
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);
*/
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");
}
}
}
#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. */
}
#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;
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.");
}
/*
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 */
}
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) {
/*
* 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
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;
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;
break;
case 'a':
acolumn = atoi(optarg);
+ if (acolumn < 0)
+ error_msg_and_die("Bad column width '%s'", optarg);
break;
case 'e':
qualify(optarg);
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);
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':
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);
}
}
+ 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. */
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;
}
#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
* We can't do the <outfname>.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;
exit(exit_code);
}
-void
+static void
expand_tcbtab(void)
{
/* Allocate some more TCBs and expand the table.
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 *
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
#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;
}
* 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
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) {
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");
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");
}
#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;
}
#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;
}
#endif
for (;;) {
/* Wait for the child to do something. */
- if (IOCTL_WSTOP (tcp) < 0) {
+ if (IOCTL_WSTOP(tcp) < 0) {
perror("PIOCWSTOP");
return -1;
}
/* 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;
}
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,
}
static struct tcb *
-pfd2tcb(pfd)
-int pfd;
+pfd2tcb(int pfd)
{
int i;
#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;
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)
* 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");
}
break;
}
error = ptrace_restart(PTRACE_CONT, tcp,
- WSTOPSIG(status) == SYSCALLTRAP ? 0
+ WSTOPSIG(status) == syscall_trap_sig ? 0
: WSTOPSIG(status));
if (error < 0)
break;
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;
}
#endif /* USE_PROCFS */
static void
-cleanup()
+cleanup(void)
{
int i;
struct tcb *tcp;
}
static void
-interrupt(sig)
-int sig;
+interrupt(int sig)
{
interrupted = 1;
}
#endif /* HAVE_DECL_SYS_ERRLIST */
const char *
-strerror(err_no)
-int err_no;
+strerror(int err_no)
{
static char buf[64];
#endif
const char *
-strsignal(sig)
-int sig;
+strsignal(int sig)
{
static char buf[64];
#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];
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)
}
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;
switch (fork()) {
case -1:
- perror("fork");
- _exit(1);
+ perror_msg_and_die("fork");
case 0:
break;
default:
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++) {
* 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;
}
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;
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;
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;
}
/* 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:
/* 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
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
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",
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;
#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:
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;
#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()
{
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) {
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;
}
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
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
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(" <unfinished ... exit status %d>\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)) {
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;
}
/*
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);
}
}
#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");
}
}
}
goto tracing;
}
- if (WSTOPSIG(status) != SYSCALLTRAP) {
+ if (WSTOPSIG(status) != syscall_trap_sig) {
if (WSTOPSIG(status) == SIGSTOP &&
(tcp->flags & TCB_SIGTRAPPED)) {
/*
# 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.
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(" <unfinished ...>");
}
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;
#endif /* !USE_PROCFS */
-#include <stdarg.h>
-
void
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
+ ? "<writing to pipe>" : outfname);
+ }
+}
+
+void
+printleader(struct tcb *tcp)
{
if (tcp_last) {
if (tcp_last->ptrace_errno) {
if (tcp_last->flags & TCB_INSYSCALL) {
- tprintf(" <unavailable>)");
- tabto(acolumn);
+ tprintf(" <unavailable>) ");
+ tabto();
}
tprintf("= ? <unavailable>\n");
tcp_last->ptrace_errno = 0;
}
void
-tabto(col)
-int col;
+tabto(void)
{
- if (curcol < col)
- tprintf("%*s", col - curcol, "");
+ if (curcol < acolumn)
+ tprints(acolumn_spaces + curcol);
}
void