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 <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 10
32
33#define DEBUG 0
34
35#define STR1(x) #x
36#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
45bool
46wait_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 (num_seconds: 2, event);
53 if (event.GetType() == SBProcess::eBroadcastBitStateChanged)
54 {
55 if (process.GetState() == StateType::eStateStopped
56 || process.GetState() == StateType::eStateCrashed
57 || process.GetState() == StateType::eStateDetached
58 || process.GetState() == StateType::eStateExited)
59 {
60 stopped = true;
61 }
62 }
63 }
64 return stopped;
65}
66
67bool
68walk_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 (idx: curr_frame);
81 if (strcmp (s1: frame.GetFunctionName(), s2: "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#endif
101
102#if DEBUG == 1
103 printf ("#%lld: Starting debug session\n", threadnum);
104#endif
105
106 SBDebugger debugger = lldb::SBDebugger::Create (source_init_files: false);
107 if (debugger.IsValid ())
108 {
109 debugger.SetAsync (true);
110 SBTarget target = debugger.CreateTargetWithFileAndArch(filename: 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 (symbol_name: "bar", module_name: "testprog");
116 if (!bar_br.IsValid())
117 {
118 printf (format: "#%" PRIu64 ": failed to set breakpoint on bar, exiting.\n", threadnum);
119 exit (status: 1);
120 }
121 SBBreakpoint foo_br = target.BreakpointCreateByName (symbol_name: "foo", module_name: "testprog");
122 if (!foo_br.IsValid())
123 {
124 printf (format: "#%" 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, event_mask: SBProcess::eBroadcastBitStateChanged);
135 if (rc == 0)
136 {
137 printf (format: "adding listener failed\n");
138 exit (status: 1);
139 }
140
141 wait_for_stop_event (process, listener);
142
143 if (!walk_stack_to_main (thread: process.GetThreadAtIndex(index: 0)))
144 {
145 printf (format: "#%" 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(index: 0).GetFrameAtIndex(idx: 0).GetFunctionName();
152 if (strcmp (s1: hit_fn, s2: "foo") != 0 && strcmp (s1: hit_fn, s2: "foo()") != 0)
153 {
154#if DEBUG == 1
155 printf ("#%" PRIu64 ": First breakpoint did not stop at foo(), instead stopped at '%s'\n", threadnum, process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetFunctionName());
156#endif
157 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 (format: "#%" PRIu64 ": Process exited\n", threadnum);
168 completed_threads_array[threadnum] = true;
169 return (void *) 1;
170 }
171
172
173 if (!walk_stack_to_main (thread: process.GetThreadAtIndex(index: 0)))
174 {
175 printf (format: "#%" PRIu64 ": backtrace while @ bar() failed\n", threadnum);
176 completed_threads_array[threadnum] = true;
177 return (void *) 1;
178 }
179
180 hit_fn = process.GetThreadAtIndex(index: 0).GetFrameAtIndex(idx: 0).GetFunctionName();
181 if (strcmp (s1: hit_fn, s2: "bar") != 0 && strcmp (s1: hit_fn, s2: "bar()") != 0)
182 {
183 printf (format: "#%" 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 == 1
195 printf ("#%" PRIu64 ": All good!\n", threadnum);
196#endif
197 successful_threads_array[threadnum] = true;
198 completed_threads_array[threadnum] = true;
199 return (void*) 0;
200 }
201 else
202 {
203 printf(format: "#%" 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 else
210 {
211 printf (format: "#%" 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 else
218 {
219 printf (format: "#%" 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#endif
249
250 SBDebugger::Initialize();
251
252 completed_threads_array = (bool *) malloc (size: sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
253 memset (s: completed_threads_array, c: 0, n: sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
254 successful_threads_array = (bool *) malloc (size: sizeof (bool) * NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
255 memset (s: successful_threads_array, c: 0, n: 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 seconds
270 if (getenv(name: "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(rtime: 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 == 1
280 printf ("All threads completed.\n");
281 printf ("%d threads completed successfully out of %d\n", successful_threads, NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
282#endif
283 SBDebugger::Terminate();
284 exit(status: 0);
285 }
286 else
287 {
288#if DEBUG == 1
289 printf ("%d threads completed so far (%d successfully), out of %d\n", total_completed_threads, successful_threads, NUMBER_OF_SIMULTANEOUS_DEBUG_SESSIONS);
290#endif
291 }
292 if (iter == max_time_to_wait)
293 printf(format: "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 destroy
301 // data that might be being used by threads that are still running. Which
302 // would change the timeout into an unrelated crash.
303 // _exit instead of exit, to skip more things that could cause a crash.
304 _exit(status: 1);
305}
306

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