Skip to content

Commit 9fdd46f

Browse files
committed
refactor remote function calls to their own methods
1 parent c0257d8 commit 9fdd46f

File tree

1 file changed

+83
-41
lines changed

1 file changed

+83
-41
lines changed

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

Lines changed: 83 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,36 @@ internal final class AndroidRemoteProcess: LinuxRemoteProcess {
3636
case heapIterationFailed
3737
}
3838

39+
struct RemoteSymbol {
40+
let addr: UInt64?
41+
let name: String
42+
init(_ name: String, _ symbolCache: SymbolCache) {
43+
self.name = name
44+
if let symbolRange = symbolCache.address(of: name) {
45+
self.addr = symbolRange.start
46+
} else {
47+
self.addr = nil
48+
}
49+
}
50+
}
51+
3952
let ptrace: SwiftInspectLinux.PTrace
4053

54+
// We call mmap/munmap in the remote process to alloc/free memory for our own
55+
// use without impacting existing allocations in the remote process.
56+
lazy var mmapSymbol: RemoteSymbol = RemoteSymbol("mmap", self.symbolCache)
57+
lazy var munmapSymbol: RemoteSymbol = RemoteSymbol("munmap", self.symbolCache)
58+
59+
// We call malloc_iterate in the remote process to enumerate all items in the
60+
// remote process' heap. We use malloc_disable/malloc_enable to ensure no
61+
// malloc/free requests can race with malloc_iterate.
62+
lazy var mallocDisableSymbol: RemoteSymbol = RemoteSymbol("malloc_disable", self.symbolCache)
63+
lazy var mallocEnableSymbol: RemoteSymbol = RemoteSymbol("malloc_enable", self.symbolCache)
64+
lazy var mallocIterateSymbol: RemoteSymbol = RemoteSymbol("malloc_iterate", self.symbolCache)
65+
4166
override init?(processId: ProcessIdentifier) {
4267
do {
43-
let ptrace = try SwiftInspectLinux.PTrace(process: processId)
44-
self.ptrace = ptrace
68+
self.ptrace = try SwiftInspectLinux.PTrace(process: processId)
4569
} catch {
4670
print("failed initialization: \(error)")
4771
return nil
@@ -90,41 +114,18 @@ internal final class AndroidRemoteProcess: LinuxRemoteProcess {
90114
}
91115
}
92116

117+
// Iterate a single heap region in the remote process and return an array
118+
// of (base, len) pairs describing each heap allocation in the region.
93119
internal func iterateHeapRegion(region: MemoryMap.Entry) throws -> [(
94120
base: swift_addr_t, len: UInt64
95121
)] {
96-
// We call mmap/munmap in the remote process to alloc/free memory for our
97-
// own use without impacting existing allocations in the remote process.
98-
guard let (mmapAddr, _) = symbolCache.address(of: "mmap") else {
99-
throw RemoteProcessError.missingSymbol("mmap")
100-
}
101-
guard let (munmapAddr, _) = symbolCache.address(of: "munmap") else {
102-
throw RemoteProcessError.missingSymbol("munmap")
103-
}
104-
105-
// We call malloc_iterate in the remote process to enumerate all items in
106-
// remote process' heap. We use malloc_disable/malloc_enable to ensure no
107-
// malloc/free requests can race with malloc_iterate.
108-
guard let (mallocDisableAddr, _) = symbolCache.address(of: "malloc_disable") else {
109-
throw RemoteProcessError.missingSymbol("malloc_disable")
110-
}
111-
guard let (mallocIterateAddr, _) = symbolCache.address(of: "malloc_iterate") else {
112-
throw RemoteProcessError.missingSymbol("malloc_iterate")
113-
}
114-
guard let (mallocEnableAddr, _) = symbolCache.address(of: "malloc_enable") else {
115-
throw RemoteProcessError.missingSymbol("malloc_enable")
116-
}
117-
118122
// Allocate a page-sized buffer in the remote process that malloc_iterate
119123
// will populaate with metadata describing each heap entry it enumerates.
120124
let dataLen = sysconf(Int32(_SC_PAGESIZE))
121-
var mmapArgs = [
122-
0, UInt64(dataLen), UInt64(PROT_READ | PROT_WRITE), UInt64(MAP_ANON | MAP_PRIVATE),
123-
]
124-
let remoteDataAddr: UInt64 = try self.ptrace.callRemoteFunction(at: mmapAddr, with: mmapArgs)
125+
let remoteDataAddr = try self.mmapRemote(
126+
len: dataLen, prot: PROT_READ | PROT_WRITE, flags: MAP_ANON | MAP_PRIVATE)
125127
defer {
126-
let munmapArgs: [UInt64] = [remoteDataAddr, UInt64(dataLen)]
127-
_ = try? self.ptrace.callRemoteFunction(at: munmapAddr, with: munmapArgs)
128+
_ = try? self.munmapRemote(addr: remoteDataAddr, len: dataLen)
128129
}
129130

130131
// Allocate and inialize a local buffer that will be used to copy metadata
@@ -139,37 +140,41 @@ internal final class AndroidRemoteProcess: LinuxRemoteProcess {
139140

140141
// Allocate an rwx region to hold the malloc_iterate callback that will be
141142
// executed in the remote process.
142-
let codeLen = UInt64(heap_iterate_callback_len())
143-
mmapArgs = [
144-
0, codeLen, UInt64(PROT_READ | PROT_WRITE | PROT_EXEC), UInt64(MAP_ANON | MAP_PRIVATE),
145-
]
146-
let remoteCodeAddr: UInt64 = try self.ptrace.callRemoteFunction(at: mmapAddr, with: mmapArgs)
143+
let codeLen = heap_iterate_callback_len()
144+
let remoteCodeAddr = try mmapRemote(
145+
len: codeLen, prot: PROT_READ | PROT_WRITE | PROT_EXEC, flags: MAP_ANON | MAP_PRIVATE)
147146
defer {
148-
let munmapArgs: [UInt64] = [remoteCodeAddr, codeLen]
149-
_ = try? self.ptrace.callRemoteFunction(at: munmapAddr, with: munmapArgs)
147+
_ = try? self.munmapRemote(addr: remoteCodeAddr, len: codeLen)
150148
}
151149

152150
// Copy the malloc_iterate callback implementation to the remote process.
153151
let codeStart = heap_iterate_callback_start()!
154152
try self.process.writeMem(
155153
remoteAddr: remoteCodeAddr, localAddr: codeStart, len: UInt(codeLen))
156154

155+
guard let mallocIterateAddr = self.mallocIterateSymbol.addr else {
156+
throw RemoteProcessError.missingSymbol(self.mallocIterateSymbol.name)
157+
}
158+
157159
// Disable malloc/free while enumerating the region to get a consistent
158160
// snapshot of existing allocations.
159-
_ = try self.ptrace.callRemoteFunction(at: mallocDisableAddr)
161+
try self.mallocDisableRemote()
160162
defer {
161-
_ = try? self.ptrace.callRemoteFunction(at: mallocEnableAddr)
163+
_ = try? self.mallocEnableRemote()
162164
}
163165

166+
// Collects (base, len) pairs describing each heap allocation in the remote
167+
// process.
164168
var allocations: [(base: swift_addr_t, len: UInt64)] = []
169+
165170
let regionLen = region.endAddr - region.startAddr
166171
let args = [region.startAddr, regionLen, remoteCodeAddr, remoteDataAddr]
167172
_ = try self.ptrace.callRemoteFunction(at: mallocIterateAddr, with: args) {
168173
// This callback is invoked when a SIGTRAP is encountered in the remote
169174
// process. In this context, this signal indicates there is no more room
170175
// in the allocated metadata region (see AndroidCLib/heap.c).
171-
// Immediately read and process the heap metadata from the remote process,
172-
// skip past the trap/break instruction and resume the remote process.
176+
// Immediately read the heap metadata from the remote process, skip past
177+
// the trap/break instruction, and resume the remote process.
173178
try self.process.readMem(remoteAddr: remoteDataAddr, localAddr: buffer, len: UInt(dataLen))
174179
allocations.append(contentsOf: try self.processHeapMetadata(buffer: buffer, len: dataLen))
175180

@@ -191,6 +196,9 @@ internal final class AndroidRemoteProcess: LinuxRemoteProcess {
191196
return allocations
192197
}
193198

199+
// Process heap metadata generated by our malloc_iterate callback in the
200+
// remote process and return an array of (base, len) pairs describing each
201+
// heap allocation.
194202
internal func processHeapMetadata(buffer: UnsafeMutableRawPointer, len: Int) throws -> [(
195203
base: UInt64, len: UInt64
196204
)] {
@@ -209,6 +217,40 @@ internal final class AndroidRemoteProcess: LinuxRemoteProcess {
209217

210218
return allocations
211219
}
220+
221+
// call mmap in the remote process with the provided arguments
222+
internal func mmapRemote(len: Int, prot: Int32, flags: Int32) throws -> UInt64 {
223+
guard let sym = self.mmapSymbol.addr else {
224+
throw RemoteProcessError.missingSymbol(self.mmapSymbol.name)
225+
}
226+
let args = [0, UInt64(len), UInt64(prot), UInt64(flags)]
227+
return try self.ptrace.callRemoteFunction(at: sym, with: args)
228+
}
229+
230+
// call munmap in the remote process with the provdied arguments
231+
internal func munmapRemote(addr: UInt64, len: Int) throws -> UInt64 {
232+
guard let sym = self.munmapSymbol.addr else {
233+
throw RemoteProcessError.missingSymbol(self.munmapSymbol.name)
234+
}
235+
let args: [UInt64] = [addr, UInt64(len)]
236+
return try self.ptrace.callRemoteFunction(at: sym, with: args)
237+
}
238+
239+
// call malloc_disable in the remote process
240+
internal func mallocDisableRemote() throws {
241+
guard let sym = self.mallocDisableSymbol.addr else {
242+
throw RemoteProcessError.missingSymbol(self.mallocDisableSymbol.name)
243+
}
244+
_ = try self.ptrace.callRemoteFunction(at: sym)
245+
}
246+
247+
// call malloc_enable in the remote process
248+
internal func mallocEnableRemote() throws {
249+
guard let sym = self.mallocEnableSymbol.addr else {
250+
throw RemoteProcessError.missingSymbol(self.mallocEnableSymbol.name)
251+
}
252+
_ = try self.ptrace.callRemoteFunction(at: sym)
253+
}
212254
}
213255

214256
#endif

0 commit comments

Comments
 (0)