[PATCH 3/3] Handle followfork using ptrace_setoptions if available

Wang Chao wang.chao at cn.fujitsu.com
Thu Sep 16 08:35:36 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](reparent): Need not to do reparent for *fork syscalls.
* 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>
---
 process.c |    7 ++++++
 strace.c  |   62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 69 insertions(+), 0 deletions(-)

diff --git a/process.c b/process.c
index 3cf6d28..740d66f 100644
--- a/process.c
+++ b/process.c
@@ -794,6 +794,8 @@ change_syscall(struct tcb *tcp, int new)
 #ifdef LINUX
 void reparent(struct tcb *tcp, struct tcb *tcpchild)
 {
+	if (sysent[tcp->scno].sys_func != sys_clone)
+		return;
 	/*
 	 * Save the flags used in this call,
 	 * in case we point TCP to our parent below.
@@ -835,6 +837,11 @@ void reparent(struct tcb *tcp, struct tcb *tcpchild)
 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)) {
 		if (!followfork)
 			return 0;
diff --git a/strace.c b/strace.c
index e694f17..299fb55 100644
--- a/strace.c
+++ b/strace.c
@@ -2356,6 +2356,56 @@ handle_group_exit(struct tcb *tcp, int sig)
 }
 #endif
 
+#ifdef LINUX
+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) {
+		struct tcb *tcpchild = NULL;
+		int childpid;
+
+		ptrace(PTRACE_GETEVENTMSG, tcp->pid, NULL, &childpid);
+		tcpchild = pid2tcb(childpid);
+		if (tcpchild == NULL) {
+			tcpchild = alloctcb(childpid);
+		}
+		else 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",
+					childpid, tcp->pid);
+
+		tcpchild->flags |= TCB_ATTACHED;
+		if (tcpchild->flags & TCB_SUSPENDED) {
+			tcpchild->flags &= ~TCB_SUSPENDED;
+			if (ptrace_restart(PTRACE_SYSCALL, tcpchild, 0) < 0) {
+				fprintf(stderr, "Restart suspended child %d with error\n",
+					tcpchild->pid);
+				return 1;
+			}
+
+			if (!qflag)
+				fprintf(stderr,
+					"Process %u resumed (parent %d ready)\n",
+					childpid, tcp->pid);
+		}
+		else {
+			if (!qflag)
+				fprintf(stderr, "Process %d attached\n", childpid);
+		}
+		tcpchild->parent = tcp;
+		++tcp->nchildren;
+#ifdef TCB_CLONE_THREAD
+		reparent(tcp, tcpchild);
+#endif
+		return 1;
+	}
+	return 0;
+}
+#endif
+
 static int
 trace()
 {
@@ -2541,6 +2591,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))
+				goto tracing;
+		}
+
 		/*
 		 * Interestingly, the process may stop
 		 * with STOPSIG equal to some other signal
@@ -2568,6 +2623,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)
+					/* should not happen. */
+					ptrace_setoptions = 0;
+#endif
 			goto tracing;
 		}
 
-- 
1.6.5.2





More information about the Strace-devel mailing list