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>
1999451b44SJordan Rupprecht
2099451b44SJordan Rupprecht #include "lldb/API/LLDB.h"
2199451b44SJordan Rupprecht #include "lldb/API/SBCommandInterpreter.h"
2299451b44SJordan Rupprecht #include "lldb/API/SBCommandReturnObject.h"
2399451b44SJordan Rupprecht #include "lldb/API/SBDebugger.h"
2499451b44SJordan Rupprecht
2599451b44SJordan Rupprecht #include <chrono>
26*8b67b707SJonas Devlieghere #include <csignal>
2799451b44SJordan Rupprecht #include <thread>
2899451b44SJordan Rupprecht
2999451b44SJordan Rupprecht #define NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS 10
3099451b44SJordan Rupprecht
3199451b44SJordan Rupprecht #define DEBUG 0
3299451b44SJordan Rupprecht
3399451b44SJordan Rupprecht using namespace lldb;
3499451b44SJordan Rupprecht
3599451b44SJordan Rupprecht bool *completed_threads_array = 0;
3699451b44SJordan Rupprecht bool *successful_threads_array = 0;
3799451b44SJordan Rupprecht
3899451b44SJordan Rupprecht const char *inferior_process_name = "testprog";
3999451b44SJordan Rupprecht
4099451b44SJordan Rupprecht bool
wait_for_stop_event(SBProcess process,SBListener listener)4199451b44SJordan Rupprecht wait_for_stop_event (SBProcess process, SBListener listener)
4299451b44SJordan Rupprecht {
4399451b44SJordan Rupprecht bool stopped = false;
4499451b44SJordan Rupprecht while (!stopped)
4599451b44SJordan Rupprecht {
4699451b44SJordan Rupprecht SBEvent event;
4799451b44SJordan Rupprecht bool waitfor_ret = listener.WaitForEvent (2, event);
4899451b44SJordan Rupprecht if (event.GetType() == SBProcess::eBroadcastBitStateChanged)
4999451b44SJordan Rupprecht {
5099451b44SJordan Rupprecht if (process.GetState() == StateType::eStateStopped
5199451b44SJordan Rupprecht || process.GetState() == StateType::eStateCrashed
5299451b44SJordan Rupprecht || process.GetState() == StateType::eStateDetached
5399451b44SJordan Rupprecht || process.GetState() == StateType::eStateExited)
5499451b44SJordan Rupprecht {
5599451b44SJordan Rupprecht stopped = true;
5699451b44SJordan Rupprecht }
5799451b44SJordan Rupprecht }
5899451b44SJordan Rupprecht }
5999451b44SJordan Rupprecht return stopped;
6099451b44SJordan Rupprecht }
6199451b44SJordan Rupprecht
6299451b44SJordan Rupprecht bool
walk_stack_to_main(SBThread thread)6399451b44SJordan Rupprecht walk_stack_to_main (SBThread thread)
6499451b44SJordan Rupprecht {
6599451b44SJordan Rupprecht if (thread.IsValid() == 0)
6699451b44SJordan Rupprecht {
6799451b44SJordan Rupprecht return false;
6899451b44SJordan Rupprecht }
6999451b44SJordan Rupprecht
7099451b44SJordan Rupprecht bool found_main = false;
7199451b44SJordan Rupprecht uint32_t curr_frame = 0;
7299451b44SJordan Rupprecht const uint32_t framecount = thread.GetNumFrames();
7399451b44SJordan Rupprecht while (!found_main && curr_frame < framecount)
7499451b44SJordan Rupprecht {
7599451b44SJordan Rupprecht SBFrame frame = thread.GetFrameAtIndex (curr_frame);
7699451b44SJordan Rupprecht if (strcmp (frame.GetFunctionName(), "main") == 0)
7799451b44SJordan Rupprecht {
7899451b44SJordan Rupprecht found_main = true;
7999451b44SJordan Rupprecht break;
8099451b44SJordan Rupprecht }
8199451b44SJordan Rupprecht curr_frame += 1;
8299451b44SJordan Rupprecht }
8399451b44SJordan Rupprecht return found_main;
8499451b44SJordan Rupprecht }
8599451b44SJordan Rupprecht
do_one_debugger(void * in)8699451b44SJordan Rupprecht void *do_one_debugger (void *in)
8799451b44SJordan Rupprecht {
8899451b44SJordan Rupprecht uint64_t threadnum = (uint64_t) in;
8999451b44SJordan Rupprecht
9099451b44SJordan Rupprecht #if defined (__APPLE__)
9199451b44SJordan Rupprecht char *threadname;
9299451b44SJordan Rupprecht asprintf (&threadname, "thread #%lld", threadnum);
9399451b44SJordan Rupprecht pthread_setname_np (threadname);
9499451b44SJordan Rupprecht free (threadname);
9599451b44SJordan Rupprecht #endif
9699451b44SJordan Rupprecht
9799451b44SJordan Rupprecht #if DEBUG == 1
9899451b44SJordan Rupprecht printf ("#%lld: Starting debug session\n", threadnum);
9999451b44SJordan Rupprecht #endif
10099451b44SJordan Rupprecht
10199451b44SJordan Rupprecht SBDebugger debugger = lldb::SBDebugger::Create (false);
10299451b44SJordan Rupprecht if (debugger.IsValid ())
10399451b44SJordan Rupprecht {
10499451b44SJordan Rupprecht debugger.SetAsync (true);
10599451b44SJordan Rupprecht SBTarget target = debugger.CreateTargetWithFileAndArch(inferior_process_name, "x86_64");
10699451b44SJordan Rupprecht SBCommandInterpreter command_interp = debugger.GetCommandInterpreter();
10799451b44SJordan Rupprecht if (target.IsValid())
10899451b44SJordan Rupprecht {
10999451b44SJordan Rupprecht SBBreakpoint bar_br = target.BreakpointCreateByName ("bar", "testprog");
11099451b44SJordan Rupprecht if (!bar_br.IsValid())
11199451b44SJordan Rupprecht {
11299451b44SJordan Rupprecht printf ("#%lld: failed to set breakpoint on bar, exiting.\n", threadnum);
11399451b44SJordan Rupprecht exit (1);
11499451b44SJordan Rupprecht }
11599451b44SJordan Rupprecht SBBreakpoint foo_br = target.BreakpointCreateByName ("foo", "testprog");
11699451b44SJordan Rupprecht if (!foo_br.IsValid())
11799451b44SJordan Rupprecht {
11899451b44SJordan Rupprecht printf ("#%lld: Failed to set breakpoint on foo()\n", threadnum);
11999451b44SJordan Rupprecht }
12099451b44SJordan Rupprecht
12199451b44SJordan Rupprecht SBLaunchInfo launch_info (NULL);
12299451b44SJordan Rupprecht SBError error;
12399451b44SJordan Rupprecht SBProcess process = target.Launch (launch_info, error);
12499451b44SJordan Rupprecht if (process.IsValid())
12599451b44SJordan Rupprecht {
12699451b44SJordan Rupprecht SBListener listener = debugger.GetListener();
12799451b44SJordan Rupprecht SBBroadcaster broadcaster = process.GetBroadcaster();
12899451b44SJordan Rupprecht uint32_t rc = broadcaster.AddListener (listener, SBProcess::eBroadcastBitStateChanged);
12999451b44SJordan Rupprecht if (rc == 0)
13099451b44SJordan Rupprecht {
13199451b44SJordan Rupprecht printf ("adding listener failed\n");
13299451b44SJordan Rupprecht exit (1);
13399451b44SJordan Rupprecht }
13499451b44SJordan Rupprecht
13599451b44SJordan Rupprecht wait_for_stop_event (process, listener);
13699451b44SJordan Rupprecht
13799451b44SJordan Rupprecht if (!walk_stack_to_main (process.GetThreadAtIndex(0)))
13899451b44SJordan Rupprecht {
13999451b44SJordan Rupprecht printf ("#%lld: backtrace while @ foo() failed\n", threadnum);
14099451b44SJordan Rupprecht completed_threads_array[threadnum] = true;
14199451b44SJordan Rupprecht return (void *) 1;
14299451b44SJordan Rupprecht }
14399451b44SJordan Rupprecht
14499451b44SJordan Rupprecht if (strcmp (process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetFunctionName(), "foo") != 0)
14599451b44SJordan Rupprecht {
14699451b44SJordan Rupprecht #if DEBUG == 1
14799451b44SJordan Rupprecht printf ("#%lld: First breakpoint did not stop at foo(), instead stopped at '%s'\n", threadnum, process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetFunctionName());
14899451b44SJordan Rupprecht #endif
14999451b44SJordan Rupprecht completed_threads_array[threadnum] = true;
15099451b44SJordan Rupprecht return (void*) 1;
15199451b44SJordan Rupprecht }
15299451b44SJordan Rupprecht
15399451b44SJordan Rupprecht process.Continue();
15499451b44SJordan Rupprecht
15599451b44SJordan Rupprecht wait_for_stop_event (process, listener);
15699451b44SJordan Rupprecht
15799451b44SJordan Rupprecht if (process.GetState() == StateType::eStateExited)
15899451b44SJordan Rupprecht {
15999451b44SJordan Rupprecht printf ("#%lld: Process exited\n", threadnum);
16099451b44SJordan Rupprecht completed_threads_array[threadnum] = true;
16199451b44SJordan Rupprecht return (void *) 1;
16299451b44SJordan Rupprecht }
16399451b44SJordan Rupprecht
16499451b44SJordan Rupprecht
16599451b44SJordan Rupprecht if (!walk_stack_to_main (process.GetThreadAtIndex(0)))
16699451b44SJordan Rupprecht {
16799451b44SJordan Rupprecht printf ("#%lld: backtrace while @ bar() failed\n", threadnum);
16899451b44SJordan Rupprecht completed_threads_array[threadnum] = true;
16999451b44SJordan Rupprecht return (void *) 1;
17099451b44SJordan Rupprecht }
17199451b44SJordan Rupprecht
17299451b44SJordan Rupprecht if (strcmp (process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetFunctionName(), "bar") != 0)
17399451b44SJordan Rupprecht {
17499451b44SJordan Rupprecht printf ("#%lld: First breakpoint did not stop at bar()\n", threadnum);
17599451b44SJordan Rupprecht completed_threads_array[threadnum] = true;
17699451b44SJordan Rupprecht return (void*) 1;
17799451b44SJordan Rupprecht }
17899451b44SJordan Rupprecht
17999451b44SJordan Rupprecht process.Kill();
18099451b44SJordan Rupprecht
18199451b44SJordan Rupprecht wait_for_stop_event (process, listener);
18299451b44SJordan Rupprecht
18399451b44SJordan Rupprecht SBDebugger::Destroy(debugger);
18499451b44SJordan Rupprecht
18599451b44SJordan Rupprecht #if DEBUG == 1
18699451b44SJordan Rupprecht printf ("#%lld: All good!\n", threadnum);
18799451b44SJordan Rupprecht #endif
18899451b44SJordan Rupprecht successful_threads_array[threadnum] = true;
18999451b44SJordan Rupprecht completed_threads_array[threadnum] = true;
19099451b44SJordan Rupprecht return (void*) 0;
19199451b44SJordan Rupprecht }
19299451b44SJordan Rupprecht else
19399451b44SJordan Rupprecht {
19499451b44SJordan Rupprecht printf("#%lld: process failed to launch\n", threadnum);
19599451b44SJordan Rupprecht successful_threads_array[threadnum] = false;
19699451b44SJordan Rupprecht completed_threads_array[threadnum] = true;
19799451b44SJordan Rupprecht return (void*) 0;
19899451b44SJordan Rupprecht }
19999451b44SJordan Rupprecht }
20099451b44SJordan Rupprecht else
20199451b44SJordan Rupprecht {
20299451b44SJordan Rupprecht printf ("#%lld: did not get valid target\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 {
21099451b44SJordan Rupprecht printf ("#%lld: did not get debugger\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 completed_threads_array[threadnum] = true;
21699451b44SJordan Rupprecht return (void*) 1;
21799451b44SJordan Rupprecht }
21899451b44SJordan Rupprecht
main(int argc,char ** argv)21999451b44SJordan Rupprecht int main (int argc, char **argv)
22099451b44SJordan Rupprecht {
22199451b44SJordan Rupprecht #if !defined(_MSC_VER)
22299451b44SJordan Rupprecht signal(SIGPIPE, SIG_IGN);
22399451b44SJordan Rupprecht #endif
22499451b44SJordan Rupprecht
22599451b44SJordan Rupprecht SBDebugger::Initialize();
22699451b44SJordan Rupprecht
22799451b44SJordan Rupprecht completed_threads_array = (bool *) malloc (sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
22899451b44SJordan Rupprecht memset (completed_threads_array, 0, sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
22999451b44SJordan Rupprecht successful_threads_array = (bool *) malloc (sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
23099451b44SJordan Rupprecht memset (successful_threads_array, 0, sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
23199451b44SJordan Rupprecht
23299451b44SJordan Rupprecht if (argc > 1 && argv[1] != NULL)
23399451b44SJordan Rupprecht {
23499451b44SJordan Rupprecht inferior_process_name = argv[1];
23599451b44SJordan Rupprecht }
23699451b44SJordan Rupprecht
23799451b44SJordan Rupprecht std::thread threads[NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS];
23899451b44SJordan Rupprecht for (uint64_t i = 0; i< NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS; i++)
23999451b44SJordan Rupprecht {
24099451b44SJordan Rupprecht threads[i] = std::move(std::thread(do_one_debugger, (void*)i));
24199451b44SJordan Rupprecht }
24299451b44SJordan Rupprecht
24399451b44SJordan Rupprecht
24499451b44SJordan Rupprecht int max_time_to_wait = 20; // 20 iterations, or 60 seconds
24599451b44SJordan Rupprecht int iter = 0;
24699451b44SJordan Rupprecht while (1)
24799451b44SJordan Rupprecht {
24899451b44SJordan Rupprecht std::this_thread::sleep_for(std::chrono::seconds(3));
24999451b44SJordan Rupprecht bool all_done = true;
25099451b44SJordan Rupprecht int successful_threads = 0;
25199451b44SJordan Rupprecht int total_completed_threads = 0;
25299451b44SJordan Rupprecht for (uint64_t i = 0; i < NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS; i++)
25399451b44SJordan Rupprecht {
25499451b44SJordan Rupprecht if (successful_threads_array[i] == true)
25599451b44SJordan Rupprecht successful_threads++;
25699451b44SJordan Rupprecht if (completed_threads_array[i] == true)
25799451b44SJordan Rupprecht total_completed_threads++;
25899451b44SJordan Rupprecht if (completed_threads_array[i] == false)
25999451b44SJordan Rupprecht {
26099451b44SJordan Rupprecht all_done = false;
26199451b44SJordan Rupprecht }
26299451b44SJordan Rupprecht }
26399451b44SJordan Rupprecht if (all_done)
26499451b44SJordan Rupprecht {
26599451b44SJordan Rupprecht #if DEBUG == 1
26699451b44SJordan Rupprecht printf ("All threads completed.\n");
26799451b44SJordan Rupprecht printf ("%d threads completed successfully out of %d\n", successful_threads, NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
26899451b44SJordan Rupprecht #endif
26999451b44SJordan Rupprecht SBDebugger::Terminate();
27099451b44SJordan Rupprecht exit(0);
27199451b44SJordan Rupprecht }
27299451b44SJordan Rupprecht else
27399451b44SJordan Rupprecht {
27499451b44SJordan Rupprecht #if DEBUG == 1
27599451b44SJordan Rupprecht printf ("%d threads completed so far (%d successfully), out of %d\n", total_completed_threads, successful_threads, NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
27699451b44SJordan Rupprecht #endif
27799451b44SJordan Rupprecht }
27899451b44SJordan Rupprecht if (iter++ == max_time_to_wait)
27999451b44SJordan Rupprecht {
28099451b44SJordan Rupprecht 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);
28199451b44SJordan Rupprecht break;
28299451b44SJordan Rupprecht }
28399451b44SJordan Rupprecht }
28499451b44SJordan Rupprecht
28599451b44SJordan Rupprecht
28699451b44SJordan Rupprecht SBDebugger::Terminate();
28799451b44SJordan Rupprecht exit (1);
28899451b44SJordan Rupprecht }
289