""" Test getting return-values correctly when stepping out """ import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class ReturnValueTestCase(TestBase): def affected_by_pr33042(self): return ( "clang" in self.getCompiler() and self.isAArch64() and self.getPlatform() == "linux" ) def affected_by_pr44132(self): return self.getArchitecture() in ["aarch64", "arm"] and self.getPlatform() in [ "freebsd", "linux", ] # ABIMacOSX_arm64 and the SysV_arm64 don't restore the storage value for memory returns on function # exit, so lldb shouldn't attempt to fetch memory for those return types, as there is # no easy way to guarantee that they will be correct. This is a list of the memory # return functions defined in the test file: arm_no_return_values = [ "return_five_int", "return_one_int_one_double_one_int", "return_one_short_one_double_one_short", "return_vector_size_float32_32", "return_ext_vector_size_float32_8", ] def should_report_return_value(self, func_name): abi = self.target.GetABIName() if not abi in ["SysV-arm64", "ABIMacOSX_arm64", "macosx-arm"]: return True return not func_name in self.arm_no_return_values @expectedFailureAll( oslist=["freebsd"], archs=["i386"], bugnumber="llvm.org/pr48376" ) @expectedFailureAll( oslist=["macosx"], archs=["i386"], bugnumber="" ) @expectedFailureAll( oslist=["linux"], compiler="clang", compiler_version=["<=", "3.6"], archs=["i386"], ) @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24778") @add_test_categories(["pyapi"]) def test_with_python(self): """Test getting return values from stepping out.""" self.build() exe = self.getBuildArtifact("a.out") ( self.target, self.process, thread, inner_sint_bkpt, ) = lldbutil.run_to_name_breakpoint(self, "inner_sint", exe_name=exe) error = lldb.SBError() # inner_sint returns the variable value, so capture that here: in_int = thread.GetFrameAtIndex(0).FindVariable("value").GetValueAsSigned(error) self.assertSuccess(error) thread.StepOut() self.assertState(self.process.GetState(), lldb.eStateStopped) self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonPlanComplete) frame = thread.GetFrameAtIndex(0) fun_name = frame.GetFunctionName() self.assertEqual(fun_name, "outer_sint(int)") return_value = thread.GetStopReturnValue() self.assertTrue(return_value.IsValid()) ret_int = return_value.GetValueAsSigned(error) self.assertSuccess(error) self.assertEqual(in_int, ret_int) # Run again and we will stop in inner_sint the second time outer_sint is called. # Then test stepping out two frames at once: thread_list = lldbutil.continue_to_breakpoint(self.process, inner_sint_bkpt) self.assertEqual(len(thread_list), 1) thread = thread_list[0] # We are done with the inner_sint breakpoint: self.target.BreakpointDelete(inner_sint_bkpt.GetID()) frame = thread.GetFrameAtIndex(1) fun_name = frame.GetFunctionName() self.assertEqual(fun_name, "outer_sint(int)") in_int = frame.FindVariable("value").GetValueAsSigned(error) self.assertSuccess(error) thread.StepOutOfFrame(frame) self.assertState(self.process.GetState(), lldb.eStateStopped) self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonPlanComplete) frame = thread.GetFrameAtIndex(0) fun_name = frame.GetFunctionName() self.assertEqual(fun_name, "main") ret_value = thread.GetStopReturnValue() self.assertTrue(return_value.IsValid()) ret_int = ret_value.GetValueAsSigned(error) self.assertSuccess(error) self.assertEqual(2 * in_int, ret_int) # Now try some simple returns that have different types: inner_float_bkpt = self.target.BreakpointCreateByName("inner_float(float)", exe) self.assertTrue(inner_float_bkpt, VALID_BREAKPOINT) self.process.Continue() thread_list = lldbutil.get_threads_stopped_at_breakpoint( self.process, inner_float_bkpt ) self.assertEqual(len(thread_list), 1) thread = thread_list[0] self.target.BreakpointDelete(inner_float_bkpt.GetID()) frame = thread.GetFrameAtIndex(0) in_value = frame.FindVariable("value") in_float = float(in_value.GetValue()) thread.StepOut() self.assertState(self.process.GetState(), lldb.eStateStopped) self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonPlanComplete) frame = thread.GetFrameAtIndex(0) fun_name = frame.GetFunctionName() self.assertEqual(fun_name, "outer_float(float)") # return_value = thread.GetStopReturnValue() # self.assertTrue(return_value.IsValid()) # return_float = float(return_value.GetValue()) # self.assertEqual(in_float, return_float) if not self.affected_by_pr44132(): self.return_and_test_struct_value("return_one_int") self.return_and_test_struct_value("return_two_int") self.return_and_test_struct_value("return_three_int") self.return_and_test_struct_value("return_four_int") if not self.affected_by_pr33042(): self.return_and_test_struct_value("return_five_int") self.return_and_test_struct_value("return_two_double") self.return_and_test_struct_value("return_one_double_two_float") self.return_and_test_struct_value("return_one_int_one_float_one_int") self.return_and_test_struct_value("return_one_pointer") self.return_and_test_struct_value("return_two_pointer") self.return_and_test_struct_value("return_one_float_one_pointer") self.return_and_test_struct_value("return_one_int_one_pointer") self.return_and_test_struct_value("return_three_short_one_float") self.return_and_test_struct_value("return_one_int_one_double") self.return_and_test_struct_value("return_one_int_one_double_one_int") self.return_and_test_struct_value("return_one_short_one_double_one_short") self.return_and_test_struct_value("return_one_float_one_int_one_float") self.return_and_test_struct_value("return_two_float") # I am leaving out the packed test until we have a way to tell CLANG # about alignment when reading DWARF for packed types. # self.return_and_test_struct_value ("return_one_int_one_double_packed") self.return_and_test_struct_value("return_one_int_one_long") @expectedFailureAll( oslist=["freebsd"], archs=["i386"], bugnumber="llvm.org/pr48376" ) @expectedFailureAll( oslist=["macosx"], archs=["i386"], bugnumber="" ) @expectedFailureAll( oslist=["linux"], compiler="clang", compiler_version=["<=", "3.6"], archs=["i386"], ) @expectedFailureAll(compiler=["gcc"], archs=["x86_64", "i386"]) @expectedFailureAll( oslist=["windows"], archs=["i[3-6]86", "x86_64"], bugnumber="llvm.org/pr24778" ) def test_vector_values(self): self.build() exe = self.getBuildArtifact("a.out") error = lldb.SBError() self.target = self.dbg.CreateTarget(exe) self.assertTrue(self.target, VALID_TARGET) main_bktp = self.target.BreakpointCreateByName("main", exe) self.assertTrue(main_bktp, VALID_BREAKPOINT) self.process = self.target.LaunchSimple( None, None, self.get_process_working_directory() ) self.assertEqual( len(lldbutil.get_threads_stopped_at_breakpoint(self.process, main_bktp)), 1 ) self.return_and_test_struct_value("return_vector_size_float32_8") self.return_and_test_struct_value("return_vector_size_float32_16") if not self.affected_by_pr44132(): self.return_and_test_struct_value("return_vector_size_float32_32") self.return_and_test_struct_value("return_ext_vector_size_float32_2") self.return_and_test_struct_value("return_ext_vector_size_float32_4") if not self.affected_by_pr44132(): self.return_and_test_struct_value("return_ext_vector_size_float32_8") # limit the nested struct and class tests to only x86_64 @skipIf(archs=no_match(["x86_64"])) @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24778") def test_for_cpp_support(self): self.build() exe = self.getBuildArtifact("a.out") ( self.target, self.process, thread, inner_sint_bkpt, ) = lldbutil.run_to_name_breakpoint(self, "inner_sint", exe_name=exe) error = lldb.SBError() self.target = self.dbg.CreateTarget(exe) self.assertTrue(self.target, VALID_TARGET) main_bktp = self.target.BreakpointCreateByName("main", exe) self.assertTrue(main_bktp, VALID_BREAKPOINT) self.process = self.target.LaunchSimple( None, None, self.get_process_working_directory() ) self.assertEqual( len(lldbutil.get_threads_stopped_at_breakpoint(self.process, main_bktp)), 1 ) # nested struct tests self.return_and_test_struct_value("return_nested_one_float_three_base") self.return_and_test_struct_value("return_double_nested_one_float_one_nested") self.return_and_test_struct_value("return_nested_float_struct") # class test self.return_and_test_struct_value("return_base_class_one_char") self.return_and_test_struct_value("return_nested_class_float_and_base") self.return_and_test_struct_value("return_double_nested_class_float_and_nested") self.return_and_test_struct_value("return_base_class") self.return_and_test_struct_value("return_derived_class") @skipIf(compiler="clang", compiler_version=["<", "7.0"]) def return_and_test_struct_value(self, func_name): """Pass in the name of the function to return from - takes in value, returns value.""" # Set the breakpoint, run to it, finish out. bkpt = self.target.BreakpointCreateByName(func_name) self.assertGreater( bkpt.GetNumResolvedLocations(), 0, "Got wrong number of locations for {0}".format(func_name), ) self.process.Continue() thread_list = lldbutil.get_threads_stopped_at_breakpoint(self.process, bkpt) self.assertEqual(len(thread_list), 1) thread = thread_list[0] self.target.BreakpointDelete(bkpt.GetID()) in_value = thread.GetFrameAtIndex(0).FindVariable("value") self.assertTrue(in_value.IsValid()) num_in_children = in_value.GetNumChildren() # This is a little hokey, but if we don't get all the children now, then # once we've stepped we won't be able to get them? for idx in range(0, num_in_children): in_child = in_value.GetChildAtIndex(idx) in_child_str = in_child.GetValue() thread.StepOut() self.assertState(self.process.GetState(), lldb.eStateStopped) self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonPlanComplete) # Assuming all these functions step out to main. Could figure out the caller dynamically # if that would add something to the test. frame = thread.GetFrameAtIndex(0) fun_name = frame.GetFunctionName() self.assertEqual(fun_name, "main") frame = thread.GetFrameAtIndex(0) ret_value = thread.GetStopReturnValue() if not self.should_report_return_value(func_name): self.assertFalse(ret_value.IsValid(), "Shouldn't have gotten a value") return self.assertTrue(ret_value.IsValid()) num_ret_children = ret_value.GetNumChildren() self.assertEqual(num_in_children, num_ret_children) for idx in range(0, num_ret_children): in_child = in_value.GetChildAtIndex(idx) ret_child = ret_value.GetChildAtIndex(idx) in_child_str = in_child.GetValue() ret_child_str = ret_child.GetValue() self.assertEqual(in_child_str, ret_child_str)