xref: /llvm-project/lldb/test/API/functionalities/unwind/zeroth_frame/TestZerothFrame.py (revision 4c23625357b0b6091bc97478a79d522a832c2b21)
1"""
2Test that line information is recalculated properly for a frame when it moves
3from the middle of the backtrace to a zero index.
4
5This is a regression test for a StackFrame bug, where whether frame is zero or
6not depends on an internal field. When LLDB was updating its frame list value
7of the field wasn't copied into existing StackFrame instances, so those
8StackFrame instances, would use an incorrect line entry evaluation logic in
9situations if it was in the middle of the stack frame list (not zeroth), and
10then moved to the top position. The difference in logic is that for zeroth
11frames line entry is returned for program counter, while for other frame
12(except for those that "behave like zeroth") it is for the instruction
13preceding PC, as PC points to the next instruction after function call. When
14the bug is present, when execution stops at the second breakpoint
15SBFrame.GetLineEntry() returns line entry for the previous line, rather than
16the one with a breakpoint. Note that this is specific to
17SBFrame.GetLineEntry(), SBFrame.GetPCAddress().GetLineEntry() would return
18correct entry.
19
20This bug doesn't reproduce through an LLDB interpretator, however it happens
21when using API directly, for example in LLDB-MI.
22"""
23
24import lldb
25from lldbsuite.test.decorators import *
26from lldbsuite.test.lldbtest import *
27from lldbsuite.test import lldbutil
28
29
30class ZerothFrame(TestBase):
31    def test(self):
32        """
33        Test that line information is recalculated properly for a frame when it moves
34        from the middle of the backtrace to a zero index.
35        """
36        self.build()
37        self.setTearDownCleanup()
38
39        exe = self.getBuildArtifact("a.out")
40        target = self.dbg.CreateTarget(exe)
41        self.assertTrue(target, VALID_TARGET)
42
43        main_dot_c = lldb.SBFileSpec("main.c")
44        bp1 = target.BreakpointCreateBySourceRegex(
45            "// Set breakpoint 1 here", main_dot_c
46        )
47        bp2 = target.BreakpointCreateBySourceRegex(
48            "// Set breakpoint 2 here", main_dot_c
49        )
50
51        process = target.LaunchSimple(None, None, self.get_process_working_directory())
52        self.assertTrue(process, VALID_PROCESS)
53
54        thread = self.thread()
55
56        if self.TraceOn():
57            print("Backtrace at the first breakpoint:")
58            for f in thread.frames:
59                print(f)
60
61        # Check that we have stopped at correct breakpoint.
62        self.assertEqual(
63            thread.frame[0].GetLineEntry().GetLine(),
64            bp1.GetLocationAtIndex(0).GetAddress().GetLineEntry().GetLine(),
65            "LLDB reported incorrect line number.",
66        )
67
68        # Important to use SBProcess::Continue() instead of
69        # self.runCmd('continue'), because the problem doesn't reproduce with
70        # 'continue' command.
71        process.Continue()
72
73        if self.TraceOn():
74            print("Backtrace at the second breakpoint:")
75            for f in thread.frames:
76                print(f)
77        # Check that we have stopped at the breakpoint
78        self.assertEqual(
79            thread.frame[0].GetLineEntry().GetLine(),
80            bp2.GetLocationAtIndex(0).GetAddress().GetLineEntry().GetLine(),
81            "LLDB reported incorrect line number.",
82        )
83        # Double-check with GetPCAddress()
84        self.assertEqual(
85            thread.frame[0].GetLineEntry().GetLine(),
86            thread.frame[0].GetPCAddress().GetLineEntry().GetLine(),
87            "LLDB reported incorrect line number.",
88        )
89