xref: /llvm-project/lldb/test/API/commands/thread/backtrace/TestThreadBacktraceRepeat.py (revision a4c18137d84bc48df49ee0101bef465a955e62ac)
1"""
2Test that we page getting a long backtrace on more than one thread
3"""
4
5
6import lldb
7from lldbsuite.test.decorators import *
8from lldbsuite.test.lldbtest import *
9from lldbsuite.test import lldbutil
10
11
12class TestThreadBacktracePage(TestBase):
13    NO_DEBUG_INFO_TESTCASE = True
14
15    def test_thread_backtrace_one_thread(self):
16        """Run a simplified version of the test that just hits one breakpoint and
17        doesn't care about synchronizing the two threads - hopefully this will
18        run on more systems."""
19        self.build()
20        (
21            self.inferior_target,
22            self.process,
23            thread,
24            bkpt,
25        ) = lldbutil.run_to_source_breakpoint(
26            self, self.bkpt_string, lldb.SBFileSpec("main.cpp"), only_one_thread=False
27        )
28
29        # We hit the breakpoint on at least one thread.  If we hit it on both threads
30        # simultaneously, we are ready to run our tests.  Otherwise, suspend the thread
31        # that hit the breakpoint, and continue till the second thread hits
32        # the breakpoint:
33
34        (breakpoint_threads, other_threads) = ([], [])
35        lldbutil.sort_stopped_threads(
36            self.process,
37            breakpoint_threads=breakpoint_threads,
38            other_threads=other_threads,
39        )
40        self.assertGreater(
41            len(breakpoint_threads), 0, "We hit at least one breakpoint thread"
42        )
43        self.assertGreater(len(breakpoint_threads[0].frames), 2, "I can go up")
44        thread_id = breakpoint_threads[0].idx
45        name = breakpoint_threads[0].frame[1].name.split("(")[0]
46        self.check_one_thread(thread_id, name)
47
48    def setUp(self):
49        # Call super's setUp().
50        TestBase.setUp(self)
51        # Find the line number for our breakpoint.
52        self.bkpt_string = "// Set breakpoint here"
53
54    def check_one_thread(self, thread_id, func_name):
55        # Now issue some thread backtrace commands and make sure they
56        # get the right answer back.
57        interp = self.dbg.GetCommandInterpreter()
58        result = lldb.SBCommandReturnObject()
59
60        # Run the real backtrace, remember to pass True for add_to_history since
61        # we don't generate repeat commands for commands that aren't going into the history.
62        interp.HandleCommand(
63            "thread backtrace --count 10 {0}".format(thread_id), result, True
64        )
65        self.assertTrue(result.Succeeded(), "bt with count succeeded")
66        # There should be 11 lines:
67        lines = result.GetOutput().splitlines()
68        self.assertEqual(len(lines), 11, "Got the right number of lines")
69        # First frame is stop_here:
70        self.assertNotEqual(lines[1].find("stop_here"), -1, "Found Stop Here")
71        for line in lines[2:10]:
72            self.assertNotEqual(
73                line.find(func_name),
74                -1,
75                "Name {0} not found in line: {1}".format(func_name, line),
76            )
77        # The last entry should be 43:
78        self.assertNotEqual(lines[10].find("count=43"), -1, "First show ends at 43")
79
80        # Now try a repeat, and make sure we get 10 more on this thread:
81        # import pdb; pdb.set_trace()
82        interp.HandleCommand("", result, True)
83        self.assertTrue(
84            result.Succeeded(), "repeat command failed: {0}".format(result.GetError())
85        )
86        lines = result.GetOutput().splitlines()
87        self.assertEqual(len(lines), 11, "Repeat got 11 lines")
88        # Every line should now be the recurse function:
89        for line in lines[1:10]:
90            self.assertNotEqual(line.find(func_name), -1, "Name in every line")
91        self.assertNotEqual(lines[10].find("count=33"), -1, "Last one is now 33")
92
93    def check_two_threads(
94        self, result_str, thread_id_1, name_1, thread_id_2, name_2, start_idx, end_idx
95    ):
96        # We should have 2 occurrences ot the thread header:
97        self.assertEqual(
98            result_str.count("thread #{0}".format(thread_id_1)), 1, "One for thread 1"
99        )
100        self.assertEqual(
101            result_str.count("thread #{0}".format(thread_id_2)), 1, "One for thread 2"
102        )
103        # We should have 10 occurrences of each name:
104        self.assertEqual(result_str.count(name_1), 10, "Found 10 of {0}".format(name_1))
105        self.assertEqual(result_str.count(name_2), 10, "Found 10 of {0}".format(name_1))
106        # There should be two instances of count=<start_idx> and none of count=<start-1>:
107        self.assertEqual(
108            result_str.count("count={0}".format(start_idx)),
109            2,
110            "Two instances of start_idx",
111        )
112        self.assertEqual(
113            result_str.count("count={0}".format(start_idx - 1)),
114            0,
115            "No instances of start_idx - 1",
116        )
117        # There should be two instances of count=<end_idx> and none of count=<end_idx+1>:
118        self.assertEqual(
119            result_str.count("count={0}".format(end_idx)), 2, "Two instances of end_idx"
120        )
121        self.assertEqual(
122            result_str.count("count={0}".format(end_idx + 1)),
123            0,
124            "No instances after end idx",
125        )
126
127    # The setup of this test was copied from the step-out test, and I can't tell from
128    # the comments whether it was getting two threads to the same breakpoint that was
129    # problematic, or the step-out part.  This test stops at the rendevous point so I'm
130    # removing the skipIfLinux to see if we see any flakiness in just this part of the test.
131    @skipIfWindows  # This test will hang on windows llvm.org/pr21753
132    @expectedFailureAll(oslist=["windows"])
133    @expectedFailureNetBSD
134    def test_thread_backtrace_two_threads(self):
135        """Test that repeat works even when backtracing on more than one thread."""
136        self.build()
137        (
138            self.inferior_target,
139            self.process,
140            thread,
141            bkpt,
142        ) = lldbutil.run_to_source_breakpoint(
143            self, self.bkpt_string, lldb.SBFileSpec("main.cpp"), only_one_thread=False
144        )
145
146        # We hit the breakpoint on at least one thread.  If we hit it on both threads
147        # simultaneously, we are ready to run our tests.  Otherwise, suspend the thread
148        # that hit the breakpoint, and continue till the second thread hits
149        # the breakpoint:
150
151        (breakpoint_threads, other_threads) = ([], [])
152        lldbutil.sort_stopped_threads(
153            self.process,
154            breakpoint_threads=breakpoint_threads,
155            other_threads=other_threads,
156        )
157        if len(breakpoint_threads) == 1:
158            success = thread.Suspend()
159            self.assertTrue(success, "Couldn't suspend a thread")
160            breakpoint_threads = lldbutil.continue_to_breakpoint(self.process, bkpt)
161            self.assertEqual(len(breakpoint_threads), 2, "Second thread stopped")
162
163        # Figure out which thread is which:
164        thread_id_1 = breakpoint_threads[0].idx
165        self.assertGreater(len(breakpoint_threads[0].frames), 2, "I can go up")
166        name_1 = breakpoint_threads[0].frame[1].name.split("(")[0]
167
168        thread_id_2 = breakpoint_threads[1].idx
169        self.assertGreater(len(breakpoint_threads[1].frames), 2, "I can go up")
170        name_2 = breakpoint_threads[1].frame[1].name.split("(")[0]
171
172        # Check that backtrace and repeat works on one thread, then works on the second
173        # when we switch to it:
174        self.check_one_thread(thread_id_1, name_1)
175        self.check_one_thread(thread_id_2, name_2)
176
177        # The output is looking right at this point, let's just do a couple more quick checks
178        # to see we handle two threads and a start count:
179        interp = self.dbg.GetCommandInterpreter()
180        result = lldb.SBCommandReturnObject()
181
182        interp.HandleCommand(
183            "thread backtrace --count 10 --start 10 {0} {1}".format(
184                thread_id_1, thread_id_2
185            ),
186            result,
187            True,
188        )
189        self.assertTrue(result.Succeeded(), "command succeeded for two threads")
190
191        result.Clear()
192        interp.HandleCommand("", result, True)
193        self.assertTrue(result.Succeeded(), "repeat command succeeded for two threads")
194        result_str = result.GetOutput()
195        self.check_two_threads(
196            result_str, thread_id_1, name_1, thread_id_2, name_2, 23, 32
197        )
198
199        # Finally make sure the repeat repeats:
200        result.Clear()
201        interp.HandleCommand("", result, True)
202        self.assertTrue(result.Succeeded(), "repeat command succeeded for two threads")
203        result_str = result.GetOutput()
204        self.check_two_threads(
205            result_str, thread_id_1, name_1, thread_id_2, name_2, 13, 22
206        )
207