gdb: fix handling of vfork by multi-threaded program (follow-fork-mode=parent, detach...
[deliverable/binutils-gdb.git] / gdb / testsuite / gdb.threads / vfork-multi-thread.c
1 #include <assert.h>
2 #include <pthread.h>
3 #include <stdio.h>
4 #include <unistd.h>
5 #include <sys/wait.h>
6
7 // Start with:
8 //
9 // ./gdb -nx -q --data-directory=data-directory repro -ex "tb break_here_first" -ex r -ex "b should_break_here"
10 //
11 // Then do "continue".
12 //
13 // The main thread will likely cross should_break_here while we are handling
14 // the vfork and breakpoints are removed, therefore missing the breakpoint.
15
16 static volatile int release_vfork = 0;
17 static volatile int release_main = 0;
18
19 static void *vforker(void *arg)
20 {
21 while (!release_vfork);
22
23 pid_t pid = vfork ();
24 if (pid == 0) {
25 /* A vfork child is not supposed to mess with the state of the program,
26 but it is helpful for the purpose of this test. */
27 release_main = 1;
28 _exit(7);
29 }
30
31 int stat;
32 int ret = waitpid (pid, &stat, 0);
33 assert (ret == pid);
34 assert (WIFEXITED (stat));
35 assert (WEXITSTATUS (stat) == 7);
36
37 return NULL;
38 }
39
40 static void should_break_here(void) {}
41
42 int main()
43 {
44
45 pthread_t thread;
46 int ret = pthread_create(&thread, NULL, vforker, NULL);
47 assert(ret == 0);
48
49 /* We break here first, while the thread is stuck on `!release_fork`. */
50 release_vfork = 1;
51
52 /* We set a breakpoint on should_break_here.
53
54 We then set "release_fork" from the debugger and continue. The main
55 thread hangs on `!release_main` while the non-main thread vforks. During
56 the window of time where the two processes have a shared address space
57 (after vfork, before _exit), GDB removes the breakpoints from the address
58 space. During that window, only the vfork-ing thread (the non-main
59 thread) is frozen by the kernel. The main thread is free to execute. The
60 child process sets `release_main`, releasing the main thread. A buggy GDB
61 would let the main thread execute during that window, leading to the
62 breakpoint on should_break_here being missed. A fixed GDB does not resume
63 the threads of the vforking process other than the vforking thread. When
64 the vfork child exits, the fixed GDB resumes the main thread, after
65 breakpoints are reinserted, so the breakpoint is not missed. */
66
67 while (!release_main);
68
69 should_break_here();
70
71 pthread_join (thread, NULL);
72
73 return 6;
74 }
This page took 0.032066 seconds and 4 git commands to generate.