""" Test that line information is recalculated properly for a frame when it moves from the middle of the backtrace to a zero index. This is a regression test for a StackFrame bug, where whether frame is zero or not depends on an internal field. When LLDB was updating its frame list value of the field wasn't copied into existing StackFrame instances, so those StackFrame instances, would use an incorrect line entry evaluation logic in situations if it was in the middle of the stack frame list (not zeroth), and then moved to the top position. The difference in logic is that for zeroth frames line entry is returned for program counter, while for other frame (except for those that "behave like zeroth") it is for the instruction preceding PC, as PC points to the next instruction after function call. When the bug is present, when execution stops at the second breakpoint SBFrame.GetLineEntry() returns line entry for the previous line, rather than the one with a breakpoint. Note that this is specific to SBFrame.GetLineEntry(), SBFrame.GetPCAddress().GetLineEntry() would return correct entry. This bug doesn't reproduce through an LLDB interpretator, however it happens when using API directly, for example in LLDB-MI. """ import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class ZerothFrame(TestBase): def test(self): """ Test that line information is recalculated properly for a frame when it moves from the middle of the backtrace to a zero index. """ self.build() self.setTearDownCleanup() exe = self.getBuildArtifact("a.out") target = self.dbg.CreateTarget(exe) self.assertTrue(target, VALID_TARGET) main_dot_c = lldb.SBFileSpec("main.c") bp1 = target.BreakpointCreateBySourceRegex( "// Set breakpoint 1 here", main_dot_c ) bp2 = target.BreakpointCreateBySourceRegex( "// Set breakpoint 2 here", main_dot_c ) process = target.LaunchSimple(None, None, self.get_process_working_directory()) self.assertTrue(process, VALID_PROCESS) thread = self.thread() if self.TraceOn(): print("Backtrace at the first breakpoint:") for f in thread.frames: print(f) # Check that we have stopped at correct breakpoint. self.assertEqual( thread.frame[0].GetLineEntry().GetLine(), bp1.GetLocationAtIndex(0).GetAddress().GetLineEntry().GetLine(), "LLDB reported incorrect line number.", ) # Important to use SBProcess::Continue() instead of # self.runCmd('continue'), because the problem doesn't reproduce with # 'continue' command. process.Continue() if self.TraceOn(): print("Backtrace at the second breakpoint:") for f in thread.frames: print(f) # Check that we have stopped at the breakpoint self.assertEqual( thread.frame[0].GetLineEntry().GetLine(), bp2.GetLocationAtIndex(0).GetAddress().GetLineEntry().GetLine(), "LLDB reported incorrect line number.", ) # Double-check with GetPCAddress() self.assertEqual( thread.frame[0].GetLineEntry().GetLine(), thread.frame[0].GetPCAddress().GetLineEntry().GetLine(), "LLDB reported incorrect line number.", )