From b13c0de058585de7d47778a8923426b89bfffbb5 Mon Sep 17 00:00:00 2001 From: Wang Chao Date: Fri, 12 Nov 2010 17:25:19 +0800 Subject: [PATCH] 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 --- defs.h | 26 ++++++++++++++++++ strace.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) diff --git a/defs.h b/defs.h index fc7e362a..eee47109 100644 --- 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; diff --git a/strace.c b/strace.c index 497b8d15..09aedacd 100644 --- 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 -- 2.40.0