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 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 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 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 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