Skip to content

Commit 03dae5d

Browse files
committed
[swift-inspect] minor cleanup and documentation
1 parent 0a436ba commit 03dae5d

File tree

5 files changed

+54
-21
lines changed

5 files changed

+54
-21
lines changed

tools/swift-inspect/Sources/AndroidCLib/heap.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
#error("only aarch64 and x86_64 are supported")
5858
#endif
5959

60-
// Callback for heap_iterate. Because this function is meant to be copied to
60+
// Callback for malloc_iterate. Because this function is meant to be copied to
6161
// a different process for execution, it must not make any function calls. It
6262
// could be written as asm, but simple C is more readable/maintainable and
6363
// should consistently compile to movable, position-independent code.

tools/swift-inspect/Sources/SwiftInspectLinux/PTrace.swift

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 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+
113
import Foundation
214
import LinuxSystemHeaders
315

@@ -10,6 +22,9 @@ public class PTrace {
1022

1123
let pid: pid_t
1224

25+
// Initializing a PTrace instance attaches to the target process, waits for
26+
// it to stop, and leaves it in a stopped state. The caller may resume the
27+
// process by calling cont().
1328
public init(process pid: pid_t) throws {
1429
guard ptrace_attach(pid) != -1 else {
1530
throw PTraceError.ptraceFailure(PTRACE_ATTACH, pid: pid)
@@ -34,11 +49,17 @@ public class PTrace {
3449
deinit { _ = ptrace_detach(self.pid) }
3550

3651
public func cont() throws {
37-
guard ptrace_continue(self.pid) != -1 else {
52+
guard ptrace_cont(self.pid) != -1 else {
3853
throw PTraceError.ptraceFailure(PTRACE_CONT, pid: self.pid)
3954
}
4055
}
4156

57+
public func interrupt() throws {
58+
guard ptrace_interrupt(self.pid) != -1 else {
59+
throw PTraceError.ptraceFailure(PTRACE_INTERRUPT, pid: self.pid)
60+
}
61+
}
62+
4263
public func getSigInfo() throws -> siginfo_t {
4364
var sigInfo = siginfo_t()
4465
guard ptrace_getsiginfo(self.pid, &sigInfo) != -1 else {
@@ -62,6 +83,7 @@ public class PTrace {
6283
throw PTraceError.ptraceFailure(PTRACE_GETREGSET, pid: self.pid)
6384
}
6485
}
86+
6587
return regSet
6688
}
6789

@@ -75,6 +97,10 @@ public class PTrace {
7597
}
7698
}
7799

100+
// Call the function at the specified address in the attached process. Caller
101+
// may pass up to six 8-byte arguments. The optional callback is invoked when
102+
// the process is stopped with a SIGTRAP signal. In this case, the caller is
103+
// responsible for taking action on the signal.
78104
public func callRemoteFunction(
79105
at address: UInt64, with args: [UInt64] = [], onTrap callback: (() throws -> Void)? = nil
80106
) throws -> UInt64 {
@@ -111,7 +137,6 @@ public class PTrace {
111137

112138
// give the caller the opportunity to handle SIGTRAP
113139
try callback()
114-
try self.cont()
115140
}
116141

117142
let sigInfo = try self.getSigInfo()

tools/swift-inspect/Sources/SwiftInspectLinux/SystemHeaders/ptrace.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,15 @@ int ptrace_detach(pid_t pid) {
3838
}
3939

4040
static inline
41-
int ptrace_continue(pid_t pid) {
41+
int ptrace_cont(pid_t pid) {
4242
return ptrace_retry(PTRACE_CONT, pid, 0, 0);
4343
}
4444

45+
static inline
46+
int ptrace_interrupt(pid_t pid) {
47+
return ptrace_retry(PTRACE_INTERRUPT, pid, 0, 0);
48+
}
49+
4550
static inline
4651
int ptrace_getsiginfo(pid_t pid, siginfo_t *siginfo) {
4752
return ptrace_retry(PTRACE_GETSIGINFO, pid, 0, siginfo);

tools/swift-inspect/Sources/swift-inspect/AndroidRemoteProcess.swift

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212

1313
#if os(Android)
1414

15-
import Foundation
1615
import AndroidCLib
16+
import Foundation
1717
import LinuxSystemHeaders
1818
import SwiftInspectLinux
1919
import SwiftRemoteMirror
@@ -49,20 +49,24 @@ internal final class AndroidRemoteProcess: LinuxRemoteProcess {
4949
super.init(processId: processId)
5050
}
5151

52+
// Linux and Android have no supported method to enumerate allocations in the
53+
// heap of a remote process. Android does, however, support the malloc_iterate
54+
// API, which enumerates allocations in the current process. We leverage this
55+
// API by invoking it in the remote process with ptrace and using simple IPC
56+
// (SIGTRAP and process_vm_readv and process_vm_writev) to fetch the results.
5257
override internal func iterateHeap(_ body: (swift_addr_t, UInt64) -> Void) {
5358
var regionCount = 0
5459
var allocCount = 0
5560
for entry in self.memoryMap.entries {
5661
// Limiting malloc_iterate calls to only memory regions that are known
5762
// to contain heap allocations is not strictly necessary but it does
58-
// significantly improves the speed of heap iteration.
63+
// significantly improve the speed of heap iteration.
5964
guard entry.isHeapRegion() else { continue }
6065

6166
// collect all of the allocations in this heap region
6267
let allocations: [(base: swift_addr_t, len: UInt64)]
6368
do {
64-
allocations = try self.iterateHeapRegion(
65-
startAddr: entry.startAddr, endAddr: entry.endAddr)
69+
allocations = try self.iterateHeapRegion(region: entry)
6670
regionCount += 1
6771
} catch {
6872
print("failed iterating remote heap: \(error)")
@@ -75,7 +79,7 @@ internal final class AndroidRemoteProcess: LinuxRemoteProcess {
7579
for alloc in allocations { body(alloc.base, alloc.len) }
7680
}
7781

78-
if allocCount == 0 {
82+
if regionCount == 0 {
7983
// This condition most likely indicates the MemoryMap.Entry.isHeapRegion
8084
// filtering is needs to be modified to support a new heap region naming
8185
// convention in a newer Android version.
@@ -86,12 +90,7 @@ internal final class AndroidRemoteProcess: LinuxRemoteProcess {
8690
}
8791
}
8892

89-
// Linux and Android have no supported method to enumerate allocations in the
90-
// heap of a remote process. Android does, however, support the malloc_iterate
91-
// API, which enumerates allocations in the current process. We leverage this
92-
// API by invoking it in the remote process using ptrace and using simple IPC
93-
// (SIGTRAP and process_vm_readv and process_vm_writev).
94-
internal func iterateHeapRegion(startAddr: UInt64, endAddr: UInt64) throws -> [(
93+
internal func iterateHeapRegion(region: MemoryMap.Entry) throws -> [(
9594
base: swift_addr_t, len: UInt64
9695
)] {
9796
// We call mmap/munmap in the remote process to alloc/free memory for our
@@ -119,7 +118,9 @@ internal final class AndroidRemoteProcess: LinuxRemoteProcess {
119118
// Allocate a page-sized buffer in the remote process that malloc_iterate
120119
// will populaate with metadata describing each heap entry it enumerates.
121120
let dataLen = sysconf(Int32(_SC_PAGESIZE))
122-
var mmapArgs = [0, UInt64(dataLen), UInt64(PROT_READ | PROT_WRITE), UInt64(MAP_ANON | MAP_PRIVATE)]
121+
var mmapArgs = [
122+
0, UInt64(dataLen), UInt64(PROT_READ | PROT_WRITE), UInt64(MAP_ANON | MAP_PRIVATE),
123+
]
123124
let remoteDataAddr: UInt64 = try self.ptrace.callRemoteFunction(at: mmapAddr, with: mmapArgs)
124125
defer {
125126
let munmapArgs: [UInt64] = [remoteDataAddr, UInt64(dataLen)]
@@ -128,7 +129,8 @@ internal final class AndroidRemoteProcess: LinuxRemoteProcess {
128129

129130
// Allocate and inialize a local buffer that will be used to copy metadata
130131
// to/from the target process.
131-
let buffer = UnsafeMutableRawPointer.allocate(byteCount: dataLen, alignment: MemoryLayout<UInt64>.alignment)
132+
let buffer = UnsafeMutableRawPointer.allocate(
133+
byteCount: dataLen, alignment: MemoryLayout<UInt64>.alignment)
132134
defer { buffer.deallocate() }
133135
guard heap_iterate_metadata_init(buffer, dataLen) else {
134136
throw RemoteProcessError.heapIterationFailed
@@ -160,8 +162,8 @@ internal final class AndroidRemoteProcess: LinuxRemoteProcess {
160162
}
161163

162164
var allocations: [(base: swift_addr_t, len: UInt64)] = []
163-
let regionLen = endAddr - startAddr
164-
let args = [startAddr, regionLen, remoteCodeAddr, remoteDataAddr]
165+
let regionLen = region.endAddr - region.startAddr
166+
let args = [region.startAddr, regionLen, remoteCodeAddr, remoteDataAddr]
165167
_ = try self.ptrace.callRemoteFunction(at: mallocIterateAddr, with: args) {
166168
// This callback is invoked when a SIGTRAP is encountered in the remote
167169
// process. In this context, this signal indicates there is no more room
@@ -180,6 +182,7 @@ internal final class AndroidRemoteProcess: LinuxRemoteProcess {
180182
regs.step(RegisterSet.trapInstructionSize)
181183

182184
try self.ptrace.setRegSet(regSet: regs)
185+
try self.ptrace.cont()
183186
}
184187

185188
try self.process.readMem(remoteAddr: remoteDataAddr, localAddr: buffer, len: UInt(dataLen))

tools/swift-inspect/Sources/swift-inspect/Process.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,9 @@ internal func process(matching: String) -> ProcessIdentifier? {
178178
return nil
179179
}
180180

181-
let procfs_path = "/proc/\(processId)"
181+
let procfsPath = "/proc/\(processId)"
182182
var isDirectory: Bool = false
183-
guard FileManager.default.fileExists(atPath: procfs_path, isDirectory: &isDirectory)
183+
guard FileManager.default.fileExists(atPath: procfsPath, isDirectory: &isDirectory)
184184
&& isDirectory else {
185185
return nil
186186
}

0 commit comments

Comments
 (0)