]> granicus.if.org Git - strace/commitdiff
Add new test program: test/threaded_execve.c
authorDenys Vlasenko <vda.linux@googlemail.com>
Fri, 27 Jan 2012 14:37:13 +0000 (15:37 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Fri, 27 Jan 2012 14:37:13 +0000 (15:37 +0100)
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
test/.gitignore
test/Makefile
test/threaded_execve.c [new file with mode: 0644]

index 5c689bd679cf961fb95b43055eb46316f6b95caa..7d2cd1e78846054e59880b8cc1add58c0eec20bf 100644 (file)
@@ -7,3 +7,4 @@ leaderkill
 childthread
 sigkill_rain
 wait_must_be_interruptible
+threaded_execve
index 64bc811fb95de3fddbc6cd159acce3dcc13515ef..8d2400cf928864ed54d93d7457ffa3291abf2e85 100644 (file)
@@ -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 (file)
index 0000000..51a4360
--- /dev/null
@@ -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;
+}