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
37using namespace lldb;
38
39bool *completed_threads_array = 0;
40bool *successful_threads_array = 0;
41
42const char *inferior_process_name = "testprog";
43
44bool
45wait_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 (num_seconds: 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
66bool
67walk_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 (idx: curr_frame);
80 if (strcmp (s1: frame.GetFunctionName(), s2: "main") == 0)
81 {
82 found_main = true;
83 break;
84 }
85 curr_frame += 1;
86 }
87 return found_main;
88}
89
90void *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 (source_init_files: false);
106 if (debugger.IsValid ())
107 {
108 debugger.SetAsync (true);
109 SBTarget target = debugger.CreateTargetWithFileAndArch(filename: 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 (symbol_name: "bar", module_name: "testprog");
115 if (!bar_br.IsValid())
116 {
117 printf (format: "#%" PRIu64 ": failed to set breakpoint on bar, exiting.\n", threadnum);
118 exit (status: 1);
119 }
120 SBBreakpoint foo_br = target.BreakpointCreateByName (symbol_name: "foo", module_name: "testprog");
121 if (!foo_br.IsValid())
122 {
123 printf (format: "#%" 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, event_mask: SBProcess::eBroadcastBitStateChanged);
134 if (rc == 0)
135 {
136 printf (format: "adding listener failed\n");
137 exit (status: 1);
138 }
139
140 wait_for_stop_event (process, listener);
141
142 if (!walk_stack_to_main (thread: process.GetThreadAtIndex(index: 0)))
143 {
144 printf (format: "#%" 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(index: 0).GetFrameAtIndex(idx: 0).GetFunctionName();
151 if (strcmp (s1: hit_fn, s2: "foo") != 0 && strcmp (s1: hit_fn, s2: "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 (format: "#%" PRIu64 ": Process exited\n", threadnum);
167 completed_threads_array[threadnum] = true;
168 return (void *) 1;
169 }
170
171
172 if (!walk_stack_to_main (thread: process.GetThreadAtIndex(index: 0)))
173 {
174 printf (format: "#%" PRIu64 ": backtrace while @ bar() failed\n", threadnum);
175 completed_threads_array[threadnum] = true;
176 return (void *) 1;
177 }
178
179 hit_fn = process.GetThreadAtIndex(index: 0).GetFrameAtIndex(idx: 0).GetFunctionName();
180 if (strcmp (s1: hit_fn, s2: "bar") != 0 && strcmp (s1: hit_fn, s2: "bar()") != 0)
181 {
182 printf (format: "#%" 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(format: "#%" 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 (format: "#%" 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 (format: "#%" 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
227int 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
235int 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
243int 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 (size: sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
252 memset (s: completed_threads_array, c: 0, n: sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
253 successful_threads_array = (bool *) malloc (size: sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
254 memset (s: successful_threads_array, c: 0, n: 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(name: "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(rtime: 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(status: 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(format: "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 (status: 1);
301}
302

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