157 lines · python
1"""2Test stepping out from a function in a multi-threaded program.3"""4 5 6import lldb7from lldbsuite.test.decorators import *8from lldbsuite.test.lldbtest import *9from lldbsuite.test import lldbutil10 11 12class ThreadStepOutTestCase(TestBase):13 @skipIfWindows # This test will hang on windows llvm.org/pr2175314 @expectedFailureAll(oslist=["windows"])15 @expectedFailureNetBSD16 def test_step_single_thread(self):17 """Test thread step out on one thread via command interpreter."""18 self.build()19 self.step_out_test(self.step_out_single_thread_with_cmd)20 21 @skipIfWindows # This test will hang on windows llvm.org/pr2175322 @expectedFailureAll(oslist=["windows"])23 @expectedFailureAll(24 oslist=["watchos"], archs=["armv7k"], bugnumber="rdar://problem/34674488"25 ) # stop reason is trace when it should be step-out26 @expectedFailureNetBSD27 def test_step_all_threads(self):28 """Test thread step out on all threads via command interpreter."""29 self.build()30 self.step_out_test(self.step_out_all_threads_with_cmd)31 32 @skipIfWindows # This test will hang on windows llvm.org/pr2175333 @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24681")34 @expectedFailureNetBSD35 def test_python(self):36 """Test thread step out on one thread via Python API (dwarf)."""37 self.build()38 self.step_out_test(self.step_out_with_python)39 40 def setUp(self):41 # Call super's setUp().42 TestBase.setUp(self)43 # Find the line number for our breakpoint.44 self.bkpt_string = "// Set breakpoint here"45 self.breakpoint = line_number("main.cpp", self.bkpt_string)46 self.step_in_line = line_number("main.cpp", "// But we might still be here")47 self.step_out_dest = line_number(48 "main.cpp", "// Expect to stop here after step-out."49 )50 51 def check_stepping_thread(self):52 zeroth_frame = self.step_out_thread.frames[0]53 line_entry = zeroth_frame.line_entry54 self.assertTrue(line_entry.IsValid(), "Stopped at a valid line entry")55 self.assertEqual("main.cpp", line_entry.file.basename, "Still in main.cpp")56 # We can't really tell whether we stay on our line57 # or get to the next line, it depends on whether there are any58 # instructions between the call and the return.59 line = line_entry.line60 self.assertTrue(61 line == self.step_out_dest or line == self.step_in_line,62 "Stepped to the wrong line: {0}".format(line),63 )64 65 def step_out_single_thread_with_cmd(self):66 other_threads = {}67 for thread in self.process.threads:68 if thread.GetIndexID() == self.step_out_thread.GetIndexID():69 continue70 other_threads[thread.GetIndexID()] = thread.frames[0].line_entry71 72 # There should be other threads...73 self.assertNotEqual(len(other_threads), 0)74 self.step_out_with_cmd("this-thread")75 # The other threads should not have made progress:76 for thread in self.process.threads:77 index_id = thread.GetIndexID()78 line_entry = other_threads.get(index_id)79 if line_entry:80 self.assertEqual(81 thread.frames[0].line_entry.file.basename,82 line_entry.file.basename,83 "Thread {0} moved by file".format(index_id),84 )85 self.assertEqual(86 thread.frames[0].line_entry.line,87 line_entry.line,88 "Thread {0} moved by line".format(index_id),89 )90 91 def step_out_all_threads_with_cmd(self):92 self.step_out_with_cmd("all-threads")93 94 def step_out_with_cmd(self, run_mode):95 self.runCmd("thread select %d" % self.step_out_thread.GetIndexID())96 self.runCmd("thread step-out -m %s" % run_mode)97 self.expect(98 "process status",99 "Expected stop reason to be step-out",100 substrs=["stop reason = step out"],101 )102 103 selected_thread = self.process.GetSelectedThread()104 self.assertEqual(105 selected_thread.GetIndexID(),106 self.step_out_thread.GetIndexID(),107 "Step out changed selected thread.",108 )109 self.check_stepping_thread()110 111 def step_out_with_python(self):112 self.step_out_thread.StepOut()113 114 reason = self.step_out_thread.GetStopReason()115 self.assertEqual(116 lldb.eStopReasonPlanComplete,117 reason,118 "Expected thread stop reason 'plancomplete', but got '%s'"119 % lldbutil.stop_reason_to_str(reason),120 )121 self.check_stepping_thread()122 123 def step_out_test(self, step_out_func):124 """Test single thread step out of a function."""125 (126 self.inferior_target,127 self.process,128 thread,129 bkpt,130 ) = lldbutil.run_to_source_breakpoint(131 self, self.bkpt_string, lldb.SBFileSpec("main.cpp"), only_one_thread=False132 )133 134 # We hit the breakpoint on at least one thread. If we hit it on both threads135 # simultaneously, we can try the step out. Otherwise, suspend the thread136 # that hit the breakpoint, and continue till the second thread hits137 # the breakpoint:138 139 (breakpoint_threads, other_threads) = ([], [])140 lldbutil.sort_stopped_threads(141 self.process,142 breakpoint_threads=breakpoint_threads,143 other_threads=other_threads,144 )145 if len(breakpoint_threads) == 1:146 success = thread.Suspend()147 self.assertTrue(success, "Couldn't suspend a thread")148 breakpoint_threads = lldbutil.continue_to_breakpoint(self.process, bkpt)149 self.assertEqual(len(breakpoint_threads), 2, "Second thread stopped")150 success = thread.Resume()151 self.assertTrue(success, "Couldn't resume a thread")152 153 self.step_out_thread = breakpoint_threads[0]154 155 # Step out of thread stopped at breakpoint156 step_out_func()157