[PATCH/v2 2/2] Handle followfork using ptrace_setoptions if available
Wang Chao
wang.chao at cn.fujitsu.com
Thu Oct 7 02:31:44 UTC 2010
If PTRACE_O_TRACECLONE et al options are supported by kernel,
we use them to do followfork rather than the original setbpt
method which change registers ourselves.
* process.c [LINUX](internal_fork): Do nothing if these options
are supported by kernel.
[LINUX](handle_new_child): New common function which move most
work at exit point of internal_fork here, except a trivial
change: only do reparent work for syscall clone.
* strace.c [LINUX](handle_ptrace_event): New function.
[LINUX](trace): If ptrace_setoptions is in effect:
1) Call the new function to handle PTRACE_EVENT_* status.
2) Set PTRACE_SETOPTIONS when we see the initial stop of tracee.
Signed-off-by: Wang Chao <wang.chao at cn.fujitsu.com>
---
defs.h | 3 +
process.c | 224 ++++++++++++++++++++++++++++++++----------------------------
strace.c | 34 +++++++++
3 files changed, 156 insertions(+), 105 deletions(-)
diff --git a/defs.h b/defs.h
index eee4710..7a7a071 100644
--- a/defs.h
+++ b/defs.h
@@ -571,6 +571,9 @@ extern int internal_fork(struct tcb *);
extern int internal_exec(struct tcb *);
extern int internal_wait(struct tcb *, int);
extern int internal_exit(struct tcb *);
+#ifdef LINUX
+extern int handle_new_child(struct tcb *, int, int);
+#endif
extern const struct ioctlent *ioctl_lookup(long);
extern const struct ioctlent *ioctl_next_match(const struct ioctlent *);
diff --git a/process.c b/process.c
index 2366d1f..7b3eda1 100644
--- a/process.c
+++ b/process.c
@@ -793,8 +793,126 @@ change_syscall(struct tcb *tcp, int new)
#ifdef LINUX
int
+handle_new_child(struct tcb *tcp, int pid, int bpt)
+{
+ struct tcb *tcpchild;
+
+#ifdef CLONE_PTRACE /* See new setbpt code. */
+ tcpchild = pid2tcb(pid);
+ if (tcpchild != NULL) {
+ /* The child already reported its startup trap
+ before the parent reported its syscall return. */
+ if ((tcpchild->flags
+ & (TCB_STARTUP|TCB_ATTACHED|TCB_SUSPENDED))
+ != (TCB_STARTUP|TCB_ATTACHED|TCB_SUSPENDED))
+ fprintf(stderr, "\
+[preattached child %d of %d in weird state!]\n",
+ pid, tcp->pid);
+ }
+ else
+#endif /* CLONE_PTRACE */
+ {
+ fork_tcb(tcp);
+ tcpchild = alloctcb(pid);
+ }
+
+#ifndef CLONE_PTRACE
+ /* Attach to the new child */
+ if (ptrace(PTRACE_ATTACH, pid, (char *) 1, 0) < 0) {
+ if (bpt)
+ clearbpt(tcp);
+ perror("PTRACE_ATTACH");
+ fprintf(stderr, "Too late?\n");
+ droptcb(tcpchild);
+ return 0;
+ }
+#endif /* !CLONE_PTRACE */
+
+ if (bpt)
+ clearbpt(tcp);
+
+ tcpchild->flags |= TCB_ATTACHED;
+ /* Child has BPT too, must be removed on first occasion. */
+ if (bpt) {
+ tcpchild->flags |= TCB_BPTSET;
+ tcpchild->baddr = tcp->baddr;
+ memcpy(tcpchild->inst, tcp->inst,
+ sizeof tcpchild->inst);
+ }
+ tcpchild->parent = tcp;
+ tcp->nchildren++;
+ if (tcpchild->flags & TCB_SUSPENDED) {
+ /* The child was born suspended, due to our having
+ forced CLONE_PTRACE. */
+ if (bpt)
+ clearbpt(tcpchild);
+
+ tcpchild->flags &= ~(TCB_SUSPENDED|TCB_STARTUP);
+ if (ptrace_restart(PTRACE_SYSCALL, tcpchild, 0) < 0)
+ return -1;
+
+ if (!qflag)
+ fprintf(stderr, "\
+Process %u resumed (parent %d ready)\n",
+ pid, tcp->pid);
+ }
+ else {
+ if (!qflag)
+ fprintf(stderr, "Process %d attached\n", pid);
+ }
+
+#ifdef TCB_CLONE_THREAD
+ if (sysent[tcp->scno].sys_func == sys_clone)
+ {
+ /*
+ * Save the flags used in this call,
+ * in case we point TCP to our parent below.
+ */
+ int call_flags = tcp->u_arg[ARG_FLAGS];
+ if ((tcp->flags & TCB_CLONE_THREAD) &&
+ tcp->parent != NULL) {
+ /* The parent in this clone is itself a
+ thread belonging to another process.
+ There is no meaning to the parentage
+ relationship of the new child with the
+ thread, only with the process. We
+ associate the new thread with our
+ parent. Since this is done for every
+ new thread, there will never be a
+ TCB_CLONE_THREAD process that has
+ children. */
+ --tcp->nchildren;
+ tcp = tcp->parent;
+ tcpchild->parent = tcp;
+ ++tcp->nchildren;
+ }
+ if (call_flags & CLONE_THREAD) {
+ tcpchild->flags |= TCB_CLONE_THREAD;
+ ++tcp->nclone_threads;
+ }
+ if ((call_flags & CLONE_PARENT) &&
+ !(call_flags & CLONE_THREAD)) {
+ --tcp->nchildren;
+ tcpchild->parent = NULL;
+ if (tcp->parent != NULL) {
+ tcp = tcp->parent;
+ tcpchild->parent = tcp;
+ ++tcp->nchildren;
+ }
+ }
+ }
+#endif /* TCB_CLONE_THREAD */
+ return 0;
+}
+
+int
internal_fork(struct tcb *tcp)
{
+ if ((ptrace_setoptions
+ & (PTRACE_O_TRACECLONE | PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK))
+ == (PTRACE_O_TRACECLONE | PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK))
+ return 0;
+
if (entering(tcp)) {
tcp->flags &= ~TCB_FOLLOWFORK;
if (!followfork)
@@ -811,7 +929,6 @@ internal_fork(struct tcb *tcp)
if (setbpt(tcp) < 0)
return 0;
} else {
- struct tcb *tcpchild;
int pid;
int bpt;
@@ -828,110 +945,7 @@ internal_fork(struct tcb *tcp)
pid = tcp->u_rval;
-#ifdef CLONE_PTRACE /* See new setbpt code. */
- tcpchild = pid2tcb(pid);
- if (tcpchild != NULL) {
- /* The child already reported its startup trap
- before the parent reported its syscall return. */
- if ((tcpchild->flags
- & (TCB_STARTUP|TCB_ATTACHED|TCB_SUSPENDED))
- != (TCB_STARTUP|TCB_ATTACHED|TCB_SUSPENDED))
- fprintf(stderr, "\
-[preattached child %d of %d in weird state!]\n",
- pid, tcp->pid);
- }
- else
-#endif /* CLONE_PTRACE */
- {
- fork_tcb(tcp);
- tcpchild = alloctcb(pid);
- }
-
-#ifndef CLONE_PTRACE
- /* Attach to the new child */
- if (ptrace(PTRACE_ATTACH, pid, (char *) 1, 0) < 0) {
- if (bpt)
- clearbpt(tcp);
- perror("PTRACE_ATTACH");
- fprintf(stderr, "Too late?\n");
- droptcb(tcpchild);
- return 0;
- }
-#endif /* !CLONE_PTRACE */
-
- if (bpt)
- clearbpt(tcp);
-
- tcpchild->flags |= TCB_ATTACHED;
- /* Child has BPT too, must be removed on first occasion. */
- if (bpt) {
- tcpchild->flags |= TCB_BPTSET;
- tcpchild->baddr = tcp->baddr;
- memcpy(tcpchild->inst, tcp->inst,
- sizeof tcpchild->inst);
- }
- tcpchild->parent = tcp;
- tcp->nchildren++;
- if (tcpchild->flags & TCB_SUSPENDED) {
- /* The child was born suspended, due to our having
- forced CLONE_PTRACE. */
- if (bpt)
- clearbpt(tcpchild);
-
- tcpchild->flags &= ~(TCB_SUSPENDED|TCB_STARTUP);
- if (ptrace_restart(PTRACE_SYSCALL, tcpchild, 0) < 0)
- return -1;
-
- if (!qflag)
- fprintf(stderr, "\
-Process %u resumed (parent %d ready)\n",
- pid, tcp->pid);
- }
- else {
- if (!qflag)
- fprintf(stderr, "Process %d attached\n", pid);
- }
-
-#ifdef TCB_CLONE_THREAD
- {
- /*
- * Save the flags used in this call,
- * in case we point TCP to our parent below.
- */
- int call_flags = tcp->u_arg[ARG_FLAGS];
- if ((tcp->flags & TCB_CLONE_THREAD) &&
- tcp->parent != NULL) {
- /* The parent in this clone is itself a
- thread belonging to another process.
- There is no meaning to the parentage
- relationship of the new child with the
- thread, only with the process. We
- associate the new thread with our
- parent. Since this is done for every
- new thread, there will never be a
- TCB_CLONE_THREAD process that has
- children. */
- --tcp->nchildren;
- tcp = tcp->parent;
- tcpchild->parent = tcp;
- ++tcp->nchildren;
- }
- if (call_flags & CLONE_THREAD) {
- tcpchild->flags |= TCB_CLONE_THREAD;
- ++tcp->nclone_threads;
- }
- if ((call_flags & CLONE_PARENT) &&
- !(call_flags & CLONE_THREAD)) {
- --tcp->nchildren;
- tcpchild->parent = NULL;
- if (tcp->parent != NULL) {
- tcp = tcp->parent;
- tcpchild->parent = tcp;
- ++tcp->nchildren;
- }
- }
- }
-#endif /* TCB_CLONE_THREAD */
+ return handle_new_child(tcp, pid, bpt);
}
return 0;
}
diff --git a/strace.c b/strace.c
index 09aedac..5316ce7 100644
--- a/strace.c
+++ b/strace.c
@@ -2363,6 +2363,28 @@ handle_group_exit(struct tcb *tcp, int sig)
}
#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) {
+ int childpid;
+
+ if (do_ptrace(PTRACE_GETEVENTMSG, tcp, NULL, &childpid) < 0) {
+ if (errno != ESRCH) {
+ cleanup();
+ exit(1);
+ }
+ return -1;
+ }
+ return handle_new_child(tcp, childpid, 0);
+ }
+ return 1;
+}
+#endif
+
static int
trace()
{
@@ -2548,6 +2570,11 @@ Process %d attached (waiting for parent)\n",
fprintf(stderr, "pid %u stopped, [%s]\n",
pid, signame(WSTOPSIG(status)));
+ if (ptrace_setoptions && (status >> 16)) {
+ if (handle_ptrace_event(status, tcp) != 1)
+ goto tracing;
+ }
+
/*
* Interestingly, the process may stop
* with STOPSIG equal to some other signal
@@ -2575,6 +2602,13 @@ Process %d attached (waiting for parent)\n",
return -1;
}
}
+#ifdef LINUX
+ if (followfork && (tcp->parent == NULL) && ptrace_setoptions)
+ if (ptrace(PTRACE_SETOPTIONS, tcp->pid,
+ NULL, ptrace_setoptions) < 0 &&
+ errno != ESRCH)
+ ptrace_setoptions = 0;
+#endif
goto tracing;
}
--
1.6.5.2
More information about the Strace-devel
mailing list