131 lines
5.0 KiB
Python
131 lines
5.0 KiB
Python
"""Test SBThread.GetExtendedBacktraceThread API with queue debugging."""
|
|
|
|
import os
|
|
import lldb
|
|
from lldbsuite.test.decorators import *
|
|
from lldbsuite.test.lldbtest import *
|
|
from lldbsuite.test.lldbplatformutil import findBacktraceRecordingDylib
|
|
from lldbsuite.test import lldbutil
|
|
|
|
|
|
class TestExtendedBacktraceAPI(TestBase):
|
|
NO_DEBUG_INFO_TESTCASE = True
|
|
|
|
def setUp(self):
|
|
TestBase.setUp(self)
|
|
self.main_source = "main.m"
|
|
|
|
@skipUnlessDarwin
|
|
@add_test_categories(["objc", "pyapi"])
|
|
def test_extended_backtrace_thread_api(self):
|
|
"""Test GetExtendedBacktraceThread with queue debugging."""
|
|
self.build()
|
|
exe = self.getBuildArtifact("a.out")
|
|
|
|
libbtr_path = findBacktraceRecordingDylib()
|
|
self.assertTrue(
|
|
libbtr_path,
|
|
"libBacktraceRecording.dylib was not found on the system.",
|
|
)
|
|
|
|
self.assertTrue(
|
|
os.path.isfile("/usr/lib/system/introspection/libdispatch.dylib"),
|
|
"introspection libdispatch dylib not installed.",
|
|
)
|
|
|
|
# Create launch info with environment variables for libBacktraceRecording.
|
|
launch_info = lldb.SBLaunchInfo(None)
|
|
launch_info.SetWorkingDirectory(self.get_process_working_directory())
|
|
launch_info.SetEnvironmentEntries(
|
|
[
|
|
f"DYLD_INSERT_LIBRARIES={libbtr_path}",
|
|
"DYLD_LIBRARY_PATH=/usr/lib/system/introspection",
|
|
],
|
|
True,
|
|
)
|
|
|
|
# Launch the process and run to breakpoint.
|
|
target, process, thread, bp = lldbutil.run_to_name_breakpoint(
|
|
self, "do_work_level_5", launch_info=launch_info, bkpt_module="a.out"
|
|
)
|
|
|
|
self.assertTrue(target.IsValid(), VALID_TARGET)
|
|
self.assertTrue(process.IsValid(), PROCESS_IS_VALID)
|
|
self.assertTrue(thread.IsValid(), "Stopped thread is valid")
|
|
self.assertTrue(bp.IsValid(), VALID_BREAKPOINT)
|
|
|
|
# Call GetNumQueues to ensure queue information is loaded.
|
|
num_queues = process.GetNumQueues()
|
|
|
|
# Check that we can find the com.apple.main-thread queue.
|
|
main_thread_queue_found = False
|
|
for i in range(num_queues):
|
|
queue = process.GetQueueAtIndex(i)
|
|
if queue.GetName() == "com.apple.main-thread":
|
|
main_thread_queue_found = True
|
|
break
|
|
|
|
# Verify we have at least 5 frames.
|
|
self.assertGreaterEqual(
|
|
thread.GetNumFrames(),
|
|
5,
|
|
"Thread should have at least 5 frames in backtrace",
|
|
)
|
|
|
|
# Get frame 2 BEFORE calling GetExtendedBacktraceThread.
|
|
# This mimics what Xcode does - it has the frame objects ready.
|
|
frame2 = thread.GetFrameAtIndex(2)
|
|
self.assertTrue(frame2.IsValid(), "Frame 2 is valid")
|
|
|
|
# Now test GetExtendedBacktraceThread.
|
|
# This is the critical part - getting the extended backtrace calls into
|
|
# libBacktraceRecording which does an inferior function call, and this
|
|
# invalidates/clears the unwinder state.
|
|
extended_thread = thread.GetExtendedBacktraceThread("libdispatch")
|
|
|
|
# This should be valid since we injected libBacktraceRecording.
|
|
self.assertTrue(
|
|
extended_thread.IsValid(),
|
|
"Extended backtrace thread for 'libdispatch' should be valid with libBacktraceRecording loaded",
|
|
)
|
|
|
|
# The extended thread should have frames.
|
|
self.assertGreater(
|
|
extended_thread.GetNumFrames(),
|
|
0,
|
|
"Extended backtrace thread should have at least one frame",
|
|
)
|
|
|
|
# Test frame 2 on the extended backtrace thread.
|
|
self.assertGreater(
|
|
extended_thread.GetNumFrames(),
|
|
2,
|
|
"Extended backtrace thread should have at least 3 frames to access frame 2",
|
|
)
|
|
|
|
extended_frame2 = extended_thread.GetFrameAtIndex(2)
|
|
self.assertTrue(extended_frame2.IsValid(), "Extended thread frame 2 is valid")
|
|
|
|
# NOW try to access variables from frame 2 of the ORIGINAL thread.
|
|
# This is the key test - after GetExtendedBacktraceThread() has executed
|
|
# an inferior function call, the unwinder state may be invalidated.
|
|
# Xcode exhibits this bug where variables show "register fp is not available"
|
|
# after extended backtrace retrieval.
|
|
|
|
# Set frame 2 as the selected frame so expect_var_path works.
|
|
thread.SetSelectedFrame(2)
|
|
|
|
variables = frame2.GetVariables(False, True, False, True)
|
|
self.assertGreater(
|
|
variables.GetSize(), 0, "Frame 2 should have at least one variable"
|
|
)
|
|
|
|
# Test all variables in frame 2, like Xcode does.
|
|
# Use expect_var_path to verify each variable is accessible without errors.
|
|
for i in range(variables.GetSize()):
|
|
var = variables.GetValueAtIndex(i)
|
|
var_name = var.GetName()
|
|
|
|
# This will fail if the variable contains "not available" or has errors.
|
|
self.expect_var_path(var_name)
|