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