xref: /llvm-project/lldb/test/API/api/multiple-debuggers/multi-process-driver.cpp (revision aa07282a25c3d6df04af9a4d34874cc433c9c68a)
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