xref: /llvm-project/lldb/test/API/api/multiple-debuggers/multi-process-driver.cpp (revision aa07282a25c3d6df04af9a4d34874cc433c9c68a)
1 
2 // This program creates NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS of pthreads,
3 // creates an lldb Debugger on each thread, creates targets, inserts two
4 // breakpoints, runs to the first breakpoint, backtraces, runs to the second
5 // breakpoint, backtraces, kills the inferior process, closes down the
6 // debugger.
7 
8 // The main thread keeps track of which pthreads have completed and which
9 // pthreads have completed successfully, and exits when all pthreads have
10 // completed successfully, or our time limit has been exceeded.
11 
12 // This test file helps to uncover race conditions and locking mistakes
13 // that are hit when lldb is being used to debug multiple processes
14 // simultaneously.
15 
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <inttypes.h>
20 
21 #include "lldb/API/LLDB.h"
22 #include "lldb/API/SBCommandInterpreter.h"
23 #include "lldb/API/SBCommandReturnObject.h"
24 #include "lldb/API/SBDebugger.h"
25 
26 #include <chrono>
27 #include <csignal>
28 #include <thread>
29 
30 #define NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS 10
31 
32 #define DEBUG 0
33 
34 #define STR1(x) #x
35 #define STR(x) STR1(x)
36 
37 using namespace lldb;
38 
39 bool *completed_threads_array = 0;
40 bool *successful_threads_array  = 0;
41 
42 const char *inferior_process_name = "testprog";
43 
44 bool
45 wait_for_stop_event (SBProcess process, SBListener listener)
46 {
47     bool stopped = false;
48     while (!stopped)
49     {
50         SBEvent event;
51         bool waitfor_ret = listener.WaitForEvent (2, event);
52         if (event.GetType() == SBProcess::eBroadcastBitStateChanged)
53         {
54             if (process.GetState() == StateType::eStateStopped
55                 || process.GetState() == StateType::eStateCrashed
56                 || process.GetState() == StateType::eStateDetached
57                 || process.GetState() == StateType::eStateExited)
58             {
59                 stopped = true;
60             }
61         }
62     }
63     return stopped;
64 }
65 
66 bool
67 walk_stack_to_main (SBThread thread)
68 {
69     if (thread.IsValid() == 0)
70     {
71         return false;
72     }
73 
74     bool found_main = false;
75     uint32_t curr_frame = 0;
76     const uint32_t framecount = thread.GetNumFrames();
77     while (!found_main && curr_frame < framecount)
78     {
79         SBFrame frame = thread.GetFrameAtIndex (curr_frame);
80         if (strcmp (frame.GetFunctionName(), "main") == 0)
81         {
82             found_main = true;
83             break;
84         }
85         curr_frame += 1;
86     }
87     return found_main;
88 }
89 
90 void *do_one_debugger (void *in)
91 {
92     uint64_t threadnum = (uint64_t) in;
93 
94 #if defined (__APPLE__)
95     char *threadname;
96     asprintf (&threadname, "thread #%lld", threadnum);
97     pthread_setname_np (threadname);
98     free (threadname);
99 #endif
100 
101 #if DEBUG == 1
102     printf ("#%lld: Starting debug session\n", threadnum);
103 #endif
104 
105     SBDebugger debugger = lldb::SBDebugger::Create (false);
106     if (debugger.IsValid ())
107     {
108         debugger.SetAsync (true);
109         SBTarget target = debugger.CreateTargetWithFileAndArch(inferior_process_name,
110                                                                STR(LLDB_HOST_ARCH));
111         SBCommandInterpreter command_interp = debugger.GetCommandInterpreter();
112         if (target.IsValid())
113         {
114             SBBreakpoint bar_br = target.BreakpointCreateByName ("bar", "testprog");
115             if (!bar_br.IsValid())
116             {
117                 printf ("#%" PRIu64 ": failed to set breakpoint on bar, exiting.\n", threadnum);
118                 exit (1);
119             }
120             SBBreakpoint foo_br = target.BreakpointCreateByName ("foo", "testprog");
121             if (!foo_br.IsValid())
122             {
123                 printf ("#%" PRIu64 ": Failed to set breakpoint on foo()\n", threadnum);
124             }
125 
126             SBLaunchInfo launch_info (NULL);
127             SBError error;
128             SBProcess process = target.Launch (launch_info, error);
129             if (process.IsValid())
130             {
131                 SBListener listener = debugger.GetListener();
132                 SBBroadcaster broadcaster = process.GetBroadcaster();
133                 uint32_t rc = broadcaster.AddListener (listener, SBProcess::eBroadcastBitStateChanged);
134                 if (rc == 0)
135                 {
136                     printf ("adding listener failed\n");
137                     exit (1);
138                 }
139 
140                 wait_for_stop_event (process, listener);
141 
142                 if (!walk_stack_to_main (process.GetThreadAtIndex(0)))
143                 {
144                     printf ("#%" PRIu64 ": backtrace while @ foo() failed\n", threadnum);
145                     completed_threads_array[threadnum] = true;
146                     return (void *) 1;
147                 }
148 
149                 // On Linux the () are included.
150                 const char* hit_fn = process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetFunctionName();
151                 if (strcmp (hit_fn, "foo") != 0 && strcmp (hit_fn, "foo()") != 0)
152                 {
153 #if DEBUG == 1
154                     printf ("#%" PRIu64 ": First breakpoint did not stop at foo(), instead stopped at '%s'\n", threadnum, process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetFunctionName());
155 #endif
156                     completed_threads_array[threadnum] = true;
157                     return (void*) 1;
158                 }
159 
160                 process.Continue();
161 
162                 wait_for_stop_event (process, listener);
163 
164                 if (process.GetState() == StateType::eStateExited)
165                 {
166                     printf ("#%" PRIu64 ": Process exited\n", threadnum);
167                     completed_threads_array[threadnum] = true;
168                     return (void *) 1;
169                 }
170 
171 
172                 if (!walk_stack_to_main (process.GetThreadAtIndex(0)))
173                 {
174                     printf ("#%" PRIu64 ": backtrace while @ bar() failed\n", threadnum);
175                     completed_threads_array[threadnum] = true;
176                     return (void *) 1;
177                 }
178 
179                 hit_fn = process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetFunctionName();
180                 if (strcmp (hit_fn, "bar") != 0 && strcmp (hit_fn, "bar()") != 0)
181                 {
182                     printf ("#%" PRIu64 ": First breakpoint did not stop at bar()\n", threadnum);
183                     completed_threads_array[threadnum] = true;
184                     return (void*) 1;
185                 }
186 
187                 process.Kill();
188 
189                 wait_for_stop_event (process, listener);
190 
191                 SBDebugger::Destroy(debugger);
192 
193 #if DEBUG == 1
194                 printf ("#%" PRIu64 ": All good!\n", threadnum);
195 #endif
196                 successful_threads_array[threadnum] = true;
197                 completed_threads_array[threadnum] = true;
198                 return (void*) 0;
199             }
200             else
201             {
202                 printf("#%" PRIu64 ": process failed to launch\n", threadnum);
203                 successful_threads_array[threadnum] = false;
204                 completed_threads_array[threadnum] = true;
205                 return (void*) 0;
206             }
207         }
208         else
209         {
210             printf ("#%" PRIu64 ": did not get valid target\n", threadnum);
211             successful_threads_array[threadnum] = false;
212             completed_threads_array[threadnum] = true;
213             return (void*) 0;
214         }
215     }
216     else
217     {
218         printf ("#%" PRIu64 ": did not get debugger\n", threadnum);
219         successful_threads_array[threadnum] = false;
220         completed_threads_array[threadnum] = true;
221         return (void*) 0;
222     }
223     completed_threads_array[threadnum] = true;
224     return (void*) 1;
225 }
226 
227 int count_completed_threads(int num_threads) {
228   int num_completed_threads = 0;
229   for (int i = 0; i < num_threads; i++)
230     if (completed_threads_array[i])
231       num_completed_threads++;
232   return num_completed_threads;
233 }
234 
235 int count_successful_threads(int num_threads) {
236   int num_successful_threads = 0;
237   for (int i = 0; i < num_threads; i++)
238     if (successful_threads_array[i])
239       num_successful_threads++;
240   return num_successful_threads;
241 }
242 
243 int main (int argc, char **argv)
244 {
245 #if !defined(_MSC_VER)
246   signal(SIGPIPE, SIG_IGN);
247 #endif
248 
249     SBDebugger::Initialize();
250 
251     completed_threads_array = (bool *) malloc (sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
252     memset (completed_threads_array, 0, sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
253     successful_threads_array = (bool *) malloc (sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
254     memset (successful_threads_array, 0, sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
255 
256     if (argc > 1 && argv[1] != NULL)
257     {
258         inferior_process_name = argv[1];
259     }
260 
261     std::thread threads[NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS];
262     for (uint64_t i = 0; i< NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS; i++)
263     {
264         threads[i] = std::move(std::thread(do_one_debugger, (void*)i));
265     }
266 
267 
268     int max_time_to_wait = 40;  // 40 iterations, or 120 seconds
269     if (getenv("ASAN_OPTIONS"))
270       max_time_to_wait *= 4;
271     for (int iter = 0; iter < max_time_to_wait; iter++) {
272         std::this_thread::sleep_for(std::chrono::seconds(3));
273         int successful_threads = count_successful_threads(NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
274         int total_completed_threads = count_completed_threads(NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
275 
276         if (total_completed_threads == NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS)
277         {
278 #if DEBUG == 1
279             printf ("All threads completed.\n");
280             printf ("%d threads completed successfully out of %d\n", successful_threads, NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
281 #endif
282             SBDebugger::Terminate();
283             exit(0);
284         }
285         else
286         {
287 #if DEBUG == 1
288             printf ("%d threads completed so far (%d successfully), out of %d\n", total_completed_threads, successful_threads, NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
289 #endif
290         }
291         if (iter == max_time_to_wait)
292           printf("reached maximum timeout but only %d threads have completed "
293                  "so far "
294                  "(%d successfully), out of %d.  Exiting.\n",
295                  total_completed_threads, successful_threads,
296                  NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
297     }
298 
299     SBDebugger::Terminate();
300     exit (1);
301 }
302