diff options
Diffstat (limited to 'lldb/test/API')
113 files changed, 5051 insertions, 289 deletions
diff --git a/lldb/test/API/commands/expression/weak_symbols/TestWeakSymbols.py b/lldb/test/API/commands/expression/weak_symbols/TestWeakSymbols.py index 50efecb..bed129a 100644 --- a/lldb/test/API/commands/expression/weak_symbols/TestWeakSymbols.py +++ b/lldb/test/API/commands/expression/weak_symbols/TestWeakSymbols.py @@ -15,7 +15,7 @@ class TestWeakSymbolsInExpressions(TestBase): NO_DEBUG_INFO_TESTCASE = True @skipUnlessDarwin - @skipIf(compiler="clang", compiler_version=["<", "7.0"]) + @skipIf(compiler="clang", compiler_version=["<", "19.0"]) def test_weak_symbol_in_expr(self): """Tests that we can refer to weak symbols in expressions.""" self.build() diff --git a/lldb/test/API/commands/frame/select-hidden/Makefile b/lldb/test/API/commands/frame/select-hidden/Makefile new file mode 100644 index 0000000..99998b2 --- /dev/null +++ b/lldb/test/API/commands/frame/select-hidden/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/commands/frame/select-hidden/TestNavigateHiddenFrame.py b/lldb/test/API/commands/frame/select-hidden/TestNavigateHiddenFrame.py new file mode 100644 index 0000000..698447b5 --- /dev/null +++ b/lldb/test/API/commands/frame/select-hidden/TestNavigateHiddenFrame.py @@ -0,0 +1,32 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class NavigateHiddenFrameTestCase(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + @add_test_categories(["libc++"]) + def test(self): + """Test going up/down a backtrace but we started in a hidden frame.""" + self.build() + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, "Break here", lldb.SBFileSpec("main.cpp") + ) + # up + self.assertIn("__impl2", thread.selected_frame.GetFunctionName()) + self.expect("up") + self.assertIn("__impl1", thread.selected_frame.GetFunctionName()) + self.expect("up") + self.assertIn("__impl", thread.selected_frame.GetFunctionName()) + self.expect("up") + self.assertIn("non_impl", thread.selected_frame.GetFunctionName()) + + # Back down again. + self.expect("down") + self.assertIn("__impl", thread.selected_frame.GetFunctionName()) + self.expect("down") + self.assertIn("__impl1", thread.selected_frame.GetFunctionName()) + self.expect("down") + self.assertIn("__impl2", thread.selected_frame.GetFunctionName()) diff --git a/lldb/test/API/commands/frame/select-hidden/main.cpp b/lldb/test/API/commands/frame/select-hidden/main.cpp new file mode 100644 index 0000000..dc97abb --- /dev/null +++ b/lldb/test/API/commands/frame/select-hidden/main.cpp @@ -0,0 +1,13 @@ +namespace std { +namespace __1 { +static const char *__impl2() { return "Break here"; } +static const char *__impl1() { return __impl2(); } +static const char *__impl() { return __impl1(); } +static const char *non_impl() { return __impl(); } +} // namespace __1 +} // namespace std + +int main() { + std::__1::non_impl(); + __builtin_debugtrap(); +} diff --git a/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py index 4d55767..5c91755 100644 --- a/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py +++ b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py @@ -19,7 +19,7 @@ class TestFrameVarDILGlobalVariableLookup(TestBase): NO_DEBUG_INFO_TESTCASE = True @skipIf(macos_version=["<", "15.0"], archs=["arm64", "arm64e"]) - @expectedFailureAll( + @skipIf( dwarf_version=["<", "5"], oslist=[lldbplatformutil.getDarwinOSTriples()], ) diff --git a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/Makefile b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/Makefile new file mode 100644 index 0000000..99998b2 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py new file mode 100644 index 0000000..53a85fe --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/TestFrameVarDILArithmetic.py @@ -0,0 +1,46 @@ +""" +Test DIL arithmetic. +""" + +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +from lldbsuite.test import lldbutil + + +class TestFrameVarDILArithmetic(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def test_arithmetic(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp") + ) + + self.runCmd("settings set target.experimental.use-DIL true") + + # Check unary results and integral promotion + self.expect_var_path("+0", value="0") + self.expect_var_path("-0", value="0") + self.expect_var_path("+1", value="1") + self.expect_var_path("-1", value="-1") + self.expect_var_path("-9223372036854775808", value="9223372036854775808") + self.expect_var_path("s", value="10", type="short") + self.expect_var_path("+s", value="10", type="int") + self.expect_var_path("-s", value="-10", type="int") + self.expect_var_path("+us", value="1", type="int") + self.expect_var_path("-us", value="-1", type="int") + self.expect_var_path("+ref", value="2", type="int") + self.expect_var_path("-ref", value="-2", type="int") + self.expect_var_path("+0.0", value="0") + self.expect_var_path("-0.0", value="-0") + self.expect_var_path("+enum_one", value="1") + self.expect_var_path("-enum_one", value="-1") + self.expect_var_path("+wchar", value="1") + self.expect_var_path("+char16", value="2") + self.expect_var_path("+char32", value="3") + self.expect_var_path("-bitfield.a", value="-1", type="int") + self.expect_var_path("+bitfield.a", value="1", type="int") + self.expect_var_path("+bitfield.b", value="2", type="int") + self.expect_var_path("+bitfield.c", value="3", type="unsigned int") + self.expect_var_path("+bitfield.d", value="4", type="uint64_t") diff --git a/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp new file mode 100644 index 0000000..2c70e93 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/expr/Arithmetic/main.cpp @@ -0,0 +1,23 @@ +#include <cstdint> + +int main(int argc, char **argv) { + short s = 10; + unsigned short us = 1; + + int x = 2; + int &ref = x; + enum Enum { kZero, kOne } enum_one = kOne; + wchar_t wchar = 1; + char16_t char16 = 2; + char32_t char32 = 3; + + struct BitFieldStruct { + char a : 4; + int b : 32; + unsigned int c : 32; + uint64_t d : 48; + }; + BitFieldStruct bitfield = {1, 2, 3, 4}; + + return 0; // Set a breakpoint here +} diff --git a/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/Makefile b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/Makefile new file mode 100644 index 0000000..99998b2 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/TestFrameVarDILExprPointerArithmetic.py b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/TestFrameVarDILExprPointerArithmetic.py new file mode 100644 index 0000000..448cd5b --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/TestFrameVarDILExprPointerArithmetic.py @@ -0,0 +1,29 @@ +""" +Test DIL pointer arithmetic. +""" + +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +from lldbsuite.test import lldbutil + + +class TestFrameVarDILExprPointerArithmetic(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def test_pointer_arithmetic(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp") + ) + + self.runCmd("settings set target.experimental.use-DIL true") + + self.expect_var_path("+array", type="int *") + self.expect_var_path("+array_ref", type="int *") + self.expect_var_path("+p_int0", type="int *") + self.expect( + "frame var -- '-p_int0'", + error=True, + substrs=["invalid argument type 'int *' to unary expression"], + ) diff --git a/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/main.cpp new file mode 100644 index 0000000..b4e0e88 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/expr/PointerArithmetic/main.cpp @@ -0,0 +1,11 @@ +void stop() {} + +int main(int argc, char **argv) { + int array[10]; + array[0] = 0; + int (&array_ref)[10] = array; + int *p_int0 = &array[0]; + + stop(); // Set a breakpoint here + return 0; +} diff --git a/lldb/test/API/commands/target/auto-install-main-executable/Makefile b/lldb/test/API/commands/target/auto-install-main-executable/Makefile index 07e6c9a..d0578fb 100644 --- a/lldb/test/API/commands/target/auto-install-main-executable/Makefile +++ b/lldb/test/API/commands/target/auto-install-main-executable/Makefile @@ -6,4 +6,4 @@ a.out: a.device.out include Makefile.rules a.device.out: - $(CXX) $(CXXFLAGS) -DBUILD=74 -o $@ $(SRCDIR)/main.cpp + $(CXX) $(ASAN_LDFLAGS) $(CXXFLAGS) -DBUILD=74 -o $@ $(SRCDIR)/main.cpp diff --git a/lldb/test/API/commands/target/stop-hooks/TestStopHookScripted.py b/lldb/test/API/commands/target/stop-hooks/TestStopHookScripted.py index 954cac1..8e91781 100644 --- a/lldb/test/API/commands/target/stop-hooks/TestStopHookScripted.py +++ b/lldb/test/API/commands/target/stop-hooks/TestStopHookScripted.py @@ -48,6 +48,39 @@ class TestStopHooks(TestBase): "Got the right error", ) + def test_self_deleting(self): + """Test that we can handle a stop hook that deletes itself""" + self.script_setup() + # Run to the first breakpoint before setting the stop hook + # so we don't have to figure out where it showed up in the new + # target. + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, "Stop here first", self.main_source_file + ) + + # Now add our stop hook and register it: + result = lldb.SBCommandReturnObject() + command = "target stop-hook add -P stop_hook.self_deleting_stop" + self.interp.HandleCommand(command, result) + self.assertCommandReturn(result, f"Added my stop hook: {result.GetError()}") + + result_str = result.GetOutput() + p = re.compile("Stop hook #([0-9]+) added.") + m = p.match(result_str) + current_stop_hook_id = m.group(1) + command = "command script add -o -f stop_hook.handle_stop_hook_id handle_id" + self.interp.HandleCommand(command, result) + self.assertCommandReturn(result, "Added my command") + + command = f"handle_id {current_stop_hook_id}" + self.interp.HandleCommand(command, result) + self.assertCommandReturn(result, "Registered my stop ID") + + # Now step the process and make sure the stop hook was deleted. + thread.StepOver() + self.interp.HandleCommand("target stop-hook list", result) + self.assertEqual(result.GetOutput().rstrip(), "No stop hooks.", "Deleted hook") + def test_stop_hooks_scripted(self): """Test that a scripted stop hook works with no specifiers""" self.stop_hooks_scripted(5, "-I false") diff --git a/lldb/test/API/commands/target/stop-hooks/stop_hook.py b/lldb/test/API/commands/target/stop-hooks/stop_hook.py index cb7a433..a41190ba 100644 --- a/lldb/test/API/commands/target/stop-hooks/stop_hook.py +++ b/lldb/test/API/commands/target/stop-hooks/stop_hook.py @@ -48,3 +48,28 @@ class bad_handle_stop: class no_handle_stop: def __init__(self, target, extra_args, dict): print("I am okay") + + +class self_deleting_stop: + def __init__(self, target, extra_args, dict): + self.target = target + + def handle_stop(self, exe_ctx, stream): + interp = exe_ctx.target.debugger.GetCommandInterpreter() + result = lldb.SBCommandReturnObject() + interp.HandleCommand("handle_id", result) + id_str = result.GetOutput().rstrip() + + command = f"target stop-hook delete {id_str}" + interp.HandleCommand(command, result) + + +stop_hook_id = 0 + + +def handle_stop_hook_id(debugger, command, exe_ctx, result, extra_args): + global stop_hook_id + if command == "": + result.AppendMessage(str(stop_hook_id)) + else: + stop_hook_id = int(command) diff --git a/lldb/test/API/functionalities/abbreviation/TestAbbreviations.py b/lldb/test/API/functionalities/abbreviation/TestAbbreviations.py index cc767ed..5dd4f6b 100644 --- a/lldb/test/API/functionalities/abbreviation/TestAbbreviations.py +++ b/lldb/test/API/functionalities/abbreviation/TestAbbreviations.py @@ -41,7 +41,7 @@ class AbbreviationsTestCase(TestBase): # "pl" could be "platform" or "plugin". command_interpreter.ResolveCommand("pl", result) self.assertFalse(result.Succeeded()) - self.assertTrue(result.GetError().startswith("Ambiguous command")) + self.assertTrue(result.GetError().startswith("error: Ambiguous command")) # Make sure an unabbreviated command is not mangled. command_interpreter.ResolveCommand( diff --git a/lldb/test/API/functionalities/ambigous_commands/TestAmbiguousCommands.py b/lldb/test/API/functionalities/ambigous_commands/TestAmbiguousCommands.py index 14c66fe..31b67d7 100644 --- a/lldb/test/API/functionalities/ambigous_commands/TestAmbiguousCommands.py +++ b/lldb/test/API/functionalities/ambigous_commands/TestAmbiguousCommands.py @@ -24,7 +24,7 @@ class AmbiguousCommandTestCase(TestBase): self.assertFalse(result.Succeeded()) self.assertEqual( result.GetError(), - "Ambiguous command 'co'. Possible matches:\n\tcommand\n\tcontinue\n\tcorefile\n", + "error: Ambiguous command 'co'. Possible matches:\n\tcommand\n\tcontinue\n\tcorefile\n", ) command_interpreter.HandleCommand("command unalias continue", result) diff --git a/lldb/test/API/functionalities/breakpoint/breakpoint_by_line_and_column/TestBreakpointByLineAndColumn.py b/lldb/test/API/functionalities/breakpoint/breakpoint_by_line_and_column/TestBreakpointByLineAndColumn.py index 5798c8f..1f57181 100644 --- a/lldb/test/API/functionalities/breakpoint/breakpoint_by_line_and_column/TestBreakpointByLineAndColumn.py +++ b/lldb/test/API/functionalities/breakpoint/breakpoint_by_line_and_column/TestBreakpointByLineAndColumn.py @@ -27,6 +27,25 @@ class BreakpointByLineAndColumnTestCase(TestBase): in_then |= b_loc.GetColumn() == 50 self.assertTrue(in_then) + def testBreakpointByLineAndColumnUsingCLI(self): + self.build() + src_file = lldb.SBFileSpec("main.cpp") + line = ( + line_number("main.cpp", "At the beginning of a function name (col:50)") + 1 + ) # Next line after comment + target, process, _, _ = lldbutil.run_to_source_breakpoint(self, "This is a random comment", src_file) + bkpt_no = lldbutil.run_break_set_by_file_and_line(self, "main.cpp", line, "--column 50") + breakpoint = target.FindBreakpointByID(bkpt_no) + threads = lldbutil.continue_to_breakpoint(process, breakpoint) + self.assertEqual(len(threads), 1, "Stopped at our breakpoint") + self.expect("fr v did_call", substrs=["1"]) + in_then = False + for i in range(breakpoint.GetNumLocations()): + b_loc = breakpoint.GetLocationAtIndex(i).GetAddress().GetLineEntry() + self.assertEqual(b_loc.GetLine(), line) + in_then |= b_loc.GetColumn() == 50 + self.assertTrue(in_then) + ## Skip gcc version less 7.1 since it doesn't support -gcolumn-info @skipIf(compiler="gcc", compiler_version=["<", "7.1"]) def testBreakpointByLine(self): diff --git a/lldb/test/API/functionalities/breakpoint/breakpoint_locations/after_rebuild/TestLocationsAfterRebuild.py b/lldb/test/API/functionalities/breakpoint/breakpoint_locations/after_rebuild/TestLocationsAfterRebuild.py index 1c7bb53..bc53fea 100644 --- a/lldb/test/API/functionalities/breakpoint/breakpoint_locations/after_rebuild/TestLocationsAfterRebuild.py +++ b/lldb/test/API/functionalities/breakpoint/breakpoint_locations/after_rebuild/TestLocationsAfterRebuild.py @@ -54,6 +54,24 @@ class TestLocationsAfterRebuild(TestBase): self, target, bkpt ) + # After enabling locate_module callback for main executables, + # the number of locations may vary depending on the platform. + num_locs = bkpt.GetNumLocations() bkpt_id = bkpt.GetID() - loc_string = f"{bkpt_id}.3" - self.runCmd(f"break disable {loc_string}") + + self.assertGreater( + num_locs, + 0, + f"Expected at least one breakpoint location, but found {num_locs}", + ) + + # Iterate through all valid locations and verify we can disable each one. + # This tests that breakpoint location IDs remain valid after rebuilds. + for loc_idx in range(num_locs): + loc = bkpt.GetLocationAtIndex(loc_idx) + self.assertTrue(loc.IsValid(), f"Location at index {loc_idx} is not valid") + + # Get the actual location ID from the location object + loc_id = loc.GetID() + loc_string = f"{bkpt_id}.{loc_id}" + self.runCmd(f"break disable {loc_string}") diff --git a/lldb/test/API/functionalities/breakpoint/hardware_breakpoints/simple_hw_breakpoints/TestSimpleHWBreakpoints.py b/lldb/test/API/functionalities/breakpoint/hardware_breakpoints/simple_hw_breakpoints/TestSimpleHWBreakpoints.py index ccbb235..acf75df 100644 --- a/lldb/test/API/functionalities/breakpoint/hardware_breakpoints/simple_hw_breakpoints/TestSimpleHWBreakpoints.py +++ b/lldb/test/API/functionalities/breakpoint/hardware_breakpoints/simple_hw_breakpoints/TestSimpleHWBreakpoints.py @@ -7,13 +7,7 @@ from functionalities.breakpoint.hardware_breakpoints.base import * class SimpleHWBreakpointTest(HardwareBreakpointTestBase): - def does_not_support_hw_breakpoints(self): - # FIXME: Use HardwareBreakpointTestBase.supports_hw_breakpoints - if super().supports_hw_breakpoints() is None: - return "Hardware breakpoints are unsupported" - return None - - @skipTestIfFn(does_not_support_hw_breakpoints) + @skipTestIfFn(HardwareBreakpointTestBase.hw_breakpoints_unsupported) def test(self): """Test SBBreakpoint::SetIsHardware""" self.build() @@ -35,16 +29,13 @@ class SimpleHWBreakpointTest(HardwareBreakpointTestBase): # breakpoint will be marked as a hardware breakpoint. self.assertTrue(break_on_me_bp.IsHardware()) - if super().supports_hw_breakpoints(): - self.assertSuccess(error) - - # Continue to our Hardware breakpoint and verify that's the reason - # we're stopped. - process.Continue() - self.expect( - "thread list", - STOPPED_DUE_TO_BREAKPOINT, - substrs=["stopped", "stop reason = breakpoint"], - ) - else: - self.assertFailure(error) + self.assertSuccess(error) + + # Continue to our Hardware breakpoint and verify that's the reason + # we're stopped. + process.Continue() + self.expect( + "thread list", + STOPPED_DUE_TO_BREAKPOINT, + substrs=["stopped", "stop reason = breakpoint"], + ) diff --git a/lldb/test/API/functionalities/breakpoint/serialize/TestBreakpointSerialization.py b/lldb/test/API/functionalities/breakpoint/serialize/TestBreakpointSerialization.py index 411ce9c..55cc12e 100644 --- a/lldb/test/API/functionalities/breakpoint/serialize/TestBreakpointSerialization.py +++ b/lldb/test/API/functionalities/breakpoint/serialize/TestBreakpointSerialization.py @@ -210,6 +210,11 @@ class BreakpointSerialization(TestBase): "Source and dest breakpoints are not identical: \nsource: %s\ndest: %s" % (source_text, copy_text), ) + self.assertEqual( + source_bp.GetNumLocations(), + copy_bp.GetNumLocations(), + "Source and dest num locations are not the same", + ) def do_check_resolvers(self): """Use Python APIs to check serialization of breakpoint resolvers""" @@ -386,7 +391,7 @@ class BreakpointSerialization(TestBase): source_bps.Clear() bkpt = self.orig_target.BreakpointCreateByName( - "blubby", lldb.eFunctionNameTypeAuto, empty_module_list, empty_cu_list + "main", lldb.eFunctionNameTypeAuto, empty_module_list, empty_cu_list ) bkpt.SetIgnoreCount(10) bkpt.SetThreadName("grubby") @@ -394,7 +399,7 @@ class BreakpointSerialization(TestBase): all_bps.Append(bkpt) bkpt = self.orig_target.BreakpointCreateByName( - "blubby", lldb.eFunctionNameTypeFull, empty_module_list, empty_cu_list + "main", lldb.eFunctionNameTypeFull, empty_module_list, empty_cu_list ) bkpt.SetCondition("something != something_else") bkpt.SetQueueName("grubby") diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/TestDataFormatterGenericForwardList.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/TestDataFormatterGenericForwardList.py index 45695c4..1db0c48 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/TestDataFormatterGenericForwardList.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/TestDataFormatterGenericForwardList.py @@ -9,6 +9,8 @@ from lldbsuite.test import lldbutil class TestDataFormatterGenericForwardList(TestBase): + TEST_WITH_PDB_DEBUG_INFO = True + def setUp(self): TestBase.setUp(self) self.line = line_number("main.cpp", "// break here") diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/initializer_list/TestDataFormatterStdInitializerList.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/initializer_list/TestDataFormatterStdInitializerList.py index 133f8f7d..38d8cdb 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/initializer_list/TestDataFormatterStdInitializerList.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/initializer_list/TestDataFormatterStdInitializerList.py @@ -28,13 +28,25 @@ class InitializerListTestCase(TestBase): substrs=["stopped", "stop reason = breakpoint"], ) - self.expect("frame variable ili", substrs=["[1] = 2", "[4] = 5"]) + self.expect( + "frame variable ili", + substrs=["ili = size=5", "[0] = 1", "[1] = 2", "[4] = 5"], + ) self.expect( "frame variable ils", - substrs=['[4] = "surprise it is a long string!! yay!!"'], + substrs=[ + "ils = size=5", + '[0] = "1"', + '[4] = "surprise it is a long string!! yay!!"', + ], ) @add_test_categories(["libc++"]) def test_libcxx(self): self.build(dictionary={"USE_LIBCPP": 1}) self.do_test() + + @add_test_categories(["libstdcxx"]) + def test_libstdcpp(self): + self.build(dictionary={"USE_LIBSTDCPP": 1}) + self.do_test() diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/initializer_list/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/initializer_list/main.cpp index a9d159e..7c62cac 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/initializer_list/main.cpp +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/initializer_list/main.cpp @@ -1,6 +1,5 @@ #include <initializer_list> #include <string> -#include <vector> int main() { std::initializer_list<int> ili{1, 2, 3, 4, 5}; diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/TestDataFormatterGenericList.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/TestDataFormatterGenericList.py index c0207e6..fbd0211 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/TestDataFormatterGenericList.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/TestDataFormatterGenericList.py @@ -10,6 +10,8 @@ from lldbsuite.test import lldbutil class GenericListDataFormatterTestCase(TestBase): + TEST_WITH_PDB_DEBUG_INFO = True + def setUp(self): # Call super's setUp(). TestBase.setUp(self) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/loop/TestDataFormatterGenericListLoop.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/loop/TestDataFormatterGenericListLoop.py index f6174dd..9c5daf7 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/loop/TestDataFormatterGenericListLoop.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/loop/TestDataFormatterGenericListLoop.py @@ -11,6 +11,7 @@ from lldbsuite.test import lldbutil class GenericListDataFormatterTestCase(TestBase): + TEST_WITH_PDB_DEBUG_INFO = True NO_DEBUG_INFO_TESTCASE = True def do_test_with_run_command(self): diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/map/TestDataFormatterStdMap.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/map/TestDataFormatterStdMap.py index 07d6c96..ca2d2d6 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/map/TestDataFormatterStdMap.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/map/TestDataFormatterStdMap.py @@ -9,6 +9,8 @@ from lldbsuite.test import lldbutil class StdMapDataFormatterTestCase(TestBase): + TEST_WITH_PDB_DEBUG_INFO = True + def setUp(self): TestBase.setUp(self) ns = "ndk" if lldbplatformutil.target_is_android() else "" diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/multimap/TestDataFormatterGenericMultiMap.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/multimap/TestDataFormatterGenericMultiMap.py index 7ac7971..4b0854b 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/multimap/TestDataFormatterGenericMultiMap.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/multimap/TestDataFormatterGenericMultiMap.py @@ -11,6 +11,8 @@ from lldbsuite.test import lldbutil class GenericMultiMapDataFormatterTestCase(TestBase): + TEST_WITH_PDB_DEBUG_INFO = True + def setUp(self): TestBase.setUp(self) self.namespace = "std" diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/multiset/TestDataFormatterGenericMultiSet.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/multiset/TestDataFormatterGenericMultiSet.py index 7e922fc..e846e07 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/multiset/TestDataFormatterGenericMultiSet.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/multiset/TestDataFormatterGenericMultiSet.py @@ -10,6 +10,8 @@ from lldbsuite.test import lldbutil class GenericMultiSetDataFormatterTestCase(TestBase): + TEST_WITH_PDB_DEBUG_INFO = True + def setUp(self): TestBase.setUp(self) self.namespace = "std" diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/TestDataFormatterGenericOptional.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/TestDataFormatterGenericOptional.py index 7bb4f75..c88e83b 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/TestDataFormatterGenericOptional.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/TestDataFormatterGenericOptional.py @@ -5,6 +5,8 @@ from lldbsuite.test import lldbutil class GenericOptionalDataFormatterTestCase(TestBase): + TEST_WITH_PDB_DEBUG_INFO = True + def do_test_with_run_command(self): """Test that that file and class static variables display correctly.""" @@ -55,7 +57,11 @@ class GenericOptionalDataFormatterTestCase(TestBase): self.expect( "frame var numbers", substrs=[ - "(optional_int_vect) numbers = Has Value=true {", + ( + "(std::optional<std::vector<int, std::allocator<int>>>) numbers = Has Value=true {" + if self.getDebugInfo() == "pdb" + else "(optional_int_vect) numbers = Has Value=true {" + ), "Value = size=4 {", "[0] = 1", "[1] = 2", @@ -69,7 +75,11 @@ class GenericOptionalDataFormatterTestCase(TestBase): self.expect( "frame var ostring", substrs=[ - "(optional_string) ostring = Has Value=true {", + ( + "(std::optional<std::basic_string<char, std::char_traits<char>, std::allocator<char>>>) ostring = Has Value=true {" + if self.getDebugInfo() == "pdb" + else "(optional_string) ostring = Has Value=true {" + ), 'Value = "hello"', "}", ], diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/set/TestDataFormatterGenericSet.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/set/TestDataFormatterGenericSet.py index 1ac5e32..355f0c6 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/set/TestDataFormatterGenericSet.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/set/TestDataFormatterGenericSet.py @@ -10,6 +10,8 @@ from lldbsuite.test import lldbutil class GenericSetDataFormatterTestCase(TestBase): + TEST_WITH_PDB_DEBUG_INFO = True + def setUp(self): TestBase.setUp(self) self.namespace = "std" diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/shared_ptr/TestDataFormatterStdSharedPtr.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/shared_ptr/TestDataFormatterStdSharedPtr.py index d71fbf8..fa03fc1 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/shared_ptr/TestDataFormatterStdSharedPtr.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/shared_ptr/TestDataFormatterStdSharedPtr.py @@ -9,6 +9,8 @@ from lldbsuite.test import lldbutil class TestCase(TestBase): + TEST_WITH_PDB_DEBUG_INFO = True + def do_test(self): """Test `frame variable` output for `std::shared_ptr` types.""" (_, process, _, bkpt) = lldbutil.run_to_source_breakpoint( @@ -62,7 +64,7 @@ class TestCase(TestBase): valobj = self.expect_var_path("sp_user", type="std::shared_ptr<User>") self.assertRegex( valobj.summary, - "element_type @ 0x0*[1-9a-f][0-9a-f]+( strong=1)? weak=0", + f"{'User' if self.getDebugInfo() == 'pdb' else 'element_type'} @ 0x0*[1-9a-f][0-9a-f]+( strong=1)? weak=0", ) self.assertNotEqual(valobj.child[0].unsigned, 0) @@ -77,7 +79,15 @@ class TestCase(TestBase): self.assertEqual(str(valobj), '(User) *pointer = (id = 30, name = "steph")') self.expect_var_path("sp_user->id", type="int", value="30") - self.expect_var_path("sp_user->name", type="std::string", summary='"steph"') + self.expect_var_path( + "sp_user->name", + type=( + "std::basic_string<char, std::char_traits<char>, std::allocator<char>>" + if self.getDebugInfo() == "pdb" + else "std::string" + ), + summary='"steph"', + ) valobj = self.expect_var_path( "si", type="std::shared_ptr<int>", summary="47 strong=2 weak=0" diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/span/TestDataFormatterStdSpan.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/span/TestDataFormatterStdSpan.py index a45c0ff..f586fb3 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/span/TestDataFormatterStdSpan.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/span/TestDataFormatterStdSpan.py @@ -74,7 +74,7 @@ class StdSpanDataFormatterTestCase(TestBase): result_summary="item 0 is 1", ) - self.runCmd("type summary delete span") + self.runCmd("type summary clear") # New span with strings lldbutil.continue_to_breakpoint(process, bkpt) @@ -155,12 +155,6 @@ class StdSpanDataFormatterTestCase(TestBase): ) self.check_size("nested", 2) - @skipIf(compiler="clang", compiler_version=["<", "11.0"]) - @add_test_categories(["libc++"]) - def test_libcxx(self): - self.build(dictionary={"USE_LIBCPP": 1}) - self.do_test() - def do_test_ref_and_ptr(self): """Test that std::span is correctly formatted when passed by ref and ptr""" (self.target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( @@ -176,6 +170,24 @@ class StdSpanDataFormatterTestCase(TestBase): @skipIf(compiler="clang", compiler_version=["<", "11.0"]) @add_test_categories(["libc++"]) + def test_libcxx(self): + self.build(dictionary={"USE_LIBCPP": 1}) + self.do_test() + + @skipIf(compiler="clang", compiler_version=["<", "11.0"]) + @add_test_categories(["libc++"]) def test_ref_and_ptr_libcxx(self): self.build(dictionary={"USE_LIBCPP": 1}) self.do_test_ref_and_ptr() + + @skipIf(compiler="clang", compiler_version=["<", "11.0"]) + @add_test_categories(["libstdcxx"]) + def test_libstdcxx(self): + self.build(dictionary={"USE_LIBSTDCPP": 1}) + self.do_test() + + @skipIf(compiler="clang", compiler_version=["<", "11.0"]) + @add_test_categories(["libstdcxx"]) + def test_ref_and_ptr_libstdcxx(self): + self.build(dictionary={"USE_LIBSTDCPP": 1}) + self.do_test_ref_and_ptr() diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/string/TestDataFormatterStdString.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/string/TestDataFormatterStdString.py index 6a27b5d..00047e41 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/string/TestDataFormatterStdString.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/string/TestDataFormatterStdString.py @@ -11,6 +11,8 @@ from lldbsuite.test import lldbutil class StdStringDataFormatterTestCase(TestBase): + TEST_WITH_PDB_DEBUG_INFO = True + def setUp(self): # Call super's setUp(). TestBase.setUp(self) @@ -18,6 +20,17 @@ class StdStringDataFormatterTestCase(TestBase): self.main_spec = lldb.SBFileSpec("main.cpp") self.namespace = "std" + def _makeStringName(self, typedef: str, char_type: str, allocator=None): + if allocator is None: + allocator = self.namespace + "::allocator" + + if self.getDebugInfo() == "pdb": + return f"{self.namespace}::basic_string<{char_type}, std::char_traits<{char_type}>, {allocator}<{char_type}>>" + + if typedef.startswith("::"): + return self.namespace + typedef + return typedef + def do_test(self): """Test that that file and class static variables display correctly.""" (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( @@ -36,10 +49,17 @@ class StdStringDataFormatterTestCase(TestBase): # Execute the cleanup function during test case tear down. self.addTearDownHook(cleanup) - ns = self.namespace + string_name = self._makeStringName("::string", "char") + wstring_name = self._makeStringName("::wstring", "wchar_t") + custom_string_name = self._makeStringName( + "CustomString", "char", allocator="CustomAlloc" + ) + custom_wstring_name = self._makeStringName( + "CustomWString", "wchar_t", allocator="CustomAlloc" + ) # Check 'S' pre-assignment. - self.expect("frame variable S", substrs=['(%s::wstring) S = L"!!!!"' % ns]) + self.expect("frame variable S", substrs=[f'({wstring_name}) S = L"!!!!"']) thread.StepOver() @@ -54,34 +74,31 @@ class StdStringDataFormatterTestCase(TestBase): ) self.expect_expr( - "s", result_type=ns + "::wstring", result_summary='L"hello world! מזל טוב!"' + "s", result_type=wstring_name, result_summary='L"hello world! מזל טוב!"' ) - self.expect_expr( - "q", result_type=ns + "::string", result_summary='"hello world"' - ) + self.expect_expr("q", result_type=string_name, result_summary='"hello world"') self.expect_expr( "Q", - result_type=ns + "::string", + result_type=string_name, result_summary='"quite a long std::strin with lots of info inside it"', ) self.expect( "frame variable", substrs=[ - '(%s::wstring) wempty = L""' % ns, - '(%s::wstring) s = L"hello world! מזל טוב!"' % ns, - '(%s::wstring) S = L"!!!!!"' % ns, + f'({wstring_name}) wempty = L""', + f'({wstring_name}) s = L"hello world! מזל טוב!"', + f'({wstring_name}) S = L"!!!!!"', "(const wchar_t *) mazeltov = 0x", 'L"מזל טוב"', - '(%s::string) empty = ""' % ns, - '(%s::string) q = "hello world"' % ns, - '(%s::string) Q = "quite a long std::strin with lots of info inside it"' - % ns, - "(%s::string *) null_str = nullptr" % ns, - '(CustomString) custom_str = "hello!"', - '(CustomWString) custom_wstr = L"hello!"', + f'({string_name}) empty = ""', + f'({string_name}) q = "hello world"', + f'({string_name}) Q = "quite a long std::strin with lots of info inside it"', + f"({string_name} *) null_str = nullptr", + f'({custom_string_name}) custom_str = "hello!"', + f'({custom_wstring_name}) custom_wstr = L"hello!"', ], ) @@ -136,19 +153,26 @@ class StdStringDataFormatterTestCase(TestBase): self, "Set break point at this line.", self.main_spec ) - ns = self.namespace + u16string_name = self._makeStringName("::u16string", "char16_t") + u32string_name = self._makeStringName("::u32string", "char32_t") + custom_u16string_name = self._makeStringName( + "CustomStringU16", "char16_t", allocator="CustomAlloc" + ) + custom_u32string_name = self._makeStringName( + "CustomStringU32", "char32_t", allocator="CustomAlloc" + ) self.expect( "frame variable", substrs=[ - '(%s::u16string) u16_string = u"ß水氶"' % ns, - '(%s::u16string) u16_empty = u""' % ns, - '(%s::u32string) u32_string = U"🍄🍅🍆🍌"' % ns, - '(%s::u32string) u32_empty = U""' % ns, - '(CustomStringU16) custom_u16 = u"ß水氶"', - '(CustomStringU16) custom_u16_empty = u""', - '(CustomStringU32) custom_u32 = U"🍄🍅🍆🍌"', - '(CustomStringU32) custom_u32_empty = U""', + f'({u16string_name}) u16_string = u"ß水氶"', + f'({u16string_name}) u16_empty = u""', + f'({u32string_name}) u32_string = U"🍄🍅🍆🍌"', + f'({u32string_name}) u32_empty = U""', + f'({custom_u16string_name}) custom_u16 = u"ß水氶"', + f'({custom_u16string_name}) custom_u16_empty = u""', + f'({custom_u32string_name}) custom_u32 = U"🍄🍅🍆🍌"', + f'({custom_u32string_name}) custom_u32_empty = U""', ], ) @@ -271,9 +295,8 @@ class StdStringDataFormatterTestCase(TestBase): self.expect( "frame variable", substrs=[ - '(%s::string) IHaveEmbeddedZeros = "a\\0b\\0c\\0d"' % ns, - '(%s::wstring) IHaveEmbeddedZerosToo = L"hello world!\\0てざ ル゜䋨ミ㠧槊 きゅへ狦穤襩 じゃ馩リョ 䤦監"' - % ns, + f'({self._makeStringName("::string", "char")}) IHaveEmbeddedZeros = "a\\0b\\0c\\0d"', + f'({self._makeStringName("::wstring", "wchar_t")}) IHaveEmbeddedZerosToo = L"hello world!\\0てざ ル゜䋨ミ㠧槊 きゅへ狦穤襩 じゃ馩リョ 䤦監"', ], ) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/string_view/TestDataFormatterStdStringView.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/string_view/TestDataFormatterStdStringView.py index 1811418..5c915b6 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/string_view/TestDataFormatterStdStringView.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/string_view/TestDataFormatterStdStringView.py @@ -11,6 +11,8 @@ from lldbsuite.test import lldbutil class StdStringViewDataFormatterTestCase(TestBase): + TEST_WITH_PDB_DEBUG_INFO = True + def setUp(self): # Call super's setUp(). TestBase.setUp(self) @@ -20,6 +22,12 @@ class StdStringViewDataFormatterTestCase(TestBase): "main.cpp", "// Break here to look at bad string view." ) + def _makeStringName(self, typedef: str, char_type: str): + if self.getDebugInfo() == "pdb": + return f"std::basic_string_view<{char_type}, std::char_traits<{char_type}>>" + + return typedef + def do_test(self): """Test that that file and class static variables display correctly.""" self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) @@ -51,39 +59,47 @@ class StdStringViewDataFormatterTestCase(TestBase): # Execute the cleanup function during test case tear down. self.addTearDownHook(cleanup) - self.expect_var_path("wempty", type="std::wstring_view", summary='L""') + string_view_name = self._makeStringName("std::string_view", "char") + wstring_view_name = self._makeStringName("std::wstring_view", "wchar_t") + u16string_view_name = self._makeStringName("std::u16string_view", "char16_t") + u32string_view_name = self._makeStringName("std::u32string_view", "char32_t") + string_name = ( + "std::basic_string<char, std::char_traits<char>, std::allocator<char>>" + if self.getDebugInfo() == "pdb" + else "std::string" + ) + + self.expect_var_path("wempty", type=wstring_view_name, summary='L""') self.expect_var_path( - "s", type="std::wstring_view", summary='L"hello world! מזל טוב!"' + "s", type=wstring_view_name, summary='L"hello world! מזל טוב!"' ) - self.expect_var_path("S", type="std::wstring_view", summary='L"!!!!"') - self.expect_var_path("empty", type="std::string_view", summary='""') - self.expect_var_path("q_source", type="std::string", summary='"hello world"') - self.expect_var_path("q", type="std::string_view", summary='"hello world"') + self.expect_var_path("S", type=wstring_view_name, summary='L"!!!!"') + self.expect_var_path("empty", type=string_view_name, summary='""') + self.expect_var_path("q_source", type=string_name, summary='"hello world"') + self.expect_var_path("q", type=string_view_name, summary='"hello world"') self.expect_var_path( "Q", - type="std::string_view", + type=string_view_name, summary='"quite a long std::strin with lots of info inside it"', ) self.expect_var_path( - "IHaveEmbeddedZeros", type="std::string_view", summary='"a\\0b\\0c\\0d"' + "IHaveEmbeddedZeros", type=string_view_name, summary='"a\\0b\\0c\\0d"' ) self.expect_var_path( "IHaveEmbeddedZerosToo", - type="std::wstring_view", + type=wstring_view_name, summary='L"hello world!\\0てざ ル゜䋨ミ㠧槊 きゅへ狦穤襩 じゃ馩リョ 䤦監"', ) - self.expect_var_path("u16_string", type="std::u16string_view", summary='u"ß水氶"') - self.expect_var_path("u16_empty", type="std::u16string_view", summary='u""') - self.expect_var_path( - "u32_string", type="std::u32string_view", summary='U"🍄🍅🍆🍌"' - ) - self.expect_var_path("u32_empty", type="std::u32string_view", summary='U""') + self.expect_var_path("u16_string", type=u16string_view_name, summary='u"ß水氶"') + self.expect_var_path("u16_empty", type=u16string_view_name, summary='u""') + self.expect_var_path("u32_string", type=u32string_view_name, summary='U"🍄🍅🍆🍌"') + self.expect_var_path("u32_empty", type=u32string_view_name, summary='U""') # GetSummary returns None so can't be checked by expect_var_path, so we # use the str representation instead null_obj = self.frame().GetValueForVariablePath("null_str") self.assertEqual(null_obj.GetSummary(), "Summary Unavailable") - self.assertEqual(str(null_obj), "(std::string_view *) null_str = nullptr") + self.assertEqual(str(null_obj), f"({string_view_name} *) null_str = nullptr") self.runCmd("n") @@ -108,37 +124,35 @@ class StdStringViewDataFormatterTestCase(TestBase): self.expect_expr( "s", - result_type="std::wstring_view", + result_type=wstring_view_name, result_summary='L"hello world! מזל טוב!"', ) - self.expect_var_path("wempty", type="std::wstring_view", summary='L""') + self.expect_var_path("wempty", type=wstring_view_name, summary='L""') self.expect_var_path( - "s", type="std::wstring_view", summary='L"hello world! מזל טוב!"' + "s", type=wstring_view_name, summary='L"hello world! מזל טוב!"' ) - self.expect_var_path("S", type="std::wstring_view", summary='L"!!!!"') - self.expect_var_path("empty", type="std::string_view", summary='""') - self.expect_var_path("q_source", type="std::string", summary='"Hello world"') - self.expect_var_path("q", type="std::string_view", summary='"Hello world"') + self.expect_var_path("S", type=wstring_view_name, summary='L"!!!!"') + self.expect_var_path("empty", type=string_view_name, summary='""') + self.expect_var_path("q_source", type=string_name, summary='"Hello world"') + self.expect_var_path("q", type=string_view_name, summary='"Hello world"') self.expect_var_path( "Q", - type="std::string_view", + type=string_view_name, summary='"quite a long std::strin with lots of info inside it"', ) self.expect_var_path( - "IHaveEmbeddedZeros", type="std::string_view", summary='"a\\0b\\0c\\0d"' + "IHaveEmbeddedZeros", type=string_view_name, summary='"a\\0b\\0c\\0d"' ) self.expect_var_path( "IHaveEmbeddedZerosToo", - type="std::wstring_view", + type=wstring_view_name, summary='L"hello world!\\0てざ ル゜䋨ミ㠧槊 きゅへ狦穤襩 じゃ馩リョ 䤦監"', ) - self.expect_var_path("u16_string", type="std::u16string_view", summary='u"ß水氶"') - self.expect_var_path("u16_empty", type="std::u16string_view", summary='u""') - self.expect_var_path( - "u32_string", type="std::u32string_view", summary='U"🍄🍅🍆🍌"' - ) - self.expect_var_path("u32_empty", type="std::u32string_view", summary='U""') + self.expect_var_path("u16_string", type=u16string_view_name, summary='u"ß水氶"') + self.expect_var_path("u16_empty", type=u16string_view_name, summary='u""') + self.expect_var_path("u32_string", type=u32string_view_name, summary='U"🍄🍅🍆🍌"') + self.expect_var_path("u32_empty", type=u32string_view_name, summary='U""') self.runCmd("cont") self.expect( diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/tuple/TestDataFormatterStdTuple.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/tuple/TestDataFormatterStdTuple.py index b23d549..8984387 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/tuple/TestDataFormatterStdTuple.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/tuple/TestDataFormatterStdTuple.py @@ -9,6 +9,8 @@ from lldbsuite.test import lldbutil class TestDataFormatterStdTuple(TestBase): + TEST_WITH_PDB_DEBUG_INFO = True + def setUp(self): TestBase.setUp(self) self.line = line_number("main.cpp", "// break here") diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/u8string/TestDataFormatterStdU8String.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/u8string/TestDataFormatterStdU8String.py index b983ee1..dda97945f 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/u8string/TestDataFormatterStdU8String.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/u8string/TestDataFormatterStdU8String.py @@ -11,18 +11,26 @@ from lldbsuite.test import lldbutil class StdU8StringDataFormatterTestCase(TestBase): + TEST_WITH_PDB_DEBUG_INFO = True + def do_test(self): lldbutil.run_to_source_breakpoint( self, "Set break point at this line.", lldb.SBFileSpec("main.cpp") ) + string_name = ( + "std::basic_string<char8_t, std::char_traits<char8_t>, std::allocator<char8_t>>" + if self.getDebugInfo() == "pdb" + else "std::u8string" + ) + self.expect( "frame variable", substrs=[ - '(std::u8string) u8_string_small = u8"🍄"', - '(std::u8string) u8_string = u8"❤️👍📄📁😃🧑🌾"', - '(std::u8string) u8_empty = u8""', - '(std::u8string) u8_text = u8"ABCd"', + f'({string_name}) u8_string_small = u8"🍄"', + f'({string_name}) u8_string = u8"❤️👍📄📁😃🧑🌾"', + f'({string_name}) u8_empty = u8""', + f'({string_name}) u8_text = u8"ABCd"', ], ) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/u8string_view/TestDataFormatterStdU8StringView.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/u8string_view/TestDataFormatterStdU8StringView.py index 1e35a0f..6cf72d1 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/u8string_view/TestDataFormatterStdU8StringView.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/u8string_view/TestDataFormatterStdU8StringView.py @@ -11,18 +11,26 @@ from lldbsuite.test import lldbutil class StdU8StringViewDataFormatterTestCase(TestBase): + TEST_WITH_PDB_DEBUG_INFO = True + def do_test(self): lldbutil.run_to_source_breakpoint( self, "Set break point at this line.", lldb.SBFileSpec("main.cpp") ) + string_view_name = ( + "std::basic_string_view<char8_t, std::char_traits<char8_t>>" + if self.getDebugInfo() == "pdb" + else "std::u8string_view" + ) + self.expect( "frame variable", substrs=[ - '(std::u8string_view) u8_string_small = u8"🍄"', - '(std::u8string_view) u8_string = u8"❤️👍📄📁😃🧑🌾"', - '(std::u8string_view) u8_empty = u8""', - '(std::u8string_view) u8_text = u8"ABCd"', + f'({string_view_name}) u8_string_small = u8"🍄"', + f'({string_view_name}) u8_string = u8"❤️👍📄📁😃🧑🌾"', + f'({string_view_name}) u8_empty = u8""', + f'({string_view_name}) u8_text = u8"ABCd"', ], ) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unique_ptr/TestDataFormatterStdUniquePtr.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unique_ptr/TestDataFormatterStdUniquePtr.py index 0b68b1b..1516db6 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unique_ptr/TestDataFormatterStdUniquePtr.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unique_ptr/TestDataFormatterStdUniquePtr.py @@ -9,6 +9,8 @@ from lldbsuite.test import lldbutil class TestCase(TestBase): + TEST_WITH_PDB_DEBUG_INFO = True + def do_test(self): """Test `frame variable` output for `std::unique_ptr` types.""" @@ -84,7 +86,15 @@ class TestCase(TestBase): self.assertNotEqual(valobj.child[0].unsigned, 0) self.expect_var_path("up_user->id", type="int", value="30") - self.expect_var_path("up_user->name", type="std::string", summary='"steph"') + self.expect_var_path( + "up_user->name", + type=( + "std::basic_string<char, std::char_traits<char>, std::allocator<char>>" + if self.getDebugInfo() == "pdb" + else "std::string" + ), + summary='"steph"', + ) self.runCmd("settings set target.experimental.use-DIL true") self.expect_var_path("ptr_node->value", value="1") diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/vbool/TestDataFormatterStdVBool.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/vbool/TestDataFormatterStdVBool.py index dd142d2..f74092c 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/vbool/TestDataFormatterStdVBool.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/vbool/TestDataFormatterStdVBool.py @@ -9,6 +9,8 @@ from lldbsuite.test import lldbutil class StdVBoolDataFormatterTestCase(TestBase): + TEST_WITH_PDB_DEBUG_INFO = True + def setUp(self): # Call super's setUp(). TestBase.setUp(self) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/invalid-string/TestDataFormatterLibcxxInvalidString.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/invalid-string/TestDataFormatterLibcxxInvalidString.py index ae8e0ac..b504c01 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/invalid-string/TestDataFormatterLibcxxInvalidString.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/invalid-string/TestDataFormatterLibcxxInvalidString.py @@ -20,7 +20,7 @@ class LibcxxInvalidStringDataFormatterTestCase(TestBase): frame = thread.frames[0] if not self.process().GetAddressByteSize() == 8: - self.skip() + self.skipTest("The test requires a 64-bit process") # The test assumes that std::string is in its cap-size-data layout. self.expect( diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestConnectRemoteDetach.py b/lldb/test/API/functionalities/gdb_remote_client/TestConnectRemoteDetach.py new file mode 100644 index 0000000..4380455 --- /dev/null +++ b/lldb/test/API/functionalities/gdb_remote_client/TestConnectRemoteDetach.py @@ -0,0 +1,67 @@ +""" +Test that ConnectRemote sets ShouldDetach flag correctly. + +When connecting to a remote process that stops after connection, +the process should be marked for detach (not kill) on destruction. +""" + +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +from lldbsuite.test.gdbclientutils import * +from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase +from lldbsuite.test import lldbutil + + +class TestConnectRemoteDetach(GDBRemoteTestBase): + """Test that ConnectRemote properly sets ShouldDetach flag.""" + + class StoppedResponder(MockGDBServerResponder): + """A responder that returns a stopped process.""" + + def qfThreadInfo(self): + return "m1" + + def qsThreadInfo(self): + return "l" + + def qC(self): + return "QC1" + + def haltReason(self): + # Return that we're stopped + return "T05thread:1;" + + def cont(self): + # Stay stopped + return "T05thread:1;" + + def D(self): + # Detach packet: this is what we want to verify gets called. + return "OK" + + def k(self): + # Kill packet: this is what we want to verify doesn't get called. + raise RuntimeError("should not receive k(ill) packet") + + def test_connect_remote_sets_detach(self): + """Test that ConnectRemote to a stopped process sets ShouldDetach.""" + self.server.responder = self.StoppedResponder() + + target = self.createTarget("a.yaml") + process = self.connect(target) + + # Wait for the process to be in stopped state after connecting. + # When ConnectRemote connects to a remote process that is stopped, + # it should call SetShouldDetach(true) before CompleteAttach(). + lldbutil.expect_state_changes( + self, self.dbg.GetListener(), process, [lldb.eStateStopped] + ) + + # Now destroy the process. Because ShouldDetach was set to true + # during ConnectRemote, this should send a 'D' (detach) packet + # rather than a 'k' (kill) packet when the process is destroyed. + process.Destroy() + + # Verify that the (D)etach packet was sent. + self.assertPacketLogReceived(["D"]) diff --git a/lldb/test/API/functionalities/scripted_frame_provider/Makefile b/lldb/test/API/functionalities/scripted_frame_provider/Makefile new file mode 100644 index 0000000..99998b2 --- /dev/null +++ b/lldb/test/API/functionalities/scripted_frame_provider/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py b/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py new file mode 100644 index 0000000..922cb7f --- /dev/null +++ b/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py @@ -0,0 +1,555 @@ +""" +Test scripted frame provider functionality. +""" + +import os + +import lldb +import lldbsuite.test.lldbplatformutil as lldbplatformutil +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import TestBase +from lldbsuite.test import lldbutil + +@skipIf(oslist=["linux"], archs=["arm$"]) +class ScriptedFrameProviderTestCase(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def setUp(self): + TestBase.setUp(self) + self.source = "main.cpp" + + def test_replace_all_frames(self): + """Test that we can replace the entire stack.""" + self.build() + target, process, thread, bkpt = lldbutil.run_to_source_breakpoint( + self, "Break here", lldb.SBFileSpec(self.source), only_one_thread=False + ) + + # Import the test frame provider. + script_path = os.path.join(self.getSourceDir(), "test_frame_providers.py") + self.runCmd("command script import " + script_path) + + # Attach the Replace provider. + error = lldb.SBError() + provider_id = target.RegisterScriptedFrameProvider( + "test_frame_providers.ReplaceFrameProvider", + lldb.SBStructuredData(), + error, + ) + self.assertTrue(error.Success(), f"Failed to register provider: {error}") + self.assertNotEqual(provider_id, 0, "Provider ID should be non-zero") + + # Verify we have exactly 3 synthetic frames. + self.assertEqual(thread.GetNumFrames(), 3, "Should have 3 synthetic frames") + + # Verify frame indices and PCs (dictionary-based frames don't have custom function names). + frame0 = thread.GetFrameAtIndex(0) + self.assertIsNotNone(frame0) + self.assertEqual(frame0.GetPC(), 0x1000) + + frame1 = thread.GetFrameAtIndex(1) + self.assertIsNotNone(frame1) + self.assertIn("thread_func", frame1.GetFunctionName()) + + frame2 = thread.GetFrameAtIndex(2) + self.assertIsNotNone(frame2) + self.assertEqual(frame2.GetPC(), 0x3000) + + def test_prepend_frames(self): + """Test that we can add frames before real stack.""" + self.build() + target, process, thread, bkpt = lldbutil.run_to_source_breakpoint( + self, "Break here", lldb.SBFileSpec(self.source), only_one_thread=False + ) + + # Get original frame count and PC. + original_frame_count = thread.GetNumFrames() + self.assertGreaterEqual( + original_frame_count, 2, "Should have at least 2 real frames" + ) + + # Import and attach Prepend provider. + script_path = os.path.join(self.getSourceDir(), "test_frame_providers.py") + self.runCmd("command script import " + script_path) + + error = lldb.SBError() + provider_id = target.RegisterScriptedFrameProvider( + "test_frame_providers.PrependFrameProvider", + lldb.SBStructuredData(), + error, + ) + self.assertTrue(error.Success(), f"Failed to register provider: {error}") + self.assertNotEqual(provider_id, 0, "Provider ID should be non-zero") + + # Verify we have 2 more frames. + new_frame_count = thread.GetNumFrames() + self.assertEqual(new_frame_count, original_frame_count + 2) + + # Verify first 2 frames are synthetic (check PCs, not function names). + frame0 = thread.GetFrameAtIndex(0) + self.assertEqual(frame0.GetPC(), 0x9000) + + frame1 = thread.GetFrameAtIndex(1) + self.assertEqual(frame1.GetPC(), 0xA000) + + # Verify frame 2 is the original real frame 0. + frame2 = thread.GetFrameAtIndex(2) + self.assertIn("thread_func", frame2.GetFunctionName()) + + def test_append_frames(self): + """Test that we can add frames after real stack.""" + self.build() + target, process, thread, bkpt = lldbutil.run_to_source_breakpoint( + self, "Break here", lldb.SBFileSpec(self.source), only_one_thread=False + ) + + # Get original frame count. + original_frame_count = thread.GetNumFrames() + + # Import and attach Append provider. + script_path = os.path.join(self.getSourceDir(), "test_frame_providers.py") + self.runCmd("command script import " + script_path) + + error = lldb.SBError() + provider_id = target.RegisterScriptedFrameProvider( + "test_frame_providers.AppendFrameProvider", + lldb.SBStructuredData(), + error, + ) + self.assertTrue(error.Success(), f"Failed to register provider: {error}") + self.assertNotEqual(provider_id, 0, "Provider ID should be non-zero") + + # Verify we have 1 more frame. + new_frame_count = thread.GetNumFrames() + self.assertEqual(new_frame_count, original_frame_count + 1) + + # Verify first frames are still real. + frame0 = thread.GetFrameAtIndex(0) + self.assertIn("thread_func", frame0.GetFunctionName()) + + frame_n_plus_1 = thread.GetFrameAtIndex(new_frame_count - 1) + self.assertEqual(frame_n_plus_1.GetPC(), 0x10) + + def test_scripted_frame_objects(self): + """Test that provider can return ScriptedFrame objects.""" + self.build() + target, process, thread, bkpt = lldbutil.run_to_source_breakpoint( + self, "Break here", lldb.SBFileSpec(self.source), only_one_thread=False + ) + + # Import the provider that returns ScriptedFrame objects. + script_path = os.path.join(self.getSourceDir(), "test_frame_providers.py") + self.runCmd("command script import " + script_path) + + error = lldb.SBError() + provider_id = target.RegisterScriptedFrameProvider( + "test_frame_providers.ScriptedFrameObjectProvider", + lldb.SBStructuredData(), + error, + ) + self.assertTrue(error.Success(), f"Failed to register provider: {error}") + self.assertNotEqual(provider_id, 0, "Provider ID should be non-zero") + + # Verify we have 5 frames. + self.assertEqual( + thread.GetNumFrames(), 5, "Should have 5 custom scripted frames" + ) + + # Verify frame properties from CustomScriptedFrame. + frame0 = thread.GetFrameAtIndex(0) + self.assertIsNotNone(frame0) + self.assertEqual(frame0.GetFunctionName(), "custom_scripted_frame_0") + self.assertEqual(frame0.GetPC(), 0x5000) + self.assertTrue(frame0.IsSynthetic(), "Frame should be marked as synthetic") + + frame1 = thread.GetFrameAtIndex(1) + self.assertIsNotNone(frame1) + self.assertEqual(frame1.GetPC(), 0x6000) + + frame2 = thread.GetFrameAtIndex(2) + self.assertIsNotNone(frame2) + self.assertEqual(frame2.GetFunctionName(), "custom_scripted_frame_2") + self.assertEqual(frame2.GetPC(), 0x7000) + self.assertTrue(frame2.IsSynthetic(), "Frame should be marked as synthetic") + + def test_applies_to_thread(self): + """Test that applies_to_thread filters which threads get the provider.""" + self.build() + target, process, thread, bkpt = lldbutil.run_to_source_breakpoint( + self, "Break here", lldb.SBFileSpec(self.source), only_one_thread=False + ) + + # We should have at least 2 threads (worker threads) at the breakpoint. + num_threads = process.GetNumThreads() + self.assertGreaterEqual( + num_threads, 2, "Should have at least 2 threads at breakpoint" + ) + + # Import the test frame provider. + script_path = os.path.join(self.getSourceDir(), "test_frame_providers.py") + self.runCmd("command script import " + script_path) + + # Collect original thread info before applying provider. + thread_info = {} + for i in range(num_threads): + t = process.GetThreadAtIndex(i) + thread_info[t.GetIndexID()] = { + "frame_count": t.GetNumFrames(), + "pc": t.GetFrameAtIndex(0).GetPC(), + } + + # Register the ThreadFilterFrameProvider which only applies to thread ID 1. + error = lldb.SBError() + provider_id = target.RegisterScriptedFrameProvider( + "test_frame_providers.ThreadFilterFrameProvider", + lldb.SBStructuredData(), + error, + ) + self.assertTrue(error.Success(), f"Failed to register provider: {error}") + self.assertNotEqual(provider_id, 0, "Provider ID should be non-zero") + + # Check each thread. + thread_id_1_found = False + # On ARM32, FixCodeAddress clears bit 0, so synthetic PCs get modified. + is_arm_32bit = lldbplatformutil.getArchitecture() == "arm" + expected_synthetic_pc = 0xFFFE if is_arm_32bit else 0xFFFF + + for i in range(num_threads): + t = process.GetThreadAtIndex(i) + thread_id = t.GetIndexID() + + if thread_id == 1: + # Thread with ID 1 should have synthetic frame. + thread_id_1_found = True + self.assertEqual( + t.GetNumFrames(), + 1, + f"Thread with ID 1 should have 1 synthetic frame", + ) + self.assertEqual( + t.GetFrameAtIndex(0).GetPC(), + expected_synthetic_pc, + f"Thread with ID 1 should have synthetic PC {expected_synthetic_pc:#x}", + ) + else: + # Other threads should keep their original frames. + self.assertEqual( + t.GetNumFrames(), + thread_info[thread_id]["frame_count"], + f"Thread with ID {thread_id} should not be affected by provider", + ) + self.assertEqual( + t.GetFrameAtIndex(0).GetPC(), + thread_info[thread_id]["pc"], + f"Thread with ID {thread_id} should have its original PC", + ) + + # We should have found at least one thread with ID 1. + self.assertTrue( + thread_id_1_found, + "Should have found a thread with ID 1 to test filtering", + ) + + def test_remove_frame_provider_by_id(self): + """Test that RemoveScriptedFrameProvider removes a specific provider by ID.""" + self.build() + target, process, thread, bkpt = lldbutil.run_to_source_breakpoint( + self, "Break here", lldb.SBFileSpec(self.source), only_one_thread=False + ) + + # Import the test frame providers. + script_path = os.path.join(self.getSourceDir(), "test_frame_providers.py") + self.runCmd("command script import " + script_path) + + # Get original frame count. + original_frame_count = thread.GetNumFrames() + original_pc = thread.GetFrameAtIndex(0).GetPC() + + # Register the first provider and get its ID. + error = lldb.SBError() + provider_id_1 = target.RegisterScriptedFrameProvider( + "test_frame_providers.ReplaceFrameProvider", + lldb.SBStructuredData(), + error, + ) + self.assertTrue(error.Success(), f"Failed to register provider 1: {error}") + + # Verify first provider is active (3 synthetic frames). + self.assertEqual(thread.GetNumFrames(), 3, "Should have 3 synthetic frames") + self.assertEqual( + thread.GetFrameAtIndex(0).GetPC(), 0x1000, "Should have first provider's PC" + ) + + # Register a second provider and get its ID. + provider_id_2 = target.RegisterScriptedFrameProvider( + "test_frame_providers.PrependFrameProvider", + lldb.SBStructuredData(), + error, + ) + self.assertTrue(error.Success(), f"Failed to register provider 2: {error}") + + # Verify IDs are different + self.assertNotEqual( + provider_id_1, provider_id_2, "Provider IDs should be unique" + ) + + # Now remove the first provider by ID + result = target.RemoveScriptedFrameProvider(provider_id_1) + self.assertSuccess( + result, f"Should successfully remove provider with ID {provider_id_1}" + ) + + # After removing the first provider, the second provider should still be + # active. The PrependFrameProvider adds 2 frames before the real stack. + # Since ReplaceFrameProvider had 3 frames, and we removed it, we should now + # have the original frames (from real stack) with PrependFrameProvider applied. + new_frame_count = thread.GetNumFrames() + self.assertEqual( + new_frame_count, + original_frame_count + 2, + "Should have original frames + 2 prepended frames", + ) + + # First two frames should be from PrependFrameProvider. + self.assertEqual( + thread.GetFrameAtIndex(0).GetPC(), + 0x9000, + "First frame should be from PrependFrameProvider", + ) + self.assertEqual( + thread.GetFrameAtIndex(1).GetPC(), + 0xA000, + "Second frame should be from PrependFrameProvider", + ) + + # Remove the second provider. + result = target.RemoveScriptedFrameProvider(provider_id_2) + self.assertSuccess( + result, f"Should successfully remove provider with ID {provider_id_2}" + ) + + # After removing both providers, frames should be back to original. + self.assertEqual( + thread.GetNumFrames(), + original_frame_count, + "Should restore original frame count", + ) + self.assertEqual( + thread.GetFrameAtIndex(0).GetPC(), + original_pc, + "Should restore original PC", + ) + + # Try to remove a provider that doesn't exist. + result = target.RemoveScriptedFrameProvider(999999) + self.assertTrue(result.Fail(), "Should fail to remove non-existent provider") + + def test_circular_dependency_fix(self): + """Test that accessing input_frames in __init__ doesn't cause circular dependency. + + This test verifies the fix for the circular dependency issue where: + 1. Thread::GetStackFrameList() creates the frame provider + 2. Provider's __init__ accesses input_frames and calls methods on frames + 3. SBFrame methods trigger ExecutionContextRef::GetFrameSP() + 4. Before the fix: GetFrameSP() would call Thread::GetStackFrameList() again -> circular dependency! + 5. After the fix: GetFrameSP() uses the remembered frame list -> no circular dependency + + The fix works by: + - StackFrame stores m_frame_list_wp (weak pointer to originating list) + - ExecutionContextRef stores m_frame_list_wp when created from a frame + - ExecutionContextRef::GetFrameSP() tries the remembered list first before asking the thread + """ + self.build() + target, process, thread, bkpt = lldbutil.run_to_source_breakpoint( + self, "Break here", lldb.SBFileSpec(self.source), only_one_thread=False + ) + + # Get original frame count and PC. + original_frame_count = thread.GetNumFrames() + original_pc = thread.GetFrameAtIndex(0).GetPC() + self.assertGreaterEqual( + original_frame_count, 2, "Should have at least 2 real frames" + ) + + # Import the provider that accesses input frames in __init__. + script_path = os.path.join(self.getSourceDir(), "test_frame_providers.py") + self.runCmd("command script import " + script_path) + + # Register the CircularDependencyTestProvider. + # Before the fix, this would crash or hang due to circular dependency. + error = lldb.SBError() + provider_id = target.RegisterScriptedFrameProvider( + "test_frame_providers.CircularDependencyTestProvider", + lldb.SBStructuredData(), + error, + ) + + # If we get here without crashing, the fix is working! + self.assertTrue(error.Success(), f"Failed to register provider: {error}") + self.assertNotEqual(provider_id, 0, "Provider ID should be non-zero") + + # Verify the provider worked correctly, + # Should have 1 synthetic frame + all original frames. + new_frame_count = thread.GetNumFrames() + self.assertEqual( + new_frame_count, + original_frame_count + 1, + "Should have original frames + 1 synthetic frame", + ) + + # On ARM32, FixCodeAddress clears bit 0, so synthetic PCs get modified. + is_arm_32bit = lldbplatformutil.getArchitecture() == "arm" + expected_synthetic_pc = 0xDEADBEEE if is_arm_32bit else 0xDEADBEEF + + # First frame should be synthetic. + frame0 = thread.GetFrameAtIndex(0) + self.assertIsNotNone(frame0) + self.assertEqual( + frame0.GetPC(), + expected_synthetic_pc, + f"First frame should be synthetic frame with PC {expected_synthetic_pc:#x}", + ) + + # Second frame should be the original first frame. + frame1 = thread.GetFrameAtIndex(1) + self.assertIsNotNone(frame1) + self.assertEqual( + frame1.GetPC(), + original_pc, + "Second frame should be original first frame", + ) + + # Verify we can still call methods on frames (no circular dependency!). + for i in range(min(3, new_frame_count)): + frame = thread.GetFrameAtIndex(i) + self.assertIsNotNone(frame) + # These calls should not trigger circular dependency. + pc = frame.GetPC() + self.assertNotEqual(pc, 0, f"Frame {i} should have valid PC") + + def test_python_source_frames(self): + """Test that frames can point to Python source files and display properly.""" + self.build() + target, process, thread, bkpt = lldbutil.run_to_source_breakpoint( + self, "Break here", lldb.SBFileSpec(self.source), only_one_thread=False + ) + + # Get original frame count. + original_frame_count = thread.GetNumFrames() + self.assertGreaterEqual( + original_frame_count, 2, "Should have at least 2 real frames" + ) + + # Import the provider. + script_path = os.path.join(self.getSourceDir(), "test_frame_providers.py") + self.runCmd("command script import " + script_path) + + # Register the PythonSourceFrameProvider. + error = lldb.SBError() + provider_id = target.RegisterScriptedFrameProvider( + "test_frame_providers.PythonSourceFrameProvider", + lldb.SBStructuredData(), + error, + ) + self.assertTrue(error.Success(), f"Failed to register provider: {error}") + self.assertNotEqual(provider_id, 0, "Provider ID should be non-zero") + + # Verify we have 3 more frames (Python frames). + new_frame_count = thread.GetNumFrames() + self.assertEqual( + new_frame_count, + original_frame_count + 3, + "Should have original frames + 3 Python frames", + ) + + # Verify first three frames are Python source frames. + frame0 = thread.GetFrameAtIndex(0) + self.assertIsNotNone(frame0) + self.assertEqual( + frame0.GetFunctionName(), + "compute_fibonacci", + "First frame should be compute_fibonacci", + ) + self.assertTrue(frame0.IsSynthetic(), "Frame should be marked as synthetic") + # PC-less frames should show invalid address and not crash. + self.assertEqual( + frame0.GetPC(), + lldb.LLDB_INVALID_ADDRESS, + "PC-less frame should have LLDB_INVALID_ADDRESS", + ) + + self.assertEqual( + frame0.GetFP(), + lldb.LLDB_INVALID_ADDRESS, + "PC-less frame FP should return LLDB_INVALID_ADDRESS", + ) + self.assertEqual( + frame0.GetSP(), + lldb.LLDB_INVALID_ADDRESS, + "PC-less frame SP should return LLDB_INVALID_ADDRESS", + ) + self.assertEqual( + frame0.GetCFA(), + 0, + "PC-less frame CFA should return 0", + ) + + frame1 = thread.GetFrameAtIndex(1) + self.assertIsNotNone(frame1) + self.assertEqual( + frame1.GetFunctionName(), + "process_data", + "Second frame should be process_data", + ) + self.assertTrue(frame1.IsSynthetic(), "Frame should be marked as synthetic") + + frame2 = thread.GetFrameAtIndex(2) + self.assertIsNotNone(frame2) + self.assertEqual(frame2.GetFunctionName(), "main", "Third frame should be main") + self.assertTrue(frame2.IsSynthetic(), "Frame should be marked as synthetic") + + # Verify line entry information is present. + line_entry0 = frame0.GetLineEntry() + self.assertTrue(line_entry0.IsValid(), "Frame 0 should have a valid line entry") + self.assertEqual(line_entry0.GetLine(), 7, "Frame 0 should point to line 7") + file_spec0 = line_entry0.GetFileSpec() + self.assertTrue(file_spec0.IsValid(), "Frame 0 should have valid file spec") + self.assertEqual( + file_spec0.GetFilename(), + "python_helper.py", + "Frame 0 should point to python_helper.py", + ) + + line_entry1 = frame1.GetLineEntry() + self.assertTrue(line_entry1.IsValid(), "Frame 1 should have a valid line entry") + self.assertEqual(line_entry1.GetLine(), 16, "Frame 1 should point to line 16") + + line_entry2 = frame2.GetLineEntry() + self.assertTrue(line_entry2.IsValid(), "Frame 2 should have a valid line entry") + self.assertEqual(line_entry2.GetLine(), 27, "Frame 2 should point to line 27") + + # Verify the frames display properly in backtrace. + # This tests that PC-less frames don't show 0xffffffffffffffff. + self.runCmd("bt") + output = self.res.GetOutput() + + # Should show function names. + self.assertIn("compute_fibonacci", output) + self.assertIn("process_data", output) + self.assertIn("main", output) + + # Should show Python file. + self.assertIn("python_helper.py", output) + + # Should show line numbers. + self.assertIn(":7", output) # compute_fibonacci line. + self.assertIn(":16", output) # process_data line. + self.assertIn(":27", output) # main line. + + # Should NOT show invalid address (0xffffffffffffffff). + self.assertNotIn("0xffffffffffffffff", output.lower()) + + # Verify frame 3 is the original real frame 0. + frame3 = thread.GetFrameAtIndex(3) + self.assertIsNotNone(frame3) + self.assertIn("thread_func", frame3.GetFunctionName()) diff --git a/lldb/test/API/functionalities/scripted_frame_provider/circular_dependency/Makefile b/lldb/test/API/functionalities/scripted_frame_provider/circular_dependency/Makefile new file mode 100644 index 0000000..1049594 --- /dev/null +++ b/lldb/test/API/functionalities/scripted_frame_provider/circular_dependency/Makefile @@ -0,0 +1,3 @@ +C_SOURCES := main.c + +include Makefile.rules diff --git a/lldb/test/API/functionalities/scripted_frame_provider/circular_dependency/TestFrameProviderCircularDependency.py b/lldb/test/API/functionalities/scripted_frame_provider/circular_dependency/TestFrameProviderCircularDependency.py new file mode 100644 index 0000000..b15bfb2 --- /dev/null +++ b/lldb/test/API/functionalities/scripted_frame_provider/circular_dependency/TestFrameProviderCircularDependency.py @@ -0,0 +1,119 @@ +""" +Test that frame providers wouldn't cause a hang due to a circular dependency +during its initialization. +""" + +import os +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import TestBase +from lldbsuite.test import lldbutil + +@skipIf(oslist=["linux"], archs=["arm$"]) +class FrameProviderCircularDependencyTestCase(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def setUp(self): + TestBase.setUp(self) + self.source = "main.c" + + @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24778") + def test_circular_dependency_with_function_replacement(self): + """ + Test the circular dependency fix with a provider that replaces function names. + """ + self.build() + + target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) + self.assertTrue(target, "Target should be valid") + + bkpt = target.BreakpointCreateBySourceRegex( + "break here", lldb.SBFileSpec(self.source) + ) + self.assertTrue(bkpt.IsValid(), "Breakpoint should be valid") + self.assertEqual(bkpt.GetNumLocations(), 1, "Should have 1 breakpoint location") + + process = target.LaunchSimple(None, None, self.get_process_working_directory()) + self.assertTrue(process, "Process should be valid") + self.assertEqual( + process.GetState(), lldb.eStateStopped, "Process should be stopped" + ) + + thread = process.GetSelectedThread() + self.assertTrue(thread.IsValid(), "Thread should be valid") + + frame0 = thread.GetFrameAtIndex(0) + self.assertIn("bar", frame0.GetFunctionName(), "Should be stopped in bar()") + + original_frame_count = thread.GetNumFrames() + self.assertGreaterEqual( + original_frame_count, 3, "Should have at least 3 frames: bar, foo, main" + ) + + frame_names = [thread.GetFrameAtIndex(i).GetFunctionName() for i in range(3)] + self.assertEqual(frame_names[0], "bar", "Frame 0 should be bar") + self.assertEqual(frame_names[1], "foo", "Frame 1 should be foo") + self.assertEqual(frame_names[2], "main", "Frame 2 should be main") + + script_path = os.path.join(self.getSourceDir(), "frame_provider.py") + self.runCmd("command script import " + script_path) + + # Register the frame provider that accesses input_frames. + # Before the fix, this registration would trigger the circular dependency: + # - Thread::GetStackFrameList() creates provider + # - Provider's get_frame_at_index() accesses input_frames[0] + # - Calls frame.GetFunctionName() -> ExecutionContextRef::GetFrameSP() + # - Before fix: Calls Thread::GetStackFrameList() again -> CIRCULAR! + # - After fix: Uses remembered m_frame_list_wp -> Works! + error = lldb.SBError() + provider_id = target.RegisterScriptedFrameProvider( + "frame_provider.ScriptedFrameObjectProvider", + lldb.SBStructuredData(), + error, + ) + + # If we reach here without crashing/hanging, the fix is working! + self.assertTrue( + error.Success(), + f"Should successfully register provider (if this fails, circular dependency!): {error}", + ) + self.assertNotEqual(provider_id, 0, "Provider ID should be non-zero") + + # Verify the provider is working correctly. + # Frame count should be unchanged (we're replacing frames, not adding). + new_frame_count = thread.GetNumFrames() + self.assertEqual( + new_frame_count, + original_frame_count, + "Frame count should be unchanged (replacement, not addition)", + ) + + # Verify that "bar" was replaced with "baz". + frame0_new = thread.GetFrameAtIndex(0) + self.assertIsNotNone(frame0_new, "Frame 0 should exist") + self.assertEqual( + frame0_new.GetFunctionName(), + "baz", + "Frame 0 function should be replaced: bar -> baz", + ) + + # Verify other frames are unchanged. + frame1_new = thread.GetFrameAtIndex(1) + self.assertEqual( + frame1_new.GetFunctionName(), "foo", "Frame 1 should still be foo" + ) + + frame2_new = thread.GetFrameAtIndex(2) + self.assertEqual( + frame2_new.GetFunctionName(), "main", "Frame 2 should still be main" + ) + + # Verify we can call methods on all frames (no circular dependency!). + for i in range(new_frame_count): + frame = thread.GetFrameAtIndex(i) + self.assertIsNotNone(frame, f"Frame {i} should exist") + # These calls should not trigger circular dependency. + pc = frame.GetPC() + self.assertNotEqual(pc, 0, f"Frame {i} should have valid PC") + func_name = frame.GetFunctionName() + self.assertIsNotNone(func_name, f"Frame {i} should have function name") diff --git a/lldb/test/API/functionalities/scripted_frame_provider/circular_dependency/frame_provider.py b/lldb/test/API/functionalities/scripted_frame_provider/circular_dependency/frame_provider.py new file mode 100644 index 0000000..f27f18c --- /dev/null +++ b/lldb/test/API/functionalities/scripted_frame_provider/circular_dependency/frame_provider.py @@ -0,0 +1,102 @@ +""" +Frame provider that reproduces the circular dependency issue. + +This provider accesses input_frames and calls methods on them, +which before the fix would cause a circular dependency. +""" + +import lldb +from lldb.plugins.scripted_process import ScriptedFrame +from lldb.plugins.scripted_frame_provider import ScriptedFrameProvider + + +class CustomScriptedFrame(ScriptedFrame): + """Custom scripted frame with full control over frame behavior.""" + + def __init__(self, thread, idx, pc, function_name): + args = lldb.SBStructuredData() + super().__init__(thread, args) + + self.idx = idx + self.pc = pc + self.function_name = function_name + + def get_id(self): + """Return the frame index.""" + return self.idx + + def get_pc(self): + """Return the program counter.""" + return self.pc + + def get_function_name(self): + """Return the function name.""" + return self.function_name + + def is_artificial(self): + """Mark as artificial frame.""" + return False + + def is_hidden(self): + """Not hidden.""" + return False + + def get_register_context(self): + return None + + +class ScriptedFrameObjectProvider(ScriptedFrameProvider): + """ + Provider that returns ScriptedFrame objects and accesses input_frames. + + This provider demonstrates the circular dependency bug fix: + 1. During get_frame_at_index(), we access input_frames[idx] + 2. We call frame.GetFunctionName() and frame.GetPC() on input frames + 3. Before the fix: These calls would trigger ExecutionContextRef::GetFrameSP() + which would call Thread::GetStackFrameList() -> circular dependency! + 4. After the fix: ExecutionContextRef uses the remembered frame list -> no circular dependency + """ + + def __init__(self, input_frames, args): + super().__init__(input_frames, args) + self.replacement_count = 0 + if self.target.process: + baz_symbol_ctx = self.target.FindFunctions("baz") + self.baz_symbol_ctx = None + if len(baz_symbol_ctx) == 1: + self.baz_symbol_ctx = baz_symbol_ctx[0] + + @staticmethod + def get_description(): + """Return a description of this provider.""" + return "Provider that replaces 'bar' function with 'baz'" + + def get_frame_at_index(self, idx): + """ + Replace frames named 'bar' with custom frames named 'baz'. + + This accesses input_frames and calls methods on them, which would + trigger the circular dependency bug before the fix. + """ + if idx < len(self.input_frames): + # This access and method calls would cause circular dependency before fix! + frame = self.input_frames[idx] + + # Calling GetFunctionName() triggers ExecutionContextRef resolution. + function_name = frame.GetFunctionName() + + if function_name == "bar" and self.baz_symbol_ctx: + # Replace "bar" with "baz". + baz_func = self.baz_symbol_ctx.GetFunction() + new_function_name = baz_func.GetName() + pc = baz_func.GetStartAddress().GetLoadAddress(self.target) + custom_frame = CustomScriptedFrame( + self.thread, idx, pc, new_function_name + ) + self.replacement_count += 1 + return custom_frame + + # Pass through other frames by returning their index. + return idx + + return None diff --git a/lldb/test/API/functionalities/scripted_frame_provider/circular_dependency/main.c b/lldb/test/API/functionalities/scripted_frame_provider/circular_dependency/main.c new file mode 100644 index 0000000..bbd1028 --- /dev/null +++ b/lldb/test/API/functionalities/scripted_frame_provider/circular_dependency/main.c @@ -0,0 +1,21 @@ +#include <stdio.h> + +int baz() { + printf("baz\n"); + return 666; +} + +int bar() { + printf("bar\n"); + return 42; // break here. +} + +int foo() { + printf("foo\n"); + return bar(); +} + +int main() { + printf("main\n"); + return foo(); +} diff --git a/lldb/test/API/functionalities/scripted_frame_provider/main.cpp b/lldb/test/API/functionalities/scripted_frame_provider/main.cpp new file mode 100644 index 0000000..0298e88 --- /dev/null +++ b/lldb/test/API/functionalities/scripted_frame_provider/main.cpp @@ -0,0 +1,53 @@ +// Multi-threaded test program for testing frame providers. + +#include <condition_variable> +#include <iostream> +#include <mutex> +#include <thread> + +std::mutex mtx; +std::condition_variable cv; +int ready_count = 0; +constexpr int NUM_THREADS = 2; + +void thread_func(int thread_num) { + std::cout << "Thread " << thread_num << " started\n"; + + { + std::unique_lock<std::mutex> lock(mtx); + ready_count++; + if (ready_count == NUM_THREADS + 1) { + cv.notify_all(); + } else { + cv.wait(lock, [] { return ready_count == NUM_THREADS + 1; }); + } + } + + std::cout << "Thread " << thread_num << " at breakpoint\n"; // Break here. +} + +int main(int argc, char **argv) { + std::thread threads[NUM_THREADS]; + + for (int i = 0; i < NUM_THREADS; i++) { + threads[i] = std::thread(thread_func, i); + } + + { + std::unique_lock<std::mutex> lock(mtx); + ready_count++; + if (ready_count == NUM_THREADS + 1) { + cv.notify_all(); + } else { + cv.wait(lock, [] { return ready_count == NUM_THREADS + 1; }); + } + } + + std::cout << "Main thread at barrier\n"; + + for (int i = 0; i < NUM_THREADS; i++) + threads[i].join(); + + std::cout << "All threads completed\n"; + return 0; +} diff --git a/lldb/test/API/functionalities/scripted_frame_provider/python_helper.py b/lldb/test/API/functionalities/scripted_frame_provider/python_helper.py new file mode 100644 index 0000000..27f3816 --- /dev/null +++ b/lldb/test/API/functionalities/scripted_frame_provider/python_helper.py @@ -0,0 +1,36 @@ +""" +Sample Python module to demonstrate Python source display in scripted frames. +""" + + +def compute_fibonacci(n): + """Compute the nth Fibonacci number.""" + if n <= 1: + return n + a, b = 0, 1 + for _ in range(n - 1): + a, b = b, a + b + return b + + +def process_data(data): + """Process some data and return result.""" + result = [] + for item in data: + if isinstance(item, int): + result.append(item * 2) + elif isinstance(item, str): + result.append(item.upper()) + return result + + +def main(): + """Main entry point for testing.""" + fib_10 = compute_fibonacci(10) + data = [1, 2, "hello", 3, "world"] + processed = process_data(data) + return fib_10, processed + + +if __name__ == "__main__": + main() diff --git a/lldb/test/API/functionalities/scripted_frame_provider/test_frame_providers.py b/lldb/test/API/functionalities/scripted_frame_provider/test_frame_providers.py new file mode 100644 index 0000000..76f8597 --- /dev/null +++ b/lldb/test/API/functionalities/scripted_frame_provider/test_frame_providers.py @@ -0,0 +1,316 @@ +""" +Test frame providers for scripted frame provider functionality. + +These providers demonstrate various merge strategies: +- Replace: Replace entire stack +- Prepend: Add frames before real stack +- Append: Add frames after real stack + +It also shows the ability to mix a dictionary, a ScriptedFrame or an SBFrame +index to create stackframes +""" + +import os +import lldb +from lldb.plugins.scripted_process import ScriptedFrame +from lldb.plugins.scripted_frame_provider import ScriptedFrameProvider + + +class ReplaceFrameProvider(ScriptedFrameProvider): + """Replace entire stack with custom frames.""" + + def __init__(self, input_frames, args): + super().__init__(input_frames, args) + self.frames = [ + { + "idx": 0, + "pc": 0x1000, + }, + 0, + { + "idx": 2, + "pc": 0x3000, + }, + ] + + @staticmethod + def get_description(): + """Return a description of this provider.""" + return "Replace entire stack with 3 custom frames" + + def get_frame_at_index(self, index): + if index >= len(self.frames): + return None + return self.frames[index] + + +class PrependFrameProvider(ScriptedFrameProvider): + """Prepend synthetic frames before real stack.""" + + def __init__(self, input_frames, args): + super().__init__(input_frames, args) + + @staticmethod + def get_description(): + """Return a description of this provider.""" + return "Prepend 2 synthetic frames before real stack" + + def get_frame_at_index(self, index): + if index == 0: + return {"pc": 0x9000} + elif index == 1: + return {"pc": 0xA000} + elif index - 2 < len(self.input_frames): + return index - 2 # Return real frame index. + return None + + +class AppendFrameProvider(ScriptedFrameProvider): + """Append synthetic frames after real stack.""" + + def __init__(self, input_frames, args): + super().__init__(input_frames, args) + + @staticmethod + def get_description(): + """Return a description of this provider.""" + return "Append 1 synthetic frame after real stack" + + def get_frame_at_index(self, index): + if index < len(self.input_frames): + return index # Return real frame index. + elif index == len(self.input_frames): + return { + "idx": 1, + "pc": 0x10, + } + return None + + +class CustomScriptedFrame(ScriptedFrame): + """Custom scripted frame with full control over frame behavior.""" + + def __init__(self, thread, idx, pc, function_name): + args = lldb.SBStructuredData() + super().__init__(thread, args) + + self.idx = idx + self.pc = pc + self.function_name = function_name + + def get_id(self): + """Return the frame index.""" + return self.idx + + def get_pc(self): + """Return the program counter.""" + return self.pc + + def get_function_name(self): + """Return the function name.""" + return self.function_name + + def is_artificial(self): + """Mark as artificial frame.""" + return False + + def is_hidden(self): + """Not hidden.""" + return False + + def get_register_context(self): + """No register context for this test.""" + return None + + +class ScriptedFrameObjectProvider(ScriptedFrameProvider): + """Provider that returns ScriptedFrame objects instead of dictionaries.""" + + def __init__(self, input_frames, args): + super().__init__(input_frames, args) + + @staticmethod + def get_description(): + """Return a description of this provider.""" + return "Provider returning custom ScriptedFrame objects" + + def get_frame_at_index(self, index): + """Return ScriptedFrame objects or dictionaries based on index.""" + if index == 0: + return CustomScriptedFrame( + self.thread, 0, 0x5000, "custom_scripted_frame_0" + ) + elif index == 1: + return {"pc": 0x6000} + elif index == 2: + return CustomScriptedFrame( + self.thread, 2, 0x7000, "custom_scripted_frame_2" + ) + elif index == 3: + return len(self.input_frames) - 2 # Real frame index. + elif index == 4: + return len(self.input_frames) - 1 # Real frame index. + return None + + +class ThreadFilterFrameProvider(ScriptedFrameProvider): + """Provider that only applies to thread with ID 1.""" + + @staticmethod + def applies_to_thread(thread): + """Only apply to thread with index ID 1.""" + return thread.GetIndexID() == 1 + + def __init__(self, input_frames, args): + super().__init__(input_frames, args) + + @staticmethod + def get_description(): + """Return a description of this provider.""" + return "Provider that only applies to thread ID 1" + + def get_frame_at_index(self, index): + """Return a single synthetic frame.""" + if index == 0: + return {"pc": 0xFFFF} + return None + + +class CircularDependencyTestProvider(ScriptedFrameProvider): + """ + Provider that tests the circular dependency fix. + + This provider accesses input_frames during __init__ and calls methods + on those frames. Before the fix, this would cause a circular dependency: + - Thread::GetStackFrameList() creates provider + - Provider's __init__ accesses input_frames[0] + - SBFrame::GetPC() tries to resolve ExecutionContextRef + - ExecutionContextRef::GetFrameSP() calls Thread::GetStackFrameList() + - Re-enters initialization -> circular dependency! + + With the fix, ExecutionContextRef remembers the frame list, so it doesn't + re-enter Thread::GetStackFrameList(). + """ + + def __init__(self, input_frames, args): + super().__init__(input_frames, args) + + # This would cause circular dependency before the fix! + # Accessing frames and calling methods on them during __init__ + self.original_frame_count = len(input_frames) + self.original_pcs = [] + + # Call GetPC() on each input frame - this triggers ExecutionContextRef resolution. + for i in range(min(3, len(input_frames))): + frame = input_frames[i] + if frame.IsValid(): + pc = frame.GetPC() + self.original_pcs.append(pc) + + @staticmethod + def get_description(): + """Return a description of this provider.""" + return "Provider that tests circular dependency fix by accessing frames in __init__" + + def get_frame_at_index(self, index): + """Prepend a synthetic frame, then pass through original frames.""" + if index == 0: + # Synthetic frame at index 0. + return {"pc": 0xDEADBEEF} + elif index - 1 < self.original_frame_count: + # Pass through original frames at indices 1, 2, 3, ... + return index - 1 + return None + + +class PythonSourceFrame(ScriptedFrame): + """Scripted frame that points to Python source code.""" + + def __init__(self, thread, idx, function_name, python_file, line_number): + args = lldb.SBStructuredData() + super().__init__(thread, args) + + self.idx = idx + self.function_name = function_name + self.python_file = python_file + self.line_number = line_number + + def get_id(self): + """Return the frame index.""" + return self.idx + + def get_pc(self): + """PC-less frame - return invalid address.""" + return lldb.LLDB_INVALID_ADDRESS + + def get_function_name(self): + """Return the function name.""" + return self.function_name + + def get_symbol_context(self): + """Return a symbol context with LineEntry pointing to Python source.""" + # Create a LineEntry pointing to the Python source file + line_entry = lldb.SBLineEntry() + line_entry.SetFileSpec(lldb.SBFileSpec(self.python_file, True)) + line_entry.SetLine(self.line_number) + line_entry.SetColumn(0) + + # Create a symbol context with the line entry + sym_ctx = lldb.SBSymbolContext() + sym_ctx.SetLineEntry(line_entry) + + return sym_ctx + + def is_artificial(self): + """Not artificial.""" + return False + + def is_hidden(self): + """Not hidden.""" + return False + + def get_register_context(self): + """No register context for PC-less frames.""" + return None + + +class PythonSourceFrameProvider(ScriptedFrameProvider): + """ + Provider that demonstrates Python source display in scripted frames. + + This provider prepends frames pointing to Python source code, showing + that PC-less frames can display Python source files with proper line + numbers and module/compile unit information. + """ + + def __init__(self, input_frames, args): + super().__init__(input_frames, args) + + # Find the python_helper.py file + current_dir = os.path.dirname(os.path.abspath(__file__)) + self.python_file = os.path.join(current_dir, "python_helper.py") + + @staticmethod + def get_description(): + """Return a description of this provider.""" + return "Provider that prepends frames pointing to Python source" + + def get_frame_at_index(self, index): + """Return Python source frames followed by original frames.""" + if index == 0: + # Frame pointing to compute_fibonacci function (line 7) + return PythonSourceFrame( + self.thread, 0, "compute_fibonacci", self.python_file, 7 + ) + elif index == 1: + # Frame pointing to process_data function (line 16) + return PythonSourceFrame( + self.thread, 1, "process_data", self.python_file, 16 + ) + elif index == 2: + # Frame pointing to main function (line 27) + return PythonSourceFrame(self.thread, 2, "main", self.python_file, 27) + elif index - 3 < len(self.input_frames): + # Pass through original frames + return index - 3 + return None diff --git a/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py b/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py index 8352672..f676cfb 100644 --- a/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py +++ b/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py @@ -8,6 +8,15 @@ from lldb.plugins.scripted_process import ScriptedThread from lldb.plugins.scripted_process import ScriptedFrame +def my_python_function(x, y): + """A sample Python function to demonstrate Python source display in scripted frames.""" + result = x + y + if result > 100: + return result * 2 + else: + return result + + class DummyStopHook: def __init__(self, target, args): self.target = target @@ -74,6 +83,40 @@ class DummyScriptedThread(ScriptedThread): self.frames.append(DummyScriptedFrame(self, args, len(self.frames), "bar")) self.frames.append(DummyScriptedFrame(self, args, len(self.frames), "foo")) + cwd = os.path.dirname(os.path.abspath(__file__)) + + le = lldb.SBLineEntry() + le.SetFileSpec(lldb.SBFileSpec(os.path.join(cwd, "baz.cpp"), True)) + le.SetLine(9) + le.SetColumn(10) + + sym_ctx = lldb.SBSymbolContext() + sym_ctx.SetLineEntry(le) + + self.frames.append( + DummyScriptedFrame(self, args, len(self.frames), "baz", sym_ctx) + ) + + # Add a frame with Python source + code = my_python_function.__code__ + lineno = code.co_firstlineno + col_offset = getattr( + code, "co_firstcol_offset", 0 + ) # Python ≥3.11 has column info + py_le = lldb.SBLineEntry() + py_le.SetFileSpec(lldb.SBFileSpec(__file__, True)) + py_le.SetLine(lineno) # Line where my_python_function is defined + py_le.SetColumn(col_offset) + + py_sym_ctx = lldb.SBSymbolContext() + py_sym_ctx.SetLineEntry(py_le) + + self.frames.append( + DummyScriptedFrame( + self, args, len(self.frames), "my_python_function", py_sym_ctx + ) + ) + def get_thread_id(self) -> int: return 0x19 diff --git a/lldb/test/API/functionalities/statusline/TestStatusline.py b/lldb/test/API/functionalities/statusline/TestStatusline.py index ca376cc..4ffa864 100644 --- a/lldb/test/API/functionalities/statusline/TestStatusline.py +++ b/lldb/test/API/functionalities/statusline/TestStatusline.py @@ -71,8 +71,10 @@ class TestStatusline(PExpectTest): ) self.expect('set set separator "| "') - # Hide the statusline and check or the control character. - self.expect("set set show-statusline false", ["\x1b[1;0r"]) + # Hide the statusline and check for the control character. + self.expect( + "set set show-statusline false", ["\x1b[1;{}r".format(self.TERMINAL_HEIGHT)] + ) def test_no_color(self): """Basic test for the statusline with colors disabled.""" diff --git a/lldb/test/API/functionalities/thread/step_until/function.list b/lldb/test/API/functionalities/thread/step_until/function.list index 5900fe8..d8caa20 100644 --- a/lldb/test/API/functionalities/thread/step_until/function.list +++ b/lldb/test/API/functionalities/thread/step_until/function.list @@ -1 +1,4 @@ -!call_me +v1 +f call_me +c 0 +c 1 diff --git a/lldb/test/API/functionalities/unwind/libunwind_ret_injection/Makefile b/lldb/test/API/functionalities/unwind/libunwind_ret_injection/Makefile new file mode 100644 index 0000000..4698eaa --- /dev/null +++ b/lldb/test/API/functionalities/unwind/libunwind_ret_injection/Makefile @@ -0,0 +1,6 @@ +CXX_SOURCES := main.cpp + +# Build with C++ exceptions enabled +CXXFLAGS := -g -O0 -fexceptions + +include Makefile.rules diff --git a/lldb/test/API/functionalities/unwind/libunwind_ret_injection/TestLibUnwindRetInjection.py b/lldb/test/API/functionalities/unwind/libunwind_ret_injection/TestLibUnwindRetInjection.py new file mode 100644 index 0000000..e03234d --- /dev/null +++ b/lldb/test/API/functionalities/unwind/libunwind_ret_injection/TestLibUnwindRetInjection.py @@ -0,0 +1,177 @@ +""" +Test that libunwind correctly injects 'ret' instructions to rebalance execution flow +when unwinding C++ exceptions. This is important for Apple Processor Trace analysis. +""" + +import lldb +import os +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil +from lldbsuite.test import configuration + + +class LibunwindRetInjectionTestCase(TestBase): + @skipIf(archs=no_match(["arm64", "arm64e", "aarch64"])) + @skipUnlessDarwin + @skipIfOutOfTreeLibunwind + def test_ret_injection_on_exception_unwind(self): + """Test that __libunwind_Registers_arm64_jumpto receives correct walkedFrames count and injects the right number of ret instructions.""" + self.build() + + exe = self.getBuildArtifact("a.out") + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Find the just-built libunwind, not the system one. + # llvm_tools_dir is typically <build>/bin, so lib is a sibling. + self.assertIsNotNone( + configuration.llvm_tools_dir, + "llvm_tools_dir must be set to find in-tree libunwind", + ) + + llvm_lib_dir = os.path.join( + os.path.dirname(configuration.llvm_tools_dir), "lib" + ) + + # Find the libunwind library (platform-agnostic). + libunwind_path = None + for filename in os.listdir(llvm_lib_dir): + if filename.startswith("libunwind.") or filename.startswith("unwind."): + libunwind_path = os.path.join(llvm_lib_dir, filename) + break + + self.assertIsNotNone( + libunwind_path, f"Could not find libunwind in {llvm_lib_dir}" + ) + + # Set breakpoint in __libunwind_Registers_arm64_jumpto. + # This is the function that performs the actual jump and ret injection. + bp = target.BreakpointCreateByName("__libunwind_Registers_arm64_jumpto") + self.assertTrue(bp.IsValid()) + self.assertGreater(bp.GetNumLocations(), 0) + + # Set up DYLD_INSERT_LIBRARIES to use the just-built libunwind. + launch_info = lldb.SBLaunchInfo(None) + env = target.GetEnvironment() + env.Set("DYLD_INSERT_LIBRARIES", libunwind_path, True) + launch_info.SetEnvironment(env, False) + + # Launch the process with our custom libunwind. + error = lldb.SBError() + process = target.Launch(launch_info, error) + self.assertSuccess( + error, f"Failed to launch process with libunwind at {libunwind_path}" + ) + self.assertTrue(process, PROCESS_IS_VALID) + + # We should hit the breakpoint in __libunwind_Registers_arm64_jumpto + # during the exception unwinding phase 2. + threads = lldbutil.get_threads_stopped_at_breakpoint(process, bp) + self.assertEqual(len(threads), 1, "Should have stopped at breakpoint") + + thread = threads[0] + frame = thread.GetFrameAtIndex(0) + + # Verify we're in __libunwind_Registers_arm64_jumpto. + function_name = frame.GetFunctionName() + self.assertTrue( + "__libunwind_Registers_arm64_jumpto" in function_name, + f"Expected to be in __libunwind_Registers_arm64_jumpto, got {function_name}", + ) + + # On ARM64, the walkedFrames parameter should be in register x1 (second parameter). + # According to the ARM64 calling convention, integer arguments are passed in x0-x7. + # x0 = Registers_arm64* pointer. + # x1 = unsigned walkedFrames. + error = lldb.SBError() + x1_value = frame.register["x1"].GetValueAsUnsigned(error) + self.assertSuccess(error, "Failed to read x1 register") + + # According to the code in UnwindCursor.hpp, the walkedFrames value represents: + # 1. The number of frames walked in unwind_phase2 to reach the landing pad. + # 2. Plus _EXTRA_LIBUNWIND_FRAMES_WALKED = 5 - 1 = 4 additional libunwind frames. + # + # From the comment in the code: + # frame #0: __libunwind_Registers_arm64_jumpto + # frame #1: Registers_arm64::returnto + # frame #2: UnwindCursor::jumpto + # frame #3: __unw_resume + # frame #4: __unw_resume_with_frames_walked + # frame #5: unwind_phase2 + # + # Since __libunwind_Registers_arm64_jumpto returns to the landing pad, + # we subtract 1, so _EXTRA_LIBUNWIND_FRAMES_WALKED = 4. + # + # For our test program: + # - unwind_phase2 starts walking (frame 0 counted here). + # - Walks through: func_d (throw site), func_c, func_b, func_a. + # - Finds landing pad in main. + # That's approximately 4-5 frames from the user code. + # Plus the 4 extra libunwind frames. + # + # So we expect x1 to be roughly 8-10. + expected_min_frames = 8 + expected_max_frames = 13 # Allow some variation for libc++abi frames. + + self.assertGreaterEqual( + x1_value, + expected_min_frames, + f"walkedFrames (x1) should be >= {expected_min_frames}, got {x1_value}. " + "This is the number of 'ret' instructions that will be executed.", + ) + + self.assertLessEqual( + x1_value, + expected_max_frames, + f"walkedFrames (x1) should be <= {expected_max_frames}, got {x1_value}. " + "Value seems too high.", + ) + + # Now step through the ret injection loop and count the actual number of 'ret' executions. + # The loop injects exactly x1_value ret instructions before continuing with register restoration. + # We step until we hit the first 'ldp' instruction (register restoration starts with 'ldp x2, x3, [x0, #0x010]'). + ret_executed_count = 0 + max_steps = 100 # Safety limit to prevent infinite loops. + + for step_count in range(max_steps): + # Get current instruction. + pc = frame.GetPC() + inst = process.ReadMemory(pc, 4, lldb.SBError()) + + # Disassemble current instruction. + current_inst = target.GetInstructions(lldb.SBAddress(pc, target), inst)[0] + mnemonic = current_inst.GetMnemonic(target) + operands = current_inst.GetOperands(target) + + # Check if we've reached the register restoration part (first ldp after the loop). + if mnemonic == "ldp": + # We've exited the ret injection loop. + break + + # Count 'ret' instructions that get executed. + if mnemonic == "ret": + self.assertEqual(operands, "x16") + ret_executed_count += 1 + + # Step one instruction. + thread.StepInstruction(False) # False = step over. + + # Update frame reference. + frame = thread.GetFrameAtIndex(0) + + # Verify we didn't hit the safety limit. + self.assertLess( + step_count, + max_steps - 1, + f"Stepped {max_steps} times without reaching 'ldp' instruction. Something is wrong.", + ) + + # The number of executed 'ret' instructions should match x1_value. + # According to the implementation, the loop executes exactly x1_value times. + self.assertEqual( + ret_executed_count, + x1_value, + f"Expected {x1_value} 'ret' instructions to be executed (matching x1 register), " + f"but counted {ret_executed_count} executed 'ret' instructions.", + ) diff --git a/lldb/test/API/functionalities/unwind/libunwind_ret_injection/main.cpp b/lldb/test/API/functionalities/unwind/libunwind_ret_injection/main.cpp new file mode 100644 index 0000000..00685e4 --- /dev/null +++ b/lldb/test/API/functionalities/unwind/libunwind_ret_injection/main.cpp @@ -0,0 +1,45 @@ +// Test program to verify libunwind ret injection feature for execution flow +// rebalancing. +// +// This test creates a multi-frame call stack and throws a C++ exception to +// trigger libunwind's two-phase exception handling. The test verifies that +// libunwind correctly injects the right amount of 'ret' instructions to +// rebalance the execution flow when returning to the landing pad, which is +// important for Apple Processor Trace analysis. + +#include <cstdio> +#include <exception> +#include <stdexcept> + +// Marker functions with noinline to ensure they appear in the stack. +static void __attribute__((noinline)) func_d() { + printf("In func_d, about to throw exception\n"); + throw std::runtime_error("test exception"); +} + +static void __attribute__((noinline)) func_c() { + printf("In func_c\n"); + func_d(); +} + +static void __attribute__((noinline)) func_b() { + printf("In func_b\n"); + func_c(); +} + +static void __attribute__((noinline)) func_a() { + printf("In func_a\n"); + func_b(); +} + +int main(int argc, char *argv[]) { + try { + printf("In main, about to call func_a\n"); + func_a(); + printf("ERROR: Should not reach here\n"); + return 1; + } catch (const std::exception &e) { + printf("Caught exception in main: %s\n", e.what()); + return 0; + } +} diff --git a/lldb/test/API/functionalities/wrong_commands/TestWrongCommands.py b/lldb/test/API/functionalities/wrong_commands/TestWrongCommands.py index 6d2ce2b..25f95f3 100644 --- a/lldb/test/API/functionalities/wrong_commands/TestWrongCommands.py +++ b/lldb/test/API/functionalities/wrong_commands/TestWrongCommands.py @@ -17,7 +17,9 @@ class UnknownCommandTestCase(TestBase): command_interpreter.HandleCommand("g", result) self.assertFalse(result.Succeeded()) - self.assertRegex(result.GetError(), "Ambiguous command 'g'. Possible matches:") + self.assertRegex( + result.GetError(), "error: Ambiguous command 'g'. Possible matches:" + ) self.assertRegex(result.GetError(), "gui") self.assertRegex(result.GetError(), "gdb-remote") self.assertEqual(1, result.GetError().count("gdb-remote")) diff --git a/lldb/test/API/lang/BoundsSafety/soft_trap/Makefile b/lldb/test/API/lang/BoundsSafety/soft_trap/Makefile new file mode 100644 index 0000000..5e83e7a --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/soft_trap/Makefile @@ -0,0 +1,10 @@ +# FIXME: mockSoftTrapRuntime.c shouldn't really be built with -fbounds-safety +C_SOURCES := main.c mockSoftTrapRuntime.c + +soft-trap-test-minimal: CFLAGS_EXTRAS := -fbounds-safety -fbounds-safety-soft-traps=call-minimal +soft-trap-test-minimal: all + +soft-trap-test-with-str: CFLAGS_EXTRAS := -fbounds-safety -fbounds-safety-soft-traps=call-with-str +soft-trap-test-with-str: all + +include Makefile.rules diff --git a/lldb/test/API/lang/BoundsSafety/soft_trap/TestBoundsSafetyInstrumentationPlugin.py b/lldb/test/API/lang/BoundsSafety/soft_trap/TestBoundsSafetyInstrumentationPlugin.py new file mode 100644 index 0000000..535a0bcf --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/soft_trap/TestBoundsSafetyInstrumentationPlugin.py @@ -0,0 +1,148 @@ +""" +Test the BoundsSafety instrumentation plugin +""" + +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * + + +STOP_REASON_MAX_LEN = 100 +SOFT_TRAP_FUNC_MINIMAL = "__bounds_safety_soft_trap" +SOFT_TRAP_FUNC_WITH_STR = "__bounds_safety_soft_trap_s" + + +class BoundsSafetyTestSoftTrapPlugin(TestBase): + def _check_stop_reason_impl( + self, + expected_soft_trap_func: str, + expected_stop_reason: str, + expected_func_name: str, + expected_file_name: str, + expected_line_num: int, + ): + process = self.test_target.process + thread = process.GetSelectedThread() + self.assertEqual( + thread.GetStopReason(), + lldb.eStopReasonInstrumentation, + ) + + stop_reason = thread.GetStopDescription(STOP_REASON_MAX_LEN) + self.assertEqual(stop_reason, expected_stop_reason) + + soft_trap_func_frame = thread.GetFrameAtIndex(0) + self.assertEqual(soft_trap_func_frame.name, expected_soft_trap_func) + + stop_frame = thread.GetSelectedFrame() + self.assertEqual(stop_frame.name, expected_func_name) + # The stop frame isn't frame 1 because that frame is the artificial + # frame containing the trap reason. + self.assertEqual(stop_frame.idx, 2) + file_name = stop_frame.GetLineEntry().GetFileSpec().basename + self.assertEqual(file_name, expected_file_name) + line = stop_frame.GetLineEntry().line + self.assertEqual(line, expected_line_num) + + def check_state_soft_trap_minimal( + self, stop_reason: str, func_name: str, file_name: str, line_num: int + ): + """ + Check the program state is as expected when hitting + a soft trap from -fbounds-safety-soft-traps=call-minimal + """ + self._check_stop_reason_impl( + SOFT_TRAP_FUNC_MINIMAL, + expected_stop_reason=stop_reason, + expected_func_name=func_name, + expected_file_name=file_name, + expected_line_num=line_num, + ) + + def check_state_soft_trap_with_str( + self, stop_reason: str, func_name: str, file_name: str, line_num: int + ): + """ + Check the program state is as expected when hitting + a soft trap from -fbounds-safety-soft-traps=call-with_str + """ + self._check_stop_reason_impl( + SOFT_TRAP_FUNC_WITH_STR, + expected_stop_reason=stop_reason, + expected_func_name=func_name, + expected_file_name=file_name, + expected_line_num=line_num, + ) + + # Skip the tests on Windows because they fail due to the stop reason + # being `eStopReasonNon` instead of the expected + # `eStopReasonInstrumentation`. + @skipIfWindows + @skipUnlessBoundsSafety + def test_call_minimal(self): + """ + Test the plugin on code built with + -fbounds-safety-soft-traps=call-minimal + """ + self.build(make_targets=["soft-trap-test-minimal"]) + self.test_target = self.createTestTarget() + self.runCmd("run") + + process = self.test_target.process + + # First soft trap hit + self.check_state_soft_trap_minimal( + "Soft Bounds check failed: indexing above upper bound in 'buffer[2]'", + "main", + "main.c", + 7, + ) + + process.Continue() + + # Second soft trap hit + self.check_state_soft_trap_minimal( + "Soft Bounds check failed: indexing below lower bound in 'buffer[-1]'", + "main", + "main.c", + 8, + ) + + process.Continue() + self.assertEqual(process.GetState(), lldb.eStateExited) + self.assertEqual(process.GetExitStatus(), 0) + + @skipIfWindows + @skipUnlessBoundsSafety + def test_call_with_str(self): + """ + Test the plugin on code built with + -fbounds-safety-soft-traps=call-with-str + """ + self.build(make_targets=["soft-trap-test-with-str"]) + self.test_target = self.createTestTarget() + self.runCmd("run") + + process = self.test_target.process + + # First soft trap hit + self.check_state_soft_trap_with_str( + "Soft Bounds check failed: indexing above upper bound in 'buffer[2]'", + "main", + "main.c", + 7, + ) + + process.Continue() + + # Second soft trap hit + self.check_state_soft_trap_with_str( + "Soft Bounds check failed: indexing below lower bound in 'buffer[-1]'", + "main", + "main.c", + 8, + ) + + process.Continue() + self.assertEqual(process.GetState(), lldb.eStateExited) + self.assertEqual(process.GetExitStatus(), 0) diff --git a/lldb/test/API/lang/BoundsSafety/soft_trap/main.c b/lldb/test/API/lang/BoundsSafety/soft_trap/main.c new file mode 100644 index 0000000..518afaa --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/soft_trap/main.c @@ -0,0 +1,10 @@ +#include <ptrcheck.h> + +int main(void) { + int pad; + int buffer[] = {0, 1}; + int pad2; + int tmp = buffer[2]; // access past upper bound + tmp = buffer[-1]; // access below lower bound + return 0; +} diff --git a/lldb/test/API/lang/BoundsSafety/soft_trap/mockSoftTrapRuntime.c b/lldb/test/API/lang/BoundsSafety/soft_trap/mockSoftTrapRuntime.c new file mode 100644 index 0000000..2cfbd24 --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/soft_trap/mockSoftTrapRuntime.c @@ -0,0 +1,17 @@ +#include <bounds_safety_soft_traps.h> +#include <ptrcheck.h> +#include <stdio.h> + +#if __CLANG_BOUNDS_SAFETY_SOFT_TRAP_API_VERSION > 0 +#error API version changed +#endif + +// FIXME: The runtimes really shouldn't be built with `-fbounds-safety` in +// soft trap mode because of the risk of infinite recursion. However, +// there's currently no way to have source files built with different flags + +void __bounds_safety_soft_trap_s(const char *reason) { + printf("BoundsSafety check FAILED: message:\"%s\"\n", reason ? reason : ""); +} + +void __bounds_safety_soft_trap(void) { printf("BoundsSafety check FAILED\n"); } diff --git a/lldb/test/API/lang/cpp/inline-namespace-in-typename/Makefile b/lldb/test/API/lang/cpp/inline-namespace-in-typename/Makefile new file mode 100644 index 0000000..99998b2 --- /dev/null +++ b/lldb/test/API/lang/cpp/inline-namespace-in-typename/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/lang/cpp/inline-namespace-in-typename/TestInlineNamespaceInTypename.py b/lldb/test/API/lang/cpp/inline-namespace-in-typename/TestInlineNamespaceInTypename.py new file mode 100644 index 0000000..1968136 --- /dev/null +++ b/lldb/test/API/lang/cpp/inline-namespace-in-typename/TestInlineNamespaceInTypename.py @@ -0,0 +1,30 @@ +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestInlineNamespaceInTypename(TestBase): + def test(self): + """ + Tests that we correctly omit the inline namespace when printing + the type name for "display", even if omitting the inline namespace + would be ambiguous in the current context. + """ + self.build() + target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) + + t1 = target.FindGlobalVariables("t1", 1) + self.assertTrue(len(t1), 1) + self.assertEqual(t1[0].GetDisplayTypeName(), "foo::Duplicate") + + # 'foo::Duplicate' would be an ambiguous reference, but we still + # omit the inline namespace when displaying the type. + t2 = target.FindGlobalVariables("t2", 1) + self.assertTrue(len(t2), 1) + self.assertEqual(t2[0].GetDisplayTypeName(), "foo::Duplicate") + self.assertEqual(t2[0].GetTypeName(), "foo::bar::Duplicate") + + t3 = target.FindGlobalVariables("t3", 1) + self.assertTrue(len(t3), 1) + self.assertEqual(t3[0].GetDisplayTypeName(), "foo::Unique") + self.assertEqual(t3[0].GetTypeName(), "foo::bar::Unique") diff --git a/lldb/test/API/lang/cpp/inline-namespace-in-typename/main.cpp b/lldb/test/API/lang/cpp/inline-namespace-in-typename/main.cpp new file mode 100644 index 0000000..eabd93c --- /dev/null +++ b/lldb/test/API/lang/cpp/inline-namespace-in-typename/main.cpp @@ -0,0 +1,13 @@ +namespace foo { +struct Duplicate { +} t1; + +inline namespace bar { +struct Duplicate { +} t2; +struct Unique { +} t3; +} // namespace bar +} // namespace foo + +int main() { return 0; } diff --git a/lldb/test/API/lang/cpp/libcxx-internals-recognizer/TestLibcxxInternalsRecognizer.py b/lldb/test/API/lang/cpp/libcxx-internals-recognizer/TestLibcxxInternalsRecognizer.py index 2f942da..280ac71 100644 --- a/lldb/test/API/lang/cpp/libcxx-internals-recognizer/TestLibcxxInternalsRecognizer.py +++ b/lldb/test/API/lang/cpp/libcxx-internals-recognizer/TestLibcxxInternalsRecognizer.py @@ -5,11 +5,12 @@ from lldbsuite.test import lldbutil import re +@skipIf(macos_version=[">=", "15.4"], asan=True) class LibCxxInternalsRecognizerTestCase(TestBase): NO_DEBUG_INFO_TESTCASE = True @add_test_categories(["libc++"]) - @skipIf(compiler="clang", compiler_version=["<=", "19.0"]) + @skipIf(compiler="clang", compiler_version=["<", "21.0"]) def test_frame_recognizer(self): """Test that implementation details of libc++ are hidden""" self.build() diff --git a/lldb/test/API/lang/cpp/template-alias/Makefile b/lldb/test/API/lang/cpp/template-alias/Makefile new file mode 100644 index 0000000..99998b2 --- /dev/null +++ b/lldb/test/API/lang/cpp/template-alias/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/lang/cpp/template-alias/TestTemplateAlias.py b/lldb/test/API/lang/cpp/template-alias/TestTemplateAlias.py new file mode 100644 index 0000000..b8314eb --- /dev/null +++ b/lldb/test/API/lang/cpp/template-alias/TestTemplateAlias.py @@ -0,0 +1,50 @@ +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * + + +class TestTemplateAlias(TestBase): + def do_test(self, extra_flags): + self.build(dictionary=extra_flags) + self.main_source_file = lldb.SBFileSpec("main.cpp") + lldbutil.run_to_source_breakpoint(self, "return", lldb.SBFileSpec("main.cpp")) + + self.expect_expr("f1", result_type="Foo<int>") + self.expect_expr("f2", result_type="Foo<double>") + self.expect_expr("b1", result_type="Bar<int>") + self.expect_expr("b2", result_type="Bar<double>") + self.expect_expr("bf1", result_type="Bar<int>") + self.expect_expr("bf2", result_type="Bar<double>") + self.expect_expr("bf1", result_type="Bar<int>") + self.expect_expr("bf2", result_type="Bar<double>") + self.expect_expr("cbf1", result_type="Container<int>") + + @expectedFailureAll( + bugnumber="LLDB doesn't reconstruct template alias names from template parameters" + ) + def test_tag_alias_simple(self): + self.do_test( + dict(CXXFLAGS_EXTRAS="-gdwarf-5 -gtemplate-alias -gsimple-template-names") + ) + + def test_tag_alias_no_simple(self): + self.do_test( + dict( + CXXFLAGS_EXTRAS="-gdwarf-5 -gtemplate-alias -gno-simple-template-names" + ) + ) + + def test_no_tag_alias_simple(self): + self.do_test( + dict( + CXXFLAGS_EXTRAS="-gdwarf-5 -gno-template-alias -gsimple-template-names" + ) + ) + + def test_no_tag_alias_no_simple(self): + self.do_test( + dict( + CXXFLAGS_EXTRAS="-gdwarf-5 -gno-template-alias -gno-simple-template-names" + ) + ) diff --git a/lldb/test/API/lang/cpp/template-alias/main.cpp b/lldb/test/API/lang/cpp/template-alias/main.cpp new file mode 100644 index 0000000..af6c979 --- /dev/null +++ b/lldb/test/API/lang/cpp/template-alias/main.cpp @@ -0,0 +1,16 @@ +template <typename T> using Foo = T; + +template <typename T> using Bar = Foo<T>; + +template <typename T> struct Container {}; + +int main() { + Foo<int> f1; + Foo<double> f2; + Bar<int> b1; + Bar<double> b2; + Bar<Foo<int>> bf1; + Bar<Foo<double>> bf2; + Container<Bar<Foo<int>>> cbf1; + return 0; +} diff --git a/lldb/test/API/lang/objc/foundation/TestFoundationDisassembly.py b/lldb/test/API/lang/objc/foundation/TestFoundationDisassembly.py index 245313d..75f6651a 100644 --- a/lldb/test/API/lang/objc/foundation/TestFoundationDisassembly.py +++ b/lldb/test/API/lang/objc/foundation/TestFoundationDisassembly.py @@ -13,52 +13,6 @@ class FoundationDisassembleTestCase(TestBase): NO_DEBUG_INFO_TESTCASE = True @skipIfAsan - def test_foundation_disasm(self): - """Do 'disassemble -n func' on each and every 'Code' symbol entry from the Foundation.framework.""" - self.build() - - # Enable synchronous mode - self.dbg.SetAsync(False) - - # Create a target by the debugger. - target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) - self.assertTrue(target, VALID_TARGET) - - # Now launch the process, and do not stop at entry point. - process = target.LaunchSimple(None, None, self.get_process_working_directory()) - self.assertTrue(process, PROCESS_IS_VALID) - - foundation_framework = None - for module in target.modules: - if module.file.basename == "Foundation": - foundation_framework = module.file.fullpath - break - - self.assertIsNotNone(foundation_framework, "Foundation.framework path located") - self.runCmd("image dump symtab '%s'" % foundation_framework) - raw_output = self.res.GetOutput() - # Now, grab every 'Code' symbol and feed it into the command: - # 'disassemble -n func'. - # - # The symbol name is on the last column and trails the flag column which - # looks like '0xhhhhhhhh', i.e., 8 hexadecimal digits. - codeRE = re.compile( - r""" - \ Code\ {9} # ' Code' followed by 9 SPCs, - .* # the wildcard chars, - 0x[0-9a-f]{8} # the flag column, and - \ (.+)$ # finally the function symbol. - """, - re.VERBOSE, - ) - for line in raw_output.split(os.linesep): - match = codeRE.search(line) - if match: - func = match.group(1) - self.runCmd('image lookup -s "%s"' % func) - self.runCmd('disassemble --force -n "%s"' % func) - - @skipIfAsan def test_simple_disasm(self): """Test the lldb 'disassemble' command""" self.build() diff --git a/lldb/test/API/lang/objc/modules-auto-import/TestModulesAutoImport.py b/lldb/test/API/lang/objc/modules-auto-import/TestModulesAutoImport.py index 142d27d..f3558f6 100644 --- a/lldb/test/API/lang/objc/modules-auto-import/TestModulesAutoImport.py +++ b/lldb/test/API/lang/objc/modules-auto-import/TestModulesAutoImport.py @@ -16,6 +16,7 @@ class ObjCModulesAutoImportTestCase(TestBase): self.line = line_number("main.m", "// Set breakpoint 0 here.") @skipIf(macos_version=["<", "10.12"]) + @skipIf(compiler="clang", compiler_version=["<", "19.0"]) def test_expr(self): self.build() exe = self.getBuildArtifact("a.out") diff --git a/lldb/test/API/lang/objc/modules-compile-error/Makefile b/lldb/test/API/lang/objc/modules-compile-error/Makefile deleted file mode 100644 index e031aa0..0000000 --- a/lldb/test/API/lang/objc/modules-compile-error/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -OBJC_SOURCES := main.m - -CFLAGS_EXTRAS = $(MANDATORY_MODULE_BUILD_CFLAGS) -I$(BUILDDIR) -DONLY_CLANG=1 - -include Makefile.rules diff --git a/lldb/test/API/lang/objc/modules-compile-error/TestModulesCompileError.py b/lldb/test/API/lang/objc/modules-compile-error/TestModulesCompileError.py deleted file mode 100644 index 36e302be..0000000 --- a/lldb/test/API/lang/objc/modules-compile-error/TestModulesCompileError.py +++ /dev/null @@ -1,28 +0,0 @@ -import lldb -from lldbsuite.test.decorators import * -from lldbsuite.test.lldbtest import * -from lldbsuite.test import lldbutil - - -class TestCase(TestBase): - @skipIf(compiler="clang", compiler_version=["<", "11.0"]) - def test(self): - self.build() - lldbutil.run_to_source_breakpoint( - self, "// break here", lldb.SBFileSpec("main.m") - ) - - # Try importing our custom module. This will fail as LLDB won't define - # the CLANG_ONLY define when it compiles the module for the expression - # evaluator. - # Check that the error message shows file/line/column, prints the relevant - # line from the source code and mentions the module that failed to build. - self.expect( - "expr @import LLDBTestModule", - error=True, - substrs=[ - "module.h:4:1: error: use of undeclared identifier 'syntax_error_for_lldb_to_find'", - "syntax_error_for_lldb_to_find // comment that tests source printing", - "could not build module 'LLDBTestModule'", - ], - ) diff --git a/lldb/test/API/lang/objc/modules-compile-error/main.m b/lldb/test/API/lang/objc/modules-compile-error/main.m deleted file mode 100644 index 35259dd..0000000 --- a/lldb/test/API/lang/objc/modules-compile-error/main.m +++ /dev/null @@ -1,5 +0,0 @@ -@import LLDBTestModule; - -int main() { - return foo(); // break here -} diff --git a/lldb/test/API/lang/objc/modules-compile-error/module.h b/lldb/test/API/lang/objc/modules-compile-error/module.h deleted file mode 100644 index 2edd13b..0000000 --- a/lldb/test/API/lang/objc/modules-compile-error/module.h +++ /dev/null @@ -1,5 +0,0 @@ -int foo() { return 123; } - -#ifndef ONLY_CLANG -syntax_error_for_lldb_to_find // comment that tests source printing -#endif diff --git a/lldb/test/API/lang/objc/modules-compile-error/module.modulemap b/lldb/test/API/lang/objc/modules-compile-error/module.modulemap deleted file mode 100644 index 3d44faf..0000000 --- a/lldb/test/API/lang/objc/modules-compile-error/module.modulemap +++ /dev/null @@ -1 +0,0 @@ -module LLDBTestModule { header "module.h" export * } diff --git a/lldb/test/API/lang/objc/modules-objc-property/TestModulesObjCProperty.py b/lldb/test/API/lang/objc/modules-objc-property/TestModulesObjCProperty.py index 3be064a..657a710 100644 --- a/lldb/test/API/lang/objc/modules-objc-property/TestModulesObjCProperty.py +++ b/lldb/test/API/lang/objc/modules-objc-property/TestModulesObjCProperty.py @@ -6,6 +6,7 @@ from lldbsuite.test import lldbutil class TestCase(TestBase): @no_debug_info_test + @skipIf(compiler="clang", compiler_version=["<", "19.0"]) def test_conflicting_properties(self): """Tests receiving two properties with the same name from modules.""" self.build() diff --git a/lldb/test/API/lang/rust/enum-variant-same-name/RustEnumValue.py b/lldb/test/API/lang/rust/enum-variant-same-name/RustEnumValue.py new file mode 100644 index 0000000..bc4fd7d --- /dev/null +++ b/lldb/test/API/lang/rust/enum-variant-same-name/RustEnumValue.py @@ -0,0 +1,69 @@ +"""Helper library to traverse data emitted for Rust enums """ +from lldbsuite.test.lldbtest import * + +DISCRIMINANT_MEMBER_NAME = "$discr$" +VALUE_MEMBER_NAME = "value" + + +class RustEnumValue: + def __init__(self, value: lldb.SBValue): + self.value = value + + def getAllVariantTypes(self): + result = [] + for i in range(self._inner().GetNumChildren()): + result.append(self.getVariantByIndex(i).GetDisplayTypeName()) + return result + + def _inner(self) -> lldb.SBValue: + return self.value.GetChildAtIndex(0) + + def getVariantByIndex(self, index): + return ( + self._inner() + .GetChildAtIndex(index) + .GetChildMemberWithName(VALUE_MEMBER_NAME) + ) + + @staticmethod + def _getDiscriminantValueAsUnsigned(discr_sbvalue: lldb.SBValue): + byte_size = discr_sbvalue.GetType().GetByteSize() + error = lldb.SBError() + + # when discriminant is u16 Clang emits 'unsigned char' + # and LLDB seems to treat it as character type disalowing to call GetValueAsUnsigned + if byte_size == 1: + return discr_sbvalue.GetData().GetUnsignedInt8(error, 0) + elif byte_size == 2: + return discr_sbvalue.GetData().GetUnsignedInt16(error, 0) + elif byte_size == 4: + return discr_sbvalue.GetData().GetUnsignedInt32(error, 0) + elif byte_size == 8: + return discr_sbvalue.GetData().GetUnsignedInt64(error, 0) + else: + return discr_sbvalue.GetValueAsUnsigned() + + def getCurrentVariantIndex(self): + default_index = 0 + for i in range(self._inner().GetNumChildren()): + variant: lldb.SBValue = self._inner().GetChildAtIndex(i) + discr = variant.GetChildMemberWithName(DISCRIMINANT_MEMBER_NAME) + if discr.IsValid(): + discr_unsigned_value = RustEnumValue._getDiscriminantValueAsUnsigned( + discr + ) + if variant.GetName() == f"$variant${discr_unsigned_value}": + return discr_unsigned_value + else: + default_index = i + return default_index + + def getFields(self): + result = [] + for i in range(self._inner().GetNumChildren()): + type: lldb.SBType = self._inner().GetType() + result.append(type.GetFieldAtIndex(i).GetName()) + return result + + def getCurrentValue(self) -> lldb.SBValue: + return self.getVariantByIndex(self.getCurrentVariantIndex()) diff --git a/lldb/test/API/lang/rust/enum-variant-same-name/TestRustEnumVariantSameName.py b/lldb/test/API/lang/rust/enum-variant-same-name/TestRustEnumVariantSameName.py new file mode 100644 index 0000000..0a192dc --- /dev/null +++ b/lldb/test/API/lang/rust/enum-variant-same-name/TestRustEnumVariantSameName.py @@ -0,0 +1,36 @@ +"""Test that lldb recognizes enum variant emitted by Rust compiler """ +import logging + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from RustEnumValue import RustEnumValue + + +class TestRustEnumStructs(TestBase): + def setUp(self): + TestBase.setUp(self) + src_dir = self.getSourceDir() + yaml_path = os.path.join(src_dir, "main.yaml") + obj_path = self.getBuildArtifact("main.o") + self.yaml2obj(yaml_path, obj_path) + self.dbg.CreateTarget(obj_path) + + def getFromGlobal(self, name): + values = self.target().FindGlobalVariables(name, 1) + self.assertEqual(values.GetSize(), 1) + return RustEnumValue(values[0]) + + def test_enum_instance(self): + # static ENUM_INSTANCE: A = A::A(B::B(10)); + value = self.getFromGlobal("ENUM_INSTANCE").getCurrentValue() + self.assertEqual(value.GetType().GetDisplayTypeName(), "main::A::A") + + value_b = RustEnumValue(value.GetChildAtIndex(0)) + self.assertEqual( + value_b.getCurrentValue() + .GetChildAtIndex(0) + .GetData() + .GetUnsignedInt8(lldb.SBError(), 0), + 10, + ) diff --git a/lldb/test/API/lang/rust/enum-variant-same-name/main.rs b/lldb/test/API/lang/rust/enum-variant-same-name/main.rs new file mode 100644 index 0000000..e76be3d --- /dev/null +++ b/lldb/test/API/lang/rust/enum-variant-same-name/main.rs @@ -0,0 +1,15 @@ +/// Command: +/// rustc -g --emit=obj --crate-type=bin -C panic=abort -C link-arg=-nostdlib main.rs && obj2yaml main.o -o main.yaml + +pub enum A { + A(B), +} + +pub enum B { + B(u8), +} + +static ENUM_INSTANCE: A = A::A(B::B(10)); + +pub fn main() { +} diff --git a/lldb/test/API/lang/rust/enum-variant-same-name/main.yaml b/lldb/test/API/lang/rust/enum-variant-same-name/main.yaml new file mode 100644 index 0000000..76e7cb6 --- /dev/null +++ b/lldb/test/API/lang/rust/enum-variant-same-name/main.yaml @@ -0,0 +1,1137 @@ +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 + SectionHeaderStringTable: .strtab +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x4 + - Name: .text._ZN3std2rt10lang_start17hcb2a6a78164896cfE + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Content: 4883EC2889C84889D14889F248897C2408488954241048894C24188844242748893C244889E7488D3500000000440FB6C0FF15000000004883C428C3 + - Name: '.text._ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17hda4568952b4f838eE' + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Content: 4883EC1848897C2408488B3FE800000000E800000000884424170FB6C04883C418C3 + - Name: .text._ZN3std3sys9backtrace28__rust_begin_short_backtrace17h4c89f1d03cb386ebE + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Content: 4883EC1848897C2408E8000000004883C418C3 + - Name: '.text._ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17h356d5dadf319082fE' + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Content: 4883EC1848897C2410488B3FE8000000004883C418C3 + - Name: .text._ZN4core3ops8function6FnOnce9call_once17h21112f971fb5dae8E + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Content: 4883EC1848897C2408488D7C2408E8000000004883C418C3 + - Name: .text._ZN4core3ops8function6FnOnce9call_once17hf55c273de81f71dfE + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Content: 4883EC1848897C2410FFD74883C418C3 + - Name: '.text._ZN4core3ptr85drop_in_place$LT$std..rt..lang_start$LT$$LP$$RP$$GT$..$u7b$$u7b$closure$u7d$$u7d$$GT$17h623f70e9f0cf3066E' + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Content: 48897C24F8C3 + - Name: '.text._ZN54_$LT$$LP$$RP$$u20$as$u20$std..process..Termination$GT$6report17h10aa5c9c6135be01E' + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Content: 31C0C3 + - Name: .text._ZN4main4main17h5659c6c02e5cd445E + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Content: C3 + - Name: .text.main + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Content: 504889F2488B05000000008A004863F7488D3D0000000031C9E80000000059C3 + - Name: .data.rel.ro..Lanon.4a406b9d9c1243847b49ddffb2385826.0 + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x8 + Content: '000000000000000008000000000000000800000000000000000000000000000000000000000000000000000000000000' + - Name: .rodata._ZN4main13ENUM_INSTANCE17hc6db515181ea378fE + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x1 + Content: 0A + - Name: .debug_gdb_scripts + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x1 + EntSize: 0x1 + Content: 016764625F6C6F61645F727573745F7072657474795F7072696E746572732E707900 + - Name: .debug_abbrev + Type: SHT_PROGBITS + AddressAlign: 0x1 + Content: 011101250E1305030E10171B0E110155170000023400030E4913021800000313011D13030E0B0B88010F0000040D00030E491388010F380B0000050F004913030E33060000062400030E3E0B0B0B0000073901030E0000081301030E0B0B88010F0000092E011101120640186E0E030E3A0B3B0B491300000A34000218030E88010F3A0B3B0B491300000B1D01311311011206580B590B570B00000C05000218311300000D1D00311311011206580B5905570B00000E2F004913030E00000F05000218030E3A0B3B0B49130000101301030E0B0B320B88010F0000110D00030E491388010F380B320B0000122E016E0E030E3A0B3B0549133C19000013050049130000142E011101120640186E0E030E3A0B3B0B0000150B01110112060000163400021831130000172E011101120640186E0E030E3A0B3B054913000018050002183A0B3B054913000019150000001A3400030E49133A0B3B0B88010F02186E0E00001B330100001C190100001D2E001101120640186E0E030E3A0B3B0B6A1900001E2E004713200B00001F2E014713200B0000200500030E3A0B3B0549130000212E016E0E030E3A0B3B05200B0000223400030E88010F3A0B3B054913000023050002183A0B3B0B49130000242E011101120640186E0E030E3A0B3B05000000 + - Name: .debug_info + Type: SHT_PROGBITS + AddressAlign: 0x1 + Content: F00400000400000000000801000000001C0000000000000000000000000000000000000000000000000002000000003D0000000903000000000000000003B500000000000000300804000000008B000000080004000000009F000000080804000000009F000000081004000000008B000000081804000000008B000000082004000000008B000000082800059800000000000000000000000600000000070006000000000708070000000007000000000700000000080000000008080400000000B1020000080000090000000000000000220000000157000000000000000002C7590300000A03910806000000000802C1B10200000B7303000000000000000000000300000002C7550C029117790300000D6D030000000000000000000003000000041D0810000E980000000000000000000900000000000000003C0000000157000000000000000002C0C50400000F0291080000000002C1B10200000F0291100000000002C2C50400000F0291180000000002C3CC0400000F0291270000000002C4520300000E9800000000000000000007000000000700000000070000000007000000001000000000010101110000000052030000010003120000000000000000037C0259030000136003000000000000000700000000140000000000000000130000000157000000000000000005940F029108000000000594B1020000150000000000000000000000000A02910700000000010598980000000B90030000000000000000000000000000059B0516029117A603000000000EB1020000000000000E9800000000000000000000070000000010000000000101011100000000A6010000010003120000000000000000041C0859030000135402000000000700000000170000000000000000030000000157000000000000000004A909540200001802917F04A909980000000000000005BE02000000000000000000001907000000001A00000000DE020000010C01090300000000000000000000000010000000000101011B1C0400000000F502000001000000100000000001010111000000000B030000010001000010000000000101011B1C04000000002203000001000000100000000001010111000000005203000001000100001D00000000000000000100000001570000000000000000010E00060000000007010600000000050405A601000000000000000000001EBA010000011F68020000012000000000041C0854020000000700000000070000000021000000000000000006DC01010E980000000000000022000000000106DC01980000000000070000000007000000000700000000090000000000000000160000000157000000000000000007FA590300002302911007FAE60400002302910F07FA980000000EB5000000000000000E980000000000000000090000000000000000180000000157000000000000000007FA590300002302910807FAB50000002302911707FA980000000EB5000000000000000E980000000000000000140000000000000000100000000157000000000000000007FA2302911007FAB10200002302910F07FA980000000EB1020000000000000E98000000000000000000000007000000002400000000000000000600000001570000000000000000080B0218029178080B02E60400000EB5000000000000000000000600000000050805D904000000000000000000000552030000000000000000000005B5000000000000000000000000 + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x1 + EntSize: 0x1 + Content: 0072757374632076657273696F6E20312E38372E30202831373036376539616320323032352D30352D30392900 + - Name: .note.GNU-stack + Type: SHT_PROGBITS + AddressAlign: 0x1 + - Name: .eh_frame + Type: SHT_X86_64_UNWIND + Flags: [ SHF_ALLOC ] + AddressAlign: 0x8 + Content: 1400000000000000017A5200017810011B0C070890010000140000001C000000000000003C00000000440E30770E08001400000034000000000000002200000000440E205D0E0800140000004C000000000000001300000000440E204E0E08001400000064000000000000001600000000440E20510E0800140000007C000000000000001800000000440E20530E08001400000094000000000000001000000000440E204B0E080010000000AC00000000000000060000000000000010000000C000000000000000030000000000000010000000D400000000000000010000000000000018000000E8000000000000002000000000410E105E0E080000000000 + - Name: .debug_line + Type: SHT_PROGBITS + AddressAlign: 0x1 + Content: 0D0400000400E9020000010101FB0E0D0001010101000000010000012F686F6D652F6B6976612F2E7275737475702F746F6F6C636861696E732F737461626C652D7838365F36342D756E6B6E6F776E2D6C696E75782D676E752F6C69622F727573746C69622F7372632F727573742F6C6962726172792F7374642F737263002F686F6D652F6B6976612F2E7275737475702F746F6F6C636861696E732F737461626C652D7838365F36342D756E6B6E6F776E2D6C696E75782D676E752F6C69622F727573746C69622F7372632F727573742F6C6962726172792F7374642F7372632F7379732F70726F636573732F756E6978002F686F6D652F6B6976612F2E7275737475702F746F6F6C636861696E732F737461626C652D7838365F36342D756E6B6E6F776E2D6C696E75782D676E752F6C69622F727573746C69622F7372632F727573742F6C6962726172792F7374642F7372632F737973002F686F6D652F6B6976612F2E7275737475702F746F6F6C636861696E732F737461626C652D7838365F36342D756E6B6E6F776E2D6C696E75782D676E752F6C69622F727573746C69622F7372632F727573742F6C6962726172792F636F72652F737263002F686F6D652F6B6976612F2E7275737475702F746F6F6C636861696E732F737461626C652D7838365F36342D756E6B6E6F776E2D6C696E75782D676E752F6C69622F727573746C69622F7372632F727573742F6C6962726172792F636F72652F7372632F6F7073002F686F6D652F6B6976612F2E7275737475702F746F6F6C636861696E732F737461626C652D7838365F36342D756E6B6E6F776E2D6C696E75782D676E752F6C69622F727573746C69622F7372632F727573742F6C6962726172792F636F72652F7372632F70747200006D61696E2E72730000000072742E727300010000636F6D6D6F6E2E72730002000070726F636573732E7273000100006261636B74726163652E72730003000068696E742E72730004000066756E6374696F6E2E7273000500006D6F642E727300060000000402000902000000000000000003BF0101050A0A08DD05054905020B084202050001010402000902000000000000000003C6010105460A900512063C040305090603B603D60402055D0B03CA7C3C0205000101040500090200000000000000000393010105120A940406050503C50258040505020B03C17D0102050001010407000902000000000000000003F9010105050A90060B8202050001010407000902000000000000000003F9010105050A90060B9E02050001010407000902000000000000000003F9010105050A90060B2E020500010104080009020000000000000000038A040105010A580201000101040405060A000902000000000000000003AA1301020300010105020A0009020000000000000000030E010201000101 + - Name: .rela.text._ZN3std2rt10lang_start17hcb2a6a78164896cfE + Type: SHT_RELA + Flags: [ SHF_INFO_LINK ] + Link: .symtab + AddressAlign: 0x8 + Info: .text._ZN3std2rt10lang_start17hcb2a6a78164896cfE + Relocations: + - Offset: 0x29 + Symbol: .data.rel.ro..Lanon.4a406b9d9c1243847b49ddffb2385826.0 + Type: R_X86_64_PC32 + Addend: -4 + - Offset: 0x33 + Symbol: _ZN3std2rt19lang_start_internal17h418648f91f5be3a1E + Type: R_X86_64_GOTPCREL + Addend: -4 + - Name: '.rela.text._ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17hda4568952b4f838eE' + Type: SHT_RELA + Flags: [ SHF_INFO_LINK ] + Link: .symtab + AddressAlign: 0x8 + Info: '.text._ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17hda4568952b4f838eE' + Relocations: + - Offset: 0xD + Symbol: .text._ZN3std3sys9backtrace28__rust_begin_short_backtrace17h4c89f1d03cb386ebE + Type: R_X86_64_PLT32 + Addend: -4 + - Offset: 0x12 + Symbol: '.text._ZN54_$LT$$LP$$RP$$u20$as$u20$std..process..Termination$GT$6report17h10aa5c9c6135be01E' + Type: R_X86_64_PLT32 + Addend: -4 + - Name: .rela.text._ZN3std3sys9backtrace28__rust_begin_short_backtrace17h4c89f1d03cb386ebE + Type: SHT_RELA + Flags: [ SHF_INFO_LINK ] + Link: .symtab + AddressAlign: 0x8 + Info: .text._ZN3std3sys9backtrace28__rust_begin_short_backtrace17h4c89f1d03cb386ebE + Relocations: + - Offset: 0xA + Symbol: .text._ZN4core3ops8function6FnOnce9call_once17hf55c273de81f71dfE + Type: R_X86_64_PLT32 + Addend: -4 + - Name: '.rela.text._ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17h356d5dadf319082fE' + Type: SHT_RELA + Flags: [ SHF_INFO_LINK ] + Link: .symtab + AddressAlign: 0x8 + Info: '.text._ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17h356d5dadf319082fE' + Relocations: + - Offset: 0xD + Symbol: .text._ZN4core3ops8function6FnOnce9call_once17h21112f971fb5dae8E + Type: R_X86_64_PLT32 + Addend: -4 + - Name: .rela.text._ZN4core3ops8function6FnOnce9call_once17h21112f971fb5dae8E + Type: SHT_RELA + Flags: [ SHF_INFO_LINK ] + Link: .symtab + AddressAlign: 0x8 + Info: .text._ZN4core3ops8function6FnOnce9call_once17h21112f971fb5dae8E + Relocations: + - Offset: 0xF + Symbol: '.text._ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17hda4568952b4f838eE' + Type: R_X86_64_PLT32 + Addend: -4 + - Name: .rela.text.main + Type: SHT_RELA + Flags: [ SHF_INFO_LINK ] + Link: .symtab + AddressAlign: 0x8 + Info: .text.main + Relocations: + - Offset: 0x7 + Symbol: __rustc_debug_gdb_scripts_section__ + Type: R_X86_64_GOTPCREL + Addend: -4 + - Offset: 0x13 + Symbol: .text._ZN4main4main17h5659c6c02e5cd445E + Type: R_X86_64_PC32 + Addend: -4 + - Offset: 0x1A + Symbol: _ZN3std2rt10lang_start17hcb2a6a78164896cfE + Type: R_X86_64_PLT32 + Addend: -4 + - Name: .rela.data.rel.ro..Lanon.4a406b9d9c1243847b49ddffb2385826.0 + Type: SHT_RELA + Flags: [ SHF_INFO_LINK ] + Link: .symtab + AddressAlign: 0x8 + Info: .data.rel.ro..Lanon.4a406b9d9c1243847b49ddffb2385826.0 + Relocations: + - Offset: 0x18 + Symbol: '.text._ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17h356d5dadf319082fE' + Type: R_X86_64_64 + - Offset: 0x20 + Symbol: '.text._ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17hda4568952b4f838eE' + Type: R_X86_64_64 + - Offset: 0x28 + Symbol: '.text._ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17hda4568952b4f838eE' + Type: R_X86_64_64 + - Name: .rela.debug_info + Type: SHT_RELA + Flags: [ SHF_INFO_LINK ] + Link: .symtab + AddressAlign: 0x8 + Info: .debug_info + Relocations: + - Offset: 0x6 + Symbol: .debug_abbrev + Type: R_X86_64_32 + - Offset: 0xC + Symbol: .debug_str + Type: R_X86_64_32 + - Offset: 0x12 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 57 + - Offset: 0x16 + Symbol: .debug_line + Type: R_X86_64_32 + - Offset: 0x1A + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 95 + - Offset: 0x26 + Symbol: .debug_ranges + Type: R_X86_64_32 + - Offset: 0x2B + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 176 + - Offset: 0x35 + Symbol: .data.rel.ro..Lanon.4a406b9d9c1243847b49ddffb2385826.0 + Type: R_X86_64_64 + - Offset: 0x42 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 382 + - Offset: 0x49 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 260 + - Offset: 0x54 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 287 + - Offset: 0x5F + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 298 + - Offset: 0x6A + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 304 + - Offset: 0x75 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 314 + - Offset: 0x80 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 324 + - Offset: 0x90 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 277 + - Offset: 0x99 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 274 + - Offset: 0xA0 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 292 + - Offset: 0xA7 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 334 + - Offset: 0xAC + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 338 + - Offset: 0xB1 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 341 + - Offset: 0xB6 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 362 + - Offset: 0xBD + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 352 + - Offset: 0xC9 + Symbol: '.text._ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17hda4568952b4f838eE' + Type: R_X86_64_64 + - Offset: 0xD7 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 943 + - Offset: 0xDB + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 1016 + - Offset: 0xEA + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 352 + - Offset: 0xFA + Symbol: '.text._ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17hda4568952b4f838eE' + Type: R_X86_64_64 + Addend: 26 + - Offset: 0x116 + Symbol: '.text._ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17hda4568952b4f838eE' + Type: R_X86_64_64 + Addend: 26 + - Offset: 0x12C + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 769 + - Offset: 0x133 + Symbol: .text._ZN3std2rt10lang_start17hcb2a6a78164896cfE + Type: R_X86_64_64 + - Offset: 0x141 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 879 + - Offset: 0x145 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 922 + - Offset: 0x153 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 352 + - Offset: 0x161 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 1744 + - Offset: 0x16F + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 1749 + - Offset: 0x17D + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 1781 + - Offset: 0x18C + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 769 + - Offset: 0x193 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 540 + - Offset: 0x198 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 544 + - Offset: 0x19D + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 552 + - Offset: 0x1A2 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 557 + - Offset: 0x1A7 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 564 + - Offset: 0x1AF + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 487 + - Offset: 0x1BB + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 573 + - Offset: 0x1BF + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 641 + - Offset: 0x1D5 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 835 + - Offset: 0x1DA + Symbol: .text._ZN3std3sys9backtrace28__rust_begin_short_backtrace17h4c89f1d03cb386ebE + Type: R_X86_64_64 + - Offset: 0x1E8 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 1034 + - Offset: 0x1EC + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 1106 + - Offset: 0x1F6 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 1796 + - Offset: 0x201 + Symbol: .text._ZN3std3sys9backtrace28__rust_begin_short_backtrace17h4c89f1d03cb386ebE + Type: R_X86_64_64 + Addend: 14 + - Offset: 0x211 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 1789 + - Offset: 0x221 + Symbol: .text._ZN3std3sys9backtrace28__rust_begin_short_backtrace17h4c89f1d03cb386ebE + Type: R_X86_64_64 + Addend: 14 + - Offset: 0x23F + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 1032 + - Offset: 0x248 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 769 + - Offset: 0x250 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 544 + - Offset: 0x255 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 564 + - Offset: 0x25D + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 487 + - Offset: 0x269 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 695 + - Offset: 0x26D + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 747 + - Offset: 0x280 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 869 + - Offset: 0x285 + Symbol: '.text._ZN54_$LT$$LP$$RP$$u20$as$u20$std..process..Termination$GT$6report17h10aa5c9c6135be01E' + Type: R_X86_64_64 + - Offset: 0x293 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 1616 + - Offset: 0x297 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 1703 + - Offset: 0x2B6 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 357 + - Offset: 0x2C0 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 352 + - Offset: 0x2C5 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 471 + - Offset: 0x2D2 + Symbol: .rodata._ZN4main13ENUM_INSTANCE17hc6db515181ea378fE + Type: R_X86_64_64 + - Offset: 0x2DA + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 496 + - Offset: 0x2DF + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 485 + - Offset: 0x2E9 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 485 + - Offset: 0x2F6 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 485 + - Offset: 0x2FE + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 487 + - Offset: 0x30C + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 491 + - Offset: 0x316 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 491 + - Offset: 0x323 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 491 + - Offset: 0x32B + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 487 + - Offset: 0x339 + Symbol: .text._ZN4main4main17h5659c6c02e5cd445E + Type: R_X86_64_64 + - Offset: 0x347 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 1710 + - Offset: 0x34B + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 352 + - Offset: 0x353 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 493 + - Offset: 0x35A + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 648 + - Offset: 0x365 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 652 + - Offset: 0x37A + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 754 + - Offset: 0x387 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 759 + - Offset: 0x38C + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 764 + - Offset: 0x391 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 771 + - Offset: 0x395 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 815 + - Offset: 0x3A2 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 769 + - Offset: 0x3A7 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 829 + - Offset: 0x3B6 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 845 + - Offset: 0x3BB + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 849 + - Offset: 0x3C0 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 858 + - Offset: 0x3C5 + Symbol: '.text._ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17h356d5dadf319082fE' + Type: R_X86_64_64 + - Offset: 0x3D3 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 1155 + - Offset: 0x3D7 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 1246 + - Offset: 0x3FA + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 1145 + - Offset: 0x403 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 1150 + - Offset: 0x409 + Symbol: .text._ZN4core3ops8function6FnOnce9call_once17h21112f971fb5dae8E + Type: R_X86_64_64 + - Offset: 0x417 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 1302 + - Offset: 0x41B + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 1246 + - Offset: 0x43E + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 1145 + - Offset: 0x447 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 1150 + - Offset: 0x44D + Symbol: .text._ZN4core3ops8function6FnOnce9call_once17hf55c273de81f71dfE + Type: R_X86_64_64 + - Offset: 0x45B + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 1361 + - Offset: 0x45F + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 1420 + - Offset: 0x47E + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 1145 + - Offset: 0x487 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 1150 + - Offset: 0x490 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 865 + - Offset: 0x495 + Symbol: '.text._ZN4core3ptr85drop_in_place$LT$std..rt..lang_start$LT$$LP$$RP$$GT$..$u7b$$u7b$closure$u7d$$u7d$$GT$17h623f70e9f0cf3066E' + Type: R_X86_64_64 + - Offset: 0x4A3 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 1440 + - Offset: 0x4A7 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 1560 + - Offset: 0x4BE + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 769 + - Offset: 0x4C6 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 937 + - Offset: 0x4D1 + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 1764 + - Offset: 0x4DE + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 1754 + - Offset: 0x4EB + Symbol: .debug_str + Type: R_X86_64_32 + Addend: 1798 + - Name: .rela.debug_aranges + Type: SHT_RELA + Flags: [ SHF_INFO_LINK ] + Link: .symtab + AddressAlign: 0x8 + Info: .debug_aranges + Relocations: + - Offset: 0x6 + Symbol: .debug_info + Type: R_X86_64_32 + - Offset: 0x10 + Symbol: .data.rel.ro..Lanon.4a406b9d9c1243847b49ddffb2385826.0 + Type: R_X86_64_64 + - Offset: 0x20 + Symbol: .rodata._ZN4main13ENUM_INSTANCE17hc6db515181ea378fE + Type: R_X86_64_64 + - Offset: 0x30 + Symbol: .text._ZN3std2rt10lang_start17hcb2a6a78164896cfE + Type: R_X86_64_64 + - Offset: 0x40 + Symbol: '.text._ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17hda4568952b4f838eE' + Type: R_X86_64_64 + - Offset: 0x50 + Symbol: .text._ZN3std3sys9backtrace28__rust_begin_short_backtrace17h4c89f1d03cb386ebE + Type: R_X86_64_64 + - Offset: 0x60 + Symbol: '.text._ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17h356d5dadf319082fE' + Type: R_X86_64_64 + - Offset: 0x70 + Symbol: .text._ZN4core3ops8function6FnOnce9call_once17h21112f971fb5dae8E + Type: R_X86_64_64 + - Offset: 0x80 + Symbol: .text._ZN4core3ops8function6FnOnce9call_once17hf55c273de81f71dfE + Type: R_X86_64_64 + - Offset: 0x90 + Symbol: '.text._ZN4core3ptr85drop_in_place$LT$std..rt..lang_start$LT$$LP$$RP$$GT$..$u7b$$u7b$closure$u7d$$u7d$$GT$17h623f70e9f0cf3066E' + Type: R_X86_64_64 + - Offset: 0xA0 + Symbol: '.text._ZN54_$LT$$LP$$RP$$u20$as$u20$std..process..Termination$GT$6report17h10aa5c9c6135be01E' + Type: R_X86_64_64 + - Offset: 0xB0 + Symbol: .text._ZN4main4main17h5659c6c02e5cd445E + Type: R_X86_64_64 + - Name: .rela.debug_ranges + Type: SHT_RELA + Flags: [ SHF_INFO_LINK ] + Link: .symtab + AddressAlign: 0x8 + Info: .debug_ranges + Relocations: + - Symbol: .text._ZN3std2rt10lang_start17hcb2a6a78164896cfE + Type: R_X86_64_64 + - Offset: 0x8 + Symbol: .text._ZN3std2rt10lang_start17hcb2a6a78164896cfE + Type: R_X86_64_64 + Addend: 60 + - Offset: 0x10 + Symbol: '.text._ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17hda4568952b4f838eE' + Type: R_X86_64_64 + - Offset: 0x18 + Symbol: '.text._ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17hda4568952b4f838eE' + Type: R_X86_64_64 + Addend: 34 + - Offset: 0x20 + Symbol: .text._ZN3std3sys9backtrace28__rust_begin_short_backtrace17h4c89f1d03cb386ebE + Type: R_X86_64_64 + - Offset: 0x28 + Symbol: .text._ZN3std3sys9backtrace28__rust_begin_short_backtrace17h4c89f1d03cb386ebE + Type: R_X86_64_64 + Addend: 19 + - Offset: 0x30 + Symbol: '.text._ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17h356d5dadf319082fE' + Type: R_X86_64_64 + - Offset: 0x38 + Symbol: '.text._ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17h356d5dadf319082fE' + Type: R_X86_64_64 + Addend: 22 + - Offset: 0x40 + Symbol: .text._ZN4core3ops8function6FnOnce9call_once17h21112f971fb5dae8E + Type: R_X86_64_64 + - Offset: 0x48 + Symbol: .text._ZN4core3ops8function6FnOnce9call_once17h21112f971fb5dae8E + Type: R_X86_64_64 + Addend: 24 + - Offset: 0x50 + Symbol: .text._ZN4core3ops8function6FnOnce9call_once17hf55c273de81f71dfE + Type: R_X86_64_64 + - Offset: 0x58 + Symbol: .text._ZN4core3ops8function6FnOnce9call_once17hf55c273de81f71dfE + Type: R_X86_64_64 + Addend: 16 + - Offset: 0x60 + Symbol: '.text._ZN4core3ptr85drop_in_place$LT$std..rt..lang_start$LT$$LP$$RP$$GT$..$u7b$$u7b$closure$u7d$$u7d$$GT$17h623f70e9f0cf3066E' + Type: R_X86_64_64 + - Offset: 0x68 + Symbol: '.text._ZN4core3ptr85drop_in_place$LT$std..rt..lang_start$LT$$LP$$RP$$GT$..$u7b$$u7b$closure$u7d$$u7d$$GT$17h623f70e9f0cf3066E' + Type: R_X86_64_64 + Addend: 6 + - Offset: 0x70 + Symbol: '.text._ZN54_$LT$$LP$$RP$$u20$as$u20$std..process..Termination$GT$6report17h10aa5c9c6135be01E' + Type: R_X86_64_64 + - Offset: 0x78 + Symbol: '.text._ZN54_$LT$$LP$$RP$$u20$as$u20$std..process..Termination$GT$6report17h10aa5c9c6135be01E' + Type: R_X86_64_64 + Addend: 3 + - Offset: 0x80 + Symbol: .text._ZN4main4main17h5659c6c02e5cd445E + Type: R_X86_64_64 + - Offset: 0x88 + Symbol: .text._ZN4main4main17h5659c6c02e5cd445E + Type: R_X86_64_64 + Addend: 1 + - Name: .rela.eh_frame + Type: SHT_RELA + Flags: [ SHF_INFO_LINK ] + Link: .symtab + AddressAlign: 0x8 + Info: .eh_frame + Relocations: + - Offset: 0x20 + Symbol: .text._ZN3std2rt10lang_start17hcb2a6a78164896cfE + Type: R_X86_64_PC32 + - Offset: 0x38 + Symbol: '.text._ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17hda4568952b4f838eE' + Type: R_X86_64_PC32 + - Offset: 0x50 + Symbol: .text._ZN3std3sys9backtrace28__rust_begin_short_backtrace17h4c89f1d03cb386ebE + Type: R_X86_64_PC32 + - Offset: 0x68 + Symbol: '.text._ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17h356d5dadf319082fE' + Type: R_X86_64_PC32 + - Offset: 0x80 + Symbol: .text._ZN4core3ops8function6FnOnce9call_once17h21112f971fb5dae8E + Type: R_X86_64_PC32 + - Offset: 0x98 + Symbol: .text._ZN4core3ops8function6FnOnce9call_once17hf55c273de81f71dfE + Type: R_X86_64_PC32 + - Offset: 0xB0 + Symbol: '.text._ZN4core3ptr85drop_in_place$LT$std..rt..lang_start$LT$$LP$$RP$$GT$..$u7b$$u7b$closure$u7d$$u7d$$GT$17h623f70e9f0cf3066E' + Type: R_X86_64_PC32 + - Offset: 0xC4 + Symbol: '.text._ZN54_$LT$$LP$$RP$$u20$as$u20$std..process..Termination$GT$6report17h10aa5c9c6135be01E' + Type: R_X86_64_PC32 + - Offset: 0xD8 + Symbol: .text._ZN4main4main17h5659c6c02e5cd445E + Type: R_X86_64_PC32 + - Offset: 0xEC + Symbol: .text.main + Type: R_X86_64_PC32 + - Name: .rela.debug_line + Type: SHT_RELA + Flags: [ SHF_INFO_LINK ] + Link: .symtab + AddressAlign: 0x8 + Info: .debug_line + Relocations: + - Offset: 0x2F8 + Symbol: .text._ZN3std2rt10lang_start17hcb2a6a78164896cfE + Type: R_X86_64_64 + - Offset: 0x31B + Symbol: '.text._ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17hda4568952b4f838eE' + Type: R_X86_64_64 + - Offset: 0x34B + Symbol: .text._ZN3std3sys9backtrace28__rust_begin_short_backtrace17h4c89f1d03cb386ebE + Type: R_X86_64_64 + - Offset: 0x376 + Symbol: '.text._ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17h356d5dadf319082fE' + Type: R_X86_64_64 + - Offset: 0x393 + Symbol: .text._ZN4core3ops8function6FnOnce9call_once17h21112f971fb5dae8E + Type: R_X86_64_64 + - Offset: 0x3B0 + Symbol: .text._ZN4core3ops8function6FnOnce9call_once17hf55c273de81f71dfE + Type: R_X86_64_64 + - Offset: 0x3CD + Symbol: '.text._ZN4core3ptr85drop_in_place$LT$std..rt..lang_start$LT$$LP$$RP$$GT$..$u7b$$u7b$closure$u7d$$u7d$$GT$17h623f70e9f0cf3066E' + Type: R_X86_64_64 + - Offset: 0x3EA + Symbol: '.text._ZN54_$LT$$LP$$RP$$u20$as$u20$std..process..Termination$GT$6report17h10aa5c9c6135be01E' + Type: R_X86_64_64 + - Offset: 0x401 + Symbol: .text._ZN4main4main17h5659c6c02e5cd445E + Type: R_X86_64_64 + - Type: SectionHeaderTable + Sections: + - Name: .strtab + - Name: .text + - Name: .text._ZN3std2rt10lang_start17hcb2a6a78164896cfE + - Name: .rela.text._ZN3std2rt10lang_start17hcb2a6a78164896cfE + - Name: '.text._ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17hda4568952b4f838eE' + - Name: '.rela.text._ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17hda4568952b4f838eE' + - Name: .text._ZN3std3sys9backtrace28__rust_begin_short_backtrace17h4c89f1d03cb386ebE + - Name: .rela.text._ZN3std3sys9backtrace28__rust_begin_short_backtrace17h4c89f1d03cb386ebE + - Name: '.text._ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17h356d5dadf319082fE' + - Name: '.rela.text._ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17h356d5dadf319082fE' + - Name: .text._ZN4core3ops8function6FnOnce9call_once17h21112f971fb5dae8E + - Name: .rela.text._ZN4core3ops8function6FnOnce9call_once17h21112f971fb5dae8E + - Name: .text._ZN4core3ops8function6FnOnce9call_once17hf55c273de81f71dfE + - Name: '.text._ZN4core3ptr85drop_in_place$LT$std..rt..lang_start$LT$$LP$$RP$$GT$..$u7b$$u7b$closure$u7d$$u7d$$GT$17h623f70e9f0cf3066E' + - Name: '.text._ZN54_$LT$$LP$$RP$$u20$as$u20$std..process..Termination$GT$6report17h10aa5c9c6135be01E' + - Name: .text._ZN4main4main17h5659c6c02e5cd445E + - Name: .text.main + - Name: .rela.text.main + - Name: .data.rel.ro..Lanon.4a406b9d9c1243847b49ddffb2385826.0 + - Name: .rela.data.rel.ro..Lanon.4a406b9d9c1243847b49ddffb2385826.0 + - Name: .rodata._ZN4main13ENUM_INSTANCE17hc6db515181ea378fE + - Name: .debug_gdb_scripts + - Name: .debug_abbrev + - Name: .debug_info + - Name: .rela.debug_info + - Name: .debug_aranges + - Name: .rela.debug_aranges + - Name: .debug_ranges + - Name: .rela.debug_ranges + - Name: .debug_str + - Name: .comment + - Name: .note.GNU-stack + - Name: .eh_frame + - Name: .rela.eh_frame + - Name: .debug_line + - Name: .rela.debug_line + - Name: .symtab +Symbols: + - Name: main.5f6bf0c8e9d0afce-cgu.0 + Type: STT_FILE + Index: SHN_ABS + - Name: .text._ZN3std2rt10lang_start17hcb2a6a78164896cfE + Type: STT_SECTION + Section: .text._ZN3std2rt10lang_start17hcb2a6a78164896cfE + - Name: '.text._ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17hda4568952b4f838eE' + Type: STT_SECTION + Section: '.text._ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17hda4568952b4f838eE' + - Name: '_ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17hda4568952b4f838eE' + Type: STT_FUNC + Section: '.text._ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17hda4568952b4f838eE' + Size: 0x22 + - Name: _ZN3std3sys9backtrace28__rust_begin_short_backtrace17h4c89f1d03cb386ebE + Type: STT_FUNC + Section: .text._ZN3std3sys9backtrace28__rust_begin_short_backtrace17h4c89f1d03cb386ebE + Size: 0x13 + - Name: '_ZN54_$LT$$LP$$RP$$u20$as$u20$std..process..Termination$GT$6report17h10aa5c9c6135be01E' + Type: STT_FUNC + Section: '.text._ZN54_$LT$$LP$$RP$$u20$as$u20$std..process..Termination$GT$6report17h10aa5c9c6135be01E' + Size: 0x3 + - Name: .text._ZN3std3sys9backtrace28__rust_begin_short_backtrace17h4c89f1d03cb386ebE + Type: STT_SECTION + Section: .text._ZN3std3sys9backtrace28__rust_begin_short_backtrace17h4c89f1d03cb386ebE + - Name: _ZN4core3ops8function6FnOnce9call_once17hf55c273de81f71dfE + Type: STT_FUNC + Section: .text._ZN4core3ops8function6FnOnce9call_once17hf55c273de81f71dfE + Size: 0x10 + - Name: '.text._ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17h356d5dadf319082fE' + Type: STT_SECTION + Section: '.text._ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17h356d5dadf319082fE' + - Name: '_ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17h356d5dadf319082fE' + Type: STT_FUNC + Section: '.text._ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17h356d5dadf319082fE' + Size: 0x16 + - Name: _ZN4core3ops8function6FnOnce9call_once17h21112f971fb5dae8E + Type: STT_FUNC + Section: .text._ZN4core3ops8function6FnOnce9call_once17h21112f971fb5dae8E + Size: 0x18 + - Name: .text._ZN4core3ops8function6FnOnce9call_once17h21112f971fb5dae8E + Type: STT_SECTION + Section: .text._ZN4core3ops8function6FnOnce9call_once17h21112f971fb5dae8E + - Name: .text._ZN4core3ops8function6FnOnce9call_once17hf55c273de81f71dfE + Type: STT_SECTION + Section: .text._ZN4core3ops8function6FnOnce9call_once17hf55c273de81f71dfE + - Name: '.text._ZN4core3ptr85drop_in_place$LT$std..rt..lang_start$LT$$LP$$RP$$GT$..$u7b$$u7b$closure$u7d$$u7d$$GT$17h623f70e9f0cf3066E' + Type: STT_SECTION + Section: '.text._ZN4core3ptr85drop_in_place$LT$std..rt..lang_start$LT$$LP$$RP$$GT$..$u7b$$u7b$closure$u7d$$u7d$$GT$17h623f70e9f0cf3066E' + - Name: '_ZN4core3ptr85drop_in_place$LT$std..rt..lang_start$LT$$LP$$RP$$GT$..$u7b$$u7b$closure$u7d$$u7d$$GT$17h623f70e9f0cf3066E' + Type: STT_FUNC + Section: '.text._ZN4core3ptr85drop_in_place$LT$std..rt..lang_start$LT$$LP$$RP$$GT$..$u7b$$u7b$closure$u7d$$u7d$$GT$17h623f70e9f0cf3066E' + Size: 0x6 + - Name: '.text._ZN54_$LT$$LP$$RP$$u20$as$u20$std..process..Termination$GT$6report17h10aa5c9c6135be01E' + Type: STT_SECTION + Section: '.text._ZN54_$LT$$LP$$RP$$u20$as$u20$std..process..Termination$GT$6report17h10aa5c9c6135be01E' + - Name: .text._ZN4main4main17h5659c6c02e5cd445E + Type: STT_SECTION + Section: .text._ZN4main4main17h5659c6c02e5cd445E + - Name: _ZN4main4main17h5659c6c02e5cd445E + Type: STT_FUNC + Section: .text._ZN4main4main17h5659c6c02e5cd445E + Size: 0x1 + - Name: .text.main + Type: STT_SECTION + Section: .text.main + - Name: .data.rel.ro..Lanon.4a406b9d9c1243847b49ddffb2385826.0 + Type: STT_SECTION + Section: .data.rel.ro..Lanon.4a406b9d9c1243847b49ddffb2385826.0 + - Name: _ZN4main13ENUM_INSTANCE17hc6db515181ea378fE + Type: STT_OBJECT + Section: .rodata._ZN4main13ENUM_INSTANCE17hc6db515181ea378fE + Size: 0x1 + - Name: .rodata._ZN4main13ENUM_INSTANCE17hc6db515181ea378fE + Type: STT_SECTION + Section: .rodata._ZN4main13ENUM_INSTANCE17hc6db515181ea378fE + - Name: .debug_abbrev + Type: STT_SECTION + Section: .debug_abbrev + - Name: .debug_info + Type: STT_SECTION + Section: .debug_info + - Name: .debug_ranges + Type: STT_SECTION + Section: .debug_ranges + - Name: .debug_str + Type: STT_SECTION + Section: .debug_str + - Name: .debug_line + Type: STT_SECTION + Section: .debug_line + - Name: _ZN3std2rt10lang_start17hcb2a6a78164896cfE + Type: STT_FUNC + Section: .text._ZN3std2rt10lang_start17hcb2a6a78164896cfE + Binding: STB_GLOBAL + Size: 0x3C + Other: [ STV_HIDDEN ] + - Name: _ZN3std2rt19lang_start_internal17h418648f91f5be3a1E + Binding: STB_GLOBAL + - Name: main + Type: STT_FUNC + Section: .text.main + Binding: STB_GLOBAL + Size: 0x20 + - Name: __rustc_debug_gdb_scripts_section__ + Type: STT_OBJECT + Section: .debug_gdb_scripts + Binding: STB_WEAK + Size: 0x22 +DWARF: + debug_str: + - 'clang LLVM (rustc version 1.87.0 (17067e9ac 2025-05-09))' + - 'main.rs/@/main.5f6bf0c8e9d0afce-cgu.0' + - '/home/kiva/upstream/llvm-upstream/lldb/test/API/lang/rust/enum-variant-same-name' + - '<std::rt::lang_start::{closure_env#0}<()> as core::ops::function::Fn<()>>::{vtable}' + - drop_in_place + - '()' + - '*const ()' + - size + - usize + - align + - __method3 + - __method4 + - __method5 + - std + - rt + - lang_start + - main + - 'fn()' + - '{closure_env#0}<()>' + - '<std::rt::lang_start::{closure_env#0}<()> as core::ops::function::Fn<()>>::{vtable_type}' + - ENUM_INSTANCE + - A + - __0 + - B + - u8 + - _ZN4main13ENUM_INSTANCE17hc6db515181ea378fE + - sys + - process + - unix + - common + - ExitCode + - _ZN3std3sys7process4unix6common8ExitCode6as_i3217h7893c1c2ab39ed8fE + - as_i32 + - i32 + - '&std::sys::process::unix::common::ExitCode' + - _ZN3std7process8ExitCode6to_i3217habce61080992bc43E + - to_i32 + - self + - core + - hint + - T + - _ZN4core4hint9black_box17h29a7c52ab9efa45eE + - 'black_box<()>' + - dummy + - backtrace + - ops + - function + - FnOnce + - ptr + - '{impl#59}' + - _ZN3std2rt10lang_start17hcb2a6a78164896cfE + - 'lang_start<()>' + - isize + - '_ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17hda4568952b4f838eE' + - '{closure#0}<()>' + - F + - _ZN3std3sys9backtrace28__rust_begin_short_backtrace17h4c89f1d03cb386ebE + - '__rust_begin_short_backtrace<fn(), ()>' + - Self + - Args + - '_ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17h356d5dadf319082fE' + - 'call_once<std::rt::lang_start::{closure_env#0}<()>, ()>' + - _ZN4core3ops8function6FnOnce9call_once17h21112f971fb5dae8E + - _ZN4core3ops8function6FnOnce9call_once17hf55c273de81f71dfE + - 'call_once<fn(), ()>' + - '_ZN4core3ptr85drop_in_place$LT$std..rt..lang_start$LT$$LP$$RP$$GT$..$u7b$$u7b$closure$u7d$$u7d$$GT$17h623f70e9f0cf3066E' + - 'drop_in_place<std::rt::lang_start::{closure_env#0}<()>>' + - '_ZN54_$LT$$LP$$RP$$u20$as$u20$std..process..Termination$GT$6report17h10aa5c9c6135be01E' + - report + - _ZN4main4main17h5659c6c02e5cd445E + - argc + - argv + - '*const u8' + - '*const *const u8' + - sigpipe + - result + - f + - '*mut std::rt::lang_start::{closure_env#0}<()>' + debug_aranges: + - Length: 0xCC + Version: 2 + CuOffset: 0x0 + AddressSize: 0x8 + Descriptors: + - Address: 0x0 + Length: 0x30 + - Address: 0x0 + Length: 0x1 + - Address: 0x0 + Length: 0x3C + - Address: 0x0 + Length: 0x22 + - Address: 0x0 + Length: 0x13 + - Address: 0x0 + Length: 0x16 + - Address: 0x0 + Length: 0x18 + - Address: 0x0 + Length: 0x10 + - Address: 0x0 + Length: 0x6 + - Address: 0x0 + Length: 0x3 + - Address: 0x0 + Length: 0x1 + debug_ranges: + - Offset: 0x0 + AddrSize: 0x8 + Entries: [] + - Offset: 0x10 + AddrSize: 0x8 + Entries: [] + - Offset: 0x20 + AddrSize: 0x8 + Entries: [] + - Offset: 0x30 + AddrSize: 0x8 + Entries: [] + - Offset: 0x40 + AddrSize: 0x8 + Entries: [] + - Offset: 0x50 + AddrSize: 0x8 + Entries: [] + - Offset: 0x60 + AddrSize: 0x8 + Entries: [] + - Offset: 0x70 + AddrSize: 0x8 + Entries: [] + - Offset: 0x80 + AddrSize: 0x8 + Entries: [] + - Offset: 0x90 + AddrSize: 0x8 + Entries: [] +... diff --git a/lldb/test/API/macosx/find-dsym/bundle-with-dot-in-filename/Makefile b/lldb/test/API/macosx/find-dsym/bundle-with-dot-in-filename/Makefile index 12781fd..f135840 100644 --- a/lldb/test/API/macosx/find-dsym/bundle-with-dot-in-filename/Makefile +++ b/lldb/test/API/macosx/find-dsym/bundle-with-dot-in-filename/Makefile @@ -5,7 +5,7 @@ all: clean $(EXE) include Makefile.rules $(EXE): - $(CC) $(CFLAGS) -dynamiclib -o com.apple.sbd $(SRCDIR)/bundle.c + $(CC) $(ASAN_LDFLAGS) $(CFLAGS) -dynamiclib -o com.apple.sbd $(SRCDIR)/bundle.c mkdir com.apple.sbd.xpc mv com.apple.sbd com.apple.sbd.xpc/ mkdir -p com.apple.sbd.xpc.dSYM/Contents/Resources/DWARF @@ -13,7 +13,7 @@ $(EXE): rm -rf com.apple.sbd.dSYM mkdir hide.app tar cf - com.apple.sbd.xpc com.apple.sbd.xpc.dSYM | ( cd hide.app;tar xBpf -) - $(CC) $(CFLAGS) -o find-bundle-with-dots-in-fn $(SRCDIR)/main.c + $(CC) $(ASAN_LDFLAGS) $(CFLAGS) -o find-bundle-with-dots-in-fn $(SRCDIR)/main.c clean:: rm -rf a.out a.out.dSYM hide.app com.apple.sbd com.apple.sbd.dSYM com.apple.sbd.xpc com.apple.sbd.xpc.dSYM find-bundle-with-dots-in-fn find-bundle-with-dots-in-fn.dSYM diff --git a/lldb/test/API/macosx/find-dsym/deep-bundle/Makefile b/lldb/test/API/macosx/find-dsym/deep-bundle/Makefile index 806c840..c041d9e 100644 --- a/lldb/test/API/macosx/find-dsym/deep-bundle/Makefile +++ b/lldb/test/API/macosx/find-dsym/deep-bundle/Makefile @@ -4,7 +4,7 @@ all: clean $(EXE) include Makefile.rules $(EXE): - $(CC) $(CFLAGS) -install_name $(shell pwd)/MyFramework.framework/Versions/A/MyFramework -dynamiclib -o MyFramework $(SRCDIR)/myframework.c + $(CC) $(ASAN_LDFLAGS) $(CFLAGS) -install_name $(shell pwd)/MyFramework.framework/Versions/A/MyFramework -dynamiclib -o MyFramework $(SRCDIR)/myframework.c mkdir -p MyFramework.framework/Versions/A/Headers mkdir -p MyFramework.framework/Versions/A/Resources cp MyFramework MyFramework.framework/Versions/A @@ -18,7 +18,7 @@ $(EXE): mkdir hide.app rm -f MyFramework tar cf - MyFramework.framework MyFramework.framework.dSYM | ( cd hide.app;tar xBpf -) - $(CC) $(CFLAGS) -o deep-bundle $(SRCDIR)/main.c -F. -framework MyFramework + $(CC) $(ASAN_LDFLAGS) $(CFLAGS) -o deep-bundle $(SRCDIR)/main.c -F. -framework MyFramework clean:: rm -rf a.out a.out.dSYM deep-bundle deep-bundle.dSYM MyFramework.framework MyFramework.framework.dSYM MyFramework MyFramework.dSYM hide.app diff --git a/lldb/test/API/macosx/posix_spawn/Makefile b/lldb/test/API/macosx/posix_spawn/Makefile index 7ae46ca..cbdee91 100644 --- a/lldb/test/API/macosx/posix_spawn/Makefile +++ b/lldb/test/API/macosx/posix_spawn/Makefile @@ -6,13 +6,13 @@ include Makefile.rules all: fat.out x86_64.out: x86_64.c - $(CC) -isysroot $(SDKROOT) -target x86_64-apple-macosx10.9 -o x86_64.out $< + $(CC) $(ASAN_LDFLAGS) -isysroot $(SDKROOT) -target x86_64-apple-macosx10.9 -o x86_64.out $< x86_64h.out: x86_64h.c - $(CC) -isysroot $(SDKROOT) -target x86_64h-apple-macosx10.9 -o x86_64h.out $< + $(CC) $(ASAN_LDFLAGS) -isysroot $(SDKROOT) -target x86_64h-apple-macosx10.9 -o x86_64h.out $< arm64.out: arm64.c - $(CC) -isysroot $(SDKROOT) -target arm64-apple-macosx10.9 -o arm64.out $< + $(CC) $(ASAN_LDFLAGS) -isysroot $(SDKROOT) -target arm64-apple-macosx10.9 -o arm64.out $< fat.out: x86_64.out x86_64h.out arm64.out $(LIPO) -o fat.out -create $^ diff --git a/lldb/test/API/python_api/command_script_output/TestCommandScriptOutput.py b/lldb/test/API/python_api/command_script_output/TestCommandScriptOutput.py new file mode 100644 index 0000000..abe0eec --- /dev/null +++ b/lldb/test/API/python_api/command_script_output/TestCommandScriptOutput.py @@ -0,0 +1,47 @@ +""" +Test that HandleCommand captures stdout and stderr from script commands. +""" + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * + + +class CommandScriptOutputTestCase(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def test_script_command_stdout_stderr(self): + """Test that HandleCommand captures stdout and stderr from script commands.""" + ci = self.dbg.GetCommandInterpreter() + self.assertTrue(ci, VALID_COMMAND_INTERPRETER) + + res = lldb.SBCommandReturnObject() + + # Execute a script command that writes to stdout. + ci.HandleCommand("script print('Hello stdout')", res) + self.assertTrue(res.Succeeded()) + self.assertIn("Hello stdout", res.GetOutput()) + + # Execute a script command that writes to stderr. + ci.HandleCommand("script import sys; sys.stderr.write('Hello stderr\\n')", res) + self.assertTrue(res.Succeeded()) + self.assertIn("Hello stderr", res.GetOutput()) + + # Execute a script command that writes to both stdout and stderr. + ci.HandleCommand( + "script import sys; print('Output line'); sys.stderr.write('Error line\\n')", + res, + ) + self.assertTrue(res.Succeeded()) + self.assertIn("Output line", res.GetOutput()) + self.assertIn("Error line", res.GetOutput()) + + # Test that multiple print statements are captured. + ci.HandleCommand( + "script print('Line 1'); print('Line 2'); print('Line 3')", res + ) + self.assertTrue(res.Succeeded()) + output = res.GetOutput() + self.assertIn("Line 1", output) + self.assertIn("Line 2", output) + self.assertIn("Line 3", output) diff --git a/lldb/test/API/python_api/exprpath_register/Makefile b/lldb/test/API/python_api/exprpath_register/Makefile new file mode 100644 index 0000000..1049594 --- /dev/null +++ b/lldb/test/API/python_api/exprpath_register/Makefile @@ -0,0 +1,3 @@ +C_SOURCES := main.c + +include Makefile.rules diff --git a/lldb/test/API/python_api/exprpath_register/TestExprPathRegisters.py b/lldb/test/API/python_api/exprpath_register/TestExprPathRegisters.py new file mode 100644 index 0000000..4ffbc5e --- /dev/null +++ b/lldb/test/API/python_api/exprpath_register/TestExprPathRegisters.py @@ -0,0 +1,64 @@ +""" +Test Getting the expression path for registers works correctly +""" + +import lldb +from lldbsuite.test import lldbutil +from lldbsuite.test.lldbtest import TestBase, VALID_BREAKPOINT, VALID_TARGET + + +class TestExprPathRegisters(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def verify_register_path(self, reg_value: lldb.SBValue): + stream = lldb.SBStream() + reg_name = reg_value.name + self.assertTrue( + reg_value.GetExpressionPath(stream), + f"Expected an expression path for register {reg_name}.", + ) + reg_expr_path = stream.GetData() + self.assertEqual(reg_expr_path, f"${reg_name}") + + def test_float_registers(self): + """Verify the expression path of the registers is valid.""" + self.build() + _, _, thread, _ = lldbutil.run_to_name_breakpoint(self, "my_foo") + frame = thread.GetSelectedFrame() + self.assertTrue(frame, "Expected a valid Frame.") + + # possible floating point register on some cpus. + register_names = [ + "xmm0", + "ymm0", + "v0", + "v1", + "f0", + "f1", + "d0", + "d1", + "vr0", + "vr1", + "st0", + "st1", + ] + for name in register_names: + reg_value = frame.FindRegister(name) + # some the register will not be available for the cpu + # only verify if it is valid. + if reg_value: + self.verify_register_path(reg_value) + + def test_all_registers(self): + """Test all the registers that is avaiable on the machine""" + self.build() + _, _, thread, _ = lldbutil.run_to_name_breakpoint(self, "my_foo") + frame = thread.GetSelectedFrame() + self.assertTrue(frame, "Expected a valid Frame.") + + register_sets = frame.GetRegisters() + self.assertTrue(register_sets.IsValid(), "Expected Frame Registers") + + for register_set in register_sets: + for register in register_set.children: + self.verify_register_path(register) diff --git a/lldb/test/API/python_api/exprpath_register/main.c b/lldb/test/API/python_api/exprpath_register/main.c new file mode 100644 index 0000000..4809a87cd --- /dev/null +++ b/lldb/test/API/python_api/exprpath_register/main.c @@ -0,0 +1,10 @@ + +float my_foo() { + float result = 10.0 + 20.0; + return result; +} + +int main(void) { + float result = my_foo(); + return (int)result; +} diff --git a/lldb/test/API/python_api/frame_list/Makefile b/lldb/test/API/python_api/frame_list/Makefile new file mode 100644 index 0000000..99998b2 --- /dev/null +++ b/lldb/test/API/python_api/frame_list/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/python_api/frame_list/TestSBFrameList.py b/lldb/test/API/python_api/frame_list/TestSBFrameList.py new file mode 100644 index 0000000..f348ce4 --- /dev/null +++ b/lldb/test/API/python_api/frame_list/TestSBFrameList.py @@ -0,0 +1,194 @@ +""" +Test SBFrameList API. +""" + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class FrameListAPITestCase(TestBase): + def test_frame_list_api(self): + """Test SBThread.GetFrames() returns a valid SBFrameList.""" + self.build() + self.frame_list_api() + + def test_frame_list_iterator(self): + """Test SBFrameList iterator functionality.""" + self.build() + self.frame_list_iterator() + + def test_frame_list_indexing(self): + """Test SBFrameList indexing and length.""" + self.build() + self.frame_list_indexing() + + def test_frame_list_get_thread(self): + """Test SBFrameList.GetThread() returns correct thread.""" + self.build() + self.frame_list_get_thread() + + def setUp(self): + TestBase.setUp(self) + self.main_source = "main.cpp" + + def frame_list_api(self): + """Test SBThread.GetFrames() returns a valid SBFrameList.""" + exe = self.getBuildArtifact("a.out") + + target, process, thread, bkpt = lldbutil.run_to_source_breakpoint( + self, "Set break point at this line", lldb.SBFileSpec(self.main_source) + ) + + self.assertTrue( + thread.IsValid(), "There should be a thread stopped due to breakpoint" + ) + + # Test GetFrames() returns a valid SBFrameList + frame_list = thread.GetFrames() + self.assertTrue(frame_list.IsValid(), "Frame list should be valid") + self.assertGreater( + frame_list.GetSize(), 0, "Frame list should have at least one frame" + ) + + # Verify frame list size matches thread frame count + self.assertEqual( + frame_list.GetSize(), + thread.GetNumFrames(), + "Frame list size should match thread frame count", + ) + + # Verify frames are the same + for i in range(frame_list.GetSize()): + frame_from_list = frame_list.GetFrameAtIndex(i) + frame_from_thread = thread.GetFrameAtIndex(i) + self.assertTrue( + frame_from_list.IsValid(), f"Frame {i} from list should be valid" + ) + self.assertEqual( + frame_from_list.GetPC(), + frame_from_thread.GetPC(), + f"Frame {i} PC should match", + ) + + def frame_list_iterator(self): + """Test SBFrameList iterator functionality.""" + exe = self.getBuildArtifact("a.out") + + target, process, thread, bkpt = lldbutil.run_to_source_breakpoint( + self, "Set break point at this line", lldb.SBFileSpec(self.main_source) + ) + + self.assertTrue( + thread.IsValid(), "There should be a thread stopped due to breakpoint" + ) + + frame_list = thread.GetFrames() + + # Test iteration + frame_count = 0 + for frame in frame_list: + self.assertTrue(frame.IsValid(), "Each frame should be valid") + frame_count += 1 + + self.assertEqual( + frame_count, + frame_list.GetSize(), + "Iterator should visit all frames", + ) + + # Test that we can iterate multiple times + second_count = 0 + for frame in frame_list: + second_count += 1 + + self.assertEqual( + frame_count, second_count, "Should be able to iterate multiple times" + ) + + def frame_list_indexing(self): + """Test SBFrameList indexing and length.""" + exe = self.getBuildArtifact("a.out") + + target, process, thread, bkpt = lldbutil.run_to_source_breakpoint( + self, "Set break point at this line", lldb.SBFileSpec(self.main_source) + ) + + self.assertTrue( + thread.IsValid(), "There should be a thread stopped due to breakpoint" + ) + + frame_list = thread.GetFrames() + + # Test len() + self.assertEqual( + len(frame_list), frame_list.GetSize(), "len() should return frame count" + ) + + # Test positive indexing + first_frame = frame_list[0] + self.assertTrue(first_frame.IsValid(), "First frame should be valid") + self.assertEqual( + first_frame.GetPC(), + thread.GetFrameAtIndex(0).GetPC(), + "Indexed frame should match", + ) + + # Test negative indexing + if len(frame_list) > 0: + last_frame = frame_list[-1] + self.assertTrue(last_frame.IsValid(), "Last frame should be valid") + self.assertEqual( + last_frame.GetPC(), + thread.GetFrameAtIndex(len(frame_list) - 1).GetPC(), + "Negative indexing should work", + ) + + # Test out of bounds returns None + out_of_bounds = frame_list[10000] + self.assertIsNone(out_of_bounds, "Out of bounds index should return None") + + # Test bool conversion + self.assertTrue(bool(frame_list), "Non-empty frame list should be truthy") + + # Test Clear() + frame_list.Clear() + # Note: Clear() clears the underlying StackFrameList cache, + # but the frame list object itself should still be valid + self.assertTrue( + frame_list.IsValid(), "Frame list should still be valid after Clear()" + ) + + def frame_list_get_thread(self): + """Test SBFrameList.GetThread() returns correct thread.""" + exe = self.getBuildArtifact("a.out") + + target, process, thread, bkpt = lldbutil.run_to_source_breakpoint( + self, "Set break point at this line", lldb.SBFileSpec(self.main_source) + ) + + self.assertTrue( + thread.IsValid(), "There should be a thread stopped due to breakpoint" + ) + + frame_list = thread.GetFrames() + self.assertTrue(frame_list.IsValid(), "Frame list should be valid") + + # Test GetThread() returns the correct thread + thread_from_list = frame_list.GetThread() + self.assertTrue( + thread_from_list.IsValid(), "Thread from frame list should be valid" + ) + self.assertEqual( + thread_from_list.GetThreadID(), + thread.GetThreadID(), + "Frame list should return the correct thread", + ) + + # Verify it's the same thread object + self.assertEqual( + thread_from_list.GetProcess().GetProcessID(), + thread.GetProcess().GetProcessID(), + "Thread should belong to same process", + ) diff --git a/lldb/test/API/python_api/frame_list/main.cpp b/lldb/test/API/python_api/frame_list/main.cpp new file mode 100644 index 0000000..e399446 --- /dev/null +++ b/lldb/test/API/python_api/frame_list/main.cpp @@ -0,0 +1,22 @@ +#include <stdio.h> + +int c(int val) { + // Set break point at this line + return val + 3; +} + +int b(int val) { + int result = c(val); + return result; +} + +int a(int val) { + int result = b(val); + return result; +} + +int main() { + int result = a(1); + printf("Result: %d\n", result); + return 0; +} diff --git a/lldb/test/API/python_api/sblineentry/TestSBLineEntry.py b/lldb/test/API/python_api/sblineentry/TestSBLineEntry.py new file mode 100644 index 0000000..ee8404c --- /dev/null +++ b/lldb/test/API/python_api/sblineentry/TestSBLineEntry.py @@ -0,0 +1,144 @@ +""" +Test SBLineEntry APIs, particularly synthetic line entries. +""" + +import os +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class SBLineEntryTestCase(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def test_synthetic_line_entry(self): + """Test that synthetic LineEntry objects (created via SBAPI) can be + valid without a valid address range and can be set in SBSymbolContext.""" + + # Test creating a synthetic line entry via SBAPI. + line_entry = lldb.SBLineEntry() + self.assertFalse( + line_entry.IsValid(), "Default constructed line entry should be invalid" + ) + + # Set line number - this should mark the line entry as synthetic. + line_entry.SetLine(42) + self.assertTrue( + line_entry.IsValid(), + "Line entry should be valid after setting line, even without address", + ) + self.assertEqual(line_entry.GetLine(), 42) + + # Set file and column. + file_spec = lldb.SBFileSpec(os.path.join(self.getSourceDir(), "test.cpp"), True) + line_entry.SetFileSpec(file_spec) + line_entry.SetColumn(10) + + self.assertEqual(line_entry.GetColumn(), 10) + self.assertEqual(line_entry.GetFileSpec().GetFilename(), "test.cpp") + + # Verify address range is still invalid (synthetic). + start_addr = line_entry.GetStartAddress() + self.assertFalse( + start_addr.IsValid(), "Synthetic line entry should not have valid address" + ) + + # Test setting synthetic line entry in symbol context. + sym_ctx = lldb.SBSymbolContext() + sym_ctx.SetLineEntry(line_entry) + + retrieved_line_entry = sym_ctx.GetLineEntry() + self.assertTrue( + retrieved_line_entry.IsValid(), "Retrieved line entry should be valid" + ) + self.assertEqual(retrieved_line_entry.GetLine(), 42) + self.assertEqual(retrieved_line_entry.GetColumn(), 10) + self.assertEqual(retrieved_line_entry.GetFileSpec().GetFilename(), "test.cpp") + + def test_line_entry_validity_without_address(self): + """Test that line entries created via SBAPI are valid without addresses.""" + + line_entry = lldb.SBLineEntry() + + # Initially invalid. + self.assertFalse(line_entry.IsValid()) + + # Still invalid with just a file spec. + file_spec = lldb.SBFileSpec("foo.cpp", True) + line_entry.SetFileSpec(file_spec) + self.assertFalse( + line_entry.IsValid(), "Line entry should be invalid without line number" + ) + + # Valid once line number is set (marks as synthetic). + line_entry.SetLine(100) + self.assertTrue( + line_entry.IsValid(), "Line entry should be valid with line number set" + ) + + # Verify no valid address range. + self.assertFalse(line_entry.GetStartAddress().IsValid()) + self.assertFalse(line_entry.GetEndAddress().IsValid()) + + def test_line_entry_column(self): + """Test setting and getting column information on synthetic line entries.""" + + line_entry = lldb.SBLineEntry() + line_entry.SetLine(50) + + # Default column should be 0. + self.assertEqual(line_entry.GetColumn(), 0) + + # Set column. + line_entry.SetColumn(25) + self.assertEqual(line_entry.GetColumn(), 25) + + # Verify line entry is still valid. + self.assertTrue(line_entry.IsValid()) + + def test_non_synthetic_line_entry_requires_line_number(self): + """Test that non-synthetic line entries with addresses still require a line number to be valid.""" + + # A line entry is always invalid without a line number, regardless of whether it has an address. + line_entry = lldb.SBLineEntry() + self.assertFalse( + line_entry.IsValid(), "Line entry should be invalid without line number" + ) + + # Even with a file spec, it's still invalid. + file_spec = lldb.SBFileSpec("test.cpp", True) + line_entry.SetFileSpec(file_spec) + self.assertFalse( + line_entry.IsValid(), "Line entry should be invalid without line number" + ) + + # Only after setting a line number does it become valid. + line_entry.SetLine(42) + self.assertTrue( + line_entry.IsValid(), "Line entry should be valid with line number" + ) + + def test_symbol_context_with_synthetic_line_entry(self): + """Test that SBSymbolContext correctly stores and retrieves synthetic line entries.""" + + # Create a synthetic line entry. + line_entry = lldb.SBLineEntry() + line_entry.SetLine(123) + line_entry.SetColumn(45) + file_spec = lldb.SBFileSpec("source.cpp", True) + line_entry.SetFileSpec(file_spec) + + # Create symbol context and set line entry. + sym_ctx = lldb.SBSymbolContext() + sym_ctx.SetLineEntry(line_entry) + + # Retrieve and verify. + retrieved = sym_ctx.GetLineEntry() + self.assertTrue(retrieved.IsValid()) + self.assertEqual(retrieved.GetLine(), 123) + self.assertEqual(retrieved.GetColumn(), 45) + self.assertEqual(retrieved.GetFileSpec().GetFilename(), "source.cpp") + + # Verify it's still synthetic (no valid address). + self.assertFalse(retrieved.GetStartAddress().IsValid()) diff --git a/lldb/test/API/python_api/target/TestTargetAPI.py b/lldb/test/API/python_api/target/TestTargetAPI.py index d346563..d3c64d8 100644 --- a/lldb/test/API/python_api/target/TestTargetAPI.py +++ b/lldb/test/API/python_api/target/TestTargetAPI.py @@ -105,6 +105,24 @@ class TargetAPITestCase(TestBase): self.assertIsNotNone(data_section2) self.assertEqual(data_section.name, data_section2.name) + def test_get_arch_name(self): + d = {"EXE": "b.out"} + self.build(dictionary=d) + self.setTearDownCleanup(dictionary=d) + target = self.create_simple_target("b.out") + + arch_name = target.arch_name + self.assertTrue(len(arch_name) > 0, "Got an arch name") + + # Test consistency with triple. + triple = target.triple + self.assertTrue(len(triple) > 0, "Got a triple") + self.assertEqual( + triple.split("-")[0], + arch_name, + "Arch name is equal to the first item of the triple", + ) + def test_get_ABIName(self): d = {"EXE": "b.out"} self.build(dictionary=d) diff --git a/lldb/test/API/python_api/unified_section_list/Makefile b/lldb/test/API/python_api/unified_section_list/Makefile new file mode 100644 index 0000000..431e716 --- /dev/null +++ b/lldb/test/API/python_api/unified_section_list/Makefile @@ -0,0 +1,5 @@ +CXX_SOURCES := main.cpp + +SPLIT_DEBUG_SYMBOLS := YES + +include Makefile.rules diff --git a/lldb/test/API/python_api/unified_section_list/TestModuleUnifiedSectionList.py b/lldb/test/API/python_api/unified_section_list/TestModuleUnifiedSectionList.py new file mode 100644 index 0000000..93b23d0 --- /dev/null +++ b/lldb/test/API/python_api/unified_section_list/TestModuleUnifiedSectionList.py @@ -0,0 +1,285 @@ +""" +Test Unified Section List merging. +""" + +import os +import shutil + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil +from lldbsuite.test.lldbutil import symbol_type_to_str + + +class ModuleUnifiedSectionList(TestBase): + @skipUnlessPlatform(["linux", "freebsd", "netbsd"]) + def test_unified_section_list(self): + self.build() + exe = self.getBuildArtifact("a.out") + debug_info = self.getBuildArtifact("a.out.debug") + new_dir = os.path.join(os.path.dirname(debug_info), "new_dir") + os.mkdir(new_dir) + renamed_debug_info = os.path.join(new_dir, "renamed.debug") + os.rename(debug_info, renamed_debug_info) + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + self.assertGreater(target.GetNumModules(), 0) + + main_exe_module = target.GetModuleAtIndex(0) + eh_frame = main_exe_module.FindSection(".eh_frame") + self.assertTrue(eh_frame.IsValid()) + self.assertGreater(eh_frame.size, 0) + + # Should be stripped in main executable. + debug_info_section = main_exe_module.FindSection(".debug_info") + self.assertFalse(debug_info_section.IsValid()) + + ci = self.dbg.GetCommandInterpreter() + res = lldb.SBCommandReturnObject() + ci.HandleCommand(f"target symbols add {renamed_debug_info}", res) + self.assertTrue(res.Succeeded()) + + # Should be stripped in .debuginfo but be present in main executable. + main_exe_module = target.GetModuleAtIndex(0) + eh_frame = main_exe_module.FindSection(".eh_frame") + self.assertTrue(eh_frame.IsValid()) + self.assertGreater(eh_frame.size, 0) + + # Should be unified and both sections should have contents. + debug_info_section = main_exe_module.FindSection(".debug_info") + self.assertTrue(debug_info_section.IsValid()) + self.assertGreater(debug_info_section.file_size, 0) + + def test_unified_section_list_overwrite_larger_section(self): + """ + Test the merging of an ELF file with another ELF File where all the new sections are bigger, validating we + overwrite .comment from SHT_NOBITS to the new SHT_PROGBITS section and the smaller .text with the larger + .text + """ + exe = self.getBuildArtifact("a.out") + self.yaml2obj("main.yaml", exe) + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + main_exe_module = target.GetModuleAtIndex(0) + + # First we verify out .text section is the expected BEC0FFEE + text_before_merge = main_exe_module.FindSection(".text") + self.assertTrue(text_before_merge.IsValid()) + error = lldb.SBError() + section_content = text_before_merge.data.ReadRawData( + error, 0, text_before_merge.data.size + ) + self.assertTrue(error.Success()) + self.assertEqual(section_content, bytes.fromhex("BEC0FFEE")) + + # .comment in main.yaml should be SHT_NOBITS, and size 0 + comment_before_merge = main_exe_module.FindSection(".comment") + self.assertTrue(comment_before_merge.IsValid()) + self.assertEqual(comment_before_merge.data.size, 0) + + # yamlize the main.largertext.yaml and force symbol loading + debug_info = self.getBuildArtifact("a.out.debug") + self.yaml2obj("main.largertext.yaml", debug_info) + + ci = self.dbg.GetCommandInterpreter() + res = lldb.SBCommandReturnObject() + ci.HandleCommand(f"target symbols add {debug_info}", res) + self.assertTrue(res.Succeeded()) + + # verify we took the larger .text section + main_exe_module_after_merge = target.GetModuleAtIndex(0) + text_after_merge = main_exe_module_after_merge.FindSection(".text") + self.assertTrue(text_after_merge.IsValid()) + self.assertGreater(text_after_merge.data.size, text_before_merge.data.size) + section_content_after_merge = text_after_merge.data.ReadRawData( + error, 0, text_after_merge.data.size + ) + self.assertTrue(error.Success()) + self.assertEqual(section_content_after_merge, bytes.fromhex("BEC0FFEEEEFF0CEB")) + + # in main.largertext.yaml comment is not SHT_NOBITS, and so we should see + # the size > 0 and equal to BAADF00D + comment_after_merge = main_exe_module_after_merge.FindSection(".comment") + self.assertTrue(comment_after_merge.IsValid()) + comment_content_after_merge = comment_after_merge.data.ReadRawData( + error, 0, comment_after_merge.data.size + ) + + self.assertTrue(error.Success()) + self.assertEqual(comment_content_after_merge, bytes.fromhex("BAADF00D")) + + def test_unified_section_list_overwrite_smaller_section(self): + """ + Test the merging of an ELF file with another ELF File where all the existing sections are bigger, validating we don't + overwrite with the SHT_NOBITS for .comment or the smaller .text section. + """ + exe = self.getBuildArtifact("a.out") + self.yaml2obj("main.largertext.yaml", exe) + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + main_exe_module = target.GetModuleAtIndex(0) + + # Same as above test but inverse, verify our larger .text section + # is the expected BEC0FFEE palindrome + text_before_merge = main_exe_module.FindSection(".text") + self.assertTrue(text_before_merge.IsValid()) + error = lldb.SBError() + section_content = text_before_merge.data.ReadRawData( + error, 0, text_before_merge.data.size + ) + self.assertTrue(error.Success()) + self.assertEqual(section_content, bytes.fromhex("BEC0FFEEEEFF0CEB")) + + # Comment is SHT_PROGBITS on the larger yaml and should remain + # the same after merge. + comment_before_merge = main_exe_module.FindSection(".comment") + self.assertTrue(comment_before_merge.IsValid()) + comment_content = comment_before_merge.data.ReadRawData( + error, 0, comment_before_merge.data.size + ) + + self.assertTrue(error.Success()) + self.assertEqual(comment_content, bytes.fromhex("BAADF00D")) + + debug_info = self.getBuildArtifact("a.out.debug") + self.yaml2obj("main.yaml", debug_info) + + ci = self.dbg.GetCommandInterpreter() + res = lldb.SBCommandReturnObject() + ci.HandleCommand(f"target symbols add {debug_info}", res) + self.assertTrue(res.Succeeded()) + + # Verify we didn't replace the sections after merge.s + main_exe_module_after_merge = target.GetModuleAtIndex(0) + text_after_merge = main_exe_module_after_merge.FindSection(".text") + self.assertTrue(text_after_merge.IsValid()) + self.assertEqual(text_after_merge.data.size, text_before_merge.data.size) + section_content_after_merge = text_after_merge.data.ReadRawData( + error, 0, text_after_merge.data.size + ) + self.assertTrue(error.Success()) + self.assertEqual(section_content_after_merge, bytes.fromhex("BEC0FFEEEEFF0CEB")) + + comment_after_merge = main_exe_module_after_merge.FindSection(".comment") + self.assertTrue(comment_after_merge.IsValid()) + comment_content_after_merge = comment_after_merge.data.ReadRawData( + error, 0, comment_after_merge.data.size + ) + + self.assertTrue(error.Success()) + self.assertEqual(comment_content_after_merge, bytes.fromhex("BAADF00D")) + + def test_unified_section_list_overwrite_mixed_merge(self): + """ + Test the merging of an ELF file with another ELF File where the lhs has a larger .comment section + and the RHS has a larger .text section. + """ + exe = self.getBuildArtifact("a.out") + self.yaml2obj("main.largercomment.yaml", exe) + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + main_exe_module = target.GetModuleAtIndex(0) + + # Verify we have the expected smaller BEC0FFEE + text_before_merge = main_exe_module.FindSection(".text") + self.assertTrue(text_before_merge.IsValid()) + error = lldb.SBError() + section_content = text_before_merge.data.ReadRawData( + error, 0, text_before_merge.data.size + ) + self.assertTrue(error.Success()) + self.assertEqual(section_content, bytes.fromhex("BEC0FFEE")) + + # Verify we have the larger palindromic comment + comment_before_merge = main_exe_module.FindSection(".comment") + self.assertTrue(comment_before_merge.IsValid()) + comment_content = comment_before_merge.data.ReadRawData( + error, 0, comment_before_merge.data.size + ) + + self.assertTrue(error.Success()) + self.assertEqual(comment_content, bytes.fromhex("BAADF00DF00DBAAD")) + + debug_info = self.getBuildArtifact("a.out.debug") + self.yaml2obj("main.largertext.yaml", debug_info) + + ci = self.dbg.GetCommandInterpreter() + res = lldb.SBCommandReturnObject() + ci.HandleCommand(f"target symbols add {debug_info}", res) + self.assertTrue(res.Succeeded()) + + # Verify we replaced .text + main_exe_module_after_merge = target.GetModuleAtIndex(0) + text_after_merge = main_exe_module_after_merge.FindSection(".text") + self.assertTrue(text_after_merge.IsValid()) + section_content_after_merge = text_after_merge.data.ReadRawData( + error, 0, text_after_merge.data.size + ) + self.assertTrue(error.Success()) + self.assertEqual(section_content_after_merge, bytes.fromhex("BEC0FFEEEEFF0CEB")) + + # Verify .comment is still the same. + comment_after_merge = main_exe_module_after_merge.FindSection(".comment") + self.assertTrue(comment_after_merge.IsValid()) + comment_content_after_merge = comment_after_merge.data.ReadRawData( + error, 0, comment_after_merge.data.size + ) + + self.assertTrue(error.Success()) + self.assertEqual(comment_content_after_merge, bytes.fromhex("BAADF00DF00DBAAD")) + + def test_unified_section_list_overwrite_equal_size(self): + """ + Test the merging of an ELF file with an ELF file with sections of the same size with different values + .text + """ + exe = self.getBuildArtifact("a.out") + self.yaml2obj("main.yaml", exe) + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + main_exe_module = target.GetModuleAtIndex(0) + + # First we verify out .text section is the expected BEC0FFEE + text_before_merge = main_exe_module.FindSection(".text") + self.assertTrue(text_before_merge.IsValid()) + error = lldb.SBError() + section_content = text_before_merge.data.ReadRawData( + error, 0, text_before_merge.data.size + ) + self.assertTrue(error.Success()) + self.assertEqual(section_content, bytes.fromhex("BEC0FFEE")) + + # .comment in main.yaml should be SHT_NOBITS, and size 0 + comment_before_merge = main_exe_module.FindSection(".comment") + self.assertTrue(comment_before_merge.IsValid()) + self.assertEqual(comment_before_merge.data.size, 0) + + # yamlize the main with the .text reversed from BEC0FFEE + # to EEFF0CEB. We should still keep our .text with BEC0FFEE + debug_info = self.getBuildArtifact("a.out.debug") + self.yaml2obj("main.reversedtext.yaml", debug_info) + + ci = self.dbg.GetCommandInterpreter() + res = lldb.SBCommandReturnObject() + ci.HandleCommand(f"target symbols add {debug_info}", res) + self.assertTrue(res.Succeeded()) + + # verify .text did not change + main_exe_module_after_merge = target.GetModuleAtIndex(0) + text_after_merge = main_exe_module_after_merge.FindSection(".text") + self.assertTrue(text_after_merge.IsValid()) + section_content_after_merge = text_after_merge.data.ReadRawData( + error, 0, text_after_merge.data.size + ) + self.assertTrue(error.Success()) + self.assertEqual(section_content_after_merge, bytes.fromhex("BEC0FFEE")) + + # verify comment did not change + comment_afer_merge = main_exe_module_after_merge.FindSection(".comment") + self.assertTrue(comment_afer_merge.IsValid()) + self.assertEqual(comment_afer_merge.data.size, 0) diff --git a/lldb/test/API/python_api/unified_section_list/main.cpp b/lldb/test/API/python_api/unified_section_list/main.cpp new file mode 100644 index 0000000..45fd52e --- /dev/null +++ b/lldb/test/API/python_api/unified_section_list/main.cpp @@ -0,0 +1,3 @@ +#include <stdio.h> + +int main() { printf("Hello World\n"); } diff --git a/lldb/test/API/python_api/unified_section_list/main.largercomment.yaml b/lldb/test/API/python_api/unified_section_list/main.largercomment.yaml new file mode 100644 index 0000000..f786006 --- /dev/null +++ b/lldb/test/API/python_api/unified_section_list/main.largercomment.yaml @@ -0,0 +1,46 @@ +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 + Entry: 0x1040 +ProgramHeaders: + - Type: PT_PHDR + Flags: [ PF_R ] + VAddr: 0x40 + Align: 0x8 + Offset: 0x40 + - Type: PT_LOAD + Flags: [ PF_R ] + FirstSec: .text + LastSec: .fini + Align: 0x1000 + Offset: 0x0 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1040 + AddressAlign: 0x10 + Content: BEC0FFEE + - Name: .fini + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1140 + AddressAlign: 0x4 + Content: DEADBEEF + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x3140 + AddressAlign: 0x4 + Content: BAADF00DF00DBAAD +Symbols: + - Name: main + Type: STT_FUNC + Section: .text + Binding: STB_GLOBAL + Value: 0x1130 + Size: 0xF +... diff --git a/lldb/test/API/python_api/unified_section_list/main.largertext.yaml b/lldb/test/API/python_api/unified_section_list/main.largertext.yaml new file mode 100644 index 0000000..6450e67 --- /dev/null +++ b/lldb/test/API/python_api/unified_section_list/main.largertext.yaml @@ -0,0 +1,46 @@ +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 + Entry: 0x1040 +ProgramHeaders: + - Type: PT_PHDR + Flags: [ PF_R ] + VAddr: 0x40 + Align: 0x8 + Offset: 0x40 + - Type: PT_LOAD + Flags: [ PF_R ] + FirstSec: .text + LastSec: .fini + Align: 0x1000 + Offset: 0x0 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1040 + AddressAlign: 0x10 + Content: BEC0FFEEEEFF0CEB + - Name: .fini + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1140 + AddressAlign: 0x4 + Content: DEADBEEF + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x3140 + AddressAlign: 0x4 + Content: BAADF00D +Symbols: + - Name: main + Type: STT_FUNC + Section: .text + Binding: STB_GLOBAL + Value: 0x1130 + Size: 0xF +... diff --git a/lldb/test/API/python_api/unified_section_list/main.reversedtext.yaml b/lldb/test/API/python_api/unified_section_list/main.reversedtext.yaml new file mode 100644 index 0000000..5720666 --- /dev/null +++ b/lldb/test/API/python_api/unified_section_list/main.reversedtext.yaml @@ -0,0 +1,45 @@ +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 + Entry: 0x1040 +ProgramHeaders: + - Type: PT_PHDR + Flags: [ PF_R ] + VAddr: 0x40 + Align: 0x8 + Offset: 0x40 + - Type: PT_LOAD + Flags: [ PF_R ] + FirstSec: .text + LastSec: .fini + Align: 0x1000 + Offset: 0x0 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1040 + AddressAlign: 0x10 + Content: BEC0FFEE + - Name: .fini + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1140 + AddressAlign: 0x4 + Content: DEADBEEF + - Name: .comment + Type: SHT_NOBITS + Flags: [ SHF_ALLOC ] + Address: 0x3140 + AddressAlign: 0x4 +Symbols: + - Name: main + Type: STT_FUNC + Section: .text + Binding: STB_GLOBAL + Value: 0x1130 + Size: 0xF +... diff --git a/lldb/test/API/python_api/unified_section_list/main.yaml b/lldb/test/API/python_api/unified_section_list/main.yaml new file mode 100644 index 0000000..5720666 --- /dev/null +++ b/lldb/test/API/python_api/unified_section_list/main.yaml @@ -0,0 +1,45 @@ +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 + Entry: 0x1040 +ProgramHeaders: + - Type: PT_PHDR + Flags: [ PF_R ] + VAddr: 0x40 + Align: 0x8 + Offset: 0x40 + - Type: PT_LOAD + Flags: [ PF_R ] + FirstSec: .text + LastSec: .fini + Align: 0x1000 + Offset: 0x0 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1040 + AddressAlign: 0x10 + Content: BEC0FFEE + - Name: .fini + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1140 + AddressAlign: 0x4 + Content: DEADBEEF + - Name: .comment + Type: SHT_NOBITS + Flags: [ SHF_ALLOC ] + Address: 0x3140 + AddressAlign: 0x4 +Symbols: + - Name: main + Type: STT_FUNC + Section: .text + Binding: STB_GLOBAL + Value: 0x1130 + Size: 0xF +... diff --git a/lldb/test/API/symbol_ondemand/breakpoint_source_regex/TestSourceTextRegexBreakpoint.py b/lldb/test/API/symbol_ondemand/breakpoint_source_regex/TestSourceTextRegexBreakpoint.py index 8bf294b..6904d8e 100644 --- a/lldb/test/API/symbol_ondemand/breakpoint_source_regex/TestSourceTextRegexBreakpoint.py +++ b/lldb/test/API/symbol_ondemand/breakpoint_source_regex/TestSourceTextRegexBreakpoint.py @@ -20,7 +20,9 @@ class TestSourceTextRegexBreakpoint(TestBase): self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) - lldbutil.run_break_set_by_source_regexp(self, "Set break point at this line.") + lldbutil.run_break_set_by_source_regexp( + self, "Set break point at this line.", extra_options="-f main.cpp" + ) self.runCmd("run", RUN_SUCCEEDED) # The stop reason of the thread should be breakpoint. diff --git a/lldb/test/API/terminal/TestEditline.py b/lldb/test/API/terminal/TestEditline.py index 38f4f34..4696b1e 100644 --- a/lldb/test/API/terminal/TestEditline.py +++ b/lldb/test/API/terminal/TestEditline.py @@ -94,7 +94,7 @@ class EditlineTest(PExpectTest): # after the prompt. self.child.send("foo") # Check that there are no escape codes. - self.child.expect(re.escape("\n(lldb) foo")) + self.child.expect(re.escape("\n\r\x1b[K(lldb) foo")) @skipIfAsan @skipIfEditlineSupportMissing diff --git a/lldb/test/API/test_utils/pdb/Makefile b/lldb/test/API/test_utils/pdb/Makefile new file mode 100644 index 0000000..99998b2 --- /dev/null +++ b/lldb/test/API/test_utils/pdb/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/test_utils/pdb/TestPdb.py b/lldb/test/API/test_utils/pdb/TestPdb.py new file mode 100644 index 0000000..bd3a9d0 --- /dev/null +++ b/lldb/test/API/test_utils/pdb/TestPdb.py @@ -0,0 +1,18 @@ +""" +Test PDB enabled tests +""" + +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * + + +class TestBuildMethod(TestBase): + TEST_WITH_PDB_DEBUG_INFO = True + + def test(self): + self.build() + self.assertTrue(self.dbg.CreateTarget(self.getBuildArtifact())) + if self.getDebugInfo() == "pdb": + self.expect( + "target modules dump symfile", patterns=["SymbolFile (native-)?pdb"] + ) diff --git a/lldb/test/API/test_utils/pdb/main.cpp b/lldb/test/API/test_utils/pdb/main.cpp new file mode 100644 index 0000000..76e8197 --- /dev/null +++ b/lldb/test/API/test_utils/pdb/main.cpp @@ -0,0 +1 @@ +int main() { return 0; } diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py index 2db00a5..d628739 100644 --- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py +++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py @@ -75,3 +75,38 @@ class TestDAP_attach(lldbdap_testcase.DAPTestCaseBase): self.spawn_thread.start() self.attach(program=program, waitFor=True) self.continue_and_verify_pid() + + def test_attach_with_missing_debuggerId_or_targetId(self): + """ + Test that attaching with only one of debuggerId/targetId specified + fails with the expected error message. + """ + self.build_and_create_debug_adapter() + + # Test with only targetId specified (no debuggerId) + resp = self.attach(targetId=99999, expectFailure=True) + self.assertFalse(resp["success"]) + self.assertIn( + "Both debuggerId and targetId must be specified together", + resp["body"]["error"]["format"], + ) + + def test_attach_with_invalid_debuggerId_and_targetId(self): + """ + Test that attaching with both debuggerId and targetId specified but + invalid fails with an appropriate error message. + """ + self.build_and_create_debug_adapter() + + # Attach with both debuggerId=9999 and targetId=99999 (both invalid). + # Since debugger ID 9999 likely doesn't exist in the global registry, + # we expect a validation error. + resp = self.attach(debuggerId=9999, targetId=99999, expectFailure=True) + self.assertFalse(resp["success"]) + error_msg = resp["body"]["error"]["format"] + # Either error is acceptable - both indicate the debugger reuse + # validation is working correctly + self.assertTrue( + "Unable to find existing debugger" in error_msg + or f"Expected debugger/target not found error, got: {error_msg}" + ) diff --git a/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py b/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py index a542a31..df029ca 100644 --- a/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py +++ b/lldb/test/API/tools/lldb-dap/databreakpoint/TestDAP_setDataBreakpoints.py @@ -39,18 +39,21 @@ class TestDAP_setDataBreakpoints(lldbdap_testcase.DAPTestCaseBase): {"dataId": response_x["body"]["dataId"], "accessType": "write"}, ] set_response = self.dap_server.request_setDataBreakpoint(dataBreakpoints) - self.assertEqual( - set_response["body"]["breakpoints"], - [{"verified": False}, {"verified": True}, {"verified": True}], - ) + breakpoints = set_response["body"]["breakpoints"] + self.assertEqual(len(breakpoints), 3) + self.assertFalse(breakpoints[0]["verified"]) + self.assertTrue(breakpoints[1]["verified"]) + self.assertTrue(breakpoints[2]["verified"]) - self.continue_to_next_stop() + self.dap_server.request_continue() + self.verify_breakpoint_hit([breakpoints[2]["id"]]) x_val = self.dap_server.get_local_variable_value("x") i_val = self.dap_server.get_local_variable_value("i") self.assertEqual(x_val, "2") self.assertEqual(i_val, "1") - self.continue_to_next_stop() + self.dap_server.request_continue() + self.verify_breakpoint_hit([breakpoints[1]["id"]]) arr_2 = self.dap_server.get_local_variable_child("arr", "[2]") i_val = self.dap_server.get_local_variable_value("i") self.assertEqual(arr_2["value"], "42") @@ -79,18 +82,20 @@ class TestDAP_setDataBreakpoints(lldbdap_testcase.DAPTestCaseBase): {"dataId": response_arr_2["body"]["dataId"], "accessType": "write"}, ] set_response = self.dap_server.request_setDataBreakpoint(dataBreakpoints) - self.assertEqual( - set_response["body"]["breakpoints"], - [{"verified": True}, {"verified": True}], - ) + breakpoints = set_response["body"]["breakpoints"] + self.assertEqual(len(breakpoints), 2) + self.assertTrue(breakpoints[0]["verified"]) + self.assertTrue(breakpoints[1]["verified"]) - self.continue_to_next_stop() + self.dap_server.request_continue() + self.verify_breakpoint_hit([breakpoints[0]["id"]]) x_val = self.dap_server.get_local_variable_value("x") i_val = self.dap_server.get_local_variable_value("i") self.assertEqual(x_val, "2") self.assertEqual(i_val, "1") - self.continue_to_next_stop() + self.dap_server.request_continue() + self.verify_breakpoint_hit([breakpoints[1]["id"]]) arr_2 = self.dap_server.get_local_variable_child("arr", "[2]") i_val = self.dap_server.get_local_variable_value("i") self.assertEqual(arr_2["value"], "42") @@ -123,18 +128,20 @@ class TestDAP_setDataBreakpoints(lldbdap_testcase.DAPTestCaseBase): {"dataId": response_arr_2["body"]["dataId"], "accessType": "write"}, ] set_response = self.dap_server.request_setDataBreakpoint(dataBreakpoints) - self.assertEqual( - set_response["body"]["breakpoints"], - [{"verified": True}, {"verified": True}], - ) + breakpoints = set_response["body"]["breakpoints"] + self.assertEqual(len(breakpoints), 2) + self.assertTrue(breakpoints[0]["verified"]) + self.assertTrue(breakpoints[1]["verified"]) - self.continue_to_next_stop() + self.dap_server.request_continue() + self.verify_breakpoint_hit([breakpoints[0]["id"]]) x_val = self.dap_server.get_local_variable_value("x") i_val = self.dap_server.get_local_variable_value("i") self.assertEqual(x_val, "2") self.assertEqual(i_val, "1") - self.continue_to_next_stop() + self.dap_server.request_continue() + self.verify_breakpoint_hit([breakpoints[1]["id"]]) arr_2 = self.dap_server.get_local_variable_child("arr", "[2]") i_val = self.dap_server.get_local_variable_value("i") self.assertEqual(arr_2["value"], "42") @@ -153,8 +160,11 @@ class TestDAP_setDataBreakpoints(lldbdap_testcase.DAPTestCaseBase): } ] set_response = self.dap_server.request_setDataBreakpoint(dataBreakpoints) - self.assertEqual(set_response["body"]["breakpoints"], [{"verified": True}]) - self.continue_to_next_stop() + breakpoints = set_response["body"]["breakpoints"] + self.assertEqual(len(breakpoints), 1) + self.assertTrue(breakpoints[0]["verified"]) + self.dap_server.request_continue() + self.verify_breakpoint_hit([breakpoints[0]["id"]]) x_val = self.dap_server.get_local_variable_value("x") self.assertEqual(x_val, "3") @@ -167,7 +177,64 @@ class TestDAP_setDataBreakpoints(lldbdap_testcase.DAPTestCaseBase): } ] set_response = self.dap_server.request_setDataBreakpoint(dataBreakpoints) - self.assertEqual(set_response["body"]["breakpoints"], [{"verified": True}]) - self.continue_to_next_stop() + breakpoints = set_response["body"]["breakpoints"] + self.assertEqual(len(breakpoints), 1) + self.assertTrue(breakpoints[0]["verified"]) + self.dap_server.request_continue() + self.verify_breakpoint_hit([breakpoints[0]["id"]]) x_val = self.dap_server.get_local_variable_value("x") self.assertEqual(x_val, "10") + + @skipIfWindows + def test_bytes(self): + """Tests setting data breakpoints on memory range.""" + program = self.getBuildArtifact("a.out") + self.build_and_launch(program) + source = "main.cpp" + first_loop_break_line = line_number(source, "// first loop breakpoint") + self.set_source_breakpoints(source, [first_loop_break_line]) + self.continue_to_next_stop() + # Test write watchpoints on x, arr[2] + x = self.dap_server.get_local_variable("x") + response_x = self.dap_server.request_dataBreakpointInfo( + 0, x["memoryReference"], 4 + ) + arr_2 = self.dap_server.get_local_variable_child("arr", "[2]") + response_arr_2 = self.dap_server.request_dataBreakpointInfo( + 0, arr_2["memoryReference"], 4 + ) + + # Test response from dataBreakpointInfo request. + self.assertEqual( + response_x["body"]["dataId"].split("/"), [x["memoryReference"][2:], "4"] + ) + self.assertEqual(response_x["body"]["accessTypes"], self.accessTypes) + self.assertEqual( + response_arr_2["body"]["dataId"].split("/"), + [arr_2["memoryReference"][2:], "4"], + ) + self.assertEqual(response_arr_2["body"]["accessTypes"], self.accessTypes) + dataBreakpoints = [ + {"dataId": response_x["body"]["dataId"], "accessType": "write"}, + {"dataId": response_arr_2["body"]["dataId"], "accessType": "write"}, + ] + set_response = self.dap_server.request_setDataBreakpoint(dataBreakpoints) + breakpoints = set_response["body"]["breakpoints"] + self.assertEqual(len(breakpoints), 2) + self.assertTrue(breakpoints[0]["verified"]) + self.assertTrue(breakpoints[1]["verified"]) + + self.dap_server.request_continue() + self.verify_breakpoint_hit([breakpoints[0]["id"]]) + x_val = self.dap_server.get_local_variable_value("x") + i_val = self.dap_server.get_local_variable_value("i") + self.assertEqual(x_val, "2") + self.assertEqual(i_val, "1") + + self.dap_server.request_continue() + self.verify_breakpoint_hit([breakpoints[1]["id"]]) + arr_2 = self.dap_server.get_local_variable_child("arr", "[2]") + i_val = self.dap_server.get_local_variable_value("i") + self.assertEqual(arr_2["value"], "42") + self.assertEqual(i_val, "2") + self.dap_server.request_setDataBreakpoint([]) diff --git a/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py b/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py index 09e3f62..19f88d8 100644 --- a/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py +++ b/lldb/test/API/tools/lldb-dap/disconnect/TestDAP_disconnect.py @@ -3,17 +3,15 @@ Test lldb-dap disconnect request """ -import dap_server from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil import lldbdap_testcase -import subprocess import time import os -class TestDAP_launch(lldbdap_testcase.DAPTestCaseBase): +class TestDAP_disconnect(lldbdap_testcase.DAPTestCaseBase): source = "main.cpp" def disconnect_and_assert_no_output_printed(self): @@ -67,10 +65,11 @@ class TestDAP_launch(lldbdap_testcase.DAPTestCaseBase): lambda: self.run_platform_command("rm %s" % (sync_file_path)) ) - self.process = subprocess.Popen([program, sync_file_path]) + proc = self.spawnSubprocess(program, [sync_file_path]) lldbutil.wait_for_file_on_target(self, sync_file_path) - self.attach(pid=self.process.pid, disconnectAutomatically=False) + self.attach(pid=proc.pid, disconnectAutomatically=False, stopOnEntry=True) + self.continue_to_next_stop() response = self.dap_server.request_evaluate("wait_for_attach = false;") self.assertTrue(response["success"]) diff --git a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py index 20a75f4..9557378 100644 --- a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py +++ b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py @@ -1,5 +1,5 @@ """ -Test lldb-dap completions request +Test lldb-dap evaluate request """ import re @@ -7,16 +7,70 @@ import re import lldbdap_testcase from lldbsuite.test.decorators import skipIfWindows from lldbsuite.test.lldbtest import line_number +from typing import TypedDict, Optional + + +class EvaluateResponseBody(TypedDict, total=False): + result: str + variablesReference: int + type: Optional[str] + memoryReference: Optional[str] + valueLocationReference: Optional[int] class TestDAP_evaluate(lldbdap_testcase.DAPTestCaseBase): - def assertEvaluate(self, expression, regex): + def assertEvaluate( + self, + expression, + result: str, + want_type="", + want_varref=False, + want_memref=True, + want_locref=False, + is_hex=None, + ): + resp = self.dap_server.request_evaluate( + expression, context=self.context, is_hex=is_hex + ) + self.assertTrue( + resp["success"], f"Failed to evaluate expression {expression!r}" + ) + body: EvaluateResponseBody = resp["body"] self.assertRegex( - self.dap_server.request_evaluate(expression, context=self.context)["body"][ - "result" - ], - regex, + body["result"], + result, + f"Unexpected 'result' for expression {expression!r} in response body {body}", ) + if want_varref: + self.assertNotEqual( + body["variablesReference"], + 0, + f"Unexpected 'variablesReference' for expression {expression!r} in response body {body}", + ) + else: + self.assertEqual( + body["variablesReference"], + 0, + f"Unexpected 'variablesReference' for expression {expression!r} in response body {body}", + ) + if want_type: + self.assertEqual( + body["type"], + want_type, + f"Unexpected 'type' for expression {expression!r} in response body {body}", + ) + if want_memref: + self.assertIn( + "memoryReference", + body, + f"Unexpected 'memoryReference' for expression {expression!r} in response body {body}", + ) + if want_locref: + self.assertIn( + "valueLocationReference", + body, + f"Unexpected 'valueLocationReference' for expression {expression!r} in response body {body}", + ) def assertEvaluateFailure(self, expression): self.assertNotIn( @@ -71,29 +125,45 @@ class TestDAP_evaluate(lldbdap_testcase.DAPTestCaseBase): self.continue_to_breakpoint(breakpoint_1) # Expressions at breakpoint 1, which is in main - self.assertEvaluate("var1", "20") + self.assertEvaluate("var1", "20", want_type="int") # Empty expression should equate to the previous expression. if context == "repl": self.assertEvaluate("", "20") else: self.assertEvaluateFailure("") - self.assertEvaluate("var2", "21") + self.assertEvaluate("var2", "21", want_type="int") if context == "repl": - self.assertEvaluate("", "21") - self.assertEvaluate("", "21") - self.assertEvaluate("static_int", "42") - self.assertEvaluate("non_static_int", "43") - self.assertEvaluate("struct1.foo", "15") - self.assertEvaluate("struct2->foo", "16") + self.assertEvaluate("", "21", want_type="int") + self.assertEvaluate("", "21", want_type="int") + self.assertEvaluate("static_int", "0x0000002a", want_type="int", is_hex=True) + self.assertEvaluate( + "non_static_int", "0x0000002b", want_type="int", is_hex=True + ) + self.assertEvaluate("struct1.foo", "0x0000000f", want_type="int", is_hex=True) + self.assertEvaluate("struct2->foo", "0x00000010", want_type="int", is_hex=True) + self.assertEvaluate("static_int", "42", want_type="int") + self.assertEvaluate("non_static_int", "43", want_type="int") + self.assertEvaluate("struct1.foo", "15", want_type="int") + self.assertEvaluate("struct2->foo", "16", want_type="int") if self.isResultExpandedDescription(): self.assertEvaluate( "struct1", r"\(my_struct\) (struct1|\$\d+) = \(foo = 15\)", + want_type="my_struct", + want_varref=True, + ) + self.assertEvaluate( + "struct2", + r"\(my_struct \*\) (struct2|\$\d+) = 0x.*", + want_type="my_struct *", + want_varref=True, ) - self.assertEvaluate("struct2", r"\(my_struct \*\) (struct2|\$\d+) = 0x.*") self.assertEvaluate( - "struct3", r"\(my_struct \*\) (struct3|\$\d+) = nullptr" + "struct3", + r"\(my_struct \*\) (struct3|\$\d+) = nullptr", + want_type="my_struct *", + want_varref=True, ) else: self.assertEvaluate( @@ -103,16 +173,22 @@ class TestDAP_evaluate(lldbdap_testcase.DAPTestCaseBase): if enableAutoVariableSummaries else "my_struct @ 0x" ), + want_varref=True, ) self.assertEvaluate( - "struct2", "0x.* {foo:16}" if enableAutoVariableSummaries else "0x.*" + "struct2", + "0x.* {foo:16}" if enableAutoVariableSummaries else "0x.*", + want_varref=True, + want_type="my_struct *", + ) + self.assertEvaluate( + "struct3", "0x.*0", want_varref=True, want_type="my_struct *" ) - self.assertEvaluate("struct3", "0x.*0") if context == "repl": # In the repl context expressions may be interpreted as lldb # commands since no variables have the same name as the command. - self.assertEvaluate("list", r".*") + self.assertEvaluate("list", r".*", want_memref=False) else: self.assertEvaluateFailure("list") # local variable of a_function @@ -121,10 +197,26 @@ class TestDAP_evaluate(lldbdap_testcase.DAPTestCaseBase): self.assertEvaluateFailure("foo") # member of my_struct if self.isExpressionParsedExpected(): - self.assertEvaluate("a_function", "0x.*a.out`a_function.*") - self.assertEvaluate("a_function(1)", "1") - self.assertEvaluate("var2 + struct1.foo", "36") - self.assertEvaluate("foo_func", "0x.*a.out`foo_func.*") + self.assertEvaluate( + "a_function", + "0x.*a.out`a_function.*", + want_type="int (*)(int)", + want_varref=True, + want_memref=False, + want_locref=True, + ) + self.assertEvaluate( + "a_function(1)", "1", want_memref=False, want_type="int" + ) + self.assertEvaluate("var2 + struct1.foo", "36", want_memref=False) + self.assertEvaluate( + "foo_func", + "0x.*a.out`foo_func.*", + want_type="int (*)()", + want_varref=True, + want_memref=False, + want_locref=True, + ) self.assertEvaluate("foo_var", "44") else: self.assertEvaluateFailure("a_function") @@ -145,6 +237,8 @@ class TestDAP_evaluate(lldbdap_testcase.DAPTestCaseBase): self.assertEvaluate( "struct1", r"\(my_struct\) (struct1|\$\d+) = \(foo = 15\)", + want_type="my_struct", + want_varref=True, ) else: self.assertEvaluate( @@ -154,15 +248,26 @@ class TestDAP_evaluate(lldbdap_testcase.DAPTestCaseBase): if enableAutoVariableSummaries else "my_struct @ 0x" ), + want_type="my_struct", + want_varref=True, ) self.assertEvaluate("struct1.foo", "15") self.assertEvaluate("struct2->foo", "16") if self.isExpressionParsedExpected(): - self.assertEvaluate("a_function", "0x.*a.out`a_function.*") - self.assertEvaluate("a_function(1)", "1") - self.assertEvaluate("var2 + struct1.foo", "17") - self.assertEvaluate("foo_func", "0x.*a.out`foo_func.*") + self.assertEvaluate( + "a_function", + "0x.*a.out`a_function.*", + want_type="int (*)(int)", + want_varref=True, + want_memref=False, + want_locref=True, + ) + self.assertEvaluate("a_function(1)", "1", want_memref=False) + self.assertEvaluate("var2 + struct1.foo", "17", want_memref=False) + self.assertEvaluate( + "foo_func", "0x.*a.out`foo_func.*", want_varref=True, want_memref=False + ) self.assertEvaluate("foo_var", "44") else: self.assertEvaluateFailure("a_function") @@ -185,10 +290,18 @@ class TestDAP_evaluate(lldbdap_testcase.DAPTestCaseBase): self.assertEvaluateFailure("var2 + struct1.foo") if self.isExpressionParsedExpected(): - self.assertEvaluate("a_function", "0x.*a.out`a_function.*") - self.assertEvaluate("a_function(1)", "1") - self.assertEvaluate("list + 1", "43") - self.assertEvaluate("foo_func", "0x.*a.out`foo_func.*") + self.assertEvaluate( + "a_function", + "0x.*a.out`a_function.*", + want_varref=True, + want_memref=False, + want_locref=True, + ) + self.assertEvaluate("a_function(1)", "1", want_memref=False) + self.assertEvaluate("list + 1", "43", want_memref=False) + self.assertEvaluate( + "foo_func", "0x.*a.out`foo_func.*", want_varref=True, want_memref=False + ) self.assertEvaluate("foo_var", "44") else: self.assertEvaluateFailure("a_function") @@ -199,26 +312,28 @@ class TestDAP_evaluate(lldbdap_testcase.DAPTestCaseBase): # Now we check that values are updated after stepping self.continue_to_breakpoint(breakpoint_4) - self.assertEvaluate("my_vec", "size=2") + self.assertEvaluate("my_vec", "size=2", want_varref=True) self.continue_to_breakpoint(breakpoint_5) - self.assertEvaluate("my_vec", "size=3") + self.assertEvaluate("my_vec", "size=3", want_varref=True) - self.assertEvaluate("my_map", "size=2") + self.assertEvaluate("my_map", "size=2", want_varref=True) self.continue_to_breakpoint(breakpoint_6) - self.assertEvaluate("my_map", "size=3") + self.assertEvaluate("my_map", "size=3", want_varref=True) - self.assertEvaluate("my_bool_vec", "size=1") + self.assertEvaluate("my_bool_vec", "size=1", want_varref=True) self.continue_to_breakpoint(breakpoint_7) - self.assertEvaluate("my_bool_vec", "size=2") + self.assertEvaluate("my_bool_vec", "size=2", want_varref=True) self.continue_to_breakpoint(breakpoint_8) # Test memory read, especially with 'empty' repeat commands. if context == "repl": - self.assertEvaluate("memory read -c 1 &my_ints", ".* 05 .*\n") - self.assertEvaluate("", ".* 0a .*\n") - self.assertEvaluate("", ".* 0f .*\n") - self.assertEvaluate("", ".* 14 .*\n") - self.assertEvaluate("", ".* 19 .*\n") + self.assertEvaluate( + "memory read -c 1 &my_ints", ".* 05 .*\n", want_memref=False + ) + self.assertEvaluate("", ".* 0a .*\n", want_memref=False) + self.assertEvaluate("", ".* 0f .*\n", want_memref=False) + self.assertEvaluate("", ".* 14 .*\n", want_memref=False) + self.assertEvaluate("", ".* 19 .*\n", want_memref=False) self.continue_to_exit() @@ -245,4 +360,6 @@ class TestDAP_evaluate(lldbdap_testcase.DAPTestCaseBase): @skipIfWindows def test_variable_evaluate_expressions(self): # Tests expression evaluations that are triggered in the variable explorer - self.run_test_evaluate_expressions("variable", enableAutoVariableSummaries=True) + self.run_test_evaluate_expressions( + "variables", enableAutoVariableSummaries=True + ) diff --git a/lldb/test/API/tools/lldb-dap/server/TestDAP_server.py b/lldb/test/API/tools/lldb-dap/server/TestDAP_server.py index 12b321c..3c53cf2 100644 --- a/lldb/test/API/tools/lldb-dap/server/TestDAP_server.py +++ b/lldb/test/API/tools/lldb-dap/server/TestDAP_server.py @@ -37,7 +37,7 @@ class TestDAP_server(lldbdap_testcase.DAPTestCaseBase): def run_debug_session(self, connection, name, sleep_seconds_in_middle=None): self.dap_server = dap_server.DebugAdapterServer( - connection=connection, + connection=connection, spawn_helper=self.spawnSubprocess ) program = self.getBuildArtifact("a.out") source = "main.c" @@ -94,6 +94,7 @@ class TestDAP_server(lldbdap_testcase.DAPTestCaseBase): (process, connection) = self.start_server(connection="listen://localhost:0") self.dap_server = dap_server.DebugAdapterServer( connection=connection, + spawn_helper=self.spawnSubprocess, ) program = self.getBuildArtifact("a.out") source = "main.c" diff --git a/lldb/test/API/tools/lldb-dap/stackTraceCompilerGeneratedCode/Makefile b/lldb/test/API/tools/lldb-dap/stackTraceCompilerGeneratedCode/Makefile new file mode 100644 index 0000000..1049594 --- /dev/null +++ b/lldb/test/API/tools/lldb-dap/stackTraceCompilerGeneratedCode/Makefile @@ -0,0 +1,3 @@ +C_SOURCES := main.c + +include Makefile.rules diff --git a/lldb/test/API/tools/lldb-dap/stackTraceCompilerGeneratedCode/TestDAP_stackTraceCompilerGeneratedCode.py b/lldb/test/API/tools/lldb-dap/stackTraceCompilerGeneratedCode/TestDAP_stackTraceCompilerGeneratedCode.py new file mode 100644 index 0000000..4ddf924 --- /dev/null +++ b/lldb/test/API/tools/lldb-dap/stackTraceCompilerGeneratedCode/TestDAP_stackTraceCompilerGeneratedCode.py @@ -0,0 +1,66 @@ +""" +Test lldb-dap stackTrace request for compiler generated code +""" + +import os + +import lldbdap_testcase +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * + + +class TestDAP_stackTraceCompilerGeneratedCode(lldbdap_testcase.DAPTestCaseBase): + def test_non_leaf_frame_compiler_generate_code(self): + """ + Test that non-leaf frames with compiler-generated code are properly resolved. + + This test verifies that LLDB correctly handles stack frames containing + compiler-generated code (code without valid source location information). + When a non-leaf frame contains compiler-generated code immediately after a + call instruction, LLDB should resolve the frame's source location to the + call instruction's line, rather than to the compiler-generated code that + follows, which lacks proper symbolication information. + """ + program = self.getBuildArtifact("a.out") + self.build_and_launch(program) + source = "main.c" + + # Set breakpoint inside bar() function + lines = [line_number(source, "// breakpoint here")] + breakpoint_ids = self.set_source_breakpoints(source, lines) + self.assertEqual( + len(breakpoint_ids), len(lines), "expect correct number of breakpoints" + ) + + self.continue_to_breakpoints(breakpoint_ids) + + # Get the stack frames: [0] = bar(), [1] = foo(), [2] = main() + stack_frames = self.get_stackFrames() + self.assertGreater(len(stack_frames), 2, "Expected more than 2 stack frames") + + # Examine the foo() frame (stack_frames[1]) + # This is the critical frame containing compiler-generated code + foo_frame = stack_frames[1] + + # Verify that the frame's line number points to the bar() call, + # not to the compiler-generated code after it + foo_call_bar_source_line = foo_frame.get("line") + self.assertEqual( + foo_call_bar_source_line, + line_number(source, "foo call bar"), + "Expected foo call bar to be the source line of the frame", + ) + + # Verify the source file name is correctly resolved + foo_source_name = foo_frame.get("source", {}).get("name") + self.assertEqual( + foo_source_name, "main.c", "Expected foo source name to be main.c" + ) + + # When lldb fails to symbolicate a frame it will emit a fake assembly + # source with path of format <module>`<symbol> or <module>`<address> with + # sourceReference to retrieve disassembly source file. + # Verify that this didn't happen - the path should be a real file path. + foo_path = foo_frame.get("source", {}).get("path") + self.assertNotIn("`", foo_path, "Expected foo source path to not contain `") + self.continue_to_exit() diff --git a/lldb/test/API/tools/lldb-dap/stackTraceCompilerGeneratedCode/main.c b/lldb/test/API/tools/lldb-dap/stackTraceCompilerGeneratedCode/main.c new file mode 100644 index 0000000..dd3fcc2 --- /dev/null +++ b/lldb/test/API/tools/lldb-dap/stackTraceCompilerGeneratedCode/main.c @@ -0,0 +1,19 @@ +void bar() { + int val = 32; // breakpoint here +} + +void at_line_zero() {} + +int foo(); + +int main(int argc, char const *argv[]) { + foo(); + return 0; +} + +int foo() { + bar(); // foo call bar +#line 0 "test.cpp" + at_line_zero(); + return 0; +} diff --git a/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py b/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py index b487257..7e60dd2 100644 --- a/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py +++ b/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py @@ -36,3 +36,54 @@ class TestDAP_startDebugging(lldbdap_testcase.DAPTestCaseBase): request = self.dap_server.reverse_requests[0] self.assertEqual(request["arguments"]["configuration"]["pid"], 321) self.assertEqual(request["arguments"]["request"], "attach") + + def test_startDebugging_debugger_reuse(self): + """ + Tests that debugger and target IDs can be passed through startDebugging + for debugger reuse. This verifies the infrastructure for child DAP + sessions to reuse the parent's debugger and attach to an existing target. + """ + program = self.getBuildArtifact("a.out") + source = "main.c" + self.build_and_launch(program) + + breakpoint_line = line_number(source, "// breakpoint") + self.set_source_breakpoints(source, [breakpoint_line]) + self.continue_to_next_stop() + + # Use mock IDs to test the infrastructure + # In a real scenario, these would come from the parent session + test_debugger_id = 1 + test_target_id = 100 + + # Send a startDebugging request with debuggerId and targetId + # This simulates creating a child DAP session that reuses the debugger + self.dap_server.request_evaluate( + f'`lldb-dap start-debugging attach \'{{"debuggerId":{test_debugger_id},"targetId":{test_target_id}}}\'', + context="repl", + ) + + self.continue_to_exit() + + # Verify the reverse request was sent with the correct IDs + self.assertEqual( + len(self.dap_server.reverse_requests), + 1, + "Should have received one startDebugging reverse request", + ) + + request = self.dap_server.reverse_requests[0] + self.assertEqual(request["command"], "startDebugging") + self.assertEqual(request["arguments"]["request"], "attach") + + config = request["arguments"]["configuration"] + self.assertEqual( + config["debuggerId"], + test_debugger_id, + "Reverse request should include debugger ID", + ) + self.assertEqual( + config["targetId"], + test_target_id, + "Reverse request should include target ID", + ) |
