Skip to content

Commit d392c27

Browse files
committed
move demangle() func to UTF8Span and OutputSpan
1 parent cb7ddbe commit d392c27

File tree

11 files changed

+513
-81
lines changed

11 files changed

+513
-81
lines changed

benchmark/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ set(SWIFT_BENCH_MODULES
168168
single-source/RemoveWhere
169169
single-source/ReversedCollections
170170
single-source/RomanNumbers
171+
single-source/RuntimeDemangle
171172
single-source/SIMDRandomMask
172173
single-source/SIMDReduceInteger
173174
# Disabled while layout prespecializations are experimental

benchmark/utils/main.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ import ReduceInto
174174
import RemoveWhere
175175
import ReversedCollections
176176
import RomanNumbers
177+
import RuntimeDemangle
177178
import SIMDRandomMask
178179
import SIMDReduceInteger
179180
import SequenceAlgos
@@ -375,6 +376,7 @@ register(ReduceInto.benchmarks)
375376
register(RemoveWhere.benchmarks)
376377
register(ReversedCollections.benchmarks)
377378
register(RomanNumbers.benchmarks)
379+
register(RuntimeDemangle.benchmarks)
378380
register(SIMDRandomMask.benchmarks)
379381
register(SIMDReduceInteger.benchmarks)
380382
register(SequenceAlgos.benchmarks)

include/swift/Runtime/Backtrace.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ char *_swift_backtrace_demangle(const char *mangledName,
7676
size_t mangledNameLength,
7777
char *outputBuffer,
7878
size_t *outputBufferSize);
79+
7980
#ifdef __cplusplus
8081
} // namespace backtrace
8182
} // namespace runtime

include/swift/Runtime/Runtime.h

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//===--- Runtime.cpp - Runtime functions exposed in Runtime module ---- ===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2022 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+
// Definitions relating to public runtime module.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#ifndef SWIFT_RUNTIME_MANGLING_H
18+
#define SWIFT_RUNTIME_MANGLING_H
19+
20+
#ifdef __linux__
21+
#include <sys/types.h>
22+
#include <sys/wait.h>
23+
24+
#include <signal.h>
25+
#endif // defined(__linux__)
26+
27+
#include "swift/Runtime/Config.h"
28+
#include "swift/Runtime/CrashInfo.h"
29+
30+
#include "swift/shims/Visibility.h"
31+
32+
#include <inttypes.h>
33+
34+
#ifdef __cplusplus
35+
namespace swift {
36+
namespace runtime {
37+
namespace mangling {
38+
#endif
39+
40+
SWIFT_RUNTIME_STDLIB_SPI
41+
char * _swift_runtime_demangle(
42+
const char *mangledName,
43+
size_t mangledNameLength,
44+
char *outputBuffer,
45+
size_t *outputBufferSize,
46+
uint32_t flags
47+
);
48+
49+
#ifdef __cplusplus
50+
} // namespace mangling
51+
} // namespace runtime
52+
} // namespace swift
53+
#endif
54+
55+
#endif // SWIFT_RUNTIME_MANGLING_H

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: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
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+
/// If the provided bytes are not a valid mangled swift name, the output span will be initialized with zero elements.
35+
/// If mangling succeeds the output span will contain the resulting demangled string.
36+
/// A successfully demangled string is _not_ null terminated, and its length is communicated by the `initializedCount`
37+
/// of the output span.
38+
///
39+
/// The demangled output may be _truncated_ if the output span's capacity is insufficient for the
40+
/// demangled output string! You can detect this situation by inspecting the returned ``DemanglingResult``,
41+
/// for the ``DemanglingResult/truncated`` case.
42+
///
43+
/// Valid Swift symbols begin with the following prefixes:
44+
/// ┌─────────────────────╥────────┐
45+
/// │ Swift Version ║ │
46+
/// ╞═════════════════════╬════════╡
47+
/// │ Swift 3 and below ║ _T │
48+
/// ├─────────────────────╫────────┤
49+
/// │ Swift 4 ║ _T0 │
50+
/// ├─────────────────────╫────────┤
51+
/// │ Swift 4.x ║ $S │
52+
/// ├─────────────────────╫────────┤
53+
/// │ Swift 5+ ║ $s │
54+
/// └─────────────────────╨────────┘
55+
///
56+
/// - Parameters:
57+
/// - mangledName: A mangled Swift symbol.
58+
/// - Returns: A human readable demangled Swift symbol.
59+
/// - Warning: The demangled output is lossy is not not guaranteed to be stable across Swift versions.
60+
/// Future versions of Swift may choose to print more (or less) information in the demangled format.
61+
@available(StdlibDeploymentTarget 6.3, *)
62+
public func demangle(_ mangledName: String) -> String? {
63+
var length: size_t = 0
64+
65+
let demangled: UnsafeMutablePointer<Int8>? = _swift_runtime_demangle(
66+
mangledName, mangledName.utf8.count,
67+
nil, &length,
68+
/*flags=*/0
69+
)
70+
71+
guard length > 0 else {
72+
assert(demangled == nil)
73+
return nil
74+
}
75+
defer { free(demangled) }
76+
77+
return UnsafeBufferPointer(start: demangled, count: length).withMemoryRebound(to: UTF8.CodeUnit.self) { buffer in
78+
guard let demangledSpan = try? UTF8Span(validating: buffer.span) else {
79+
return nil
80+
}
81+
return String(copying: demangledSpan)
82+
}
83+
}
84+
85+
/// Given a mangled Swift symbol, demangle it into a human readable format.
86+
///
87+
/// If the provided bytes are not a valid mangled swift name, the output span will be initialized with zero elements.
88+
/// If mangling succeeds the output span will contain the resulting demangled string.
89+
/// A successfully demangled string is _not_ null terminated, and its length is communicated by the `initializedCount`
90+
/// of the output span.
91+
///
92+
/// The demangled output may be _truncated_ if the output span's capacity is insufficient for the
93+
/// demangled output string! You can detect this situation by inspecting the returned ``DemanglingResult``,
94+
/// for the ``DemanglingResult/truncated`` case.
95+
///
96+
/// Valid Swift symbols begin with the following prefixes:
97+
/// ┌─────────────────────╥────────┐
98+
/// │ Swift Version ║ │
99+
/// ╞═════════════════════╬════════╡
100+
/// │ Swift 3 and below ║ _T │
101+
/// ├─────────────────────╫────────┤
102+
/// │ Swift 4 ║ _T0 │
103+
/// ├─────────────────────╫────────┤
104+
/// │ Swift 4.x ║ $S │
105+
/// ├─────────────────────╫────────┤
106+
/// │ Swift 5+ ║ $s │
107+
/// └─────────────────────╨────────┘
108+
///
109+
/// - Parameters:
110+
/// - mangledName: Mangled name to be demangled.
111+
/// - output: A pre-allocated span to demangle the Swift symbol into.
112+
/// - Returns: An enum, `DemanglingResult`, indicating the various result states
113+
/// of demangling.
114+
/// - Warning: The demangled output is lossy is not not guaranteed to be stable across Swift versions.
115+
/// Future versions of Swift may choose to print more (or less) information in the demangled format.
116+
@available(StdlibDeploymentTarget 6.3, *)
117+
public func demangle(
118+
_ mangledName: borrowing UTF8Span,
119+
into output: inout OutputSpan<UTF8.CodeUnit>
120+
) -> DemanglingResult {
121+
var demangledLength = output.capacity
122+
let outputCapacity = output.capacity
123+
124+
let demangledPtr = output.withUnsafeMutableBufferPointer { outputBufferUInt8, outputLength in
125+
outputBufferUInt8.withMemoryRebound(to: Int8.self) { outputBuffer in
126+
mangledName.span.withUnsafeBytes { mangledNamePtr in
127+
let res = _swift_runtime_demangle(
128+
mangledNamePtr.baseAddress, mangledName.count,
129+
outputBuffer.baseAddress, &demangledLength,
130+
/*flags=*/0)
131+
132+
// If the demangled string is longer than the Span capacity, we
133+
// demangled only up-to the capacity of the span, and therefore must
134+
// indicate this here. Such situation will return a `.truncated` result.
135+
outputLength = min(outputCapacity, demangledLength)
136+
137+
return res
138+
}
139+
}
140+
}
141+
142+
guard demangledPtr != nil else {
143+
return .invalidSymbol
144+
}
145+
146+
// If the buffer size is still equal to the buffer count, the demangle was
147+
// successful.
148+
if demangledLength <= output.capacity {
149+
return .success
150+
}
151+
152+
// The result was truncated. Return the amount needed to get a full demangle.
153+
return .truncated(demangledLength)
154+
}
155+
156+
/// Represents whether or not demangling of a symbol was successful.
157+
@available(StdlibDeploymentTarget 6.3, *)
158+
public enum DemanglingResult: Equatable {
159+
/// Demangling completed successfully.
160+
case success
161+
162+
/// Demangling resulted in truncating the result. The payload value is the
163+
/// number of bytes necessary for a full demangle.
164+
case truncated(Int)
165+
166+
/// The passed Swift mangled symbol was invalid.
167+
case invalidSymbol
168+
}

stdlib/public/RuntimeModule/SymbolicatedBacktrace.swift

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -202,24 +202,28 @@ 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)
205+
if #available(SwiftStdlib 6.3, *) {
206+
// Reuse the public API demangle() when available
207+
return demangle(rawName) ?? rawName
208+
} else {
209+
// Fallback to backtrace entrypoint to the demangler.
210+
var length: size_t = 0
211+
if let demangled = _swift_backtrace_demangle(rawName, rawName.utf8.count, nil, &length) {
212+
defer { free(demangled) }
213+
214+
// length is the size of the buffer that was allocated, *not* the
215+
// length of the string.
216+
let stringLen = strlen(demangled)
217+
if stringLen > 0 {
218+
return demangled.withMemoryRebound(to: UInt8.self, capacity: stringLen) {
219+
let demangledBytes = UnsafeBufferPointer<UInt8>(start: $0,
220+
count: stringLen)
221+
return String(decoding: demangledBytes, as: UTF8.self)
222+
}
219223
}
220224
}
225+
return rawName
221226
}
222-
return rawName
223227
}
224228

225229
/// A textual description of this symbol.
@@ -294,6 +298,8 @@ public struct SymbolicatedBacktrace: CustomStringConvertible {
294298

295299
/// Create a symbolicator.
296300
private static func withSymbolicator<T>(images: ImageMap,
301+
302+
297303
useSymbolCache: Bool,
298304
fn: (CSSymbolicatorRef) throws -> T) rethrows -> T {
299305
let binaryImageList = images.map{ image in

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,29 @@ void swift_reportWarning(uint32_t flags, const char *message);
3333
bool _swift_backtrace_isThunkFunction(const char *rawName);
3434

3535
// Demangle the given raw name (supports Swift and C++)
36+
//
37+
// The demangled string IS a null-terminated c-string.
3638
char *_swift_backtrace_demangle(const char *rawName,
3739
size_t rawNameLength,
3840
char *outputBuffer,
3941
size_t *outputBufferSize);
4042

43+
// Demangle the given raw name (supports Swift and C++)
44+
//
45+
// Optionally an output buffer may be passed into which the demangled string will be written.
46+
// If null is passed as the 'outputBuffer' the runtime function will allocate a buffer and return it.
47+
// If an 'outputBuffer' is passed, the output will be written into it, and the same buffer will be returned from this
48+
//
49+
// The demangled result String is NOT null-terminated.
50+
// The demangled string length is indicated through the outputBufferSize parameter.
51+
//
52+
// Introduced in Swift 6.3.
53+
char *_swift_runtime_demangle(const char *rawName,
54+
size_t rawNameLength,
55+
char *outputBuffer,
56+
size_t *outputBufferSize,
57+
uint32_t flags);
58+
4159
#ifdef __cplusplus
4260
}
4361
#endif

0 commit comments

Comments
 (0)