xref: /openbsd-src/gnu/llvm/lldb/packages/Python/lldbsuite/test/concurrent_base.py (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1061da546Spatrick"""
2061da546SpatrickA stress-test of sorts for LLDB's handling of threads in the inferior.
3061da546Spatrick
4061da546SpatrickThis test sets a breakpoint in the main thread where test parameters (numbers of
5061da546Spatrickthreads) can be adjusted, runs the inferior to that point, and modifies the
6061da546Spatricklocals that control the event thread counts. This test also sets a breakpoint in
7061da546Spatrickbreakpoint_func (the function executed by each 'breakpoint' thread) and a
8061da546Spatrickwatchpoint on a global modified in watchpoint_func. The inferior is continued
9061da546Spatrickuntil exit or a crash takes place, and the number of events seen by LLDB is
10061da546Spatrickverified to match the expected number of events.
11061da546Spatrick"""
12061da546Spatrick
13061da546Spatrick
14061da546Spatrick
15061da546Spatrickimport unittest2
16061da546Spatrickimport lldb
17061da546Spatrickfrom lldbsuite.test.decorators import *
18061da546Spatrickfrom lldbsuite.test.lldbtest import *
19061da546Spatrickfrom lldbsuite.test import lldbutil
20061da546Spatrick
21061da546Spatrick
22061da546Spatrickclass ConcurrentEventsBase(TestBase):
23061da546Spatrick
24061da546Spatrick    # Concurrency is the primary test factor here, not debug info variants.
25061da546Spatrick    NO_DEBUG_INFO_TESTCASE = True
26061da546Spatrick
27061da546Spatrick    def setUp(self):
28061da546Spatrick        # Call super's setUp().
29061da546Spatrick        super(ConcurrentEventsBase, self).setUp()
30061da546Spatrick        # Find the line number for our breakpoint.
31061da546Spatrick        self.filename = 'main.cpp'
32061da546Spatrick        self.thread_breakpoint_line = line_number(
33061da546Spatrick            self.filename, '// Set breakpoint here')
34061da546Spatrick        self.setup_breakpoint_line = line_number(
35061da546Spatrick            self.filename, '// Break here and adjust num')
36061da546Spatrick        self.finish_breakpoint_line = line_number(
37061da546Spatrick            self.filename, '// Break here and verify one thread is active')
38061da546Spatrick
39061da546Spatrick    def describe_threads(self):
40061da546Spatrick        ret = []
41061da546Spatrick        for x in self.inferior_process:
42061da546Spatrick            id = x.GetIndexID()
43061da546Spatrick            reason = x.GetStopReason()
44061da546Spatrick            status = "stopped" if x.IsStopped() else "running"
45061da546Spatrick            reason_str = lldbutil.stop_reason_to_str(reason)
46061da546Spatrick            if reason == lldb.eStopReasonBreakpoint:
47061da546Spatrick                bpid = x.GetStopReasonDataAtIndex(0)
48061da546Spatrick                bp = self.inferior_target.FindBreakpointByID(bpid)
49061da546Spatrick                reason_str = "%s hit %d times" % (
50061da546Spatrick                    lldbutil.get_description(bp), bp.GetHitCount())
51061da546Spatrick            elif reason == lldb.eStopReasonWatchpoint:
52061da546Spatrick                watchid = x.GetStopReasonDataAtIndex(0)
53061da546Spatrick                watch = self.inferior_target.FindWatchpointByID(watchid)
54061da546Spatrick                reason_str = "%s hit %d times" % (
55061da546Spatrick                    lldbutil.get_description(watch), watch.GetHitCount())
56061da546Spatrick            elif reason == lldb.eStopReasonSignal:
57061da546Spatrick                signals = self.inferior_process.GetUnixSignals()
58061da546Spatrick                signal_name = signals.GetSignalAsCString(
59061da546Spatrick                    x.GetStopReasonDataAtIndex(0))
60061da546Spatrick                reason_str = "signal %s" % signal_name
61061da546Spatrick
62061da546Spatrick            location = "\t".join([lldbutil.get_description(
63061da546Spatrick                x.GetFrameAtIndex(i)) for i in range(x.GetNumFrames())])
64061da546Spatrick            ret.append(
65061da546Spatrick                "thread %d %s due to %s at\n\t%s" %
66061da546Spatrick                (id, status, reason_str, location))
67061da546Spatrick        return ret
68061da546Spatrick
69061da546Spatrick    def add_breakpoint(self, line, descriptions):
70061da546Spatrick        """ Adds a breakpoint at self.filename:line and appends its description to descriptions, and
71061da546Spatrick            returns the LLDB SBBreakpoint object.
72061da546Spatrick        """
73061da546Spatrick
74061da546Spatrick        bpno = lldbutil.run_break_set_by_file_and_line(
75061da546Spatrick            self, self.filename, line, num_expected_locations=-1)
76061da546Spatrick        bp = self.inferior_target.FindBreakpointByID(bpno)
77*f6aab3d8Srobert        descriptions.append(": file = 'main.cpp', line = %d" % line)
78061da546Spatrick        return bp
79061da546Spatrick
80061da546Spatrick    def inferior_done(self):
81061da546Spatrick        """ Returns true if the inferior is done executing all the event threads (and is stopped at self.finish_breakpoint,
82061da546Spatrick            or has terminated execution.
83061da546Spatrick        """
84061da546Spatrick        return self.finish_breakpoint.GetHitCount() > 0 or \
85061da546Spatrick            self.crash_count > 0 or \
86061da546Spatrick            self.inferior_process.GetState() == lldb.eStateExited
87061da546Spatrick
88061da546Spatrick    def count_signaled_threads(self):
89061da546Spatrick        count = 0
90061da546Spatrick        for thread in self.inferior_process:
91061da546Spatrick            if thread.GetStopReason() == lldb.eStopReasonSignal and thread.GetStopReasonDataAtIndex(
92061da546Spatrick                    0) == self.inferior_process.GetUnixSignals().GetSignalNumberFromName('SIGUSR1'):
93061da546Spatrick                count += 1
94061da546Spatrick        return count
95061da546Spatrick
96061da546Spatrick    def do_thread_actions(self,
97061da546Spatrick                          num_breakpoint_threads=0,
98061da546Spatrick                          num_signal_threads=0,
99061da546Spatrick                          num_watchpoint_threads=0,
100061da546Spatrick                          num_crash_threads=0,
101061da546Spatrick                          num_delay_breakpoint_threads=0,
102061da546Spatrick                          num_delay_signal_threads=0,
103061da546Spatrick                          num_delay_watchpoint_threads=0,
104061da546Spatrick                          num_delay_crash_threads=0):
105061da546Spatrick        """ Sets a breakpoint in the main thread where test parameters (numbers of threads) can be adjusted, runs the inferior
106061da546Spatrick            to that point, and modifies the locals that control the event thread counts. Also sets a breakpoint in
107061da546Spatrick            breakpoint_func (the function executed by each 'breakpoint' thread) and a watchpoint on a global modified in
108061da546Spatrick            watchpoint_func. The inferior is continued until exit or a crash takes place, and the number of events seen by LLDB
109061da546Spatrick            is verified to match the expected number of events.
110061da546Spatrick        """
111061da546Spatrick        exe = self.getBuildArtifact("a.out")
112061da546Spatrick        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
113061da546Spatrick
114061da546Spatrick        # Get the target
115061da546Spatrick        self.inferior_target = self.dbg.GetSelectedTarget()
116061da546Spatrick
117061da546Spatrick        expected_bps = []
118061da546Spatrick
119061da546Spatrick        # Initialize all the breakpoints (main thread/aux thread)
120061da546Spatrick        self.setup_breakpoint = self.add_breakpoint(
121061da546Spatrick            self.setup_breakpoint_line, expected_bps)
122061da546Spatrick        self.finish_breakpoint = self.add_breakpoint(
123061da546Spatrick            self.finish_breakpoint_line, expected_bps)
124061da546Spatrick
125061da546Spatrick        # Set the thread breakpoint
126061da546Spatrick        if num_breakpoint_threads + num_delay_breakpoint_threads > 0:
127061da546Spatrick            self.thread_breakpoint = self.add_breakpoint(
128061da546Spatrick                self.thread_breakpoint_line, expected_bps)
129061da546Spatrick
130061da546Spatrick        # Verify breakpoints
131061da546Spatrick        self.expect(
132061da546Spatrick            "breakpoint list -f",
133061da546Spatrick            "Breakpoint locations shown correctly",
134061da546Spatrick            substrs=expected_bps)
135061da546Spatrick
136061da546Spatrick        # Run the program.
137061da546Spatrick        self.runCmd("run", RUN_SUCCEEDED)
138061da546Spatrick
139061da546Spatrick        # Check we are at line self.setup_breakpoint
140061da546Spatrick        self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT,
141061da546Spatrick                    substrs=["stop reason = breakpoint 1."])
142061da546Spatrick
143061da546Spatrick        # Initialize the (single) watchpoint on the global variable (g_watchme)
144061da546Spatrick        if num_watchpoint_threads + num_delay_watchpoint_threads > 0:
145061da546Spatrick            self.runCmd("watchpoint set variable g_watchme")
146061da546Spatrick            for w in self.inferior_target.watchpoint_iter():
147061da546Spatrick                self.thread_watchpoint = w
148061da546Spatrick                self.assertTrue(
149061da546Spatrick                    "g_watchme" in str(
150061da546Spatrick                        self.thread_watchpoint),
151061da546Spatrick                    "Watchpoint location not shown correctly")
152061da546Spatrick
153061da546Spatrick        # Get the process
154061da546Spatrick        self.inferior_process = self.inferior_target.GetProcess()
155061da546Spatrick
156061da546Spatrick        # We should be stopped at the setup site where we can set the number of
157061da546Spatrick        # threads doing each action (break/crash/signal/watch)
158061da546Spatrick        self.assertEqual(
159061da546Spatrick            self.inferior_process.GetNumThreads(),
160061da546Spatrick            1,
161061da546Spatrick            'Expected to stop before any additional threads are spawned.')
162061da546Spatrick
163061da546Spatrick        self.runCmd("expr num_breakpoint_threads=%d" % num_breakpoint_threads)
164061da546Spatrick        self.runCmd("expr num_crash_threads=%d" % num_crash_threads)
165061da546Spatrick        self.runCmd("expr num_signal_threads=%d" % num_signal_threads)
166061da546Spatrick        self.runCmd("expr num_watchpoint_threads=%d" % num_watchpoint_threads)
167061da546Spatrick
168061da546Spatrick        self.runCmd(
169061da546Spatrick            "expr num_delay_breakpoint_threads=%d" %
170061da546Spatrick            num_delay_breakpoint_threads)
171061da546Spatrick        self.runCmd(
172061da546Spatrick            "expr num_delay_crash_threads=%d" %
173061da546Spatrick            num_delay_crash_threads)
174061da546Spatrick        self.runCmd(
175061da546Spatrick            "expr num_delay_signal_threads=%d" %
176061da546Spatrick            num_delay_signal_threads)
177061da546Spatrick        self.runCmd(
178061da546Spatrick            "expr num_delay_watchpoint_threads=%d" %
179061da546Spatrick            num_delay_watchpoint_threads)
180061da546Spatrick
181061da546Spatrick        # Continue the inferior so threads are spawned
182061da546Spatrick        self.runCmd("continue")
183061da546Spatrick
184061da546Spatrick        # Make sure we see all the threads. The inferior program's threads all synchronize with a pseudo-barrier; that is,
185061da546Spatrick        # the inferior program ensures all threads are started and running
186061da546Spatrick        # before any thread triggers its 'event'.
187061da546Spatrick        num_threads = self.inferior_process.GetNumThreads()
188061da546Spatrick        expected_num_threads = num_breakpoint_threads + num_delay_breakpoint_threads \
189061da546Spatrick            + num_signal_threads + num_delay_signal_threads \
190061da546Spatrick            + num_watchpoint_threads + num_delay_watchpoint_threads \
191061da546Spatrick            + num_crash_threads + num_delay_crash_threads + 1
192061da546Spatrick        self.assertEqual(
193061da546Spatrick            num_threads,
194061da546Spatrick            expected_num_threads,
195061da546Spatrick            'Expected to see %d threads, but seeing %d. Details:\n%s' %
196061da546Spatrick            (expected_num_threads,
197061da546Spatrick             num_threads,
198061da546Spatrick             "\n\t".join(
199061da546Spatrick                 self.describe_threads())))
200061da546Spatrick
201061da546Spatrick        self.signal_count = self.count_signaled_threads()
202061da546Spatrick        self.crash_count = len(
203061da546Spatrick            lldbutil.get_crashed_threads(
204061da546Spatrick                self, self.inferior_process))
205061da546Spatrick
206061da546Spatrick        # Run to completion (or crash)
207061da546Spatrick        while not self.inferior_done():
208061da546Spatrick            if self.TraceOn():
209061da546Spatrick                self.runCmd("thread backtrace all")
210061da546Spatrick            self.runCmd("continue")
211061da546Spatrick            self.signal_count += self.count_signaled_threads()
212061da546Spatrick            self.crash_count += len(
213061da546Spatrick                lldbutil.get_crashed_threads(
214061da546Spatrick                    self, self.inferior_process))
215061da546Spatrick
216061da546Spatrick        if num_crash_threads > 0 or num_delay_crash_threads > 0:
217061da546Spatrick            # Expecting a crash
218061da546Spatrick            self.assertTrue(
219061da546Spatrick                self.crash_count > 0,
220061da546Spatrick                "Expecting at least one thread to crash. Details: %s" %
221061da546Spatrick                "\t\n".join(
222061da546Spatrick                    self.describe_threads()))
223061da546Spatrick
224061da546Spatrick            # Ensure the zombie process is reaped
225061da546Spatrick            self.runCmd("process kill")
226061da546Spatrick
227061da546Spatrick        elif num_crash_threads == 0 and num_delay_crash_threads == 0:
228061da546Spatrick            # There should be a single active thread (the main one) which hit
229061da546Spatrick            # the breakpoint after joining
230061da546Spatrick            self.assertEqual(
231061da546Spatrick                1,
232061da546Spatrick                self.finish_breakpoint.GetHitCount(),
233061da546Spatrick                "Expected main thread (finish) breakpoint to be hit once")
234061da546Spatrick
235061da546Spatrick            num_threads = self.inferior_process.GetNumThreads()
236061da546Spatrick            self.assertEqual(
237061da546Spatrick                1,
238061da546Spatrick                num_threads,
239061da546Spatrick                "Expecting 1 thread but seeing %d. Details:%s" %
240061da546Spatrick                (num_threads,
241061da546Spatrick                 "\n\t".join(
242061da546Spatrick                     self.describe_threads())))
243061da546Spatrick            self.runCmd("continue")
244061da546Spatrick
245061da546Spatrick            # The inferior process should have exited without crashing
246061da546Spatrick            self.assertEqual(
247061da546Spatrick                0,
248061da546Spatrick                self.crash_count,
249061da546Spatrick                "Unexpected thread(s) in crashed state")
250061da546Spatrick            self.assertEqual(
251061da546Spatrick                self.inferior_process.GetState(),
252061da546Spatrick                lldb.eStateExited,
253061da546Spatrick                PROCESS_EXITED)
254061da546Spatrick
255061da546Spatrick            # Verify the number of actions took place matches expected numbers
256061da546Spatrick            expected_breakpoint_threads = num_delay_breakpoint_threads + num_breakpoint_threads
257061da546Spatrick            breakpoint_hit_count = self.thread_breakpoint.GetHitCount(
258061da546Spatrick            ) if expected_breakpoint_threads > 0 else 0
259061da546Spatrick            self.assertEqual(
260061da546Spatrick                expected_breakpoint_threads,
261061da546Spatrick                breakpoint_hit_count,
262061da546Spatrick                "Expected %d breakpoint hits, but got %d" %
263061da546Spatrick                (expected_breakpoint_threads,
264061da546Spatrick                 breakpoint_hit_count))
265061da546Spatrick
266061da546Spatrick            expected_signal_threads = num_delay_signal_threads + num_signal_threads
267061da546Spatrick            self.assertEqual(
268061da546Spatrick                expected_signal_threads,
269061da546Spatrick                self.signal_count,
270061da546Spatrick                "Expected %d stops due to signal delivery, but got %d" %
271061da546Spatrick                (expected_signal_threads,
272061da546Spatrick                 self.signal_count))
273061da546Spatrick
274061da546Spatrick            expected_watchpoint_threads = num_delay_watchpoint_threads + num_watchpoint_threads
275061da546Spatrick            watchpoint_hit_count = self.thread_watchpoint.GetHitCount(
276061da546Spatrick            ) if expected_watchpoint_threads > 0 else 0
277061da546Spatrick            self.assertEqual(
278061da546Spatrick                expected_watchpoint_threads,
279061da546Spatrick                watchpoint_hit_count,
280061da546Spatrick                "Expected %d watchpoint hits, got %d" %
281061da546Spatrick                (expected_watchpoint_threads,
282061da546Spatrick                 watchpoint_hit_count))
283