From df8f29b6aee05546f3cc421739a079c2643ac273 Mon Sep 17 00:00:00 2001 From: Felipe de Azevedo Piovezan Date: Thu, 31 Jul 2025 18:00:55 -0700 Subject: [PATCH 1/2] [lldb] Clear Frames when changing `disable-language-runtime-unwindplans` (#151208) This patch uses the "setting changed" callback to clear previously cached stack frames when `target.process.disable-language-runtime-unwindplans` is changed. This is necessary so that changing the setting followed by a `backtrace` command produces an accurate backtrace. With this, a user can create a custom command like below in order to quickly inspect a backtrace created without language plugins. ``` debugger.HandleCommand("settings set target.process.disable-language-runtime-unwindplans true") debugger.HandleCommand("bt " + command) debugger.HandleCommand("settings set target.process.disable-language-runtime-unwindplans false") ``` In the future, we may consider implementing this as an option to `backtrace`. Currently, this process setting is the only way of affecting the unwinder, and changing the process setting as part of the backtrace implementation doesn't feel right. There are no upstream users of this flag, so we cannot write a test for it here. (cherry picked from commit 03bb10bea6edeb6b1cbcb3fc6b590e585ae43ad6) --- lldb/include/lldb/Target/Process.h | 1 + lldb/source/Target/Process.cpp | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index e837973d04966..657fa0d5a6d72 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -98,6 +98,7 @@ class ProcessProperties : public Properties { void SetStopOnSharedLibraryEvents(bool stop); bool GetDisableLangRuntimeUnwindPlans() const; void SetDisableLangRuntimeUnwindPlans(bool disable); + void DisableLanguageRuntimeUnwindPlansCallback(); bool GetDetachKeepsStopped() const; void SetDetachKeepsStopped(bool keep_stopped); bool GetWarningsOptimization() const; diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index 8be1ecffead0f..294b7fbd1ee6b 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -207,6 +207,9 @@ ProcessProperties::ProcessProperties(lldb_private::Process *process) m_collection_sp->SetValueChangedCallback( ePropertyPythonOSPluginPath, [this] { m_process->LoadOperatingSystemPlugin(true); }); + m_collection_sp->SetValueChangedCallback( + ePropertyDisableLangRuntimeUnwindPlans, + [this] { DisableLanguageRuntimeUnwindPlansCallback(); }); } m_experimental_properties_up = @@ -321,6 +324,15 @@ void ProcessProperties::SetDisableLangRuntimeUnwindPlans(bool disable) { m_process->Flush(); } +void ProcessProperties::DisableLanguageRuntimeUnwindPlansCallback() { + if (!m_process) + return; + for (auto thread_sp : m_process->Threads()) { + thread_sp->ClearStackFrames(); + thread_sp->DiscardThreadPlans(/*force*/ true); + } +} + bool ProcessProperties::GetDetachKeepsStopped() const { const uint32_t idx = ePropertyDetachKeepsStopped; return GetPropertyAtIndexAs( From 3c902c1506efa22e5a4aa7b69f8105f46110584a Mon Sep 17 00:00:00 2001 From: Felipe de Azevedo Piovezan Date: Tue, 29 Jul 2025 12:31:19 -0700 Subject: [PATCH 2/2] [lldb][swift] Add test for disable-language-runtime-unwindplans This can only be tested on downstream LLDB, as it requires a language with a custom unwind plan. --- .../unwind/disable_language_unwinder/Makefile | 3 +++ .../TestDisableLanguageUnwinder.py | 25 +++++++++++++++++++ .../disable_language_unwinder/main.swift | 13 ++++++++++ 3 files changed, 41 insertions(+) create mode 100644 lldb/test/API/lang/swift/async/unwind/disable_language_unwinder/Makefile create mode 100644 lldb/test/API/lang/swift/async/unwind/disable_language_unwinder/TestDisableLanguageUnwinder.py create mode 100644 lldb/test/API/lang/swift/async/unwind/disable_language_unwinder/main.swift diff --git a/lldb/test/API/lang/swift/async/unwind/disable_language_unwinder/Makefile b/lldb/test/API/lang/swift/async/unwind/disable_language_unwinder/Makefile new file mode 100644 index 0000000000000..cca30b939e652 --- /dev/null +++ b/lldb/test/API/lang/swift/async/unwind/disable_language_unwinder/Makefile @@ -0,0 +1,3 @@ +SWIFT_SOURCES := main.swift +SWIFTFLAGS_EXTRAS := -parse-as-library +include Makefile.rules diff --git a/lldb/test/API/lang/swift/async/unwind/disable_language_unwinder/TestDisableLanguageUnwinder.py b/lldb/test/API/lang/swift/async/unwind/disable_language_unwinder/TestDisableLanguageUnwinder.py new file mode 100644 index 0000000000000..f640b4245f084 --- /dev/null +++ b/lldb/test/API/lang/swift/async/unwind/disable_language_unwinder/TestDisableLanguageUnwinder.py @@ -0,0 +1,25 @@ +import lldb +from lldbsuite.test.decorators import * +import lldbsuite.test.lldbtest as lldbtest +import lldbsuite.test.lldbutil as lldbutil + +class TestDisableLanguageUnwinder(lldbtest.TestBase): + + @swiftTest + @skipIf(oslist=['windows', 'linux']) + def test(self): + """Test async unwind""" + self.build() + src = lldb.SBFileSpec('main.swift') + target, process, thread, bkpt = lldbutil.run_to_source_breakpoint( + self, 'break here', src) + + self.assertIn("syncFunc", thread.GetFrameAtIndex(0).GetFunctionName()) + self.assertIn("callSyncFunc", thread.GetFrameAtIndex(1).GetFunctionName()) + self.assertIn("main", thread.GetFrameAtIndex(2).GetFunctionName()) + + self.runCmd("settings set target.process.disable-language-runtime-unwindplans true") + + self.assertIn("syncFunc", thread.GetFrameAtIndex(0).GetFunctionName()) + self.assertIn("callSyncFunc", thread.GetFrameAtIndex(1).GetFunctionName()) + self.assertNotIn("main", thread.GetFrameAtIndex(2).GetFunctionName()) diff --git a/lldb/test/API/lang/swift/async/unwind/disable_language_unwinder/main.swift b/lldb/test/API/lang/swift/async/unwind/disable_language_unwinder/main.swift new file mode 100644 index 0000000000000..32c1804b10166 --- /dev/null +++ b/lldb/test/API/lang/swift/async/unwind/disable_language_unwinder/main.swift @@ -0,0 +1,13 @@ +func syncFunc() { + print("break here") +} + +func callSyncFunc() async { + syncFunc() +} + +@main struct Main { + static func main() async { + await callSyncFunc() + } +}