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
33using namespace lldb;
34
35bool *completed_threads_array = 0;
36bool *successful_threads_array = 0;
37
38const char *inferior_process_name = "testprog";
39
40bool
41wait_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 (num_seconds: 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
62bool
63walk_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 (idx: curr_frame);
76 if (strcmp (s1: frame.GetFunctionName(), s2: "main") == 0)
77 {
78 found_main = true;
79 break;
80 }
81 curr_frame += 1;
82 }
83 return found_main;
84}
85
86void *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 (source_init_files: false);
102 if (debugger.IsValid ())
103 {
104 debugger.SetAsync (true);
105 SBTarget target = debugger.CreateTargetWithFileAndArch(filename: inferior_process_name, archname: "x86_64");
106 SBCommandInterpreter command_interp = debugger.GetCommandInterpreter();
107 if (target.IsValid())
108 {
109 SBBreakpoint bar_br = target.BreakpointCreateByName (symbol_name: "bar", module_name: "testprog");
110 if (!bar_br.IsValid())
111 {
112 printf (format: "#%lld: failed to set breakpoint on bar, exiting.\n", threadnum);
113 exit (status: 1);
114 }
115 SBBreakpoint foo_br = target.BreakpointCreateByName (symbol_name: "foo", module_name: "testprog");
116 if (!foo_br.IsValid())
117 {
118 printf (format: "#%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, event_mask: SBProcess::eBroadcastBitStateChanged);
129 if (rc == 0)
130 {
131 printf (format: "adding listener failed\n");
132 exit (status: 1);
133 }
134
135 wait_for_stop_event (process, listener);
136
137 if (!walk_stack_to_main (thread: process.GetThreadAtIndex(index: 0)))
138 {
139 printf (format: "#%lld: backtrace while @ foo() failed\n", threadnum);
140 completed_threads_array[threadnum] = true;
141 return (void *) 1;
142 }
143
144 if (strcmp (s1: process.GetThreadAtIndex(index: 0).GetFrameAtIndex(idx: 0).GetFunctionName(), s2: "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 (format: "#%lld: Process exited\n", threadnum);
160 completed_threads_array[threadnum] = true;
161 return (void *) 1;
162 }
163
164
165 if (!walk_stack_to_main (thread: process.GetThreadAtIndex(index: 0)))
166 {
167 printf (format: "#%lld: backtrace while @ bar() failed\n", threadnum);
168 completed_threads_array[threadnum] = true;
169 return (void *) 1;
170 }
171
172 if (strcmp (s1: process.GetThreadAtIndex(index: 0).GetFrameAtIndex(idx: 0).GetFunctionName(), s2: "bar") != 0)
173 {
174 printf (format: "#%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(format: "#%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 (format: "#%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 (format: "#%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
219int 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 (size: sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
228 memset (s: completed_threads_array, c: 0, n: sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
229 successful_threads_array = (bool *) malloc (size: sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
230 memset (s: successful_threads_array, c: 0, n: 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(rtime: 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(status: 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 (format: "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 (status: 1);
288}
289

source code of lldb/test/API/api/multiple-debuggers/multi-process-driver.cpp