[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