Skip to content

Commit eef3a8f

Browse files
committed
swift-inspect: restructure the project for porting
This restructures and refactors the project to split up the implementation into smaller fragments. The majority of the operations are portable: the ones which are not require iterating the heap allocations which is not guaranteed to be a portable operation. Additionally, the `Inspector` class is renamed into `RemoteProcess` which is also converted to a protocol to allow adding in an implementation for other targets. The inspection operations are split off into individual files to make it easier to follow. Take the opportunity to use `@main` for the entry point.
1 parent 83be9ba commit eef3a8f

19 files changed

+780
-821
lines changed

tools/swift-inspect/Package.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ let package = Package(
1616
dependencies: [
1717
"SymbolicationShims",
1818
.product(name: "ArgumentParser", package: "swift-argument-parser"),
19+
],
20+
swiftSettings: [
21+
.unsafeFlags([
22+
"-parse-as-library",
23+
]),
1924
]),
2025
.testTarget(
2126
name: "swiftInspectTests",

tools/swift-inspect/Sources/SymbolicationShims/SymbolicationShims.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
#if defined(__APPLE__)
14+
1315
#include <stdint.h>
1416
#include <ptrauth.h>
1517

@@ -28,3 +30,5 @@ static inline uintptr_t GetPtrauthMask(void) {
2830
return (uintptr_t)~0ull;
2931
#endif
3032
}
33+
34+
#endif

tools/swift-inspect/Sources/swift-inspect/Backtrace.swift

Lines changed: 18 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,45 +12,26 @@
1212

1313
import SwiftRemoteMirror
1414

15-
struct Backtrace {
16-
enum Style {
17-
case oneLine
18-
case long
19-
}
20-
21-
/// The pointers to the locations in the backtrace. These are stored from
22-
/// deepest to shallowest, so main() will be somewhere near the end.
23-
var ptrs: [swift_reflection_ptr_t]
24-
25-
func symbolString(
26-
ptr: swift_reflection_ptr_t,
27-
inspector: Inspector
28-
) -> String {
29-
let symbol = inspector.getSymbol(address: swift_addr_t(ptr))
30-
let name = symbol.name ?? "<unknown>"
31-
let library = symbol.library ?? "<unknown>"
32-
return "\(hex: ptr) (\(library)) \(name)"
33-
}
34-
35-
func symbolicatedOneLine(inspector: Inspector) -> String {
36-
return ptrs.reversed().map {
37-
symbolString(ptr: $0, inspector: inspector)
38-
}.joined(separator: " | ")
39-
}
15+
internal enum BacktraceStyle {
16+
case oneline
17+
case long
18+
}
4019

41-
func symbolicatedLong(inspector: Inspector) -> String {
42-
return ptrs.reversed().enumerated().map {
43-
let indent = String(repeating: " ", count: $0 + 1)
44-
return indent + symbolString(ptr: $1, inspector: inspector)
45-
}.joined(separator: "\n")
20+
internal func backtrace(_ stack: [swift_reflection_ptr_t], style: BacktraceStyle,
21+
_ symbolicate: (swift_addr_t) -> (module: String?, symbol: String?)) -> String {
22+
func entry(_ address: swift_reflection_ptr_t) -> String {
23+
let (module, symbol) = symbolicate(address)
24+
return "\(hex: address) (\(module ?? "<uknown>")) \(symbol ?? "<unknown>")"
4625
}
4726

48-
func symbolicated(style: Style, inspector: Inspector) -> String {
49-
switch style {
50-
case .oneLine:
51-
return symbolicatedOneLine(inspector: inspector)
52-
case .long:
53-
return symbolicatedLong(inspector: inspector)
54-
}
27+
// The pointers to the locations in the backtrace are stored from deepest to
28+
// shallowest, so `main` will be somewhere near the end.
29+
switch style {
30+
case .oneline:
31+
return self.reversed().map { entry($0) }.joined(separator: " | ")
32+
case .long:
33+
return self.reversed().enumerated().map {
34+
" \(String(repeating: " ", count: $0 + 1)\(entry($1))"
35+
}.joined(separtor: "\n")
5536
}
5637
}
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2020 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+
#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS)
14+
15+
import SwiftRemoteMirror
16+
@_implementationOnly import SymbolicationShims
17+
18+
internal final class DarwinRemoteProcess: RemoteProcess {
19+
public typealias ProcessIdentifier = pid_t
20+
public typealias ProcessHandle = task_t
21+
22+
private var task: task_t
23+
24+
public var process: ProcessHandle { task }
25+
public private(set) var context: SwiftReflectionContextRef!
26+
27+
private var swiftCore: ??? = ???
28+
29+
static var QueryDataLayout: QueryDataLayoutFunction {
30+
return { (context, type, _, output) in
31+
guard let output = output else { return 0 }
32+
33+
switch type {
34+
case DLQ_GetPointerSize, DLQ_GetSizeSize:
35+
let size = UInt8(MemoryLayout<UnsafeRawPointer>.stride)
36+
output.storeBytes(of: size, toByteOffset: 0, as: UInt8.self)
37+
return 1
38+
39+
case DLQ_GetPtrAuthMask:
40+
let mask = GetPtrauthMask()
41+
output.storeBytes(of: mask, toByteOffset: 0, as: UInt.size)
42+
return 1
43+
44+
case DLQ_GetObjCReservedLowBits:
45+
var size: UInt8 = 0
46+
#if os(macoS)
47+
// Only 64-bit macOS reserves pointer bit-packing.
48+
if MemoryLayout<UnsafeRawPointer>.stride == 8 { size = 1 }
49+
#endif
50+
output.storeBytes(of: size, toByteOffset: 0, as: UInt8.self)
51+
return 1
52+
53+
case DLQ_GetLeastValidPointerValue:
54+
var value: UInt64 = 0x1000
55+
#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS)
56+
// 64-bit Apple platforms reserve the low 4GiB.
57+
if MemoryLayout<UnsafeRawPointer>.stride == 8 { value = 0x1_0000_0000 }
58+
#endif
59+
output.storeBytes(of: value, toByteOffset: 0, as: UInt64.self)
60+
return 1
61+
62+
default:
63+
return 0
64+
}
65+
}
66+
}
67+
68+
static var ReadBytes: ReadBytesFunction {
69+
return { (context, address, size, _) in
70+
let process: DarwinRemoteProcess = DarwinRemoteProcess.fromOpaque(context!)
71+
return task_peek(process.task, address, size)
72+
}
73+
}
74+
75+
static var GetStringLength: GetStringLengthFunction {
76+
return { (context, address) in
77+
let process: DarwinRemoteProcess = DarwinRemoteProcess.fromOpaque(context!)
78+
if let str = task_peek_string(process.task, address) {
79+
return UInt64(strlen(str))
80+
}
81+
return 0
82+
}
83+
}
84+
85+
static var GetSymbolAddress: GetSymbolAddressFunction {
86+
return { (context, symbol, length) in
87+
let process: DarwinRemoteProcess = DarwinRemoteProcess.fromOpaque(context!)
88+
guard let symbol = symbol else { return 0 }
89+
let name: String = symbol.withMemoryRebound(to: UInt8.self, capacity: Int(length)) {
90+
let buffer = UnsafeBufferPointer(start: $0, count: Int(length))
91+
return String(decoding: buffer, as: UTF8.self)
92+
}
93+
94+
let symbol =
95+
CSSymbolOwnerGetSymbolWithMangledName(process.swiftCore, "_\(name)")
96+
// FIXME: use `__USER_LABEL_PREFIX__` instead of the hardcoded `_`.
97+
return swift_addr_t(CSSymbolGetRange(symbol).location)
98+
}
99+
}
100+
101+
init?(processId: ProcessIdentifier) {
102+
var task: task_t = task_t()
103+
let result = task_for_pid(mach_task_self_, processId, &task)
104+
guard result == KERN_SUCCESS else {
105+
print("unable to get task for pid \(processId): \(String(cString: mach_error_string(result)) (0x\(String(result, radix: 16)))")
106+
return nil
107+
}
108+
self.task = task
109+
110+
guard let context =
111+
swift_reflection_createReflectionContextWithDataLayout(self.toOpaqueRef(),
112+
Self.QueryDataLayout,
113+
Self.Free,
114+
Self.ReadBytes,
115+
Self.GetStringLength,
116+
Self.GetSymbolAddress) else {
117+
return nil
118+
}
119+
120+
self.symbolicator = CSSymbolicatorCreateWithTask(self.task)
121+
self.swiftCore =
122+
CSSymbolicatorGetSymbolOwnerWithNameAtTime(self.symbolicator,
123+
"libswiftCore.dylib", kCSNow)
124+
_ = task_start_peeking(self.task)
125+
126+
_ = CSSymbolicatorForeachSymbolOwnerAtTime(self.symbolicator, kCSNow, { owner in
127+
let address = CSSymbolOwnerGetBaseAddress(owner)
128+
_ = swift_reflection_addImage(self.context, address)
129+
})
130+
}
131+
132+
deinit {
133+
task_stop_peeking(self.task)
134+
mach_port_deallocate(mach_task_self_, self.task)
135+
}
136+
137+
func symbolicate(_ address: swift_addr_t) -> (module: String?, symbol: String?) {
138+
let symbol =
139+
CSSymbolicatorGetSymbolWithAddressAtTime(sself.symbolicator, address, kCSNow)
140+
141+
let module = CSSymbolGetSymbolOwner(symbol)
142+
return (CSSymbolOwnerGetName(module), CSSymbolGetName(symbol))
143+
}
144+
}
145+
146+
extension DarwinRemoteProcess {
147+
internal func iterateHeap(_ body: (swift_addr_t, UInt64) -> Void) {
148+
withoutActuallyEscaping(body) {
149+
withUnsafePointer(to: $0) {
150+
task_enumerate_malloc_blocks(self.task,
151+
UnsafeMutableRawPointer(mutating: $0),
152+
CUnsignedInt(MALLOC_PTR_IN_USE_RANGE_TYPE),
153+
{ (task, context, type, ranges, count) in
154+
ranges.forEach {
155+
body(swift_addr_t($0.address), UInt64($0.size))
156+
}
157+
}
158+
}
159+
}
160+
}
161+
162+
internal var currentTasks: [(threadID: UInt64, currentTask: swift_addr_t)] {
163+
var threadList: UnsafeMutablePointer<thread_t>?
164+
var threadCount: mach_msg_type_number_t = 0
165+
166+
let result = task_threads(self.task, &threadList, &threadCount)
167+
guard result == KERN_SUCCESS else {
168+
print("unable to gather threads for process: \(String(cString: mach_error_string(result)) (0x\(String(result, radix: 16)))")
169+
return []
170+
}
171+
172+
defer {
173+
// Deallocate the port rights for the threads.
174+
for i in 0 ..< Int(threadCount) {
175+
mach_port_deallocate(mach_task_self_, threadList![i])
176+
}
177+
178+
// Deallocate the thread list.
179+
let pointer = vm_address_t(truncatingIfNeeded: Int(bitPattern: threadList))
180+
let size = vm_size_t(MemoryLayout<thread_t>.size) * vm_size_t(threadCount)
181+
182+
vm_deallocate(mach_task_self_, pointer, size)
183+
}
184+
185+
var results: [(threadID: UInt64, currentTask: swift_addr_t)] = []
186+
for i in 0 ..< Int(threadCount) {
187+
let THREAD_IDENTIFIER_INFO_COUNT =
188+
MemoryLayout<thread_identifier_info_data_t>.size / MemoryLayout<natural_t>.size
189+
var info = thread_identifier_info_data_t()
190+
var infoCount = mach_msg_type_number_t(THREAD_IDENTIFIER_INFO_COUNT)
191+
192+
withUnsafeMutablePointer(to: &info) {
193+
$0.withMemoryRebound(to: integer_t.self, capacity: THREAD_IDENTIFIER_INFO_COUNT) {
194+
let result =
195+
thread_info(threadList![i], thread_flavor_t(THREAD_IDENTIFIER_INFO),
196+
$0, &infoCount)
197+
guard result == ERROR_SUCCESS else {
198+
print("unable to get info for thread \(i): \(String(cString: mach_error_string(result)) (0x\(String(result, radix: 16)))")
199+
return
200+
}
201+
202+
let tlsStart = info.thread_handle
203+
if tlsStart == 0 { return }
204+
205+
let SWIFT_CONCURRENCY_TASK_KEY = 103
206+
let currentTaskPointer = tlsStart + UInt64(SWIFT_CONCURRENCY_TASK_KEY * MemoryLayout<UnsafeRawPointer>.size)
207+
if let pointer = ReadBytes(currentTaskPointer, size: MemoryLayout<UnsafeRawPointer>.size) {
208+
let currentTask = pointer.load(as: UInt.self)
209+
results.append((threadID: info.thread_id, currentTask: swift_addr_t(currentTask)))
210+
}
211+
}
212+
}
213+
}
214+
return result
215+
}
216+
}
217+
218+
#endif

0 commit comments

Comments
 (0)