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