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