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