diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp --- a/lldb/source/Target/StackFrame.cpp +++ b/lldb/source/Target/StackFrame.cpp @@ -1861,6 +1861,7 @@ m_concrete_frame_index = curr_frame.m_concrete_frame_index; m_reg_context_sp = curr_frame.m_reg_context_sp; m_frame_code_addr = curr_frame.m_frame_code_addr; + m_behaves_like_zeroth_frame = curr_frame.m_behaves_like_zeroth_frame; assert(!m_sc.target_sp || !curr_frame.m_sc.target_sp || m_sc.target_sp.get() == curr_frame.m_sc.target_sp.get()); assert(!m_sc.module_sp || !curr_frame.m_sc.module_sp || diff --git a/lldb/test/API/functionalities/unwind/zeroth_frame/Makefile b/lldb/test/API/functionalities/unwind/zeroth_frame/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/unwind/zeroth_frame/Makefile @@ -0,0 +1,3 @@ +C_SOURCES := main.c + +include Makefile.rules diff --git a/lldb/test/API/functionalities/unwind/zeroth_frame/TestZerothFrame.py b/lldb/test/API/functionalities/unwind/zeroth_frame/TestZerothFrame.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/unwind/zeroth_frame/TestZerothFrame.py @@ -0,0 +1,96 @@ +""" +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. +""" + +from __future__ import print_function + + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class ZerothFrame(TestBase): + mydir = TestBase.compute_mydir(__file__) + + 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) + + bp1_line = line_number('main.c', '// Set breakpoint 1 here') + bp2_line = line_number('main.c', '// Set breakpoint 2 here') + + lldbutil.run_break_set_by_file_and_line( + self, + 'main.c', + bp1_line, + num_expected_locations=1) + lldbutil.run_break_set_by_file_and_line( + self, + 'main.c', + bp2_line, + num_expected_locations=1) + + process = target.LaunchSimple( + None, None, self.get_process_working_directory()) + self.assertTrue(process, VALID_PROCESS) + + thread = process.GetThreadAtIndex(0) + 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( + process.GetThreadAtIndex(0).frame[0].GetLineEntry().GetLine(), + bp1_line, + "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() + + thread = process.GetThreadAtIndex(0) + 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_line, + "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.") diff --git a/lldb/test/API/functionalities/unwind/zeroth_frame/main.c b/lldb/test/API/functionalities/unwind/zeroth_frame/main.c new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/unwind/zeroth_frame/main.c @@ -0,0 +1,8 @@ +void func_inner() { + int a = 1; // Set breakpoint 1 here +} + +int main() { + func_inner(); + return 0; // Set breakpoint 2 here +}