[PowerPC] Collect some CallLowering arguments into a struct. [NFC]
[llvm-project.git] / lldb / packages / Python / lldbsuite / test / concurrent_base.py
blob6acd71ce9e46d8acc124301d0937218e659f3c90
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 """
15 import unittest2
16 import lldb
17 from lldbsuite.test.decorators import *
18 from lldbsuite.test.lldbtest import *
19 from lldbsuite.test import lldbutil
22 class ConcurrentEventsBase(TestBase):
24 # Concurrency is the primary test factor here, not debug info variants.
25 NO_DEBUG_INFO_TESTCASE = True
27 def setUp(self):
28 # Call super's setUp().
29 super(ConcurrentEventsBase, self).setUp()
30 # Find the line number for our breakpoint.
31 self.filename = 'main.cpp'
32 self.thread_breakpoint_line = line_number(
33 self.filename, '// Set breakpoint here')
34 self.setup_breakpoint_line = line_number(
35 self.filename, '// Break here and adjust num')
36 self.finish_breakpoint_line = line_number(
37 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), bp.GetHitCount())
51 elif reason == lldb.eStopReasonWatchpoint:
52 watchid = x.GetStopReasonDataAtIndex(0)
53 watch = self.inferior_target.FindWatchpointByID(watchid)
54 reason_str = "%s hit %d times" % (
55 lldbutil.get_description(watch), watch.GetHitCount())
56 elif reason == lldb.eStopReasonSignal:
57 signals = self.inferior_process.GetUnixSignals()
58 signal_name = signals.GetSignalAsCString(
59 x.GetStopReasonDataAtIndex(0))
60 reason_str = "signal %s" % signal_name
62 location = "\t".join([lldbutil.get_description(
63 x.GetFrameAtIndex(i)) for i in range(x.GetNumFrames())])
64 ret.append(
65 "thread %d %s due to %s at\n\t%s" %
66 (id, status, reason_str, location))
67 return ret
69 def add_breakpoint(self, line, descriptions):
70 """ Adds a breakpoint at self.filename:line and appends its description to descriptions, and
71 returns the LLDB SBBreakpoint object.
72 """
74 bpno = lldbutil.run_break_set_by_file_and_line(
75 self, self.filename, line, num_expected_locations=-1)
76 bp = self.inferior_target.FindBreakpointByID(bpno)
77 descriptions.append(
78 ": file = 'main.cpp', line = %d" %
79 self.finish_breakpoint_line)
80 return bp
82 def inferior_done(self):
83 """ Returns true if the inferior is done executing all the event threads (and is stopped at self.finish_breakpoint,
84 or has terminated execution.
85 """
86 return self.finish_breakpoint.GetHitCount() > 0 or \
87 self.crash_count > 0 or \
88 self.inferior_process.GetState() == lldb.eStateExited
90 def count_signaled_threads(self):
91 count = 0
92 for thread in self.inferior_process:
93 if thread.GetStopReason() == lldb.eStopReasonSignal and thread.GetStopReasonDataAtIndex(
94 0) == self.inferior_process.GetUnixSignals().GetSignalNumberFromName('SIGUSR1'):
95 count += 1
96 return count
98 def do_thread_actions(self,
99 num_breakpoint_threads=0,
100 num_signal_threads=0,
101 num_watchpoint_threads=0,
102 num_crash_threads=0,
103 num_delay_breakpoint_threads=0,
104 num_delay_signal_threads=0,
105 num_delay_watchpoint_threads=0,
106 num_delay_crash_threads=0):
107 """ Sets a breakpoint in the main thread where test parameters (numbers of threads) can be adjusted, runs the inferior
108 to that point, and modifies the locals that control the event thread counts. Also sets a breakpoint in
109 breakpoint_func (the function executed by each 'breakpoint' thread) and a watchpoint on a global modified in
110 watchpoint_func. The inferior is continued until exit or a crash takes place, and the number of events seen by LLDB
111 is verified to match the expected number of events.
113 exe = self.getBuildArtifact("a.out")
114 self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
116 # Get the target
117 self.inferior_target = self.dbg.GetSelectedTarget()
119 expected_bps = []
121 # Initialize all the breakpoints (main thread/aux thread)
122 self.setup_breakpoint = self.add_breakpoint(
123 self.setup_breakpoint_line, expected_bps)
124 self.finish_breakpoint = self.add_breakpoint(
125 self.finish_breakpoint_line, expected_bps)
127 # Set the thread breakpoint
128 if num_breakpoint_threads + num_delay_breakpoint_threads > 0:
129 self.thread_breakpoint = self.add_breakpoint(
130 self.thread_breakpoint_line, expected_bps)
132 # Verify breakpoints
133 self.expect(
134 "breakpoint list -f",
135 "Breakpoint locations shown correctly",
136 substrs=expected_bps)
138 # Run the program.
139 self.runCmd("run", RUN_SUCCEEDED)
141 # Check we are at line self.setup_breakpoint
142 self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT,
143 substrs=["stop reason = breakpoint 1."])
145 # Initialize the (single) watchpoint on the global variable (g_watchme)
146 if num_watchpoint_threads + num_delay_watchpoint_threads > 0:
147 self.runCmd("watchpoint set variable g_watchme")
148 for w in self.inferior_target.watchpoint_iter():
149 self.thread_watchpoint = w
150 self.assertTrue(
151 "g_watchme" in str(
152 self.thread_watchpoint),
153 "Watchpoint location not shown correctly")
155 # Get the process
156 self.inferior_process = self.inferior_target.GetProcess()
158 # We should be stopped at the setup site where we can set the number of
159 # threads doing each action (break/crash/signal/watch)
160 self.assertEqual(
161 self.inferior_process.GetNumThreads(),
163 'Expected to stop before any additional threads are spawned.')
165 self.runCmd("expr num_breakpoint_threads=%d" % num_breakpoint_threads)
166 self.runCmd("expr num_crash_threads=%d" % num_crash_threads)
167 self.runCmd("expr num_signal_threads=%d" % num_signal_threads)
168 self.runCmd("expr num_watchpoint_threads=%d" % num_watchpoint_threads)
170 self.runCmd(
171 "expr num_delay_breakpoint_threads=%d" %
172 num_delay_breakpoint_threads)
173 self.runCmd(
174 "expr num_delay_crash_threads=%d" %
175 num_delay_crash_threads)
176 self.runCmd(
177 "expr num_delay_signal_threads=%d" %
178 num_delay_signal_threads)
179 self.runCmd(
180 "expr num_delay_watchpoint_threads=%d" %
181 num_delay_watchpoint_threads)
183 # Continue the inferior so threads are spawned
184 self.runCmd("continue")
186 # Make sure we see all the threads. The inferior program's threads all synchronize with a pseudo-barrier; that is,
187 # the inferior program ensures all threads are started and running
188 # before any thread triggers its 'event'.
189 num_threads = self.inferior_process.GetNumThreads()
190 expected_num_threads = num_breakpoint_threads + num_delay_breakpoint_threads \
191 + num_signal_threads + num_delay_signal_threads \
192 + num_watchpoint_threads + num_delay_watchpoint_threads \
193 + num_crash_threads + num_delay_crash_threads + 1
194 self.assertEqual(
195 num_threads,
196 expected_num_threads,
197 'Expected to see %d threads, but seeing %d. Details:\n%s' %
198 (expected_num_threads,
199 num_threads,
200 "\n\t".join(
201 self.describe_threads())))
203 self.signal_count = self.count_signaled_threads()
204 self.crash_count = len(
205 lldbutil.get_crashed_threads(
206 self, self.inferior_process))
208 # Run to completion (or crash)
209 while not self.inferior_done():
210 if self.TraceOn():
211 self.runCmd("thread backtrace all")
212 self.runCmd("continue")
213 self.signal_count += self.count_signaled_threads()
214 self.crash_count += len(
215 lldbutil.get_crashed_threads(
216 self, self.inferior_process))
218 if num_crash_threads > 0 or num_delay_crash_threads > 0:
219 # Expecting a crash
220 self.assertTrue(
221 self.crash_count > 0,
222 "Expecting at least one thread to crash. Details: %s" %
223 "\t\n".join(
224 self.describe_threads()))
226 # Ensure the zombie process is reaped
227 self.runCmd("process kill")
229 elif num_crash_threads == 0 and num_delay_crash_threads == 0:
230 # There should be a single active thread (the main one) which hit
231 # the breakpoint after joining
232 self.assertEqual(
234 self.finish_breakpoint.GetHitCount(),
235 "Expected main thread (finish) breakpoint to be hit once")
237 num_threads = self.inferior_process.GetNumThreads()
238 self.assertEqual(
240 num_threads,
241 "Expecting 1 thread but seeing %d. Details:%s" %
242 (num_threads,
243 "\n\t".join(
244 self.describe_threads())))
245 self.runCmd("continue")
247 # The inferior process should have exited without crashing
248 self.assertEqual(
250 self.crash_count,
251 "Unexpected thread(s) in crashed state")
252 self.assertEqual(
253 self.inferior_process.GetState(),
254 lldb.eStateExited,
255 PROCESS_EXITED)
257 # Verify the number of actions took place matches expected numbers
258 expected_breakpoint_threads = num_delay_breakpoint_threads + num_breakpoint_threads
259 breakpoint_hit_count = self.thread_breakpoint.GetHitCount(
260 ) if expected_breakpoint_threads > 0 else 0
261 self.assertEqual(
262 expected_breakpoint_threads,
263 breakpoint_hit_count,
264 "Expected %d breakpoint hits, but got %d" %
265 (expected_breakpoint_threads,
266 breakpoint_hit_count))
268 expected_signal_threads = num_delay_signal_threads + num_signal_threads
269 self.assertEqual(
270 expected_signal_threads,
271 self.signal_count,
272 "Expected %d stops due to signal delivery, but got %d" %
273 (expected_signal_threads,
274 self.signal_count))
276 expected_watchpoint_threads = num_delay_watchpoint_threads + num_watchpoint_threads
277 watchpoint_hit_count = self.thread_watchpoint.GetHitCount(
278 ) if expected_watchpoint_threads > 0 else 0
279 self.assertEqual(
280 expected_watchpoint_threads,
281 watchpoint_hit_count,
282 "Expected %d watchpoint hits, got %d" %
283 (expected_watchpoint_threads,
284 watchpoint_hit_count))