-
Notifications
You must be signed in to change notification settings - Fork 102
/
Copy pathBacktrace+Symbolication.swift
165 lines (150 loc) · 6.17 KB
/
Backtrace+Symbolication.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
//
private import _TestingInternals
/// A type representing a backtrace or stack trace.
@_spi(Experimental) @_spi(ForToolsIntegrationOnly)
extension Backtrace {
/// An enumeration describing the symbolication mode to use when handling
/// events containing backtraces.
public enum SymbolicationMode: Sendable {
/// The backtrace should be symbolicated, but no demangling should be
/// performed.
case mangled
/// The backtrace should be symbolicated and Swift symbols should be
/// demangled if possible.
///
/// "Foreign" symbol names such as those produced by C++ are not demangled.
case demangled
}
/// A type representing an instance of ``Backtrace/Address`` that has been
/// symbolicated by a call to ``Backtrace/symbolicate(_:)``.
public struct SymbolicatedAddress: Sendable {
/// The (unsymbolicated) address from the backtrace.
public var address: Address
/// The offset of ``address`` from the start of the corresponding function,
/// if available.
///
/// If ``address`` could not be resolved to a symbol, the value of this
/// property is `nil`.
public var offset: UInt64?
/// The name of the symbol at ``address``, if available.
///
/// If ``address`` could not be resolved to a symbol, the value of this
/// property is `nil`.
public var symbolName: String?
}
/// Symbolicate the addresses in this backtrace.
///
/// - Parameters:
/// - mode: How to symbolicate the addresses in the backtrace.
///
/// - Returns: An array of strings representing the names of symbols in
/// `addresses`.
///
/// If an address in `addresses` cannot be symbolicated, the corresponding
/// instance of ``SymbolicatedAddress`` in the resulting array has a `nil`
/// value for its ``Backtrace/SymbolicatedAddress/symbolName`` property.
public func symbolicate(_ mode: SymbolicationMode) -> [SymbolicatedAddress] {
var result = addresses.map { SymbolicatedAddress(address: $0) }
#if SWT_TARGET_OS_APPLE
for (i, address) in addresses.enumerated() {
var info = Dl_info()
if 0 != dladdr(UnsafePointer(bitPattern: UInt(clamping: address)), &info) {
let offset = address - Address(clamping: UInt(bitPattern: info.dli_saddr))
let symbolName = info.dli_sname.flatMap(String.init(validatingCString:))
result[i] = SymbolicatedAddress(address: address, offset: offset, symbolName: symbolName)
}
}
#elseif os(Linux) || os(FreeBSD) || os(Android)
// Although these platforms have dladdr(), they do not have symbol names
// from DWARF binaries by default, only from shared libraries. The standard
// library's backtracing functionality has implemented sufficient ELF/DWARF
// parsing to be able to symbolicate Linux backtraces.
// TODO: adopt the standard library's Backtrace on these platforms
#elseif os(Windows)
_withDbgHelpLibrary { hProcess in
guard let hProcess else {
return
}
for (i, address) in addresses.enumerated() {
withUnsafeTemporaryAllocation(of: SYMBOL_INFO_PACKAGEW.self, capacity: 1) { symbolInfo in
let symbolInfo = symbolInfo.baseAddress!
symbolInfo.pointee.si.SizeOfStruct = ULONG(MemoryLayout<SYMBOL_INFOW>.stride)
symbolInfo.pointee.si.MaxNameLen = ULONG(MAX_SYM_NAME)
var displacement = DWORD64(0)
if SymFromAddrW(hProcess, DWORD64(clamping: address), &displacement, symbolInfo.pointer(to: \.si)!) {
let symbolName = String.decodeCString(symbolInfo.pointer(to: \.si.Name)!, as: UTF16.self)?.result
result[i] = SymbolicatedAddress(address: address, offset: displacement, symbolName: symbolName)
}
}
}
}
#elseif os(WASI)
// WASI does not currently support backtracing let alone symbolication.
#else
#warning("Platform-specific implementation missing: backtrace symbolication unavailable")
#endif
if mode != .mangled {
result = result.map { symbolicatedAddress in
var symbolicatedAddress = symbolicatedAddress
if let demangledName = symbolicatedAddress.symbolName.flatMap(_demangle) {
symbolicatedAddress.symbolName = demangledName
}
return symbolicatedAddress
}
}
return result
}
}
// MARK: - Codable
extension Backtrace.SymbolicatedAddress: Codable {}
// MARK: - Swift runtime wrappers
/// Demangle a symbol name.
///
/// - Parameters:
/// - mangledSymbolName: The symbol name to demangle.
///
/// - Returns: The demangled form of `mangledSymbolName` according to the
/// Swift standard library or the platform's C++ standard library, or `nil`
/// if the symbol name could not be demangled.
private func _demangle(_ mangledSymbolName: String) -> String? {
mangledSymbolName.withCString { mangledSymbolName in
guard let demangledSymbolName = swift_demangle(mangledSymbolName, strlen(mangledSymbolName), nil, nil, 0) else {
return nil
}
defer {
free(demangledSymbolName)
}
return String(validatingCString: demangledSymbolName)
}
}
#if os(Windows)
/// Configure the environment to allow calling into the Debug Help library.
///
/// - Parameters:
/// - body: A function to invoke. A process handle valid for use with Debug
/// Help functions is passed in, or `nullptr` if the Debug Help library
/// could not be initialized.
/// - context: An arbitrary pointer to pass to `body`.
///
/// On Windows, the Debug Help library (DbgHelp.lib) is not thread-safe. All
/// calls into it from the Swift runtime and stdlib should route through this
/// function.
private func _withDbgHelpLibrary(_ body: (HANDLE?) -> Void) {
withoutActuallyEscaping(body) { body in
withUnsafePointer(to: body) { context in
_swift_win32_withDbgHelpLibrary({ hProcess, context in
let body = context!.load(as: ((HANDLE?) -> Void).self)
body(hProcess)
}, .init(mutating: context))
}
}
}
#endif