Test how PTRACE_SETOPTIONS support works
Currently test fork related options only. Fork a child that uses
PTRACE_TRACEME at startup and then does a fork so strace can test
how the PTRACE_SETOPTIONS support works before it handles any real
tracee. Since PTRACE_O_TRACECLONE/*FORK were introduced to kernel
at the same time, this test seems to be enough for these 3 options.
* defs.h [LINUX]: Define PTRACE_O_TRACECLONE et al macros here.
(ptrace_setoptions): New variable declaration.
* strace.c [LINUX] (test_ptrace_setoptions): New function, tests
whether kernel supports PTRACE_O_CLONE/*FORK, the result is stored
in the new variable ptrace_setoptions for later use.
(main): Call test_ptrace_setoptions() if followfork option is set.
Signed-off-by: Wang Chao <wang.chao@cn.fujitsu.com>
diff --git a/strace.c b/strace.c
index 497b8d1..09aedac 100644
--- a/strace.c
+++ b/strace.c
@@ -83,6 +83,7 @@
int debug = 0, followfork = 0;
+unsigned int ptrace_setoptions = 0;
int dtime = 0, xflag = 0, qflag = 0;
cflag_t cflag = CFLAG_NONE;
static int iflag = 0, interactive = 0, pflag_seen = 0, rflag = 0, tflag = 0;
@@ -686,6 +687,77 @@
#endif /* USE_PROCFS */
}
+#ifdef LINUX
+/*
+ * Test whether kernel support PTRACE_O_TRACECLONE et al options.
+ * First fork a new child, call ptrace with PTRACE_SETOPTIONS on it,
+ * and then see which options are supported on this kernel.
+ */
+static int
+test_ptrace_setoptions(void)
+{
+ int pid;
+
+ if ((pid = fork()) < 0)
+ return -1;
+ else if (pid == 0) {
+ if (ptrace(PTRACE_TRACEME, 0, (char *)1, 0) < 0) {
+ _exit(1);
+ }
+ kill(getpid(), SIGSTOP);
+ if ((pid = fork()) < 0) {
+ _exit(1);
+ }
+ _exit(0);
+ }
+ else {
+ int status, tracee_pid, error;
+ int no_child = 0;
+ while (1) {
+ tracee_pid = wait4(-1, &status, 0, NULL);
+ error = errno;
+ if (tracee_pid == -1) {
+ switch (error) {
+ case EINTR:
+ continue;
+ case ECHILD:
+ no_child = 1;
+ break;
+ default:
+ errno = error;
+ perror("test_ptrace_setoptions");
+ return -1;
+ }
+ }
+ if (no_child)
+ break;
+ if (tracee_pid != pid) {
+ if (ptrace(PTRACE_CONT, tracee_pid, 0, 0) < 0 &&
+ errno != ESRCH)
+ kill(tracee_pid, SIGKILL);
+ }
+ else if (WIFSTOPPED(status)) {
+ if (status >> 16 == PTRACE_EVENT_FORK)
+ ptrace_setoptions |= (PTRACE_O_TRACEVFORK |
+ PTRACE_O_TRACECLONE |
+ PTRACE_O_TRACEFORK);
+ if (WSTOPSIG(status) == SIGSTOP) {
+ if (ptrace(PTRACE_SETOPTIONS, pid, NULL,
+ PTRACE_O_TRACEFORK) < 0) {
+ kill(pid, SIGKILL);
+ return -1;
+ }
+ }
+ if (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0 &&
+ errno != ESRCH)
+ kill(pid, SIGKILL);
+ }
+ }
+ }
+ return 0;
+}
+#endif
+
int
main(int argc, char *argv[])
{
@@ -914,6 +986,15 @@
interactive = 0;
qflag = 1;
}
+
+#ifdef LINUX
+ if (followfork && test_ptrace_setoptions() < 0) {
+ fprintf(stderr, "Test for options supported by PTRACE_SETOPTIONS\
+ failed, give up using this feature\n");
+ ptrace_setoptions = 0;
+ }
+#endif
+
/* Valid states here:
optind < argc pflag_seen outfname interactive
1 0 0 1