Handle followfork using ptrace_setoptions if available
If PTRACE_O_TRACECLONE et al options are supported by kernel,
use them to do followfork rather than the original setbpt
method that changes registers ourselves.
* defs.h [LINUX] (handle_new_child): New function prototype.
* process.c [LINUX] (handle_new_child): New function based on the
code from internal_fork(), with a trivial change: do reparent only
for sys_clone.
[LINUX] (internal_fork): Use handle_new_child(). Do nothing if
ptrace_setoptions is in effect.
* strace.c [LINUX] (handle_ptrace_event): New function.
[LINUX] (trace): If ptrace_setoptions is in effect, then
call the new function to handle PTRACE_EVENT_* status, and
set PTRACE_SETOPTIONS when we see the initial stop of tracee.
Signed-off-by: Wang Chao <wang.chao@cn.fujitsu.com>
diff --git a/strace.c b/strace.c
index 09aedac..6f63ee9 100644
--- a/strace.c
+++ b/strace.c
@@ -2363,6 +2363,31 @@
}
#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) {
+ 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);
+ }
+ return 1;
+}
+#endif
+
static int
trace()
{
@@ -2548,6 +2573,11 @@
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 +2605,13 @@
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;
}