"""
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="<rdar://problem/28719652>"
)
@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="<rdar://problem/28719652>"
)
@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)