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
24from __future__ import print_function
25
26
27import lldb
28from lldbsuite.test.decorators import *
29from lldbsuite.test.lldbtest import *
30from lldbsuite.test import lldbutil
31
32
33class ZerothFrame(TestBase):
34    mydir = TestBase.compute_mydir(__file__)
35
36    def test(self):
37        """
38        Test that line information is recalculated properly for a frame when it moves
39        from the middle of the backtrace to a zero index.
40        """
41        self.build()
42        self.setTearDownCleanup()
43
44        exe = self.getBuildArtifact("a.out")
45        target = self.dbg.CreateTarget(exe)
46        self.assertTrue(target, VALID_TARGET)
47
48        bp1_line = line_number('main.c', '// Set breakpoint 1 here')
49        bp2_line = line_number('main.c', '// Set breakpoint 2 here')
50
51        lldbutil.run_break_set_by_file_and_line(
52            self,
53            'main.c',
54            bp1_line,
55            num_expected_locations=1)
56        lldbutil.run_break_set_by_file_and_line(
57            self,
58            'main.c',
59            bp2_line,
60            num_expected_locations=1)
61
62        process = target.LaunchSimple(
63            None, None, self.get_process_working_directory())
64        self.assertTrue(process, VALID_PROCESS)
65
66        thread = process.GetThreadAtIndex(0)
67        if self.TraceOn():
68            print("Backtrace at the first breakpoint:")
69            for f in thread.frames:
70                print(f)
71        # Check that we have stopped at correct breakpoint.
72        self.assertEqual(
73            process.GetThreadAtIndex(0).frame[0].GetLineEntry().GetLine(),
74            bp1_line,
75            "LLDB reported incorrect line number.")
76
77        # Important to use SBProcess::Continue() instead of
78        # self.runCmd('continue'), because the problem doesn't reproduce with
79        # 'continue' command.
80        process.Continue()
81
82        thread = process.GetThreadAtIndex(0)
83        if self.TraceOn():
84            print("Backtrace at the second breakpoint:")
85            for f in thread.frames:
86                print(f)
87        # Check that we have stopped at the breakpoint
88        self.assertEqual(
89            thread.frame[0].GetLineEntry().GetLine(),
90            bp2_line,
91            "LLDB reported incorrect line number.")
92        # Double-check with GetPCAddress()
93        self.assertEqual(
94            thread.frame[0].GetLineEntry().GetLine(),
95            thread.frame[0].GetPCAddress().GetLineEntry().GetLine(),
96            "LLDB reported incorrect line number.")
97