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