]> granicus.if.org Git - strace/commitdiff
Test how PTRACE_SETOPTIONS support works
authorWang Chao <wang.chao@cn.fujitsu.com>
Fri, 12 Nov 2010 09:25:19 +0000 (17:25 +0800)
committerDmitry V. Levin <ldv@altlinux.org>
Tue, 30 Nov 2010 17:19:09 +0000 (17:19 +0000)
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>
defs.h
strace.c

diff --git a/defs.h b/defs.h
index fc7e362add39d94444b19549b06058736cb38a0d..eee4710963b3941d193c46e0f95bbedc530d13a6 100644 (file)
--- a/defs.h
+++ b/defs.h
@@ -308,6 +308,31 @@ extern int mp_ioctl (int f, int c, void *a, int s);
 #define PR_FAULTED      S_CORE
 #endif
 
+#ifdef LINUX
+# ifndef PTRACE_SETOPTIONS
+#  define PTRACE_SETOPTIONS    0x4200
+# endif
+# ifndef PTRACE_O_TRACEFORK
+#  define PTRACE_O_TRACEFORK   0x00000002
+# endif
+# ifndef PTRACE_O_TRACEVFORK
+#  define PTRACE_O_TRACEVFORK  0x00000004
+# endif
+# ifndef PTRACE_O_TRACECLONE
+#  define PTRACE_O_TRACECLONE  0x00000008
+# endif
+
+# ifndef PTRACE_EVENT_FORK
+#  define PTRACE_EVENT_FORK    1
+# endif
+# ifndef PTRACE_EVENT_VFORK
+#  define PTRACE_EVENT_VFORK   2
+# endif
+# ifndef PTRACE_EVENT_CLONE
+#  define PTRACE_EVENT_CLONE   3
+# endif
+#endif /* LINUX */
+
 /* Trace Control Block */
 struct tcb {
        short flags;            /* See below for TCB_ values */
@@ -470,6 +495,7 @@ typedef enum {
 extern struct tcb **tcbtab;
 extern int *qual_flags;
 extern int debug, followfork;
+extern unsigned int ptrace_setoptions;
 extern int dtime, xflag, qflag;
 extern cflag_t cflag;
 extern int acolumn;
index 497b8d15ca5fb09f5b4b1eb08e854f6a2ef1266e..09aedacd3abb2122f9bb19f6378b2841be613516 100644 (file)
--- a/strace.c
+++ b/strace.c
@@ -83,6 +83,7 @@ extern char *optarg;
 
 
 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 @@ startup_child (char **argv)
 #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 @@ main(int argc, char *argv[])
                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