summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2012-01-27 15:37:13 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2012-01-27 15:37:13 +0100
commitc76ae4352e4893ec0c93010ee1e9b7ddc0056804 (patch)
tree021b7d2e0f5ca1c092ca8730edae3a606cb35263
parenteebb04d4ae8bf4b08a041f5ea442ca24c90692c2 (diff)
downloadstrace-c76ae4352e4893ec0c93010ee1e9b7ddc0056804.tar.gz
Add new test program: test/threaded_execve.c
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--test/.gitignore1
-rw-r--r--test/Makefile4
-rw-r--r--test/threaded_execve.c138
3 files changed, 141 insertions, 2 deletions
diff --git a/test/.gitignore b/test/.gitignore
index 5c689bd67..7d2cd1e78 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 64bc811fb..8d2400cf9 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 000000000..51a43606a
--- /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;
+}