306 lines · cpp
1 2// This program creates NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS of pthreads,3// creates an lldb Debugger on each thread, creates targets, inserts two4// breakpoints, runs to the first breakpoint, backtraces, runs to the second5// breakpoint, backtraces, kills the inferior process, closes down the6// debugger.7 8// The main thread keeps track of which pthreads have completed and which9// pthreads have completed successfully, and exits when all pthreads have10// completed successfully, or our time limit has been exceeded.11 12// This test file helps to uncover race conditions and locking mistakes13// that are hit when lldb is being used to debug multiple processes14// simultaneously.15 16#include <inttypes.h>17#include <stdio.h>18#include <stdlib.h>19#include <string.h>20#include <unistd.h>21 22#include "lldb/API/LLDB.h"23#include "lldb/API/SBCommandInterpreter.h"24#include "lldb/API/SBCommandReturnObject.h"25#include "lldb/API/SBDebugger.h"26 27#include <chrono>28#include <csignal>29#include <thread>30 31#define NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS 1032 33#define DEBUG 034 35#define STR1(x) #x36#define STR(x) STR1(x)37 38using namespace lldb;39 40bool *completed_threads_array = 0;41bool *successful_threads_array = 0;42 43const char *inferior_process_name = "testprog";44 45bool46wait_for_stop_event (SBProcess process, SBListener listener)47{48 bool stopped = false;49 while (!stopped)50 {51 SBEvent event;52 bool waitfor_ret = listener.WaitForEvent (2, event);53 if (event.GetType() == SBProcess::eBroadcastBitStateChanged)54 {55 if (process.GetState() == StateType::eStateStopped56 || process.GetState() == StateType::eStateCrashed57 || process.GetState() == StateType::eStateDetached58 || process.GetState() == StateType::eStateExited)59 {60 stopped = true;61 }62 }63 }64 return stopped;65}66 67bool68walk_stack_to_main (SBThread thread)69{70 if (thread.IsValid() == 0)71 {72 return false;73 }74 75 bool found_main = false;76 uint32_t curr_frame = 0;77 const uint32_t framecount = thread.GetNumFrames();78 while (!found_main && curr_frame < framecount)79 {80 SBFrame frame = thread.GetFrameAtIndex (curr_frame);81 if (strcmp (frame.GetFunctionName(), "main") == 0)82 {83 found_main = true;84 break;85 }86 curr_frame += 1;87 }88 return found_main;89}90 91void *do_one_debugger (void *in)92{93 uint64_t threadnum = (uint64_t) in;94 95#if defined (__APPLE__)96 char *threadname;97 asprintf (&threadname, "thread #%lld", threadnum);98 pthread_setname_np (threadname);99 free (threadname);100#endif101 102#if DEBUG == 1103 printf ("#%lld: Starting debug session\n", threadnum);104#endif105 106 SBDebugger debugger = lldb::SBDebugger::Create (false);107 if (debugger.IsValid ())108 {109 debugger.SetAsync (true);110 SBTarget target = debugger.CreateTargetWithFileAndArch(inferior_process_name,111 STR(LLDB_HOST_ARCH));112 SBCommandInterpreter command_interp = debugger.GetCommandInterpreter();113 if (target.IsValid())114 {115 SBBreakpoint bar_br = target.BreakpointCreateByName ("bar", "testprog");116 if (!bar_br.IsValid())117 {118 printf ("#%" PRIu64 ": failed to set breakpoint on bar, exiting.\n", threadnum);119 exit (1);120 }121 SBBreakpoint foo_br = target.BreakpointCreateByName ("foo", "testprog");122 if (!foo_br.IsValid())123 {124 printf ("#%" PRIu64 ": Failed to set breakpoint on foo()\n", threadnum);125 }126 127 SBLaunchInfo launch_info (NULL);128 SBError error;129 SBProcess process = target.Launch (launch_info, error);130 if (process.IsValid())131 {132 SBListener listener = debugger.GetListener();133 SBBroadcaster broadcaster = process.GetBroadcaster();134 uint32_t rc = broadcaster.AddListener (listener, SBProcess::eBroadcastBitStateChanged);135 if (rc == 0)136 {137 printf ("adding listener failed\n");138 exit (1);139 }140 141 wait_for_stop_event (process, listener);142 143 if (!walk_stack_to_main (process.GetThreadAtIndex(0)))144 {145 printf ("#%" PRIu64 ": backtrace while @ foo() failed\n", threadnum);146 completed_threads_array[threadnum] = true;147 return (void *) 1;148 }149 150 // On Linux the () are included.151 const char* hit_fn = process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetFunctionName();152 if (strcmp (hit_fn, "foo") != 0 && strcmp (hit_fn, "foo()") != 0)153 {154#if DEBUG == 1155 printf ("#%" PRIu64 ": First breakpoint did not stop at foo(), instead stopped at '%s'\n", threadnum, process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetFunctionName());156#endif157 completed_threads_array[threadnum] = true;158 return (void*) 1;159 }160 161 process.Continue();162 163 wait_for_stop_event (process, listener);164 165 if (process.GetState() == StateType::eStateExited)166 {167 printf ("#%" PRIu64 ": Process exited\n", threadnum);168 completed_threads_array[threadnum] = true;169 return (void *) 1;170 }171 172 173 if (!walk_stack_to_main (process.GetThreadAtIndex(0)))174 {175 printf ("#%" PRIu64 ": backtrace while @ bar() failed\n", threadnum);176 completed_threads_array[threadnum] = true;177 return (void *) 1;178 }179 180 hit_fn = process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetFunctionName();181 if (strcmp (hit_fn, "bar") != 0 && strcmp (hit_fn, "bar()") != 0)182 {183 printf ("#%" PRIu64 ": First breakpoint did not stop at bar()\n", threadnum);184 completed_threads_array[threadnum] = true;185 return (void*) 1;186 }187 188 process.Kill();189 190 wait_for_stop_event (process, listener);191 192 SBDebugger::Destroy(debugger);193 194#if DEBUG == 1195 printf ("#%" PRIu64 ": All good!\n", threadnum);196#endif197 successful_threads_array[threadnum] = true;198 completed_threads_array[threadnum] = true;199 return (void*) 0;200 }201 else202 {203 printf("#%" PRIu64 ": process failed to launch\n", threadnum);204 successful_threads_array[threadnum] = false;205 completed_threads_array[threadnum] = true;206 return (void*) 0;207 }208 }209 else210 {211 printf ("#%" PRIu64 ": did not get valid target\n", threadnum);212 successful_threads_array[threadnum] = false;213 completed_threads_array[threadnum] = true;214 return (void*) 0;215 }216 }217 else218 {219 printf ("#%" PRIu64 ": did not get debugger\n", threadnum);220 successful_threads_array[threadnum] = false;221 completed_threads_array[threadnum] = true;222 return (void*) 0;223 }224 completed_threads_array[threadnum] = true;225 return (void*) 1;226}227 228int count_completed_threads(int num_threads) {229 int num_completed_threads = 0;230 for (int i = 0; i < num_threads; i++)231 if (completed_threads_array[i])232 num_completed_threads++;233 return num_completed_threads;234}235 236int count_successful_threads(int num_threads) {237 int num_successful_threads = 0;238 for (int i = 0; i < num_threads; i++)239 if (successful_threads_array[i])240 num_successful_threads++;241 return num_successful_threads;242}243 244int main (int argc, char **argv)245{246#if !defined(_MSC_VER)247 signal(SIGPIPE, SIG_IGN);248#endif249 250 SBDebugger::Initialize();251 252 completed_threads_array = (bool *) malloc (sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);253 memset (completed_threads_array, 0, sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);254 successful_threads_array = (bool *) malloc (sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);255 memset (successful_threads_array, 0, sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);256 257 if (argc > 1 && argv[1] != NULL)258 {259 inferior_process_name = argv[1];260 }261 262 std::thread threads[NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS];263 for (uint64_t i = 0; i< NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS; i++)264 {265 threads[i] = std::move(std::thread(do_one_debugger, (void*)i));266 }267 268 269 int max_time_to_wait = 40; // 40 iterations, or 120 seconds270 if (getenv("ASAN_OPTIONS"))271 max_time_to_wait *= 4;272 for (int iter = 0; iter < max_time_to_wait; iter++) {273 std::this_thread::sleep_for(std::chrono::seconds(3));274 int successful_threads = count_successful_threads(NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);275 int total_completed_threads = count_completed_threads(NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);276 277 if (total_completed_threads == NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS)278 {279#if DEBUG == 1280 printf ("All threads completed.\n");281 printf ("%d threads completed successfully out of %d\n", successful_threads, NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);282#endif283 SBDebugger::Terminate();284 exit(0);285 }286 else287 {288#if DEBUG == 1289 printf ("%d threads completed so far (%d successfully), out of %d\n", total_completed_threads, successful_threads, NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);290#endif291 }292 if (iter == max_time_to_wait)293 printf("reached maximum timeout but only %d threads have completed "294 "so far "295 "(%d successfully), out of %d. Exiting.\n",296 total_completed_threads, successful_threads,297 NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);298 }299 300 // We do not call SBDebugger::Terminate() here because it will destroy301 // data that might be being used by threads that are still running. Which302 // would change the timeout into an unrelated crash.303 // _exit instead of exit, to skip more things that could cause a crash.304 _exit(1);305}306