199451b44SJordan Rupprecht 299451b44SJordan Rupprecht // This program creates NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS of pthreads, 399451b44SJordan Rupprecht // creates an lldb Debugger on each thread, creates targets, inserts two 499451b44SJordan Rupprecht // breakpoints, runs to the first breakpoint, backtraces, runs to the second 599451b44SJordan Rupprecht // breakpoint, backtraces, kills the inferior process, closes down the 699451b44SJordan Rupprecht // debugger. 799451b44SJordan Rupprecht 899451b44SJordan Rupprecht // The main thread keeps track of which pthreads have completed and which 999451b44SJordan Rupprecht // pthreads have completed successfully, and exits when all pthreads have 1099451b44SJordan Rupprecht // completed successfully, or our time limit has been exceeded. 1199451b44SJordan Rupprecht 1299451b44SJordan Rupprecht // This test file helps to uncover race conditions and locking mistakes 1399451b44SJordan Rupprecht // that are hit when lldb is being used to debug multiple processes 1499451b44SJordan Rupprecht // simultaneously. 1599451b44SJordan Rupprecht 1699451b44SJordan Rupprecht #include <stdio.h> 1799451b44SJordan Rupprecht #include <stdlib.h> 1899451b44SJordan Rupprecht #include <string.h> 19*aa07282aSDavid Spickett #include <inttypes.h> 2099451b44SJordan Rupprecht 2199451b44SJordan Rupprecht #include "lldb/API/LLDB.h" 2299451b44SJordan Rupprecht #include "lldb/API/SBCommandInterpreter.h" 2399451b44SJordan Rupprecht #include "lldb/API/SBCommandReturnObject.h" 2499451b44SJordan Rupprecht #include "lldb/API/SBDebugger.h" 2599451b44SJordan Rupprecht 2699451b44SJordan Rupprecht #include <chrono> 278b67b707SJonas Devlieghere #include <csignal> 2899451b44SJordan Rupprecht #include <thread> 2999451b44SJordan Rupprecht 3099451b44SJordan Rupprecht #define NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS 10 3199451b44SJordan Rupprecht 3299451b44SJordan Rupprecht #define DEBUG 0 3399451b44SJordan Rupprecht 34*aa07282aSDavid Spickett #define STR1(x) #x 35*aa07282aSDavid Spickett #define STR(x) STR1(x) 36*aa07282aSDavid Spickett 3799451b44SJordan Rupprecht using namespace lldb; 3899451b44SJordan Rupprecht 3999451b44SJordan Rupprecht bool *completed_threads_array = 0; 4099451b44SJordan Rupprecht bool *successful_threads_array = 0; 4199451b44SJordan Rupprecht 4299451b44SJordan Rupprecht const char *inferior_process_name = "testprog"; 4399451b44SJordan Rupprecht 4499451b44SJordan Rupprecht bool 4599451b44SJordan Rupprecht wait_for_stop_event (SBProcess process, SBListener listener) 4699451b44SJordan Rupprecht { 4799451b44SJordan Rupprecht bool stopped = false; 4899451b44SJordan Rupprecht while (!stopped) 4999451b44SJordan Rupprecht { 5099451b44SJordan Rupprecht SBEvent event; 5199451b44SJordan Rupprecht bool waitfor_ret = listener.WaitForEvent (2, event); 5299451b44SJordan Rupprecht if (event.GetType() == SBProcess::eBroadcastBitStateChanged) 5399451b44SJordan Rupprecht { 5499451b44SJordan Rupprecht if (process.GetState() == StateType::eStateStopped 5599451b44SJordan Rupprecht || process.GetState() == StateType::eStateCrashed 5699451b44SJordan Rupprecht || process.GetState() == StateType::eStateDetached 5799451b44SJordan Rupprecht || process.GetState() == StateType::eStateExited) 5899451b44SJordan Rupprecht { 5999451b44SJordan Rupprecht stopped = true; 6099451b44SJordan Rupprecht } 6199451b44SJordan Rupprecht } 6299451b44SJordan Rupprecht } 6399451b44SJordan Rupprecht return stopped; 6499451b44SJordan Rupprecht } 6599451b44SJordan Rupprecht 6699451b44SJordan Rupprecht bool 6799451b44SJordan Rupprecht walk_stack_to_main (SBThread thread) 6899451b44SJordan Rupprecht { 6999451b44SJordan Rupprecht if (thread.IsValid() == 0) 7099451b44SJordan Rupprecht { 7199451b44SJordan Rupprecht return false; 7299451b44SJordan Rupprecht } 7399451b44SJordan Rupprecht 7499451b44SJordan Rupprecht bool found_main = false; 7599451b44SJordan Rupprecht uint32_t curr_frame = 0; 7699451b44SJordan Rupprecht const uint32_t framecount = thread.GetNumFrames(); 7799451b44SJordan Rupprecht while (!found_main && curr_frame < framecount) 7899451b44SJordan Rupprecht { 7999451b44SJordan Rupprecht SBFrame frame = thread.GetFrameAtIndex (curr_frame); 8099451b44SJordan Rupprecht if (strcmp (frame.GetFunctionName(), "main") == 0) 8199451b44SJordan Rupprecht { 8299451b44SJordan Rupprecht found_main = true; 8399451b44SJordan Rupprecht break; 8499451b44SJordan Rupprecht } 8599451b44SJordan Rupprecht curr_frame += 1; 8699451b44SJordan Rupprecht } 8799451b44SJordan Rupprecht return found_main; 8899451b44SJordan Rupprecht } 8999451b44SJordan Rupprecht 9099451b44SJordan Rupprecht void *do_one_debugger (void *in) 9199451b44SJordan Rupprecht { 9299451b44SJordan Rupprecht uint64_t threadnum = (uint64_t) in; 9399451b44SJordan Rupprecht 9499451b44SJordan Rupprecht #if defined (__APPLE__) 9599451b44SJordan Rupprecht char *threadname; 9699451b44SJordan Rupprecht asprintf (&threadname, "thread #%lld", threadnum); 9799451b44SJordan Rupprecht pthread_setname_np (threadname); 9899451b44SJordan Rupprecht free (threadname); 9999451b44SJordan Rupprecht #endif 10099451b44SJordan Rupprecht 10199451b44SJordan Rupprecht #if DEBUG == 1 10299451b44SJordan Rupprecht printf ("#%lld: Starting debug session\n", threadnum); 10399451b44SJordan Rupprecht #endif 10499451b44SJordan Rupprecht 10599451b44SJordan Rupprecht SBDebugger debugger = lldb::SBDebugger::Create (false); 10699451b44SJordan Rupprecht if (debugger.IsValid ()) 10799451b44SJordan Rupprecht { 10899451b44SJordan Rupprecht debugger.SetAsync (true); 109*aa07282aSDavid Spickett SBTarget target = debugger.CreateTargetWithFileAndArch(inferior_process_name, 110*aa07282aSDavid Spickett STR(LLDB_HOST_ARCH)); 11199451b44SJordan Rupprecht SBCommandInterpreter command_interp = debugger.GetCommandInterpreter(); 11299451b44SJordan Rupprecht if (target.IsValid()) 11399451b44SJordan Rupprecht { 11499451b44SJordan Rupprecht SBBreakpoint bar_br = target.BreakpointCreateByName ("bar", "testprog"); 11599451b44SJordan Rupprecht if (!bar_br.IsValid()) 11699451b44SJordan Rupprecht { 117*aa07282aSDavid Spickett printf ("#%" PRIu64 ": failed to set breakpoint on bar, exiting.\n", threadnum); 11899451b44SJordan Rupprecht exit (1); 11999451b44SJordan Rupprecht } 12099451b44SJordan Rupprecht SBBreakpoint foo_br = target.BreakpointCreateByName ("foo", "testprog"); 12199451b44SJordan Rupprecht if (!foo_br.IsValid()) 12299451b44SJordan Rupprecht { 123*aa07282aSDavid Spickett printf ("#%" PRIu64 ": Failed to set breakpoint on foo()\n", threadnum); 12499451b44SJordan Rupprecht } 12599451b44SJordan Rupprecht 12699451b44SJordan Rupprecht SBLaunchInfo launch_info (NULL); 12799451b44SJordan Rupprecht SBError error; 12899451b44SJordan Rupprecht SBProcess process = target.Launch (launch_info, error); 12999451b44SJordan Rupprecht if (process.IsValid()) 13099451b44SJordan Rupprecht { 13199451b44SJordan Rupprecht SBListener listener = debugger.GetListener(); 13299451b44SJordan Rupprecht SBBroadcaster broadcaster = process.GetBroadcaster(); 13399451b44SJordan Rupprecht uint32_t rc = broadcaster.AddListener (listener, SBProcess::eBroadcastBitStateChanged); 13499451b44SJordan Rupprecht if (rc == 0) 13599451b44SJordan Rupprecht { 13699451b44SJordan Rupprecht printf ("adding listener failed\n"); 13799451b44SJordan Rupprecht exit (1); 13899451b44SJordan Rupprecht } 13999451b44SJordan Rupprecht 14099451b44SJordan Rupprecht wait_for_stop_event (process, listener); 14199451b44SJordan Rupprecht 14299451b44SJordan Rupprecht if (!walk_stack_to_main (process.GetThreadAtIndex(0))) 14399451b44SJordan Rupprecht { 144*aa07282aSDavid Spickett printf ("#%" PRIu64 ": backtrace while @ foo() failed\n", threadnum); 14599451b44SJordan Rupprecht completed_threads_array[threadnum] = true; 14699451b44SJordan Rupprecht return (void *) 1; 14799451b44SJordan Rupprecht } 14899451b44SJordan Rupprecht 149*aa07282aSDavid Spickett // On Linux the () are included. 150*aa07282aSDavid Spickett const char* hit_fn = process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetFunctionName(); 151*aa07282aSDavid Spickett if (strcmp (hit_fn, "foo") != 0 && strcmp (hit_fn, "foo()") != 0) 15299451b44SJordan Rupprecht { 15399451b44SJordan Rupprecht #if DEBUG == 1 154*aa07282aSDavid Spickett printf ("#%" PRIu64 ": First breakpoint did not stop at foo(), instead stopped at '%s'\n", threadnum, process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetFunctionName()); 15599451b44SJordan Rupprecht #endif 15699451b44SJordan Rupprecht completed_threads_array[threadnum] = true; 15799451b44SJordan Rupprecht return (void*) 1; 15899451b44SJordan Rupprecht } 15999451b44SJordan Rupprecht 16099451b44SJordan Rupprecht process.Continue(); 16199451b44SJordan Rupprecht 16299451b44SJordan Rupprecht wait_for_stop_event (process, listener); 16399451b44SJordan Rupprecht 16499451b44SJordan Rupprecht if (process.GetState() == StateType::eStateExited) 16599451b44SJordan Rupprecht { 166*aa07282aSDavid Spickett printf ("#%" PRIu64 ": Process exited\n", threadnum); 16799451b44SJordan Rupprecht completed_threads_array[threadnum] = true; 16899451b44SJordan Rupprecht return (void *) 1; 16999451b44SJordan Rupprecht } 17099451b44SJordan Rupprecht 17199451b44SJordan Rupprecht 17299451b44SJordan Rupprecht if (!walk_stack_to_main (process.GetThreadAtIndex(0))) 17399451b44SJordan Rupprecht { 174*aa07282aSDavid Spickett printf ("#%" PRIu64 ": backtrace while @ bar() failed\n", threadnum); 17599451b44SJordan Rupprecht completed_threads_array[threadnum] = true; 17699451b44SJordan Rupprecht return (void *) 1; 17799451b44SJordan Rupprecht } 17899451b44SJordan Rupprecht 179*aa07282aSDavid Spickett hit_fn = process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetFunctionName(); 180*aa07282aSDavid Spickett if (strcmp (hit_fn, "bar") != 0 && strcmp (hit_fn, "bar()") != 0) 18199451b44SJordan Rupprecht { 182*aa07282aSDavid Spickett printf ("#%" PRIu64 ": First breakpoint did not stop at bar()\n", threadnum); 18399451b44SJordan Rupprecht completed_threads_array[threadnum] = true; 18499451b44SJordan Rupprecht return (void*) 1; 18599451b44SJordan Rupprecht } 18699451b44SJordan Rupprecht 18799451b44SJordan Rupprecht process.Kill(); 18899451b44SJordan Rupprecht 18999451b44SJordan Rupprecht wait_for_stop_event (process, listener); 19099451b44SJordan Rupprecht 19199451b44SJordan Rupprecht SBDebugger::Destroy(debugger); 19299451b44SJordan Rupprecht 19399451b44SJordan Rupprecht #if DEBUG == 1 194*aa07282aSDavid Spickett printf ("#%" PRIu64 ": All good!\n", threadnum); 19599451b44SJordan Rupprecht #endif 19699451b44SJordan Rupprecht successful_threads_array[threadnum] = true; 19799451b44SJordan Rupprecht completed_threads_array[threadnum] = true; 19899451b44SJordan Rupprecht return (void*) 0; 19999451b44SJordan Rupprecht } 20099451b44SJordan Rupprecht else 20199451b44SJordan Rupprecht { 202*aa07282aSDavid Spickett printf("#%" PRIu64 ": process failed to launch\n", threadnum); 20399451b44SJordan Rupprecht successful_threads_array[threadnum] = false; 20499451b44SJordan Rupprecht completed_threads_array[threadnum] = true; 20599451b44SJordan Rupprecht return (void*) 0; 20699451b44SJordan Rupprecht } 20799451b44SJordan Rupprecht } 20899451b44SJordan Rupprecht else 20999451b44SJordan Rupprecht { 210*aa07282aSDavid Spickett printf ("#%" PRIu64 ": did not get valid target\n", threadnum); 21199451b44SJordan Rupprecht successful_threads_array[threadnum] = false; 21299451b44SJordan Rupprecht completed_threads_array[threadnum] = true; 21399451b44SJordan Rupprecht return (void*) 0; 21499451b44SJordan Rupprecht } 21599451b44SJordan Rupprecht } 21699451b44SJordan Rupprecht else 21799451b44SJordan Rupprecht { 218*aa07282aSDavid Spickett printf ("#%" PRIu64 ": did not get debugger\n", threadnum); 21999451b44SJordan Rupprecht successful_threads_array[threadnum] = false; 22099451b44SJordan Rupprecht completed_threads_array[threadnum] = true; 22199451b44SJordan Rupprecht return (void*) 0; 22299451b44SJordan Rupprecht } 22399451b44SJordan Rupprecht completed_threads_array[threadnum] = true; 22499451b44SJordan Rupprecht return (void*) 1; 22599451b44SJordan Rupprecht } 22699451b44SJordan Rupprecht 227869f5517SAdrian Prantl int count_completed_threads(int num_threads) { 228869f5517SAdrian Prantl int num_completed_threads = 0; 229869f5517SAdrian Prantl for (int i = 0; i < num_threads; i++) 230869f5517SAdrian Prantl if (completed_threads_array[i]) 231869f5517SAdrian Prantl num_completed_threads++; 232869f5517SAdrian Prantl return num_completed_threads; 233869f5517SAdrian Prantl } 234869f5517SAdrian Prantl 235869f5517SAdrian Prantl int count_successful_threads(int num_threads) { 236869f5517SAdrian Prantl int num_successful_threads = 0; 237869f5517SAdrian Prantl for (int i = 0; i < num_threads; i++) 238869f5517SAdrian Prantl if (successful_threads_array[i]) 239869f5517SAdrian Prantl num_successful_threads++; 240869f5517SAdrian Prantl return num_successful_threads; 241869f5517SAdrian Prantl } 242869f5517SAdrian Prantl 24399451b44SJordan Rupprecht int main (int argc, char **argv) 24499451b44SJordan Rupprecht { 24599451b44SJordan Rupprecht #if !defined(_MSC_VER) 24699451b44SJordan Rupprecht signal(SIGPIPE, SIG_IGN); 24799451b44SJordan Rupprecht #endif 24899451b44SJordan Rupprecht 24999451b44SJordan Rupprecht SBDebugger::Initialize(); 25099451b44SJordan Rupprecht 25199451b44SJordan Rupprecht completed_threads_array = (bool *) malloc (sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS); 25299451b44SJordan Rupprecht memset (completed_threads_array, 0, sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS); 25399451b44SJordan Rupprecht successful_threads_array = (bool *) malloc (sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS); 25499451b44SJordan Rupprecht memset (successful_threads_array, 0, sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS); 25599451b44SJordan Rupprecht 25699451b44SJordan Rupprecht if (argc > 1 && argv[1] != NULL) 25799451b44SJordan Rupprecht { 25899451b44SJordan Rupprecht inferior_process_name = argv[1]; 25999451b44SJordan Rupprecht } 26099451b44SJordan Rupprecht 26199451b44SJordan Rupprecht std::thread threads[NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS]; 26299451b44SJordan Rupprecht for (uint64_t i = 0; i< NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS; i++) 26399451b44SJordan Rupprecht { 26499451b44SJordan Rupprecht threads[i] = std::move(std::thread(do_one_debugger, (void*)i)); 26599451b44SJordan Rupprecht } 26699451b44SJordan Rupprecht 26799451b44SJordan Rupprecht 268869f5517SAdrian Prantl int max_time_to_wait = 40; // 40 iterations, or 120 seconds 269869f5517SAdrian Prantl if (getenv("ASAN_OPTIONS")) 270869f5517SAdrian Prantl max_time_to_wait *= 4; 271869f5517SAdrian Prantl for (int iter = 0; iter < max_time_to_wait; iter++) { 27299451b44SJordan Rupprecht std::this_thread::sleep_for(std::chrono::seconds(3)); 273869f5517SAdrian Prantl int successful_threads = count_successful_threads(NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS); 274869f5517SAdrian Prantl int total_completed_threads = count_completed_threads(NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS); 275869f5517SAdrian Prantl 276869f5517SAdrian Prantl if (total_completed_threads == NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS) 27799451b44SJordan Rupprecht { 27899451b44SJordan Rupprecht #if DEBUG == 1 27999451b44SJordan Rupprecht printf ("All threads completed.\n"); 28099451b44SJordan Rupprecht printf ("%d threads completed successfully out of %d\n", successful_threads, NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS); 28199451b44SJordan Rupprecht #endif 28299451b44SJordan Rupprecht SBDebugger::Terminate(); 28399451b44SJordan Rupprecht exit(0); 28499451b44SJordan Rupprecht } 28599451b44SJordan Rupprecht else 28699451b44SJordan Rupprecht { 28799451b44SJordan Rupprecht #if DEBUG == 1 28899451b44SJordan Rupprecht printf ("%d threads completed so far (%d successfully), out of %d\n", total_completed_threads, successful_threads, NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS); 28999451b44SJordan Rupprecht #endif 29099451b44SJordan Rupprecht } 291869f5517SAdrian Prantl if (iter == max_time_to_wait) 292869f5517SAdrian Prantl printf("reached maximum timeout but only %d threads have completed " 293869f5517SAdrian Prantl "so far " 294869f5517SAdrian Prantl "(%d successfully), out of %d. Exiting.\n", 295869f5517SAdrian Prantl total_completed_threads, successful_threads, 296869f5517SAdrian Prantl NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS); 29799451b44SJordan Rupprecht } 29899451b44SJordan Rupprecht 29999451b44SJordan Rupprecht SBDebugger::Terminate(); 30099451b44SJordan Rupprecht exit (1); 30199451b44SJordan Rupprecht } 302