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