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