@@ -36,12 +36,36 @@ internal final class AndroidRemoteProcess: LinuxRemoteProcess {
36
36
case heapIterationFailed
37
37
}
38
38
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
+
39
52
let ptrace : SwiftInspectLinux . PTrace
40
53
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
+
41
66
override init ? ( processId: ProcessIdentifier ) {
42
67
do {
43
- let ptrace = try SwiftInspectLinux . PTrace ( process: processId)
44
- self . ptrace = ptrace
68
+ self . ptrace = try SwiftInspectLinux . PTrace ( process: processId)
45
69
} catch {
46
70
print ( " failed initialization: \( error) " )
47
71
return nil
@@ -90,41 +114,18 @@ internal final class AndroidRemoteProcess: LinuxRemoteProcess {
90
114
}
91
115
}
92
116
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.
93
119
internal func iterateHeapRegion( region: MemoryMap . Entry ) throws -> [ (
94
120
base: swift_addr_t , len: UInt64
95
121
) ] {
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
-
118
122
// Allocate a page-sized buffer in the remote process that malloc_iterate
119
123
// will populaate with metadata describing each heap entry it enumerates.
120
124
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)
125
127
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)
128
129
}
129
130
130
131
// Allocate and inialize a local buffer that will be used to copy metadata
@@ -139,37 +140,41 @@ internal final class AndroidRemoteProcess: LinuxRemoteProcess {
139
140
140
141
// Allocate an rwx region to hold the malloc_iterate callback that will be
141
142
// 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)
147
146
defer {
148
- let munmapArgs : [ UInt64 ] = [ remoteCodeAddr, codeLen]
149
- _ = try ? self . ptrace. callRemoteFunction ( at: munmapAddr, with: munmapArgs)
147
+ _ = try ? self . munmapRemote ( addr: remoteCodeAddr, len: codeLen)
150
148
}
151
149
152
150
// Copy the malloc_iterate callback implementation to the remote process.
153
151
let codeStart = heap_iterate_callback_start ( ) !
154
152
try self . process. writeMem (
155
153
remoteAddr: remoteCodeAddr, localAddr: codeStart, len: UInt ( codeLen) )
156
154
155
+ guard let mallocIterateAddr = self . mallocIterateSymbol. addr else {
156
+ throw RemoteProcessError . missingSymbol ( self . mallocIterateSymbol. name)
157
+ }
158
+
157
159
// Disable malloc/free while enumerating the region to get a consistent
158
160
// snapshot of existing allocations.
159
- _ = try self . ptrace . callRemoteFunction ( at : mallocDisableAddr )
161
+ try self . mallocDisableRemote ( )
160
162
defer {
161
- _ = try ? self . ptrace . callRemoteFunction ( at : mallocEnableAddr )
163
+ _ = try ? self . mallocEnableRemote ( )
162
164
}
163
165
166
+ // Collects (base, len) pairs describing each heap allocation in the remote
167
+ // process.
164
168
var allocations : [ ( base: swift_addr_t , len: UInt64 ) ] = [ ]
169
+
165
170
let regionLen = region. endAddr - region. startAddr
166
171
let args = [ region. startAddr, regionLen, remoteCodeAddr, remoteDataAddr]
167
172
_ = try self . ptrace. callRemoteFunction ( at: mallocIterateAddr, with: args) {
168
173
// This callback is invoked when a SIGTRAP is encountered in the remote
169
174
// process. In this context, this signal indicates there is no more room
170
175
// 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.
173
178
try self . process. readMem ( remoteAddr: remoteDataAddr, localAddr: buffer, len: UInt ( dataLen) )
174
179
allocations. append ( contentsOf: try self . processHeapMetadata ( buffer: buffer, len: dataLen) )
175
180
@@ -191,6 +196,9 @@ internal final class AndroidRemoteProcess: LinuxRemoteProcess {
191
196
return allocations
192
197
}
193
198
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.
194
202
internal func processHeapMetadata( buffer: UnsafeMutableRawPointer , len: Int ) throws -> [ (
195
203
base: UInt64 , len: UInt64
196
204
) ] {
@@ -209,6 +217,40 @@ internal final class AndroidRemoteProcess: LinuxRemoteProcess {
209
217
210
218
return allocations
211
219
}
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
+ }
212
254
}
213
255
214
256
#endif
0 commit comments