xref: /llvm-project/lldb/test/API/functionalities/return-value/TestReturnValue.py (revision 9c2468821ec51defd09c246fea4a47886fff8c01)
1"""
2Test getting return-values correctly when stepping out
3"""
4
5
6import lldb
7from lldbsuite.test.decorators import *
8from lldbsuite.test.lldbtest import *
9from lldbsuite.test import lldbutil
10
11
12class ReturnValueTestCase(TestBase):
13    def affected_by_pr33042(self):
14        return (
15            "clang" in self.getCompiler()
16            and self.isAArch64()
17            and self.getPlatform() == "linux"
18        )
19
20    def affected_by_pr44132(self):
21        return self.getArchitecture() in ["aarch64", "arm"] and self.getPlatform() in [
22            "freebsd",
23            "linux",
24        ]
25
26    # ABIMacOSX_arm64 and the SysV_arm64 don't restore the storage value for memory returns on function
27    # exit, so lldb shouldn't attempt to fetch memory for those return types, as there is
28    # no easy way to guarantee that they will be correct.  This is a list of the memory
29    # return functions defined in the test file:
30    arm_no_return_values = [
31        "return_five_int",
32        "return_one_int_one_double_one_int",
33        "return_one_short_one_double_one_short",
34        "return_vector_size_float32_32",
35        "return_ext_vector_size_float32_8",
36    ]
37
38    def should_report_return_value(self, func_name):
39        abi = self.target.GetABIName()
40        if not abi in ["SysV-arm64", "ABIMacOSX_arm64", "macosx-arm"]:
41            return True
42        return not func_name in self.arm_no_return_values
43
44    @expectedFailureAll(
45        oslist=["freebsd"], archs=["i386"], bugnumber="llvm.org/pr48376"
46    )
47    @expectedFailureAll(
48        oslist=["macosx"], archs=["i386"], bugnumber="<rdar://problem/28719652>"
49    )
50    @expectedFailureAll(
51        oslist=["linux"],
52        compiler="clang",
53        compiler_version=["<=", "3.6"],
54        archs=["i386"],
55    )
56    @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24778")
57    @add_test_categories(["pyapi"])
58    def test_with_python(self):
59        """Test getting return values from stepping out."""
60        self.build()
61        exe = self.getBuildArtifact("a.out")
62        (
63            self.target,
64            self.process,
65            thread,
66            inner_sint_bkpt,
67        ) = lldbutil.run_to_name_breakpoint(self, "inner_sint", exe_name=exe)
68
69        error = lldb.SBError()
70
71        # inner_sint returns the variable value, so capture that here:
72        in_int = thread.GetFrameAtIndex(0).FindVariable("value").GetValueAsSigned(error)
73        self.assertSuccess(error)
74
75        thread.StepOut()
76
77        self.assertState(self.process.GetState(), lldb.eStateStopped)
78        self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonPlanComplete)
79
80        frame = thread.GetFrameAtIndex(0)
81        fun_name = frame.GetFunctionName()
82        self.assertEqual(fun_name, "outer_sint(int)")
83
84        return_value = thread.GetStopReturnValue()
85        self.assertTrue(return_value.IsValid())
86
87        ret_int = return_value.GetValueAsSigned(error)
88        self.assertSuccess(error)
89        self.assertEqual(in_int, ret_int)
90
91        # Run again and we will stop in inner_sint the second time outer_sint is called.
92        # Then test stepping out two frames at once:
93
94        thread_list = lldbutil.continue_to_breakpoint(self.process, inner_sint_bkpt)
95        self.assertEqual(len(thread_list), 1)
96        thread = thread_list[0]
97
98        # We are done with the inner_sint breakpoint:
99        self.target.BreakpointDelete(inner_sint_bkpt.GetID())
100
101        frame = thread.GetFrameAtIndex(1)
102        fun_name = frame.GetFunctionName()
103        self.assertEqual(fun_name, "outer_sint(int)")
104        in_int = frame.FindVariable("value").GetValueAsSigned(error)
105        self.assertSuccess(error)
106
107        thread.StepOutOfFrame(frame)
108
109        self.assertState(self.process.GetState(), lldb.eStateStopped)
110        self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonPlanComplete)
111        frame = thread.GetFrameAtIndex(0)
112        fun_name = frame.GetFunctionName()
113        self.assertEqual(fun_name, "main")
114
115        ret_value = thread.GetStopReturnValue()
116        self.assertTrue(return_value.IsValid())
117        ret_int = ret_value.GetValueAsSigned(error)
118        self.assertSuccess(error)
119        self.assertEqual(2 * in_int, ret_int)
120
121        # Now try some simple returns that have different types:
122        inner_float_bkpt = self.target.BreakpointCreateByName("inner_float(float)", exe)
123        self.assertTrue(inner_float_bkpt, VALID_BREAKPOINT)
124        self.process.Continue()
125        thread_list = lldbutil.get_threads_stopped_at_breakpoint(
126            self.process, inner_float_bkpt
127        )
128        self.assertEqual(len(thread_list), 1)
129        thread = thread_list[0]
130
131        self.target.BreakpointDelete(inner_float_bkpt.GetID())
132
133        frame = thread.GetFrameAtIndex(0)
134        in_value = frame.FindVariable("value")
135        in_float = float(in_value.GetValue())
136        thread.StepOut()
137
138        self.assertState(self.process.GetState(), lldb.eStateStopped)
139        self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonPlanComplete)
140
141        frame = thread.GetFrameAtIndex(0)
142        fun_name = frame.GetFunctionName()
143        self.assertEqual(fun_name, "outer_float(float)")
144
145        # return_value = thread.GetStopReturnValue()
146        # self.assertTrue(return_value.IsValid())
147        # return_float = float(return_value.GetValue())
148
149        # self.assertEqual(in_float, return_float)
150
151        if not self.affected_by_pr44132():
152            self.return_and_test_struct_value("return_one_int")
153            self.return_and_test_struct_value("return_two_int")
154            self.return_and_test_struct_value("return_three_int")
155            self.return_and_test_struct_value("return_four_int")
156            if not self.affected_by_pr33042():
157                self.return_and_test_struct_value("return_five_int")
158            self.return_and_test_struct_value("return_two_double")
159            self.return_and_test_struct_value("return_one_double_two_float")
160            self.return_and_test_struct_value("return_one_int_one_float_one_int")
161
162            self.return_and_test_struct_value("return_one_pointer")
163            self.return_and_test_struct_value("return_two_pointer")
164            self.return_and_test_struct_value("return_one_float_one_pointer")
165            self.return_and_test_struct_value("return_one_int_one_pointer")
166            self.return_and_test_struct_value("return_three_short_one_float")
167
168            self.return_and_test_struct_value("return_one_int_one_double")
169            self.return_and_test_struct_value("return_one_int_one_double_one_int")
170            self.return_and_test_struct_value("return_one_short_one_double_one_short")
171            self.return_and_test_struct_value("return_one_float_one_int_one_float")
172            self.return_and_test_struct_value("return_two_float")
173            # I am leaving out the packed test until we have a way to tell CLANG
174            # about alignment when reading DWARF for packed types.
175            # self.return_and_test_struct_value ("return_one_int_one_double_packed")
176            self.return_and_test_struct_value("return_one_int_one_long")
177
178    @expectedFailureAll(
179        oslist=["freebsd"], archs=["i386"], bugnumber="llvm.org/pr48376"
180    )
181    @expectedFailureAll(
182        oslist=["macosx"], archs=["i386"], bugnumber="<rdar://problem/28719652>"
183    )
184    @expectedFailureAll(
185        oslist=["linux"],
186        compiler="clang",
187        compiler_version=["<=", "3.6"],
188        archs=["i386"],
189    )
190    @expectedFailureAll(compiler=["gcc"], archs=["x86_64", "i386"])
191    @expectedFailureAll(
192        oslist=["windows"], archs=["i[3-6]86", "x86_64"], bugnumber="llvm.org/pr24778"
193    )
194    def test_vector_values(self):
195        self.build()
196        exe = self.getBuildArtifact("a.out")
197        error = lldb.SBError()
198
199        self.target = self.dbg.CreateTarget(exe)
200        self.assertTrue(self.target, VALID_TARGET)
201
202        main_bktp = self.target.BreakpointCreateByName("main", exe)
203        self.assertTrue(main_bktp, VALID_BREAKPOINT)
204
205        self.process = self.target.LaunchSimple(
206            None, None, self.get_process_working_directory()
207        )
208        self.assertEqual(
209            len(lldbutil.get_threads_stopped_at_breakpoint(self.process, main_bktp)), 1
210        )
211        self.return_and_test_struct_value("return_vector_size_float32_8")
212        self.return_and_test_struct_value("return_vector_size_float32_16")
213        if not self.affected_by_pr44132():
214            self.return_and_test_struct_value("return_vector_size_float32_32")
215        self.return_and_test_struct_value("return_ext_vector_size_float32_2")
216        self.return_and_test_struct_value("return_ext_vector_size_float32_4")
217        if not self.affected_by_pr44132():
218            self.return_and_test_struct_value("return_ext_vector_size_float32_8")
219
220    # limit the nested struct and class tests to only x86_64
221    @skipIf(archs=no_match(["x86_64"]))
222    @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24778")
223    def test_for_cpp_support(self):
224        self.build()
225        exe = self.getBuildArtifact("a.out")
226        (
227            self.target,
228            self.process,
229            thread,
230            inner_sint_bkpt,
231        ) = lldbutil.run_to_name_breakpoint(self, "inner_sint", exe_name=exe)
232
233        error = lldb.SBError()
234
235        self.target = self.dbg.CreateTarget(exe)
236        self.assertTrue(self.target, VALID_TARGET)
237
238        main_bktp = self.target.BreakpointCreateByName("main", exe)
239        self.assertTrue(main_bktp, VALID_BREAKPOINT)
240
241        self.process = self.target.LaunchSimple(
242            None, None, self.get_process_working_directory()
243        )
244        self.assertEqual(
245            len(lldbutil.get_threads_stopped_at_breakpoint(self.process, main_bktp)), 1
246        )
247        # nested struct tests
248        self.return_and_test_struct_value("return_nested_one_float_three_base")
249        self.return_and_test_struct_value("return_double_nested_one_float_one_nested")
250        self.return_and_test_struct_value("return_nested_float_struct")
251        # class test
252        self.return_and_test_struct_value("return_base_class_one_char")
253        self.return_and_test_struct_value("return_nested_class_float_and_base")
254        self.return_and_test_struct_value("return_double_nested_class_float_and_nested")
255        self.return_and_test_struct_value("return_base_class")
256        self.return_and_test_struct_value("return_derived_class")
257
258    @skipIf(compiler="clang", compiler_version=["<", "7.0"])
259    def return_and_test_struct_value(self, func_name):
260        """Pass in the name of the function to return from - takes in value, returns value."""
261
262        # Set the breakpoint, run to it, finish out.
263        bkpt = self.target.BreakpointCreateByName(func_name)
264        self.assertGreater(
265            bkpt.GetNumResolvedLocations(),
266            0,
267            "Got wrong number of locations for {0}".format(func_name),
268        )
269
270        self.process.Continue()
271
272        thread_list = lldbutil.get_threads_stopped_at_breakpoint(self.process, bkpt)
273
274        self.assertEqual(len(thread_list), 1)
275        thread = thread_list[0]
276
277        self.target.BreakpointDelete(bkpt.GetID())
278
279        in_value = thread.GetFrameAtIndex(0).FindVariable("value")
280
281        self.assertTrue(in_value.IsValid())
282        num_in_children = in_value.GetNumChildren()
283
284        # This is a little hokey, but if we don't get all the children now, then
285        # once we've stepped we won't be able to get them?
286
287        for idx in range(0, num_in_children):
288            in_child = in_value.GetChildAtIndex(idx)
289            in_child_str = in_child.GetValue()
290
291        thread.StepOut()
292
293        self.assertState(self.process.GetState(), lldb.eStateStopped)
294        self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonPlanComplete)
295
296        # Assuming all these functions step out to main.  Could figure out the caller dynamically
297        # if that would add something to the test.
298        frame = thread.GetFrameAtIndex(0)
299        fun_name = frame.GetFunctionName()
300        self.assertEqual(fun_name, "main")
301
302        frame = thread.GetFrameAtIndex(0)
303        ret_value = thread.GetStopReturnValue()
304        if not self.should_report_return_value(func_name):
305            self.assertFalse(ret_value.IsValid(), "Shouldn't have gotten a value")
306            return
307
308        self.assertTrue(ret_value.IsValid())
309
310        num_ret_children = ret_value.GetNumChildren()
311        self.assertEqual(num_in_children, num_ret_children)
312        for idx in range(0, num_ret_children):
313            in_child = in_value.GetChildAtIndex(idx)
314            ret_child = ret_value.GetChildAtIndex(idx)
315            in_child_str = in_child.GetValue()
316            ret_child_str = ret_child.GetValue()
317
318            self.assertEqual(in_child_str, ret_child_str)
319