From b6b477de58a9fdf207cc39291ad240cf0bd82f42 Mon Sep 17 00:00:00 2001 From: Dave Lee Date: Tue, 18 Nov 2025 09:32:28 -0800 Subject: [PATCH] [Debug] Add pointer based stringForPrintObject (#84742) Adds an overload of `_DebuggerSupport.stringForPrintObject` which takes a pointer and mangled typename as arguments. This will be used to improve performance and resilience of `po` in lldb. The pointer and mangled typename are used to construct an `Any` value, which is then passed into the primary implementation of `stringForPrintObject`. This allows calling `stringForPrintObject` without having to first construct a context that contains all necessary Swift modules. This will improve speed, and also resilience when modules cannot be loaded for whatever reason. rdar://158968103 --- stdlib/public/core/DebuggerSupport.swift | 32 ++++++++++ test/abi/Inputs/macOS/arm64/stdlib/baseline | 1 + .../macOS/arm64/stdlib/baseline-asserts | 1 + test/abi/Inputs/macOS/x86_64/stdlib/baseline | 1 + .../macOS/x86_64/stdlib/baseline-asserts | 1 + test/stdlib/DebuggerSupport.swift | 63 +++++++++++++++++++ 6 files changed, 99 insertions(+) diff --git a/stdlib/public/core/DebuggerSupport.swift b/stdlib/public/core/DebuggerSupport.swift index 93efe80075c..0eba1258947 100644 --- a/stdlib/public/core/DebuggerSupport.swift +++ b/stdlib/public/core/DebuggerSupport.swift @@ -328,6 +328,38 @@ public enum _DebuggerSupport { return target } + + // Print an object or value without the caller having a concrete type. + // + // For simplicity of data handling in LLDB avoids using an enum return type, + // using (Bool, String) instead of Optional. + @available(SwiftStdlib 6.3, *) + public static func stringForPrintObject(_ pointer: UnsafeRawPointer?, mangledTypeName: String) -> (Bool, String) { + guard let pointer = unsafe pointer else { + return (false, "invalid pointer") + } + + guard let type = + unsafe _getTypeByMangledNameInContext( + mangledTypeName, + UInt(mangledTypeName.count), + genericContext: nil, + genericArguments: nil) + else { + return (false, "type not found for mangled name: \(mangledTypeName)") + } + + func loadPointer(type: T.Type) -> Any { + if type is AnyObject.Type { + unsafe unsafeBitCast(pointer, to: T.self) + } else { + unsafe pointer.load(as: T.self) + } + } + + let anyValue = _openExistential(type, do: loadPointer) + return (true, stringForPrintObject(anyValue)) + } } public func _stringForPrintObject(_ value: Any) -> String { diff --git a/test/abi/Inputs/macOS/arm64/stdlib/baseline b/test/abi/Inputs/macOS/arm64/stdlib/baseline index 4dfa8d6a7c5..e2cefe00d87 100644 --- a/test/abi/Inputs/macOS/arm64/stdlib/baseline +++ b/test/abi/Inputs/macOS/arm64/stdlib/baseline @@ -6815,6 +6815,7 @@ _$ss16TextOutputStreamPsE11_writeASCIIyySRys5UInt8VGF _$ss16TextOutputStreamPsE5_lockyyF _$ss16TextOutputStreamPsE7_unlockyyF _$ss16TextOutputStreamTL +_$ss16_DebuggerSupportO20stringForPrintObject_15mangledTypeNameSb_SStSVSg_SStFZ _$ss16_DebuggerSupportO20stringForPrintObjectySSypFZ _$ss16_DebuggerSupportOMa _$ss16_DebuggerSupportOMn diff --git a/test/abi/Inputs/macOS/arm64/stdlib/baseline-asserts b/test/abi/Inputs/macOS/arm64/stdlib/baseline-asserts index 1589099082f..b4c2cd5b74e 100644 --- a/test/abi/Inputs/macOS/arm64/stdlib/baseline-asserts +++ b/test/abi/Inputs/macOS/arm64/stdlib/baseline-asserts @@ -6815,6 +6815,7 @@ _$ss16TextOutputStreamPsE11_writeASCIIyySRys5UInt8VGF _$ss16TextOutputStreamPsE5_lockyyF _$ss16TextOutputStreamPsE7_unlockyyF _$ss16TextOutputStreamTL +_$ss16_DebuggerSupportO20stringForPrintObject_15mangledTypeNameSb_SStSVSg_SStFZ _$ss16_DebuggerSupportO20stringForPrintObjectySSypFZ _$ss16_DebuggerSupportOMa _$ss16_DebuggerSupportOMn diff --git a/test/abi/Inputs/macOS/x86_64/stdlib/baseline b/test/abi/Inputs/macOS/x86_64/stdlib/baseline index e05241dc5d3..48dc1820c19 100644 --- a/test/abi/Inputs/macOS/x86_64/stdlib/baseline +++ b/test/abi/Inputs/macOS/x86_64/stdlib/baseline @@ -6836,6 +6836,7 @@ _$ss16TextOutputStreamPsE11_writeASCIIyySRys5UInt8VGF _$ss16TextOutputStreamPsE5_lockyyF _$ss16TextOutputStreamPsE7_unlockyyF _$ss16TextOutputStreamTL +_$ss16_DebuggerSupportO20stringForPrintObject_15mangledTypeNameSb_SStSVSg_SStFZ _$ss16_DebuggerSupportO20stringForPrintObjectySSypFZ _$ss16_DebuggerSupportOMa _$ss16_DebuggerSupportOMn diff --git a/test/abi/Inputs/macOS/x86_64/stdlib/baseline-asserts b/test/abi/Inputs/macOS/x86_64/stdlib/baseline-asserts index cc4c136826c..e9d6bfe251c 100644 --- a/test/abi/Inputs/macOS/x86_64/stdlib/baseline-asserts +++ b/test/abi/Inputs/macOS/x86_64/stdlib/baseline-asserts @@ -6836,6 +6836,7 @@ _$ss16TextOutputStreamPsE11_writeASCIIyySRys5UInt8VGF _$ss16TextOutputStreamPsE5_lockyyF _$ss16TextOutputStreamPsE7_unlockyyF _$ss16TextOutputStreamTL +_$ss16_DebuggerSupportO20stringForPrintObject_15mangledTypeNameSb_SStSVSg_SStFZ _$ss16_DebuggerSupportO20stringForPrintObjectySSypFZ _$ss16_DebuggerSupportOMa _$ss16_DebuggerSupportOMn diff --git a/test/stdlib/DebuggerSupport.swift b/test/stdlib/DebuggerSupport.swift index 8747680505c..999af04b77e 100644 --- a/test/stdlib/DebuggerSupport.swift +++ b/test/stdlib/DebuggerSupport.swift @@ -10,6 +10,11 @@ struct StructWithMembers { var b = "Hello World" } +struct StructIsNonCopyable: ~Copyable { + var a = 1 + var b = "Hello World" +} + class ClassWithMembers { var a = 1 var b = "Hello World" @@ -108,6 +113,64 @@ if #available(SwiftStdlib 6.1, *) { } } +@available(SwiftStdlib 6.3, *) +func _expectStringForPrintObject(_ pointer: UnsafePointer, output: String) { + guard let mangledTypeName = _mangledTypeName(T.self) else { + expectTrue(false) + return + } + let (success, printed) = + _DebuggerSupport.stringForPrintObject(UnsafeRawPointer(pointer), mangledTypeName: mangledTypeName) + expectTrue(success) + expectEqual(printed, output) +} + +if #available(SwiftStdlib 6.3, *) { + StringForPrintObjectTests.test("PointerWithMangledTypeName") { + var num = 33 + _expectStringForPrintObject(&num, output: "33\n") + + var val1 = StructWithMembers() + _expectStringForPrintObject(&val1, output: "▿ StructWithMembers\n - a : 1\n - b : \"Hello World\"\n") + + var val2: StructWithMembers? = StructWithMembers() + _expectStringForPrintObject(&val2, + output: "▿ Optional\n ▿ some : StructWithMembers\n - a : 1\n - b : \"Hello World\"\n") + + do { + var val3 = StructIsNonCopyable() + if let mangledTypeName = _mangledTypeName(StructIsNonCopyable.self) { + withUnsafeBytes(of: &val3) { bytes in + guard let pointer = bytes.baseAddress else { + expectTrue(false) + return + } + let (success, printed) = + _DebuggerSupport.stringForPrintObject(pointer, mangledTypeName: mangledTypeName) + expectFalse(success) + expectEqual(printed, "type not found for mangled name: \(mangledTypeName)") + } + } else { + expectTrue(false) + } + } + + do { + let obj = ClassWithMembers() + if let mangledTypeName = _mangledTypeName(ClassWithMembers.self) { + withExtendedLifetime(obj) { obj in + let pointer = unsafeBitCast(obj, to: UnsafeRawPointer.self) + let (success, printed) = _DebuggerSupport.stringForPrintObject(pointer, mangledTypeName: mangledTypeName) + expectTrue(success) + expectTrue(printed.hasPrefix("