blob: 1225d7d7f800b989a81ed69a5ef80e477938a2bb [file] [edit]
"""
Test that we can backtrace through an OUTLINED_FUNCTION which is called in a non-ABI way.
"""
import lldb
import json
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
class TestBacktraceThroughOutlinedFunction(TestBase):
NO_DEBUG_INFO_TESTCASE = True
@skipIfLLVMTargetMissing("AArch64")
def test_corefile_backtrace(self):
"""Corefile test that we can backtrace through an OUTLINED_FUNCTION that has the epilogue of a function."""
target = self.dbg.CreateTarget("")
exe = "binary.json"
with open(exe) as f:
exe_json = json.load(f)
exe_uuid = exe_json["uuid"]
target.AddModule(exe, "", exe_uuid)
self.assertTrue(target.IsValid())
core = self.getBuildArtifact("core")
self.yaml2macho_core("arm64-outlined-epilogue-core.yaml", core, exe_uuid)
if self.TraceOn():
self.runCmd("log enable lldb unwind")
process = target.LoadCore(core)
self.assertTrue(process.IsValid())
if self.TraceOn():
self.runCmd("target list")
self.runCmd("image list")
self.runCmd("target modules dump sections")
self.runCmd("target modules dump symtab")
self.backtrace_through_outlined_epilogue(process)
@skipIf(oslist=no_match([lldbplatformutil.getDarwinOSTriples()]))
@skipIf(archs=no_match(["aarch64", "arm64", "arm64e"]))
def test_live_process_backtrace(self):
"""Live process test that we can backtrace through two OUTLINED_FUNCTIONs with incorrect unwind instructions."""
self.build()
target, process, thread, bp = lldbutil.run_to_name_breakpoint(
self, "foo_midfunction"
)
self.assertTrue(target.IsValid())
self.assertTrue(process.IsValid())
self.backtrace_through_outlined_midfunction(process)
target.BreakpointCreateByName("foo_epilogue")
process.Continue()
self.assertTrue(process.IsValid())
self.backtrace_through_outlined_epilogue(process)
# Test for the behavior of an architectural default unwind plan
# getting above OUTLINED_FUNCTION_2. The OUTLINED_FUNCTION_2
# DWARF debug_frame/compact unwind instructions are invalid and
# the entire stack frame will be rejected by the unwinder -- the
# OUTLINED_FUNCTION_2 won't appear on the backtrace.
def backtrace_through_outlined_midfunction(self, process):
if self.TraceOn():
self.runCmd("bt")
thread = process.GetThreadAtIndex(0)
self.assertTrue(thread.IsValid())
important_frames = 4
# We may have a dyld `start` stack frame, or not; not relevant
# to this test.
self.assertGreaterEqual(thread.GetNumFrames(), important_frames)
stackframe_names = [
"foo_midfunction",
"OUTLINED_FUNCTION_2",
"sub_main_function",
"main",
]
for i, name in enumerate(stackframe_names):
self.assertEqual(name, thread.GetFrameAtIndex(i).GetSymbol().GetName())
# Test for the behavior of an architectural default unwind plan
# getting above OUTLINED_FUNCTION_3. If we do an instruction analysis
# unwind we'll have a duplication of OUTLINED_FUNCTION_3 in the
# backtrace.
def backtrace_through_outlined_epilogue(self, process):
if self.TraceOn():
self.runCmd("bt")
thread = process.GetThreadAtIndex(0)
self.assertTrue(thread.IsValid())
important_frames = 4
# We may have a dyld `start` stack frame, or not; not relevant
# to this test.
self.assertGreaterEqual(thread.GetNumFrames(), important_frames)
stackframe_names = [
"foo_epilogue",
"OUTLINED_FUNCTION_3",
"sub_main_function",
"main",
]
for i, name in enumerate(stackframe_names):
self.assertEqual(name, thread.GetFrameAtIndex(i).GetSymbol().GetName())