struct timeval etime; /* Syscall entry time */
/* Support for tracing forked processes */
struct tcb *parent; /* Parent of this process */
-#ifdef LINUX
- int nclone_threads; /* # of children with CLONE_THREAD */
-#endif
- /* (1st arg of wait4()) */
long baddr; /* `Breakpoint' address */
long inst[2]; /* Instructions on above */
int pfd; /* proc file descriptor */
#define TCB_INUSE 00002 /* This table entry is in use */
#define TCB_INSYSCALL 00004 /* A system call is in progress */
#define TCB_ATTACHED 00010 /* Process is not our own child */
-#define TCB_EXITING 00020 /* As far as we know, this process is exiting */
#define TCB_SUSPENDED 00040 /* Process can not be allowed to resume just now */
#define TCB_BPTSET 00100 /* "Breakpoint" set after fork(2) */
#define TCB_SIGTRAPPED 00200 /* Process wanted to block SIGTRAP */
# define TCB_WAITEXECVE 04000 /* ignore SIGTRAP after exceve */
# endif
# define TCB_CLONE_THREAD 010000 /* CLONE_THREAD set in creating syscall */
-# define TCB_GROUP_EXITING 020000 /* TCB_EXITING was exit_group, not _exit */
# include <sys/syscall.h>
# ifndef __NR_exit_group
# /* Hack: Most headers around are too old to have __NR_exit_group. */
* On newer kernels, we use PTRACE_O_TRACECLONE/TRACE[V]FORK instead.
*/
extern int setbpt(struct tcb *);
-extern int sigishandled(struct tcb *, int);
extern void printcall(struct tcb *);
extern const char *signame(int);
extern void print_sigset(struct tcb *, long, int);
extern int change_syscall(struct tcb *, int);
extern int internal_fork(struct tcb *);
extern int internal_exec(struct tcb *);
-extern int internal_exit(struct tcb *);
#ifdef LINUX
extern int handle_new_child(struct tcb *, int, int);
#endif
#endif /* SVR4 || LINUX */
-#ifdef LINUX
-
-static void
-parse_sigset_t(const char *str, sigset_t *set)
-{
- const char *p;
- unsigned int digit;
- int i;
-
- sigemptyset(set);
-
- p = strchr(str, '\n');
- if (p == NULL)
- p = strchr(str, '\0');
- for (i = 0; p-- > str; i += 4) {
- if (*p >= '0' && *p <= '9')
- digit = *p - '0';
- else if (*p >= 'a' && *p <= 'f')
- digit = *p - 'a' + 10;
- else if (*p >= 'A' && *p <= 'F')
- digit = *p - 'A' + 10;
- else
- break;
- if (digit & 1)
- sigaddset(set, i + 1);
- if (digit & 2)
- sigaddset(set, i + 2);
- if (digit & 4)
- sigaddset(set, i + 3);
- if (digit & 8)
- sigaddset(set, i + 4);
- }
-}
-
-#endif
-
-/*
- * Check process TCP for the disposition of signal SIG.
- * Return 1 if the process would somehow manage to survive signal SIG,
- * else return 0. This routine will never be called with SIGKILL.
- */
-int
-sigishandled(struct tcb *tcp, int sig)
-{
-#ifdef LINUX
- int sfd;
- char sname[32];
- char buf[2048];
- const char *s;
- int i;
- sigset_t ignored, caught;
-#endif
-#ifdef SVR4
- /*
- * Since procfs doesn't interfere with wait I think it is safe
- * to punt on this question. If not, the information is there.
- */
- return 1;
-#else /* !SVR4 */
- switch (sig) {
- case SIGCONT:
- case SIGSTOP:
- case SIGTSTP:
- case SIGTTIN:
- case SIGTTOU:
- case SIGCHLD:
- case SIGIO:
-#if defined(SIGURG) && SIGURG != SIGIO
- case SIGURG:
-#endif
- case SIGWINCH:
- /* Gloria Gaynor says ... */
- return 1;
- default:
- break;
- }
-#endif /* !SVR4 */
-#ifdef LINUX
-
- /* This is incredibly costly but it's worth it. */
- /* NOTE: LinuxThreads internally uses SIGRTMIN, SIGRTMIN + 1 and
- SIGRTMIN + 2, so we can't use the obsolete /proc/%d/stat which
- doesn't handle real-time signals). */
- sprintf(sname, "/proc/%d/status", tcp->pid);
- if ((sfd = open(sname, O_RDONLY)) == -1) {
- perror(sname);
- return 1;
- }
- i = read(sfd, buf, sizeof(buf));
- buf[i] = '\0';
- close(sfd);
- /*
- * Skip the extraneous fields. We need to skip
- * command name has any spaces in it. So be it.
- */
- s = strstr(buf, "SigIgn:\t");
- if (!s) {
- fprintf(stderr, "/proc/pid/status format error\n");
- return 1;
- }
- parse_sigset_t(s + 8, &ignored);
-
- s = strstr(buf, "SigCgt:\t");
- if (!s) {
- fprintf(stderr, "/proc/pid/status format error\n");
- return 1;
- }
- parse_sigset_t(s + 8, &caught);
-
-#ifdef DEBUG
- fprintf(stderr, "sigs: %016qx %016qx (sig=%d)\n",
- *(long long *) &ignored, *(long long *) &caught, sig);
-#endif
- if (sigismember(&ignored, sig) || sigismember(&caught, sig))
- return 1;
-#endif /* LINUX */
-
-#ifdef SUNOS4
- void (*u_signal)();
-
- if (upeek(tcp, uoff(u_signal[0]) + sig*sizeof(u_signal),
- (long *) &u_signal) < 0) {
- return 0;
- }
- if (u_signal != SIG_DFL)
- return 1;
-#endif /* SUNOS4 */
-
- return 0;
-}
-
#if defined(SUNOS4) || defined(FREEBSD)
int
if (tid != tcbtab[tcbi]->pid) {
tcp = alloctcb(tid);
tcp->flags |= TCB_ATTACHED|TCB_CLONE_THREAD;
- tcbtab[tcbi]->nclone_threads++;
tcp->parent = tcbtab[tcbi];
}
}
{
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--;
if (debug)
fprintf(stderr, "dropped tcb for pid %d, %d remain\n", tcp->pid, nprocs);
- tcp->pid = 0;
- if (tcp->parent != NULL) {
-#ifdef TCB_CLONE_THREAD
- if (tcp->flags & TCB_CLONE_THREAD)
- tcp->parent->nclone_threads--;
-#endif
-#ifdef LINUX
- /* Update 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;
- }
-
- tcp->flags = 0;
if (tcp->pfd != -1) {
close(tcp->pfd);
tcp->pfd = -1;
}
#endif /* !FREEBSD */
#ifdef USE_PROCFS
- rebuild_pollv(); /* Note, flags needs to be cleared by now. */
+ 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));
}
/* detach traced process; continue with 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)
droptcb(tcp);
-#ifdef LINUX
- if (zombie != NULL) {
- /* TCP no longer exists therefore you must not detach() it. */
- droptcb(zombie);
- }
-#endif
-
return error;
}
#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. */
- 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)
#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 ((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)) {
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;
}
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);