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