| """ | |
| Test finish out of an empty function (may be one-instruction long) | |
| """ | |
| import lldb | |
| from lldbsuite.test.decorators import * | |
| from lldbsuite.test.lldbtest import * | |
| from lldbsuite.test import lldbutil | |
| class FinishFromEmptyFunctionTestCase(TestBase): | |
| NO_DEBUG_INFO_TESTCASE = True | |
| @skipIf(compiler="clang", compiler_version=['<', '17.0']) | |
| def test_finish_from_empty_function(self): | |
| """Test that when stopped at a breakpoint located at the last instruction | |
| of a function, finish leaves it correctly.""" | |
| self.build() | |
| target, _, thread, _ = lldbutil.run_to_source_breakpoint( | |
| self, "// Set breakpoint here", lldb.SBFileSpec("main.c") | |
| ) | |
| # Find the address of the last instruction of 'done()' and set a breakpoint there. | |
| # Even though 'done()' is empty, it may contain prologue and epilogue code, so | |
| # simply setting a breakpoint at the function can place it before 'ret'. | |
| error = lldb.SBError() | |
| ret_bp_addr = lldb.SBAddress() | |
| while True: | |
| thread.StepInstruction(False, error) | |
| self.assertTrue(error.Success()) | |
| frame = thread.GetSelectedFrame() | |
| if "done" in frame.GetFunctionName(): | |
| ret_bp_addr = frame.GetPCAddress() | |
| elif ret_bp_addr.IsValid(): | |
| # The entire function 'done()' has been stepped through, so 'ret_bp_addr' | |
| # now contains the address of its last instruction, i.e. 'ret'. | |
| break | |
| ret_bp = target.BreakpointCreateByAddress(ret_bp_addr.GetLoadAddress(target)) | |
| self.assertTrue(ret_bp.IsValid()) | |
| # Resume the execution and hit the new breakpoint. | |
| self.runCmd("cont") | |
| if self.TraceOn(): | |
| self.runCmd("bt") | |
| correct_stepped_out_line = line_number("main.c", "leaving main") | |
| return_statement_line = line_number("main.c", "return 0") | |
| safety_bp = target.BreakpointCreateByLocation( | |
| lldb.SBFileSpec("main.c"), return_statement_line | |
| ) | |
| self.assertTrue(safety_bp.IsValid()) | |
| thread.StepOut(error) | |
| self.assertTrue(error.Success()) | |
| if self.TraceOn(): | |
| self.runCmd("bt") | |
| frame = thread.GetSelectedFrame() | |
| self.assertEqual( | |
| frame.line_entry.GetLine(), | |
| correct_stepped_out_line, | |
| "Step-out lost control of execution, ran too far", | |
| ) |