xref: /llvm-project/lldb/test/Shell/Subprocess/Inputs/fork.cpp (revision caf508d7124353522e7604dbfea36b429469bd39)
1 #include <sys/types.h>
2 #include <sys/wait.h>
3 #include <assert.h>
4 #if defined(TEST_CLONE)
5 #include <sched.h>
6 #endif
7 #include <stdint.h>
8 #include <stdlib.h>
9 #include <stdio.h>
10 #include <unistd.h>
11 
12 int g_val = 0;
13 
parent_func()14 void parent_func() {
15   g_val = 1;
16   printf("function run in parent\n");
17 }
18 
19 int parent_done[2];
20 char parent_done_str[16];
21 
wait_for_parent()22 void wait_for_parent() {
23   char buf[2];
24   // wait for the parent to finish its part
25   int ret = read(parent_done[0], buf, sizeof(buf));
26   assert(ret == 2);
27   ret = close(parent_done[0]);
28   assert(ret == 0);
29 }
30 
31 // This is the function we set breakpoint on.
child_func(const char * argv0)32 int child_func(const char *argv0) {
33   // we need to avoid memory modifications for vfork(), yet we want
34   // to be able to test watchpoints, so do the next best thing
35   // and restore the original value
36   g_val = 2;
37   g_val = 0;
38   execl(argv0, argv0, parent_done_str, NULL);
39   assert(0 && "this should not be reached");
40   return 1;
41 }
42 
child_top_func(void * argv0_ptr)43 int child_top_func(void *argv0_ptr) {
44   const char *argv0 = static_cast<char*>(argv0_ptr);
45 
46   int ret = close(parent_done[1]);
47   assert(ret == 0);
48 
49   // NB: when using vfork(), the parent may be suspended while running
50   // this function, so do not rely on any synchronization until we exec
51 #if defined(TEST_FORK)
52   if (TEST_FORK != vfork)
53 #endif
54     wait_for_parent();
55 
56   return child_func(argv0);
57 }
58 
main(int argc,char * argv[])59 int main(int argc, char* argv[]) {
60   alignas(uintmax_t) char stack[4096];
61   int ret;
62 
63   if (argv[1]) {
64     parent_done[0] = atoi(argv[1]);
65     assert(parent_done[0] != 0);
66 
67 #if defined(TEST_FORK)
68     // for vfork(), we need to synchronize after exec
69     if (TEST_FORK == vfork)
70       wait_for_parent();
71 #endif
72 
73     fprintf(stderr, "function run in exec'd child\n");
74     return 0;
75   }
76 
77   ret = pipe(parent_done);
78   assert(ret == 0);
79 
80   ret = snprintf(parent_done_str, sizeof(parent_done_str),
81                  "%d", parent_done[0]);
82   assert(ret != -1);
83 
84 #if defined(TEST_CLONE)
85   pid_t pid = clone(child_top_func, &stack[sizeof(stack)], 0, argv[0]);
86 #elif defined(TEST_FORK)
87   pid_t pid = TEST_FORK();
88   // NB: this must be equivalent to the clone() call above
89   if (pid == 0)
90     _exit(child_top_func(argv[0]));
91 #endif
92   assert(pid != -1);
93 
94   ret = close(parent_done[0]);
95   assert(ret == 0);
96 
97   parent_func();
98 
99   // resume the child
100   ret = write(parent_done[1], "go", 2);
101   assert(ret == 2);
102   ret = close(parent_done[1]);
103   assert(ret == 0);
104 
105   int status, wait_flags = 0;
106 #if defined(TEST_CLONE)
107   wait_flags = __WALL;
108 #endif
109   pid_t waited = waitpid(pid, &status, wait_flags);
110   assert(waited == pid);
111   assert(WIFEXITED(status));
112   assert(WEXITSTATUS(status) == 0);
113 
114   return 0;
115 }
116