blob: 51a43606a08a289efc89e95cd18be90b4f9a03ed [file] [log] [blame]
Denys Vlasenkoc76ae432012-01-27 15:37:13 +01001/*
2 * Create NUM_THREADS threads which print "1" and sleep in pause().
3 * Then create another thread which prints "2", and re-execs the program.
4 * The leader then either sleeps in pause(), or exits if $LEADER_EXIT is set.
5 * This triggers "execve'ed thread replaces thread leader" case.
6 *
7 * gcc -Wall -Os -o threaded_execve threaded_execve.c
8 *
9 * Try running it under strace like this:
10 *
11 * # Should not be confused by traced execve-ing thread
12 * # replacing traced leader:
13 * [LEADER_EXIT=1] strace -oLOG -f ./threaded_execve
14 * ^^^ so far slightly bad output with LEADER_EXIT=1
15 *
16 * # Same, but different output mode. Output after execve
17 * # should go into leader's LOG.<pid> file, not into execve'ed
18 * # thread's log file:
19 * [LEADER_EXIT=1] strace -oLOG -ff ./threaded_execve
20 *
21 * # Should not be confused by non-traced execve-ing thread
22 * # replacing traced leader:
23 * [LEADER_EXIT=1] strace -oLOG ./threaded_execve
24 * ^^^^^^^^^^^^^^^^^^^^^
25 * In Linux 3.2, non-traced execve-ing thread does not
26 * become traced after execve, even though it has pid == leader's pid
27 * after execve.
28 *
29 * # Run for NUM seconds, not just one second.
30 * # Watch top to check for memory leaks in strace:
31 * [LEADER_EXIT=1] strace -oLOG -f ./threaded_execve <NUM>
32 *
33 */
34#define NUM_THREADS 1
35
36#define _GNU_SOURCE 1
37#include <assert.h>
38#include <limits.h>
39#include <stddef.h>
40#include <stdlib.h>
41#include <string.h>
42#include <unistd.h>
43#include <errno.h>
44#include <stdio.h>
45#include <sched.h>
46#include <signal.h>
47#include <dirent.h>
48#include <fcntl.h>
49#include <sys/types.h>
50#include <sys/wait.h>
51#include <sys/syscall.h>
52
53/* Define clone2 for all arches */
54#ifdef __ia64__
55extern int __clone2(int (*fn) (void *), void *child_stack_base,
56 size_t stack_size, int flags, void *arg, ...);
57#define clone2 __clone2
58#else
59#define clone2(func, stack_base, size, flags, arg...) \
60 clone(func, (stack_base) + (size), flags, arg)
61#endif
62/* Direct calls to syscalls, avoiding libc wrappers */
63#define syscall_tgkill(pid, tid, sig) syscall(__NR_tgkill, (pid), (tid), (sig))
64#define syscall_getpid() syscall(__NR_getpid)
65#define syscall_gettid() syscall(__NR_gettid)
66#define syscall_exit(v) syscall(__NR_exit, (v));
67
68static char my_name[PATH_MAX];
69
70static int
71thread1(void *unused)
72{
73 write(1, "1", 1);
74 for(;;) pause();
75 return 0;
76}
77
78static int
79thread2(void *unused)
80{
81 write(1, "2", 1);
82 usleep(20*1000);
83 /* This fails with ENOENT if leader has exited by now! :) */
84 execl("/proc/self/exe", "exe", "exe", NULL);
85 /* So fall back to resolved name */
86 execl(my_name, "exe", "exe", NULL);
87 for(;;) pause();
88 return 0;
89}
90
91static void
92thread_leader(int die)
93{
94 /* malloc gives sufficiently aligned buffer.
95 * long buf[] does not! (on ia64).
96 */
97 int cnt = NUM_THREADS;
98 while (--cnt >= 0) {
99 /* As seen in pthread_create(): */
100 clone2(thread1, malloc(16 * 1024), 16 * 1024, 0
101 | CLONE_VM
102 | CLONE_FS
103 | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM
104 | 0 /* no signal to send on death */
105 , NULL);
106 usleep(20*1000);
107 }
108 clone2(thread2, malloc(16 * 1024), 16 * 1024, 0
109 | CLONE_VM
110 | CLONE_FS
111 | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM
112 | 0 /* no signal to send on death */
113 , NULL);
114
115 if (die) syscall_exit(42);
116 for(;;) pause();
117}
118
119int
120main(int argc, char **argv)
121{
122 int die = getenv("LEADER_EXIT") != NULL;
123
124 if (readlink("/proc/self/exe", my_name, sizeof(my_name)-1) <= 0)
125 return 1;
126
127 setbuf(stdout, NULL);
128
129 if (argv[1] && strcmp(argv[1], "exe") == 0)
130 thread_leader(die);
131
132 printf("%d: thread leader\n", getpid());
133
134 alarm(argv[1] ? atoi(argv[1]) : 1);
135 thread_leader(die);
136
137 return 0;
138}