1"""
2Test thread step-in, step-over and step-out work with the "Avoid no debug" option.
3"""
4
5
6
7
8import lldb
9from lldbsuite.test.decorators import *
10from lldbsuite.test.lldbtest import *
11from lldbsuite.test import lldbutil
12
13
14class StepAvoidsNoDebugTestCase(TestBase):
15
16    @add_test_categories(['pyapi'])
17    def test_step_out_with_python(self):
18        """Test stepping out using avoid-no-debug with dsyms."""
19        self.build()
20        self.get_to_starting_point()
21        self.do_step_out_past_nodebug()
22
23    @add_test_categories(['pyapi'])
24    @decorators.expectedFailureAll(
25        compiler="gcc", bugnumber="llvm.org/pr28549")
26    @decorators.expectedFailureAll(
27        compiler="clang",
28        compiler_version=[
29            ">=",
30            "3.9"],
31        archs=["i386"],
32        oslist=no_match(["freebsd"]),
33        bugnumber="llvm.org/pr28549")
34    def test_step_over_with_python(self):
35        """Test stepping over using avoid-no-debug with dwarf."""
36        self.build()
37        self.get_to_starting_point()
38        self.do_step_over_past_nodebug()
39
40    @add_test_categories(['pyapi'])
41    @decorators.expectedFailureAll(
42        compiler="gcc", bugnumber="llvm.org/pr28549")
43    @decorators.expectedFailureAll(
44        compiler="clang",
45        compiler_version=[
46            ">=",
47            "3.9"],
48        archs=["i386"],
49        oslist=no_match(["freebsd"]),
50        bugnumber="llvm.org/pr28549")
51    def test_step_in_with_python(self):
52        """Test stepping in using avoid-no-debug with dwarf."""
53        self.build()
54        self.get_to_starting_point()
55        self.do_step_in_past_nodebug()
56
57    def setUp(self):
58        TestBase.setUp(self)
59        self.main_source = "with-debug.c"
60        self.main_source_spec = lldb.SBFileSpec("with-debug.c")
61        self.dbg.HandleCommand(
62            "settings set target.process.thread.step-out-avoid-nodebug true")
63
64    def tearDown(self):
65        self.dbg.HandleCommand(
66            "settings set target.process.thread.step-out-avoid-nodebug false")
67        TestBase.tearDown(self)
68
69    def hit_correct_line(self, pattern):
70        target_line = line_number(self.main_source, pattern)
71        self.assertTrue(
72            target_line != 0,
73            "Could not find source pattern " +
74            pattern)
75        cur_line = self.thread.frames[0].GetLineEntry().GetLine()
76        self.assertEqual(
77            cur_line, target_line,
78            "Stepped to line %d instead of expected %d with pattern '%s'." %
79            (cur_line,
80             target_line,
81             pattern))
82
83    def hit_correct_function(self, pattern):
84        name = self.thread.frames[0].GetFunctionName()
85        self.assertTrue(
86            pattern in name, "Got to '%s' not the expected function '%s'." %
87            (name, pattern))
88
89    def get_to_starting_point(self):
90        exe = self.getBuildArtifact("a.out")
91        error = lldb.SBError()
92
93        self.target = self.dbg.CreateTarget(exe)
94        self.assertTrue(self.target, VALID_TARGET)
95
96        inner_bkpt = self.target.BreakpointCreateBySourceRegex(
97            "Stop here and step out of me", self.main_source_spec)
98        self.assertTrue(inner_bkpt, VALID_BREAKPOINT)
99
100        # Now launch the process, and do not stop at entry point.
101        self.process = self.target.LaunchSimple(
102            None, None, self.get_process_working_directory())
103
104        self.assertTrue(self.process, PROCESS_IS_VALID)
105
106        # Now finish, and make sure the return value is correct.
107        threads = lldbutil.get_threads_stopped_at_breakpoint(
108            self.process, inner_bkpt)
109        self.assertEquals(len(threads), 1, "Stopped at inner breakpoint.")
110        self.thread = threads[0]
111
112    def do_step_out_past_nodebug(self):
113        # The first step out takes us to the called_from_nodebug frame, just to make sure setting
114        # step-out-avoid-nodebug doesn't change the behavior in frames with
115        # debug info.
116        self.thread.StepOut()
117        self.hit_correct_line(
118            "intermediate_return_value = called_from_nodebug_actual(some_value)")
119        self.thread.StepOut()
120        self.hit_correct_line(
121            "int return_value = no_debug_caller(5, called_from_nodebug)")
122
123    def do_step_over_past_nodebug(self):
124        self.thread.StepOver()
125        self.hit_correct_line(
126            "intermediate_return_value = called_from_nodebug_actual(some_value)")
127        self.thread.StepOver()
128        self.hit_correct_line("return intermediate_return_value")
129        self.thread.StepOver()
130        # Note, lldb doesn't follow gdb's distinction between "step-out" and "step-over/step-in"
131        # when exiting a frame.  In all cases we leave the pc at the point where we exited the
132        # frame.  In gdb, step-over/step-in move to the end of the line they stepped out to.
133        # If we ever change this we will need to fix this test.
134        self.hit_correct_line(
135            "int return_value = no_debug_caller(5, called_from_nodebug)")
136
137    def do_step_in_past_nodebug(self):
138        self.thread.StepInto()
139        self.hit_correct_line(
140            "intermediate_return_value = called_from_nodebug_actual(some_value)")
141        self.thread.StepInto()
142        self.hit_correct_line("return intermediate_return_value")
143        self.thread.StepInto()
144        # Note, lldb doesn't follow gdb's distinction between "step-out" and "step-over/step-in"
145        # when exiting a frame.  In all cases we leave the pc at the point where we exited the
146        # frame.  In gdb, step-over/step-in move to the end of the line they stepped out to.
147        # If we ever change this we will need to fix this test.
148        self.hit_correct_line(
149            "int return_value = no_debug_caller(5, called_from_nodebug)")
150