199451b44SJordan Rupprecht""" 299451b44SJordan RupprechtTest stepping out from a function in a multi-threaded program. 399451b44SJordan Rupprecht""" 499451b44SJordan Rupprecht 599451b44SJordan Rupprecht 699451b44SJordan Rupprechtimport lldb 799451b44SJordan Rupprechtfrom lldbsuite.test.decorators import * 899451b44SJordan Rupprechtfrom lldbsuite.test.lldbtest import * 999451b44SJordan Rupprechtfrom lldbsuite.test import lldbutil 1099451b44SJordan Rupprecht 1199451b44SJordan Rupprecht 1299451b44SJordan Rupprechtclass ThreadStepOutTestCase(TestBase): 1399451b44SJordan Rupprecht @skipIfWindows # This test will hang on windows llvm.org/pr21753 1499451b44SJordan Rupprecht @expectedFailureAll(oslist=["windows"]) 1599451b44SJordan Rupprecht @expectedFailureNetBSD 1699451b44SJordan Rupprecht def test_step_single_thread(self): 1799451b44SJordan Rupprecht """Test thread step out on one thread via command interpreter.""" 18d7dbe2c4SPavel Labath self.build() 1999451b44SJordan Rupprecht self.step_out_test(self.step_out_single_thread_with_cmd) 2099451b44SJordan Rupprecht 2199451b44SJordan Rupprecht @skipIfWindows # This test will hang on windows llvm.org/pr21753 2299451b44SJordan Rupprecht @expectedFailureAll(oslist=["windows"]) 23*2238dcc3SJonas Devlieghere @expectedFailureAll( 24*2238dcc3SJonas Devlieghere oslist=["watchos"], archs=["armv7k"], bugnumber="rdar://problem/34674488" 25*2238dcc3SJonas Devlieghere ) # stop reason is trace when it should be step-out 2699451b44SJordan Rupprecht @expectedFailureNetBSD 2799451b44SJordan Rupprecht def test_step_all_threads(self): 2899451b44SJordan Rupprecht """Test thread step out on all threads via command interpreter.""" 29d7dbe2c4SPavel Labath self.build() 3099451b44SJordan Rupprecht self.step_out_test(self.step_out_all_threads_with_cmd) 3199451b44SJordan Rupprecht 3299451b44SJordan Rupprecht @skipIfWindows # This test will hang on windows llvm.org/pr21753 3399451b44SJordan Rupprecht @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24681") 3499451b44SJordan Rupprecht @expectedFailureNetBSD 3599451b44SJordan Rupprecht def test_python(self): 3699451b44SJordan Rupprecht """Test thread step out on one thread via Python API (dwarf).""" 37d7dbe2c4SPavel Labath self.build() 3899451b44SJordan Rupprecht self.step_out_test(self.step_out_with_python) 3999451b44SJordan Rupprecht 4099451b44SJordan Rupprecht def setUp(self): 4199451b44SJordan Rupprecht # Call super's setUp(). 4299451b44SJordan Rupprecht TestBase.setUp(self) 4399451b44SJordan Rupprecht # Find the line number for our breakpoint. 44*2238dcc3SJonas Devlieghere self.bkpt_string = "// Set breakpoint here" 45*2238dcc3SJonas Devlieghere self.breakpoint = line_number("main.cpp", self.bkpt_string) 46*2238dcc3SJonas Devlieghere self.step_in_line = line_number("main.cpp", "// But we might still be here") 47*2238dcc3SJonas Devlieghere self.step_out_dest = line_number( 48*2238dcc3SJonas Devlieghere "main.cpp", "// Expect to stop here after step-out." 49*2238dcc3SJonas Devlieghere ) 5099451b44SJordan Rupprecht 5171c4da83SJim Ingham def check_stepping_thread(self): 5271c4da83SJim Ingham zeroth_frame = self.step_out_thread.frames[0] 5371c4da83SJim Ingham line_entry = zeroth_frame.line_entry 5471c4da83SJim Ingham self.assertTrue(line_entry.IsValid(), "Stopped at a valid line entry") 5571c4da83SJim Ingham self.assertEqual("main.cpp", line_entry.file.basename, "Still in main.cpp") 5671c4da83SJim Ingham # We can't really tell whether we stay on our line 5771c4da83SJim Ingham # or get to the next line, it depends on whether there are any 5871c4da83SJim Ingham # instructions between the call and the return. 5971c4da83SJim Ingham line = line_entry.line 60*2238dcc3SJonas Devlieghere self.assertTrue( 61*2238dcc3SJonas Devlieghere line == self.step_out_dest or line == self.step_in_line, 62*2238dcc3SJonas Devlieghere "Stepped to the wrong line: {0}".format(line), 63*2238dcc3SJonas Devlieghere ) 6499451b44SJordan Rupprecht 6599451b44SJordan Rupprecht def step_out_single_thread_with_cmd(self): 6671c4da83SJim Ingham other_threads = {} 6771c4da83SJim Ingham for thread in self.process.threads: 6871c4da83SJim Ingham if thread.GetIndexID() == self.step_out_thread.GetIndexID(): 6971c4da83SJim Ingham continue 7071c4da83SJim Ingham other_threads[thread.GetIndexID()] = thread.frames[0].line_entry 7171c4da83SJim Ingham 7271c4da83SJim Ingham # There should be other threads... 7371c4da83SJim Ingham self.assertNotEqual(len(other_threads), 0) 7499451b44SJordan Rupprecht self.step_out_with_cmd("this-thread") 7571c4da83SJim Ingham # The other threads should not have made progress: 7671c4da83SJim Ingham for thread in self.process.threads: 7771c4da83SJim Ingham index_id = thread.GetIndexID() 7871c4da83SJim Ingham line_entry = other_threads.get(index_id) 7971c4da83SJim Ingham if line_entry: 80*2238dcc3SJonas Devlieghere self.assertEqual( 81*2238dcc3SJonas Devlieghere thread.frames[0].line_entry.file.basename, 82*2238dcc3SJonas Devlieghere line_entry.file.basename, 83*2238dcc3SJonas Devlieghere "Thread {0} moved by file".format(index_id), 84*2238dcc3SJonas Devlieghere ) 85*2238dcc3SJonas Devlieghere self.assertEqual( 86*2238dcc3SJonas Devlieghere thread.frames[0].line_entry.line, 87*2238dcc3SJonas Devlieghere line_entry.line, 88*2238dcc3SJonas Devlieghere "Thread {0} moved by line".format(index_id), 89*2238dcc3SJonas Devlieghere ) 9099451b44SJordan Rupprecht 9199451b44SJordan Rupprecht def step_out_all_threads_with_cmd(self): 9299451b44SJordan Rupprecht self.step_out_with_cmd("all-threads") 9399451b44SJordan Rupprecht 9499451b44SJordan Rupprecht def step_out_with_cmd(self, run_mode): 9599451b44SJordan Rupprecht self.runCmd("thread select %d" % self.step_out_thread.GetIndexID()) 9699451b44SJordan Rupprecht self.runCmd("thread step-out -m %s" % run_mode) 97*2238dcc3SJonas Devlieghere self.expect( 98*2238dcc3SJonas Devlieghere "process status", 99*2238dcc3SJonas Devlieghere "Expected stop reason to be step-out", 100*2238dcc3SJonas Devlieghere substrs=["stop reason = step out"], 101*2238dcc3SJonas Devlieghere ) 10299451b44SJordan Rupprecht 10371c4da83SJim Ingham selected_thread = self.process.GetSelectedThread() 104*2238dcc3SJonas Devlieghere self.assertEqual( 105*2238dcc3SJonas Devlieghere selected_thread.GetIndexID(), 106*2238dcc3SJonas Devlieghere self.step_out_thread.GetIndexID(), 107*2238dcc3SJonas Devlieghere "Step out changed selected thread.", 108*2238dcc3SJonas Devlieghere ) 10971c4da83SJim Ingham self.check_stepping_thread() 11099451b44SJordan Rupprecht 11199451b44SJordan Rupprecht def step_out_with_python(self): 11299451b44SJordan Rupprecht self.step_out_thread.StepOut() 11399451b44SJordan Rupprecht 11499451b44SJordan Rupprecht reason = self.step_out_thread.GetStopReason() 11599451b44SJordan Rupprecht self.assertEqual( 11699451b44SJordan Rupprecht lldb.eStopReasonPlanComplete, 11799451b44SJordan Rupprecht reason, 118*2238dcc3SJonas Devlieghere "Expected thread stop reason 'plancomplete', but got '%s'" 119*2238dcc3SJonas Devlieghere % lldbutil.stop_reason_to_str(reason), 120*2238dcc3SJonas Devlieghere ) 12171c4da83SJim Ingham self.check_stepping_thread() 12299451b44SJordan Rupprecht 12399451b44SJordan Rupprecht def step_out_test(self, step_out_func): 12499451b44SJordan Rupprecht """Test single thread step out of a function.""" 125*2238dcc3SJonas Devlieghere ( 126*2238dcc3SJonas Devlieghere self.inferior_target, 127*2238dcc3SJonas Devlieghere self.process, 128*2238dcc3SJonas Devlieghere thread, 129*2238dcc3SJonas Devlieghere bkpt, 130*2238dcc3SJonas Devlieghere ) = lldbutil.run_to_source_breakpoint( 131*2238dcc3SJonas Devlieghere self, self.bkpt_string, lldb.SBFileSpec("main.cpp"), only_one_thread=False 132*2238dcc3SJonas Devlieghere ) 13399451b44SJordan Rupprecht 13499451b44SJordan Rupprecht # We hit the breakpoint on at least one thread. If we hit it on both threads 13599451b44SJordan Rupprecht # simultaneously, we can try the step out. Otherwise, suspend the thread 13699451b44SJordan Rupprecht # that hit the breakpoint, and continue till the second thread hits 13799451b44SJordan Rupprecht # the breakpoint: 13899451b44SJordan Rupprecht 13999451b44SJordan Rupprecht (breakpoint_threads, other_threads) = ([], []) 140*2238dcc3SJonas Devlieghere lldbutil.sort_stopped_threads( 141*2238dcc3SJonas Devlieghere self.process, 14299451b44SJordan Rupprecht breakpoint_threads=breakpoint_threads, 143*2238dcc3SJonas Devlieghere other_threads=other_threads, 144*2238dcc3SJonas Devlieghere ) 14599451b44SJordan Rupprecht if len(breakpoint_threads) == 1: 14699451b44SJordan Rupprecht success = thread.Suspend() 14799451b44SJordan Rupprecht self.assertTrue(success, "Couldn't suspend a thread") 148*2238dcc3SJonas Devlieghere breakpoint_threads = lldbutil.continue_to_breakpoint(self.process, bkpt) 1499bde8818SPavel Labath self.assertEqual(len(breakpoint_threads), 2, "Second thread stopped") 15099451b44SJordan Rupprecht success = thread.Resume() 15199451b44SJordan Rupprecht self.assertTrue(success, "Couldn't resume a thread") 15299451b44SJordan Rupprecht 15399451b44SJordan Rupprecht self.step_out_thread = breakpoint_threads[0] 15499451b44SJordan Rupprecht 15599451b44SJordan Rupprecht # Step out of thread stopped at breakpoint 15699451b44SJordan Rupprecht step_out_func() 157