diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2012-01-27 15:37:13 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2012-01-27 15:37:13 +0100 |
commit | c76ae4352e4893ec0c93010ee1e9b7ddc0056804 (patch) | |
tree | 021b7d2e0f5ca1c092ca8730edae3a606cb35263 | |
parent | eebb04d4ae8bf4b08a041f5ea442ca24c90692c2 (diff) | |
download | strace-c76ae4352e4893ec0c93010ee1e9b7ddc0056804.tar.gz strace-c76ae4352e4893ec0c93010ee1e9b7ddc0056804.tar.bz2 strace-c76ae4352e4893ec0c93010ee1e9b7ddc0056804.tar.xz |
Add new test program: test/threaded_execve.c
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | test/.gitignore | 1 | ||||
-rw-r--r-- | test/Makefile | 4 | ||||
-rw-r--r-- | test/threaded_execve.c | 138 |
3 files changed, 141 insertions, 2 deletions
diff --git a/test/.gitignore b/test/.gitignore index 5c689bd..7d2cd1e 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -7,3 +7,4 @@ leaderkill childthread sigkill_rain wait_must_be_interruptible +threaded_execve diff --git a/test/Makefile b/test/Makefile index 64bc811..8d2400c 100644 --- a/test/Makefile +++ b/test/Makefile @@ -4,7 +4,7 @@ all: \ vfork fork sig skodic clone leaderkill childthread \ - sigkill_rain wait_must_be_interruptible + sigkill_rain wait_must_be_interruptible threaded_execve leaderkill: LDFLAGS += -pthread @@ -13,4 +13,4 @@ childthread: LDFLAGS += -pthread clean distclean: rm -f *.o core \ vfork fork sig skodic clone leaderkill childthread \ - sigkill_rain wait_must_be_interruptible + sigkill_rain wait_must_be_interruptible threaded_execve diff --git a/test/threaded_execve.c b/test/threaded_execve.c new file mode 100644 index 0000000..51a4360 --- /dev/null +++ b/test/threaded_execve.c @@ -0,0 +1,138 @@ +/* + * Create NUM_THREADS threads which print "1" and sleep in pause(). + * Then create another thread which prints "2", and re-execs the program. + * The leader then either sleeps in pause(), or exits if $LEADER_EXIT is set. + * This triggers "execve'ed thread replaces thread leader" case. + * + * gcc -Wall -Os -o threaded_execve threaded_execve.c + * + * Try running it under strace like this: + * + * # Should not be confused by traced execve-ing thread + * # replacing traced leader: + * [LEADER_EXIT=1] strace -oLOG -f ./threaded_execve + * ^^^ so far slightly bad output with LEADER_EXIT=1 + * + * # Same, but different output mode. Output after execve + * # should go into leader's LOG.<pid> file, not into execve'ed + * # thread's log file: + * [LEADER_EXIT=1] strace -oLOG -ff ./threaded_execve + * + * # Should not be confused by non-traced execve-ing thread + * # replacing traced leader: + * [LEADER_EXIT=1] strace -oLOG ./threaded_execve + * ^^^^^^^^^^^^^^^^^^^^^ + * In Linux 3.2, non-traced execve-ing thread does not + * become traced after execve, even though it has pid == leader's pid + * after execve. + * + * # Run for NUM seconds, not just one second. + * # Watch top to check for memory leaks in strace: + * [LEADER_EXIT=1] strace -oLOG -f ./threaded_execve <NUM> + * + */ +#define NUM_THREADS 1 + +#define _GNU_SOURCE 1 +#include <assert.h> +#include <limits.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <stdio.h> +#include <sched.h> +#include <signal.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/syscall.h> + +/* Define clone2 for all arches */ +#ifdef __ia64__ +extern int __clone2(int (*fn) (void *), void *child_stack_base, + size_t stack_size, int flags, void *arg, ...); +#define clone2 __clone2 +#else +#define clone2(func, stack_base, size, flags, arg...) \ + clone(func, (stack_base) + (size), flags, arg) +#endif +/* Direct calls to syscalls, avoiding libc wrappers */ +#define syscall_tgkill(pid, tid, sig) syscall(__NR_tgkill, (pid), (tid), (sig)) +#define syscall_getpid() syscall(__NR_getpid) +#define syscall_gettid() syscall(__NR_gettid) +#define syscall_exit(v) syscall(__NR_exit, (v)); + +static char my_name[PATH_MAX]; + +static int +thread1(void *unused) +{ + write(1, "1", 1); + for(;;) pause(); + return 0; +} + +static int +thread2(void *unused) +{ + write(1, "2", 1); + usleep(20*1000); + /* This fails with ENOENT if leader has exited by now! :) */ + execl("/proc/self/exe", "exe", "exe", NULL); + /* So fall back to resolved name */ + execl(my_name, "exe", "exe", NULL); + for(;;) pause(); + return 0; +} + +static void +thread_leader(int die) +{ + /* malloc gives sufficiently aligned buffer. + * long buf[] does not! (on ia64). + */ + int cnt = NUM_THREADS; + while (--cnt >= 0) { + /* As seen in pthread_create(): */ + clone2(thread1, malloc(16 * 1024), 16 * 1024, 0 + | CLONE_VM + | CLONE_FS + | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM + | 0 /* no signal to send on death */ + , NULL); + usleep(20*1000); + } + clone2(thread2, malloc(16 * 1024), 16 * 1024, 0 + | CLONE_VM + | CLONE_FS + | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM + | 0 /* no signal to send on death */ + , NULL); + + if (die) syscall_exit(42); + for(;;) pause(); +} + +int +main(int argc, char **argv) +{ + int die = getenv("LEADER_EXIT") != NULL; + + if (readlink("/proc/self/exe", my_name, sizeof(my_name)-1) <= 0) + return 1; + + setbuf(stdout, NULL); + + if (argv[1] && strcmp(argv[1], "exe") == 0) + thread_leader(die); + + printf("%d: thread leader\n", getpid()); + + alarm(argv[1] ? atoi(argv[1]) : 1); + thread_leader(die); + + return 0; +} |