Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / lldb / packages / Python / lldbsuite / test / concurrent_base.py
blob72e04bbb20a047d4ec8596bb1a0ea9ea8a05f25b
1 """
2 A stress-test of sorts for LLDB's handling of threads in the inferior.
4 This test sets a breakpoint in the main thread where test parameters (numbers of
5 threads) can be adjusted, runs the inferior to that point, and modifies the
6 locals that control the event thread counts. This test also sets a breakpoint in
7 breakpoint_func (the function executed by each 'breakpoint' thread) and a
8 watchpoint on a global modified in watchpoint_func. The inferior is continued
9 until exit or a crash takes place, and the number of events seen by LLDB is
10 verified to match the expected number of events.
11 """
14 import lldb
15 from lldbsuite.test.decorators import *
16 from lldbsuite.test.lldbtest import *
17 from lldbsuite.test import lldbutil
20 class ConcurrentEventsBase(TestBase):
21 # Concurrency is the primary test factor here, not debug info variants.
22 NO_DEBUG_INFO_TESTCASE = True
24 def setUp(self):
25 # Call super's setUp().
26 super(ConcurrentEventsBase, self).setUp()
27 # Find the line number for our breakpoint.
28 self.filename = "main.cpp"
29 self.thread_breakpoint_line = line_number(
30 self.filename, "// Set breakpoint here"
32 self.setup_breakpoint_line = line_number(
33 self.filename, "// Break here and adjust num"
35 self.finish_breakpoint_line = line_number(
36 self.filename, "// Break here and verify one thread is active"
39 def describe_threads(self):
40 ret = []
41 for x in self.inferior_process:
42 id = x.GetIndexID()
43 reason = x.GetStopReason()
44 status = "stopped" if x.IsStopped() else "running"
45 reason_str = lldbutil.stop_reason_to_str(reason)
46 if reason == lldb.eStopReasonBreakpoint:
47 bpid = x.GetStopReasonDataAtIndex(0)
48 bp = self.inferior_target.FindBreakpointByID(bpid)
49 reason_str = "%s hit %d times" % (
50 lldbutil.get_description(bp),
51 bp.GetHitCount(),
53 elif reason == lldb.eStopReasonWatchpoint:
54 watchid = x.GetStopReasonDataAtIndex(0)
55 watch = self.inferior_target.FindWatchpointByID(watchid)
56 reason_str = "%s hit %d times" % (
57 lldbutil.get_description(watch),
58 watch.GetHitCount(),
60 elif reason == lldb.eStopReasonSignal:
61 signals = self.inferior_process.GetUnixSignals()
62 signal_name = signals.GetSignalAsCString(x.GetStopReasonDataAtIndex(0))
63 reason_str = "signal %s" % signal_name
65 location = "\t".join(
67 lldbutil.get_description(x.GetFrameAtIndex(i))
68 for i in range(x.GetNumFrames())
71 ret.append(
72 "thread %d %s due to %s at\n\t%s" % (id, status, reason_str, location)
74 return ret
76 def add_breakpoint(self, line, descriptions):
77 """Adds a breakpoint at self.filename:line and appends its description to descriptions, and
78 returns the LLDB SBBreakpoint object.
79 """
81 bpno = lldbutil.run_break_set_by_file_and_line(
82 self, self.filename, line, num_expected_locations=-1
84 bp = self.inferior_target.FindBreakpointByID(bpno)
85 descriptions.append(": file = 'main.cpp', line = %d" % line)
86 return bp
88 def inferior_done(self):
89 """Returns true if the inferior is done executing all the event threads (and is stopped at self.finish_breakpoint,
90 or has terminated execution.
91 """
92 return (
93 self.finish_breakpoint.GetHitCount() > 0
94 or self.crash_count > 0
95 or self.inferior_process.GetState() == lldb.eStateExited
98 def count_signaled_threads(self):
99 count = 0
100 for thread in self.inferior_process:
101 if (
102 thread.GetStopReason() == lldb.eStopReasonSignal
103 and thread.GetStopReasonDataAtIndex(0)
104 == self.inferior_process.GetUnixSignals().GetSignalNumberFromName(
105 "SIGUSR1"
108 count += 1
109 return count
111 def do_thread_actions(
112 self,
113 num_breakpoint_threads=0,
114 num_signal_threads=0,
115 num_watchpoint_threads=0,
116 num_crash_threads=0,
117 num_delay_breakpoint_threads=0,
118 num_delay_signal_threads=0,
119 num_delay_watchpoint_threads=0,
120 num_delay_crash_threads=0,
122 """Sets a breakpoint in the main thread where test parameters (numbers of threads) can be adjusted, runs the inferior
123 to that point, and modifies the locals that control the event thread counts. Also sets a breakpoint in
124 breakpoint_func (the function executed by each 'breakpoint' thread) and a watchpoint on a global modified in
125 watchpoint_func. The inferior is continued until exit or a crash takes place, and the number of events seen by LLDB
126 is verified to match the expected number of events.
128 exe = self.getBuildArtifact("a.out")
129 self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
131 # Get the target
132 self.inferior_target = self.dbg.GetSelectedTarget()
134 expected_bps = []
136 # Initialize all the breakpoints (main thread/aux thread)
137 self.setup_breakpoint = self.add_breakpoint(
138 self.setup_breakpoint_line, expected_bps
140 self.finish_breakpoint = self.add_breakpoint(
141 self.finish_breakpoint_line, expected_bps
144 # Set the thread breakpoint
145 if num_breakpoint_threads + num_delay_breakpoint_threads > 0:
146 self.thread_breakpoint = self.add_breakpoint(
147 self.thread_breakpoint_line, expected_bps
150 # Verify breakpoints
151 self.expect(
152 "breakpoint list -f",
153 "Breakpoint locations shown correctly",
154 substrs=expected_bps,
157 # Run the program.
158 self.runCmd("run", RUN_SUCCEEDED)
160 # Check we are at line self.setup_breakpoint
161 self.expect(
162 "thread backtrace",
163 STOPPED_DUE_TO_BREAKPOINT,
164 substrs=["stop reason = breakpoint 1."],
167 # Initialize the (single) watchpoint on the global variable (g_watchme)
168 if num_watchpoint_threads + num_delay_watchpoint_threads > 0:
169 self.runCmd("watchpoint set variable g_watchme")
170 for w in self.inferior_target.watchpoint_iter():
171 self.thread_watchpoint = w
172 self.assertTrue(
173 "g_watchme" in str(self.thread_watchpoint),
174 "Watchpoint location not shown correctly",
177 # Get the process
178 self.inferior_process = self.inferior_target.GetProcess()
180 # We should be stopped at the setup site where we can set the number of
181 # threads doing each action (break/crash/signal/watch)
182 self.assertEqual(
183 self.inferior_process.GetNumThreads(),
185 "Expected to stop before any additional threads are spawned.",
188 self.runCmd("expr num_breakpoint_threads=%d" % num_breakpoint_threads)
189 self.runCmd("expr num_crash_threads=%d" % num_crash_threads)
190 self.runCmd("expr num_signal_threads=%d" % num_signal_threads)
191 self.runCmd("expr num_watchpoint_threads=%d" % num_watchpoint_threads)
193 self.runCmd(
194 "expr num_delay_breakpoint_threads=%d" % num_delay_breakpoint_threads
196 self.runCmd("expr num_delay_crash_threads=%d" % num_delay_crash_threads)
197 self.runCmd("expr num_delay_signal_threads=%d" % num_delay_signal_threads)
198 self.runCmd(
199 "expr num_delay_watchpoint_threads=%d" % num_delay_watchpoint_threads
202 # Continue the inferior so threads are spawned
203 self.runCmd("continue")
205 # Make sure we see all the threads. The inferior program's threads all synchronize with a pseudo-barrier; that is,
206 # the inferior program ensures all threads are started and running
207 # before any thread triggers its 'event'.
208 num_threads = self.inferior_process.GetNumThreads()
209 expected_num_threads = (
210 num_breakpoint_threads
211 + num_delay_breakpoint_threads
212 + num_signal_threads
213 + num_delay_signal_threads
214 + num_watchpoint_threads
215 + num_delay_watchpoint_threads
216 + num_crash_threads
217 + num_delay_crash_threads
220 self.assertEqual(
221 num_threads,
222 expected_num_threads,
223 "Expected to see %d threads, but seeing %d. Details:\n%s"
224 % (expected_num_threads, num_threads, "\n\t".join(self.describe_threads())),
227 self.signal_count = self.count_signaled_threads()
228 self.crash_count = len(
229 lldbutil.get_crashed_threads(self, self.inferior_process)
232 # Run to completion (or crash)
233 while not self.inferior_done():
234 if self.TraceOn():
235 self.runCmd("thread backtrace all")
236 self.runCmd("continue")
237 self.signal_count += self.count_signaled_threads()
238 self.crash_count += len(
239 lldbutil.get_crashed_threads(self, self.inferior_process)
242 if num_crash_threads > 0 or num_delay_crash_threads > 0:
243 # Expecting a crash
244 self.assertTrue(
245 self.crash_count > 0,
246 "Expecting at least one thread to crash. Details: %s"
247 % "\t\n".join(self.describe_threads()),
250 # Ensure the zombie process is reaped
251 self.runCmd("process kill")
253 elif num_crash_threads == 0 and num_delay_crash_threads == 0:
254 # There should be a single active thread (the main one) which hit
255 # the breakpoint after joining
256 self.assertEqual(
258 self.finish_breakpoint.GetHitCount(),
259 "Expected main thread (finish) breakpoint to be hit once",
262 num_threads = self.inferior_process.GetNumThreads()
263 self.assertEqual(
265 num_threads,
266 "Expecting 1 thread but seeing %d. Details:%s"
267 % (num_threads, "\n\t".join(self.describe_threads())),
269 self.runCmd("continue")
271 # The inferior process should have exited without crashing
272 self.assertEqual(
273 0, self.crash_count, "Unexpected thread(s) in crashed state"
275 self.assertEqual(
276 self.inferior_process.GetState(), lldb.eStateExited, PROCESS_EXITED
279 # Verify the number of actions took place matches expected numbers
280 expected_breakpoint_threads = (
281 num_delay_breakpoint_threads + num_breakpoint_threads
283 breakpoint_hit_count = (
284 self.thread_breakpoint.GetHitCount()
285 if expected_breakpoint_threads > 0
286 else 0
288 self.assertEqual(
289 expected_breakpoint_threads,
290 breakpoint_hit_count,
291 "Expected %d breakpoint hits, but got %d"
292 % (expected_breakpoint_threads, breakpoint_hit_count),
295 expected_signal_threads = num_delay_signal_threads + num_signal_threads
296 self.assertEqual(
297 expected_signal_threads,
298 self.signal_count,
299 "Expected %d stops due to signal delivery, but got %d"
300 % (expected_signal_threads, self.signal_count),
303 expected_watchpoint_threads = (
304 num_delay_watchpoint_threads + num_watchpoint_threads
306 watchpoint_hit_count = (
307 self.thread_watchpoint.GetHitCount()
308 if expected_watchpoint_threads > 0
309 else 0
311 self.assertEqual(
312 expected_watchpoint_threads,
313 watchpoint_hit_count,
314 "Expected %d watchpoint hits, got %d"
315 % (expected_watchpoint_threads, watchpoint_hit_count),