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 breakpoint_threads = lldbutil.continue_to_breakpoint(self.process, 123 bkpt) 124 self.assertEqual(len(breakpoint_threads), 2, "Second thread stopped") 125 126 # Figure out which thread is which: 127 thread_id_1 = breakpoint_threads[0].idx 128 self.assertGreater(len(breakpoint_threads[0].frames), 2, "I can go up") 129 name_1 = breakpoint_threads[0].frame[1].name.split("(")[0] 130 131 thread_id_2 = breakpoint_threads[1].idx 132 self.assertGreater(len(breakpoint_threads[1].frames), 2, "I can go up") 133 name_2 = breakpoint_threads[1].frame[1].name.split("(")[0] 134 135 # Check that backtrace and repeat works on one thread, then works on the second 136 # when we switch to it: 137 self.check_one_thread(thread_id_1, name_1) 138 self.check_one_thread(thread_id_2, name_2) 139 140 # The output is looking right at this point, let's just do a couple more quick checks 141 # to see we handle two threads and a start count: 142 interp = self.dbg.GetCommandInterpreter() 143 result = lldb.SBCommandReturnObject() 144 145 interp.HandleCommand("thread backtrace --count 10 --start 10 {0} {1}".format(thread_id_1, thread_id_2), result, True) 146 self.assertTrue(result.Succeeded(), "command succeeded for two threads") 147 148 result.Clear() 149 interp.HandleCommand("", result, True) 150 self.assertTrue(result.Succeeded(), "repeat command succeeded for two threads") 151 result_str = result.GetOutput() 152 self.check_two_threads(result_str, thread_id_1, name_1, thread_id_2, name_2, 23, 32) 153 154 # Finally make sure the repeat repeats: 155 result.Clear() 156 interp.HandleCommand("", result, True) 157 self.assertTrue(result.Succeeded(), "repeat command succeeded for two threads") 158 result_str = result.GetOutput() 159 self.check_two_threads(result_str, thread_id_1, name_1, thread_id_2, name_2, 13, 22) 160 161 162 163 164