From e0bc3cb02112c6baad0d41e8e6d7466504c95cc6 Mon Sep 17 00:00:00 2001 From: Dave Lee Date: Wed, 30 Jul 2025 15:59:36 -0700 Subject: [PATCH 1/2] [lldb] Add Swift exception breakpoint frame recognizer --- .../Language/Swift/SwiftFrameRecognizers.cpp | 73 +++++++++++++++++++ .../exception_breakpoint_recognizer/Makefile | 3 + .../TestSwiftExceptionBreakpointRecognizer.py | 30 ++++++++ .../main.swift | 20 +++++ 4 files changed, 126 insertions(+) create mode 100644 lldb/test/API/lang/swift/exception_breakpoint_recognizer/Makefile create mode 100644 lldb/test/API/lang/swift/exception_breakpoint_recognizer/TestSwiftExceptionBreakpointRecognizer.py create mode 100644 lldb/test/API/lang/swift/exception_breakpoint_recognizer/main.swift diff --git a/lldb/source/Plugins/Language/Swift/SwiftFrameRecognizers.cpp b/lldb/source/Plugins/Language/Swift/SwiftFrameRecognizers.cpp index 579dc076ebc66..50e26cc52c996 100644 --- a/lldb/source/Plugins/Language/Swift/SwiftFrameRecognizers.cpp +++ b/lldb/source/Plugins/Language/Swift/SwiftFrameRecognizers.cpp @@ -3,11 +3,14 @@ #include "lldb/Core/Module.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/Platform.h" #include "lldb/Target/Process.h" #include "lldb/Target/StackFrameRecognizer.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/FileSpec.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" @@ -220,6 +223,66 @@ class SwiftHiddenFrameRecognizer : public StackFrameRecognizer { } }; +/// A frame recognizer for Swift exception breakpoints. +class SwiftExceptionBreakpointFrameRecognizer : public StackFrameRecognizer { +public: + class SwiftExceptionFrame : public RecognizedStackFrame { + public: + SwiftExceptionFrame(StackFrameSP frame) : m_frame_sp(frame) { + m_stop_desc = "Swift exception breakpoint"; + } + + StackFrameSP GetMostRelevantFrame() override { + if (!m_frame_sp) + return {}; + + auto thread_sp = m_frame_sp->GetThread(); + if (!thread_sp) + return {}; + + StringRef symbol_name; + { + const SymbolContext &sc = + m_frame_sp->GetSymbolContext(eSymbolContextSymbol); + if (!sc.symbol) + return {}; + symbol_name = sc.symbol->GetName(); + } + + StackFrameSP relevant_frame_sp; + if (symbol_name == "swift_willThrow") + relevant_frame_sp = thread_sp->GetStackFrameAtIndex(1); + else if (symbol_name == "swift_willThrowTypedImpl") + relevant_frame_sp = thread_sp->GetStackFrameAtIndex(2); + else { + assert(false && "unexpected frame name"); + return {}; + } + + if (relevant_frame_sp) { + // Select the relevant frame only if source is available. + const SymbolContext &sc = + relevant_frame_sp->GetSymbolContext(eSymbolContextCompUnit); + if (sc.comp_unit) + return relevant_frame_sp; + } + + return {}; + } + + private: + StackFrameSP m_frame_sp; + }; + + RecognizedStackFrameSP RecognizeFrame(StackFrameSP frame) override { + return std::make_shared(frame); + }; + + std::string GetName() override { + return "Swift exception breakpoint frame recognizer"; + } +}; + void RegisterSwiftFrameRecognizers(Process &process) { RegularExpressionSP module_regex_sp = nullptr; auto &manager = process.GetTarget().GetFrameRecognizerManager(); @@ -245,6 +308,16 @@ void RegisterSwiftFrameRecognizers(Process &process) { manager.AddRecognizer(srf_sp, module_regex_sp, symbol_regex_sp, Mangled::NamePreference::ePreferMangled, false); } + { + auto srf_sp = std::make_shared(); + ConstString module_name = ConstString("swiftCore"); + if (auto platform_sp = process.GetTarget().GetPlatform()) + module_name = platform_sp->GetFullNameForDylib(module_name); + manager.AddRecognizer(srf_sp, module_name, + {ConstString("swift_willThrow"), + ConstString("swift_willThrowTypedImpl")}, + Mangled::NamePreference::ePreferMangled, false); + } } } // namespace lldb_private diff --git a/lldb/test/API/lang/swift/exception_breakpoint_recognizer/Makefile b/lldb/test/API/lang/swift/exception_breakpoint_recognizer/Makefile new file mode 100644 index 0000000000000..cca30b939e652 --- /dev/null +++ b/lldb/test/API/lang/swift/exception_breakpoint_recognizer/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/exception_breakpoint_recognizer/TestSwiftExceptionBreakpointRecognizer.py b/lldb/test/API/lang/swift/exception_breakpoint_recognizer/TestSwiftExceptionBreakpointRecognizer.py new file mode 100644 index 0000000000000..477883dfca425 --- /dev/null +++ b/lldb/test/API/lang/swift/exception_breakpoint_recognizer/TestSwiftExceptionBreakpointRecognizer.py @@ -0,0 +1,30 @@ +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +from lldbsuite.test import lldbutil + + +class TestCase(TestBase): + + @swiftTest + def test(self): + self.build() + + target = lldbutil.run_to_breakpoint_make_target(self) + bp = target.BreakpointCreateForException(lldb.eLanguageTypeSwift, False, True) + + # First breakpoint in an untyped throws function. + _, process, _, _ = lldbutil.run_to_breakpoint_do_run(self, target, bp) + thread = process.selected_thread + stop_desc = thread.GetStopDescription(128) + self.assertEqual(stop_desc, "Swift exception breakpoint") + self.assertEqual(thread.frame[0].symbol.name, "swift_willThrow") + self.assertEqual(thread.selected_frame.idx, 1) + + # Second breakpoint in an typed throws function. + process.Continue() + thread = process.selected_thread + stop_desc = thread.GetStopDescription(128) + self.assertEqual(stop_desc, "Swift exception breakpoint") + self.assertEqual(thread.frame[0].symbol.name, "swift_willThrowTypedImpl") + self.assertEqual(thread.selected_frame.idx, 2) diff --git a/lldb/test/API/lang/swift/exception_breakpoint_recognizer/main.swift b/lldb/test/API/lang/swift/exception_breakpoint_recognizer/main.swift new file mode 100644 index 0000000000000..7275e038e3413 --- /dev/null +++ b/lldb/test/API/lang/swift/exception_breakpoint_recognizer/main.swift @@ -0,0 +1,20 @@ +struct OpaqueError: Error {} + +func untyped() throws { + throw OpaqueError() +} + +func typed() throws(OpaqueError) { + throw OpaqueError() +} + +@main struct Entry { + static func main() { + do { + try untyped() + } catch {} + do { + try typed() + } catch {} + } +} From 24a758a7e6f751b6b04b8c4bac313055cd4638f1 Mon Sep 17 00:00:00 2001 From: Dave Lee Date: Tue, 5 Aug 2025 21:23:46 -0700 Subject: [PATCH 2/2] [lldb] Preserve original symbol of Mangled function names (#152201) Fixes a bug that surfaces in frame recognizers. Details about the bug: A new frame recognizer is configured to match a specific symbol (`swift_willThrow`). This is an `extern "C"` symbol defined in a C++ source file. When Swift is built with debug info, the function `ParseFunctionFromDWARF` will use the debug info to construct a function name that looks like a C++ declaration (`::swift_willThrow(void *, SwiftError**)`). The `Mangled` instance will have this string as its `m_demangled` field, and have _no_ string for its `m_mangled` field. The result is the frame recognizer would not match the symbol to the name (`swift_willThrow` != `::swift_willThrow(void *, SwiftError**)`. By changing `ParseFunctionFromDWARF` to assign both a demangled name and a mangled, frame recognizers can successfully match symbols in this configuration. (cherry picked from commit 2959051e655a5c77401285a68bab2027aa958b88) --- .../source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp | 4 +++- lldb/test/API/lang/cpp/extern_c/main.cpp | 6 ++++-- .../Shell/SymbolFile/DWARF/x86/debug-types-address-ranges.s | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index 67ccd64d15ea9..a355587c8595f 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -2543,7 +2543,9 @@ DWARFASTParserClang::ParseFunctionFromDWARF(CompileUnit &comp_unit, // If the mangled name is not present in the DWARF, generate the // demangled name using the decl context. We skip if the function is // "main" as its name is never mangled. - func_name.SetValue(ConstructDemangledNameFromDWARF(die)); + func_name.SetDemangledName(ConstructDemangledNameFromDWARF(die)); + // Ensure symbol is preserved (as the mangled name). + func_name.SetMangledName(ConstString(name)); } else func_name.SetValue(ConstString(name)); diff --git a/lldb/test/API/lang/cpp/extern_c/main.cpp b/lldb/test/API/lang/cpp/extern_c/main.cpp index 727ea9255ca7f..7017c745be178 100644 --- a/lldb/test/API/lang/cpp/extern_c/main.cpp +++ b/lldb/test/API/lang/cpp/extern_c/main.cpp @@ -8,8 +8,10 @@ extern "C" int foo() { - puts("foo"); - return 2; + puts("foo"); //% self.expect("image lookup -va $pc", + //% substrs=[' name = "::foo()"', + //% ' mangled = "foo"']) + return 2; } int main (int argc, char const *argv[], char const *envp[]) diff --git a/lldb/test/Shell/SymbolFile/DWARF/x86/debug-types-address-ranges.s b/lldb/test/Shell/SymbolFile/DWARF/x86/debug-types-address-ranges.s index 8b3b0e9d56956..792555d99d7b7 100644 --- a/lldb/test/Shell/SymbolFile/DWARF/x86/debug-types-address-ranges.s +++ b/lldb/test/Shell/SymbolFile/DWARF/x86/debug-types-address-ranges.s @@ -11,7 +11,7 @@ # RUN: %lldb %t -o "image lookup -a 0x48000 -v" -o exit | FileCheck %s # CHECK: CompileUnit: id = {0x00000001}, file = "/tmp/a.cc", language = "c++" -# CHECK: Function: id = {0x0000006a}, name = "::_start({{.*}})", range = [0x0000000000048000-0x000000000004800c) +# CHECK: Function: id = {0x0000006a}, name = "::_start({{.*}})", mangled = "_start", range = [0x0000000000048000-0x000000000004800c) # CHECK: LineEntry: [0x0000000000048000-0x000000000004800a): /tmp/a.cc:4 # CHECK: Symbol: id = {0x00000002}, range = [0x0000000000048000-0x000000000004800c), name="_start" # CHECK: Variable: id = {0x00000075}, name = "v1", {{.*}} decl = a.cc:4