Skip to content

Commit 899d2e9

Browse files
Azoyktoso
authored andcommitted
Runtime: expose demangle() in Runtime module
We should finally expose the demangle functionality; It's been widely used by calling into internal _swift_demangle and instead we should offer a real API. Previous discussions happened between 2019 and 2024, and just never materialized in a complete implementation and proposal. Right now, even more tools are in need of this API, as we are building continious profiling solutions etc, so it is a good time to revisit this topic. This PR is roughly based off @Azoy's https://github.com/swiftlang/swift/pull/25314/files#diff-fd379a721cc9a1c9ef6486eae713e945da842b42170d4d069029a57334371eba from 2019, however it brings it over to the new Runtime module which is a great place to put this functionality - even Backtrace had to recently reinvent calling the demangling infra in this module. Pending SE review.
1 parent 4f25495 commit 899d2e9

File tree

8 files changed

+338
-22
lines changed

8 files changed

+338
-22
lines changed

include/swift/Runtime/Backtrace.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,13 @@ char *_swift_backtrace_demangle(const char *mangledName,
7676
size_t mangledNameLength,
7777
char *outputBuffer,
7878
size_t *outputBufferSize);
79+
80+
SWIFT_RUNTIME_STDLIB_SPI
81+
char *_swift_runtime_demangle(const char *mangledName,
82+
size_t mangledNameLength,
83+
char *outputBuffer,
84+
size_t *outputBufferSize,
85+
uint32_t flags);
7986
#ifdef __cplusplus
8087
} // namespace backtrace
8188
} // namespace runtime

stdlib/public/RuntimeModule/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ set(RUNTIME_SOURCES
5555
ImageSource.swift
5656
Libc.swift
5757
LimitSequence.swift
58+
Mangling.swift
5859
MemoryReader.swift
5960
OSReleaseScanner.swift
6061
ProcMapsScanner.swift
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
//===--- Backtrace.swift --------------------------------------*- swift -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// Defines functions and types allowing to handle Swift's mangling scheme.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
import Swift
18+
19+
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
20+
internal import Darwin
21+
#elseif os(Windows)
22+
internal import ucrt
23+
#elseif canImport(Glibc)
24+
internal import Glibc
25+
#elseif canImport(Musl)
26+
internal import Musl
27+
#endif
28+
internal import BacktracingImpl.Runtime
29+
30+
// - MARK: Demangling
31+
32+
/// Given a mangled Swift symbol, demangle it into a human readable format.
33+
///
34+
/// Valid Swift symbols begin with the following prefixes:
35+
/// ┌─────────────────────╥────────┐
36+
/// │ Swift Version ║ │
37+
/// ╞═════════════════════╬════════╡
38+
/// │ Swift 3 and below ║ _T │
39+
/// ├─────────────────────╫────────┤
40+
/// │ Swift 4 ║ _T0 │
41+
/// ├─────────────────────╫────────┤
42+
/// │ Swift 4.x ║ $S │
43+
/// ├─────────────────────╫────────┤
44+
/// │ Swift 5+ ║ $s │
45+
/// └─────────────────────╨────────┘
46+
///
47+
/// - Parameters:
48+
/// - mangledName: A mangled Swift symbol.
49+
/// - Returns: A human readable demangled Swift symbol.
50+
/// - Warning: The demangled output is lossy is not not guaranteed to be stable across Swift versions.
51+
/// Future versions of Swift may choose to print more (or less) information in the demangled format.
52+
public func demangle(_ mangledName: String) -> String? {
53+
var length: size_t = 0
54+
55+
let demangled = _swift_runtime_demangle(
56+
mangledName, mangledName.utf8.count,
57+
nil, &length,
58+
/*flags=*/0
59+
)
60+
61+
guard let demangled else {
62+
return nil
63+
}
64+
defer { free(demangled) }
65+
66+
// length is the size of the buffer that was allocated, *not* the
67+
// length of the string.
68+
let stringLen = strlen(demangled)
69+
guard stringLen > 0 else {
70+
return nil
71+
}
72+
73+
return demangled.withMemoryRebound(to: UInt8.self, capacity: stringLen) {
74+
let demangledBytes = UnsafeBufferPointer<UInt8>(start: $0, count: stringLen)
75+
return String(decoding: demangledBytes, as: UTF8.self)
76+
}
77+
}
78+
79+
80+
/// Given a mangled Swift symbol, demangle it into a human readable format.
81+
///
82+
/// Valid Swift symbols begin with the following prefixes:
83+
/// ┌─────────────────────╥────────┐
84+
/// │ Swift Version ║ │
85+
/// ╞═════════════════════╬════════╡
86+
/// │ Swift 3 and below ║ _T │
87+
/// ├─────────────────────╫────────┤
88+
/// │ Swift 4 ║ _T0 │
89+
/// ├─────────────────────╫────────┤
90+
/// │ Swift 4.x ║ $S │
91+
/// ├─────────────────────╫────────┤
92+
/// │ Swift 5+ ║ $s │
93+
/// └─────────────────────╨────────┘
94+
///
95+
/// - Parameters:
96+
/// - mangledNameBuffer: A buffer pointer pointing to a null-terminated C
97+
/// string that contains the mangled Swift symbol.
98+
/// - buffer: A pre-allocated buffer to demangle the Swift symbol as a c-string into.
99+
/// - Returns: An enum, `DemanglingResult`, indicating the various result states
100+
/// of demangling.
101+
/// - Warning: The demangled output is lossy is not not guaranteed to be stable across Swift versions.
102+
/// Future versions of Swift may choose to print more (or less) information in the demangled format.
103+
public func demangle(
104+
_ mangledNameBuffer: UnsafeBufferPointer<Int8>,
105+
into buffer: UnsafeMutableBufferPointer<Int8>
106+
) -> DemanglingResult {
107+
var bufferSize = buffer.count
108+
109+
let demangledPtr = _swift_runtime_demangle(
110+
/*mangledName=*/mangledNameBuffer.baseAddress,
111+
/*mangledNameLength=*/mangledNameBuffer.count - 1,
112+
/*outputBuffer=*/buffer.baseAddress,
113+
/*outputBufferSize=*/&bufferSize,
114+
/*flags=*/0
115+
)
116+
117+
guard let demangledPtr else {
118+
return .invalidSymbol
119+
}
120+
121+
// If the buffer size is still equal to the buffer count, the demangle was
122+
// successful.
123+
if bufferSize <= buffer.count {
124+
return .success
125+
}
126+
127+
// However if it's not equal, the result was truncated. Return the amount
128+
// needed to get a full demangle.
129+
return .truncated(bufferSize)
130+
}
131+
132+
/// Given a mangled Swift symbol, demangle it into a human readable format.
133+
///
134+
/// Valid Swift symbols begin with the following prefixes:
135+
/// ┌─────────────────────╥────────┐
136+
/// │ Swift Version ║ │
137+
/// ╞═════════════════════╬════════╡
138+
/// │ Swift 3 and below ║ _T │
139+
/// ├─────────────────────╫────────┤
140+
/// │ Swift 4 ║ _T0 │
141+
/// ├─────────────────────╫────────┤
142+
/// │ Swift 4.x ║ $S │
143+
/// ├─────────────────────╫────────┤
144+
/// │ Swift 5+ ║ $s │
145+
/// └─────────────────────╨────────┘
146+
///
147+
/// - Parameters:
148+
/// - mangledName: A mangled Swift symbol.
149+
/// - buffer: A pre-allocated buffer to demangle the Swift symbol as a c-string into.
150+
/// - Returns: An enum, `DemanglingResult`, indicating the various result states
151+
/// of demangling.
152+
/// - Warning: The demangled output is lossy is not not guaranteed to be stable across Swift versions.
153+
/// Future versions of Swift may choose to print more (or less) information in the demangled format.
154+
public func demangle(
155+
_ mangledName: String,
156+
into buffer: UnsafeMutableBufferPointer<Int8>
157+
) -> DemanglingResult {
158+
mangledName.utf8CString.withUnsafeBufferPointer {
159+
demangle($0, into: buffer)
160+
}
161+
}
162+
163+
/// Represents whether or not demangling of a symbol was successful.
164+
public enum DemanglingResult: Equatable {
165+
/// Demangling completed successfully.
166+
case success
167+
168+
/// Demangling resulted in truncating the result. The payload value is the
169+
/// number of bytes necessary for a full demangle.
170+
case truncated(Int)
171+
172+
/// The passed Swift mangled symbol was invalid.
173+
case invalidSymbol
174+
}

stdlib/public/RuntimeModule/SymbolicatedBacktrace.swift

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -202,24 +202,7 @@ public struct SymbolicatedBacktrace: CustomStringConvertible {
202202

203203
/// Demangle the raw name, if possible.
204204
private func demangleRawName() -> String {
205-
var length: size_t = 0
206-
if let demangled = _swift_backtrace_demangle(rawName, rawName.utf8.count,
207-
nil, &length) {
208-
defer { free(demangled) }
209-
210-
// length is the size of the buffer that was allocated, *not* the
211-
// length of the string.
212-
let stringLen = strlen(demangled)
213-
if stringLen > 0 {
214-
return demangled.withMemoryRebound(to: UInt8.self,
215-
capacity: stringLen) {
216-
let demangledBytes = UnsafeBufferPointer<UInt8>(start: $0,
217-
count: stringLen)
218-
return String(decoding: demangledBytes, as: UTF8.self)
219-
}
220-
}
221-
}
222-
return rawName
205+
demangle(rawName) ?? rawName
223206
}
224207

225208
/// A textual description of this symbol.

stdlib/public/RuntimeModule/modules/Runtime/Runtime.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ char *_swift_backtrace_demangle(const char *rawName,
3838
char *outputBuffer,
3939
size_t *outputBufferSize);
4040

41+
// Demangle the given raw name (supports Swift and C++)
42+
char *_swift_runtime_demangle(const char *rawName,
43+
size_t rawNameLength,
44+
char *outputBuffer,
45+
size_t *outputBufferSize,
46+
uint32_t flags);
47+
4148
#ifdef __cplusplus
4249
}
4350
#endif

stdlib/public/runtime/Backtrace.cpp

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -996,10 +996,18 @@ _swift_backtrace_isThunkFunction(const char *mangledName) {
996996

997997
// Try to demangle a symbol.
998998
SWIFT_RUNTIME_STDLIB_SPI char *
999-
_swift_backtrace_demangle(const char *mangledName,
999+
_swift_runtime_demangle(const char *mangledName,
10001000
size_t mangledNameLength,
10011001
char *outputBuffer,
1002-
size_t *outputBufferSize) {
1002+
size_t *outputBufferSize,
1003+
uint32_t flags) {
1004+
if (flags > 1) {
1005+
swift::fatalError(0, "Only 'flags' value of '0' and '1' is currently supported.");
1006+
}
1007+
if (outputBuffer != nullptr && outputBufferSize == nullptr) {
1008+
swift::fatalError(0, "'outputBuffer' is passed but the size is 'nullptr'.");
1009+
}
1010+
10031011
llvm::StringRef name = llvm::StringRef(mangledName, mangledNameLength);
10041012

10051013
// You must provide buffer size if you're providing your own output buffer
@@ -1008,8 +1016,13 @@ _swift_backtrace_demangle(const char *mangledName,
10081016
}
10091017

10101018
if (Demangle::isSwiftSymbol(name)) {
1011-
// This is a Swift mangling
1012-
auto options = DemangleOptions::SimplifiedUIDemangleOptions();
1019+
// Determine demangling/formatting options:
1020+
auto options = DemangleOptions();
1021+
if (flags == 1) {
1022+
// simplified display options, for backtraces
1023+
options = DemangleOptions::SimplifiedUIDemangleOptions();
1024+
}
1025+
10131026
auto result = Demangle::demangleSymbolAsString(name, options);
10141027
size_t bufferSize;
10151028

@@ -1067,6 +1080,17 @@ _swift_backtrace_demangle(const char *mangledName,
10671080
return nullptr;
10681081
}
10691082

1083+
SWIFT_RUNTIME_STDLIB_SPI char *
1084+
_swift_backtrace_demangle(const char *mangledName,
1085+
size_t mangledNameLength,
1086+
char *outputBuffer,
1087+
size_t *outputBufferSize) {
1088+
return _swift_runtime_demangle(
1089+
mangledName, mangledNameLength,
1090+
outputBuffer, outputBufferSize,
1091+
/*flags=*/1 /* to use the SimplifiedUIDemangleOptions*/);
1092+
}
1093+
10701094
#if SWIFT_BACKTRACE_ON_CRASH_SUPPORTED
10711095
namespace {
10721096

stdlib/public/runtime/Demangle.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,3 +1033,14 @@ char *swift_demangle(const char *mangledName,
10331033
return outputBuffer;
10341034
#endif
10351035
}
1036+
1037+
1038+
SWIFT_RUNTIME_STDLIB_SPI char *
1039+
_swift_stdlib_demangle(const char *mangledName,
1040+
size_t mangledNameLength,
1041+
char *outputBuffer,
1042+
size_t *outputBufferSize,
1043+
uint32_t flags) {
1044+
return swift_demangle(mangledName, mangledNameLength, outputBuffer,
1045+
outputBufferSize, flags);
1046+
}

0 commit comments

Comments
 (0)