blob: 483a487d76899b32c0f7a857e7c0a982f287ffb6 [file] [log] [blame]
"""Test that lldb backtraces a frameless function that faults correctly."""
import lldbsuite.test.lldbutil as lldbutil
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
import shutil
import os
class TestUnwindFramelessFaulted(TestBase):
NO_DEBUG_INFO_TESTCASE = True
@skipIf(oslist=no_match([lldbplatformutil.getDarwinOSTriples()]))
@skipIf(archs=no_match(["aarch64", "arm64", "arm64e"]))
# The static linker in Xcode 15.0-15.2 on macOS 14 will mislink
# the eh_frame addresses; ld-classic in those tools is one workaround.
# This issue was fixed in Xcode 15.3, but it's not straightforward
# to test for the linker version or Xcode version so tie this to
# macOS 15 which uses Xcode 16 and does not have the issues.
@skipIf(macos_version=["<", "15.0"])
def test_frameless_faulted_unwind(self):
self.build()
(target, process, thread, bp) = lldbutil.run_to_name_breakpoint(
self, "main", only_one_thread=False
)
# The test program will have a backtrace like this at its deepest:
#
# * frame #0: 0x0000000102adc468 a.out`break_to_debugger + 4
# frame #1: 0x0000000102adc458 a.out`trap + 16
# frame #2: 0x0000000102adc440 a.out`to_be_interrupted + 20
# frame #3: 0x0000000102adc418 a.out`main at main.c:4:7
# frame #4: 0x0000000193b7eb4c dyld`start + 6000
correct_frames = ["break_to_debugger", "trap", "to_be_interrupted", "main"]
# Keep track of when main has branch & linked, instruction step until we're
# back in main()
main_has_bl_ed = False
# Instruction step through the binary until we are in a function not
# listed in correct_frames.
frame = thread.GetFrameAtIndex(0)
step_count = 0
max_step_count = 200
while (
process.GetState() == lldb.eStateStopped
and frame.name in correct_frames
and step_count < max_step_count
):
starting_index = 0
if self.TraceOn():
self.runCmd("bt")
# Find which index into correct_frames the current stack frame is
for idx, name in enumerate(correct_frames):
if frame.name == name:
starting_index = idx
# Test that all frames after the current frame listed in
# correct_frames appears in the backtrace.
frame_idx = 0
for expected_frame in correct_frames[starting_index:]:
self.assertEqual(thread.GetFrameAtIndex(frame_idx).name, expected_frame)
frame_idx = frame_idx + 1
# When we're at our deepest level, test that register passing of
# x0 and x20 follow the by-hand UnwindPlan rules.
# In this test program, we can get x0 in the middle of the stack
# and we CAN'T get x20. The opposites of the normal AArch64 SysV
# ABI.
if frame.name == "break_to_debugger":
tbi_frame = thread.GetFrameAtIndex(2)
self.assertEqual(tbi_frame.name, "to_be_interrupted")
# The original argument to to_be_interrupted(), 10
# Normally can't get x0 mid-stack, but UnwindPlans have
# special rules to make this possible.
x0_reg = tbi_frame.register["x0"]
self.assertTrue(x0_reg.IsValid())
self.assertEqual(x0_reg.GetValueAsUnsigned(), 10)
# The incremented return value from to_be_interrupted(), 11
x24_reg = tbi_frame.register["x24"]
self.assertTrue(x24_reg.IsValid())
self.assertEqual(x24_reg.GetValueAsUnsigned(), 11)
# x20 can normally be fetched mid-stack, but the UnwindPlan
# has a rule saying it can't be fetched.
x20_reg = tbi_frame.register["x20"]
self.assertTrue(x20_reg.error.fail)
trap_frame = thread.GetFrameAtIndex(1)
self.assertEqual(trap_frame.name, "trap")
# Confirm that we can fetch x0 from trap() which
# is normally not possible w/ SysV AbI, but special
# UnwindPlans in use.
x0_reg = trap_frame.register["x0"]
self.assertTrue(x0_reg.IsValid())
self.assertEqual(x0_reg.GetValueAsUnsigned(), 10)
x1_reg = trap_frame.register["x1"]
self.assertTrue(x1_reg.error.fail)
main_frame = thread.GetFrameAtIndex(3)
self.assertEqual(main_frame.name, "main")
# x20 can normally be fetched mid-stack, but the UnwindPlan
# has a rule saying it can't be fetched.
x20_reg = main_frame.register["x20"]
self.assertTrue(x20_reg.error.fail)
# x21 can be fetched mid-stack.
x21_reg = main_frame.register["x21"]
self.assertTrue(x21_reg.error.success)
# manually move past the BRK instruction in
# break_to_debugger(). lldb-server doesn't
# advance past the builtin_debugtrap() BRK
# instruction.
if (
thread.GetStopReason() == lldb.eStopReasonException
and frame.name == "break_to_debugger"
):
frame.SetPC(frame.GetPC() + 4)
if self.TraceOn():
print("StepInstruction")
thread.StepInstruction(False)
frame = thread.GetFrameAtIndex(0)
step_count = step_count + 1