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>
This commit is contained in:
Wang Chao 2010-11-12 17:25:19 +08:00 committed by Dmitry V. Levin
parent 09fa7f8765
commit b13c0de058
2 changed files with 107 additions and 0 deletions

26
defs.h
View File

@ -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;

View File

@ -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