forked from Gracker/Android-App-Memory-Analysis
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsmaps_parser.py
More file actions
348 lines (327 loc) · 12.8 KB
/
smaps_parser.py
File metadata and controls
348 lines (327 loc) · 12.8 KB
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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
#!/usr/bin/env python
# -*- coding:utf-8 -*-
#@Time : 2019/6/13 上午10:55
#@Author: yangzhiting
#@File : parse.py
import argparse
import re
from collections import Counter
import os
import subprocess
type_length = 17
pssSum_count = [0] * type_length
pss_count = [0] * type_length
swapPss_count = [0] * type_length
HEAP_UNKNOWN = 0
HEAP_DALVIK = 1
HEAP_NATIVE = 2
HEAP_DALVIK_OTHER = 3
HEAP_STACK = 4
HEAP_CURSOR = 5
HEAP_ASHMEM = 6
HEAP_GL_DEV = 7
HEAP_UNKNOWN_DEV = 8
HEAP_SO = 9
HEAP_JAR = 10
HEAP_APK = 11
HEAP_TTF = 12
HEAP_DEX = 13
HEAP_OAT = 14
HEAP_ART = 15
HEAP_UNKNOWN_MAP = 16
HEAP_GRAPHICS = 17
HEAP_GL = 18
HEAP_OTHER_MEMTRACK = 19
# Dalvik extra sections (heap)
HEAP_DALVIK_NORMAL = 20
HEAP_DALVIK_LARGE = 21
HEAP_DALVIK_ZYGOTE = 22
HEAP_DALVIK_NON_MOVING = 23
# Dalvik other extra sections.
HEAP_DALVIK_OTHER_LINEARALLOC = 24
HEAP_DALVIK_OTHER_ACCOUNTING = 25
HEAP_DALVIK_OTHER_ZYGOTE_CODE_CACHE = 26
HEAP_DALVIK_OTHER_APP_CODE_CACHE = 27
HEAP_DALVIK_OTHER_COMPILER_METADATA = 28
HEAP_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE = 29
# Boot vdex / app dex / app vdex
HEAP_DEX_BOOT_VDEX = 30
HEAP_DEX_APP_DEX = 31
HEAP_DEX_APP_VDEX = 32
# App art, boot art.
HEAP_ART_APP = 33
HEAP_ART_BOOT = 34
_NUM_HEAP = 35
_NUM_EXCLUSIVE_HEAP = HEAP_OTHER_MEMTRACK+1
_NUM_CORE_HEAP = HEAP_NATIVE+1
#pss_type = ["HEAP_UNKNOWN", "HEAP_DALVIK", "HEAP_NATIVE", "HEAP_DALVIK_OTHER", "HEAP_STACK", "HEAP_CURSOR", "HEAP_ASHMEM", "HEAP_GL_DEV", \
# "HEAP_UNKNOWN_DEV", "HEAP_SO", "HEAP_JAR", "HEAP_APK", "HEAP_TTF", "HEAP_DEX", "HEAP_OAT", "HEAP_ART", "HEAP_UNKNOWN_MAP" ,\
# "HEAP_GRAPHICS","HEAP_GL","HEAP_OTHER_MEMTRACK",\
# "HEAP_DALVIK_NORMAL,"HEAP_DALVIK_LARGE","HEAP_DALVIK_ZYGOTE","HEAP_DALVIK_NON_MOVING" ,\
# "HEAP_DALVIK_OTHER_LINEARALLOC","HEAP_DALVIK_OTHER_ACCOUNTING","HEAP_DALVIK_OTHER_ZYGOTE_CODE_CACHE","HEAP_DALVIK_OTHER_APP_CODE_CACHE","HEAP_DALVIK_OTHER_COMPILER_METADATA","HEAP_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE", \
# "HEAP_DEX_BOOT_VDEX", "HEAP_DEX_APP_DEX","HEAP_DEX_APP_VDEX", \
# "HEAP_ART_APP","HEAP_ART_BOOT" ]
pss_type = ["Unknown", "Dalvik", "Native", "Dalvik Other", "Stack", "Cursor", "Ashmem", "Gfx dev", \
"Other dev", ".so mmap", ".jar mmap", ".apk mmap", ".ttf mmap", ".dex mmap", ".oat mmap", ".art mmap", "Other mmap", \
"graphics","gl","other memtrack", \
"dalvik normal","dalvik large","dalvik zygote","dalvik non moving" ,\
"dalvik other lineralloc","dalvik other accounting","dalvik other zygote code cache","dalvik other app code cache","dalvik other compiler metadata","dalvik other indirect reference table" ,\
"dex boot vdex","dex app dex","dex app vdex", \
"heap art app","heap art boot"]
type_list = []
for i in range(type_length):
type_list.append({})
#制作提示
def help():
parse = argparse.ArgumentParser(description="smaps parser")
parse.add_argument('-p', '--pid', help="pid")
parse.add_argument('-f', '--filename', help="smaps file")
parse.add_argument('-t', '--type', help="Unknown, Dalvik, Native, Dalvik Other, Stack, Cursor, Ashmem, Gfx dev, \
Other dev, .so mmap, .jar mmap, .apk mmap, .ttf mmap, .dex mmap, .oat mmap, .art mmap, Other mmap", default="ALL")
parse.add_argument('-o', '--output', help="output file", default="smaps_analysis.txt")
parse.add_argument('-s', '--simple', action="store_true", help="simple output", default=False)
return parse.parse_args()
def match_head(line):
return re.match(r'(\w*)-(\w*) (\S*) (\w*) (\w*):(\w*) (\w*)\s*(.+)$', line, re.I)
def match_type(name, prewhat):
which_heap = HEAP_UNKNOWN
sub_heap = HEAP_UNKNOWN
is_swappable = False
if(name.endswith(" (deleted)")):
name = name[0 : len(name)- len(' (deleted)')]
size = len(name)
if name.startswith("[heap]"):
which_heap = HEAP_NATIVE
elif name.startswith("[anon:libc_malloc]"):
which_heap = HEAP_NATIVE
elif name.startswith("[anon:scudo:"):
which_heap = HEAP_NATIVE
elif name.startswith("[anon:GWP-ASan"):
which_heap = HEAP_NATIVE
elif name.startswith("[stack"):
which_heap = HEAP_STACK
elif name.startswith("[anon:stack_and_tls:"):
which_heap = HEAP_STACK
elif name.endswith(".so"):
which_heap = HEAP_SO
is_swappable = True
elif name.endswith(".jar"):
which_heap = HEAP_JAR
is_swappable = True
elif name.endswith(".apk"):
which_heap = HEAP_APK
is_swappable = True
elif name.endswith(".ttf"):
which_heap = HEAP_TTF
is_swappable = True
elif name.endswith(".odex") | (size > 4 and name.__contains__(".dex")) :
which_heap = HEAP_DEX
sub_heap = HEAP_DEX_APP_DEX
is_swappable = True
elif name.endswith(".vdex"):
which_heap = HEAP_DEX
# Handle system@framework@boot and system/framework/boot|apex
if name.__contains__("@boot") | name.__contains__("/boot") | name.__contains__("/apex"):
sub_heap = HEAP_DEX_BOOT_VDEX
else:
sub_heap = HEAP_DEX_APP_VDEX
is_swappable = True
elif name.endswith(".oat"):
which_heap = HEAP_OAT
is_swappable = True
elif name.endswith(".art") | name.endswith(".art]"):
which_heap = HEAP_ART
# Handle system@framework@boot* and system/framework/boot|apex*
if name.__contains__("@boot") | name.__contains__("/boot") | name.__contains__("/apex"):
sub_heap = HEAP_ART_BOOT
else:
sub_heap = HEAP_ART_APP
is_swappable = True
elif name.startswith("/dev"):
which_heap = HEAP_UNKNOWN_DEV
if name.startswith("/dev/kgsl-3d0"):
which_heap = HEAP_GL_DEV
elif name.__contains__("/dev/ashmem/CursorWindow"):
which_heap = HEAP_CURSOR
elif name.startswith("/dev/ashmem/jit-zygote-cache"):
which_heap = HEAP_DALVIK_OTHER
sub_heap = HEAP_DALVIK_OTHER_ZYGOTE_CODE_CACHE
elif name.__contains__("/dev/ashmem"):
which_heap = HEAP_ASHMEM
elif name.startswith("/memfd:jit-cache"):
which_heap = HEAP_DALVIK_OTHER
sub_heap = HEAP_DALVIK_OTHER_APP_CODE_CACHE
elif name.startswith("/memfd:jit-zygote-cache"):
which_heap = HEAP_DALVIK_OTHER;
sub_heap = HEAP_DALVIK_OTHER_ZYGOTE_CODE_CACHE;
elif name.startswith("[anon:"):
which_heap = HEAP_UNKNOWN
if name.startswith("[anon:dalvik-"):
which_heap = HEAP_DALVIK_OTHER
if name.startswith("[anon:dalvik-LinearAlloc"):
sub_heap = HEAP_DALVIK_OTHER_LINEARALLOC
elif name.startswith("[anon:dalvik-alloc space") | name.startswith("[anon:dalvik-main space"):
# This is the regular Dalvik heap.
which_heap = HEAP_DALVIK
sub_heap = HEAP_DALVIK_NORMAL
elif name.startswith("[anon:dalvik-large object space") | name.startswith("[anon:dalvik-free list large object space"):
which_heap = HEAP_DALVIK
sub_heap = HEAP_DALVIK_LARGE
elif name.startswith("[anon:dalvik-non moving space"):
which_heap = HEAP_DALVIK
sub_heap = HEAP_DALVIK_NON_MOVING
elif name.startswith("[anon:dalvik-zygote space"):
which_heap = HEAP_DALVIK
sub_heap = HEAP_DALVIK_ZYGOTE
elif name.startswith("[anon:dalvik-indirect ref"):
sub_heap = HEAP_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE
elif name.startswith("[anon:dalvik-jit-code-cache") | name.startswith("[anon:dalvik-data-code-cache"):
sub_heap = HEAP_DALVIK_OTHER_APP_CODE_CACHE
elif name.startswith("[anon:dalvik-CompilerMetadata"):
sub_heap = HEAP_DALVIK_OTHER_COMPILER_METADATA
else:
sub_heap = HEAP_DALVIK_OTHER_ACCOUNTING
elif not name.__eq__(" "):
if name.__sizeof__() > 0:
which_heap = HEAP_UNKNOWN_MAP
elif prewhat == 10:
which_heap = 10
return which_heap
def match_pss(line):
tmp = re.match('Pss:\s+([0-9]*) kB', line, re.I)
if tmp:
return tmp
def match_swapPss(line):
tmp = re.match('SwapPss:\s+([0-9]*) kB', line, re.I)
if tmp:
return tmp
def parse_smaps(filename):
file = open(filename, 'r')
line = file.readline()
if not line:
return
what = 0
prewhat = 0
while 1:
tmp = match_head(line)
if tmp:
name = tmp.group(8)
# print("name:" + name)
what = match_type(name, prewhat)
# print "name = " + name + "what = " + str(what)
while 1:
line2 = file.readline()
if not line2:
return
tmp2 = match_pss(line2)
tmp3 = match_swapPss(line2)
if tmp2 or tmp3:
if what >= 0:
if tmp2:
pss = int(tmp2.group(1))
pss_count[what] += pss
if tmp3:
pss = int(tmp3.group(1))
swapPss_count[what] += pss
# print("what:%d, pss:%d" % (what, pss))
if pss > 0:
pssSum_count[what] += pss
tmplist = type_list[what]
if name in tmplist:
tmplist[name] += pss
else:
tmplist[name] = pss
else:
tmp3 = match_head(line2)
if tmp3:
line = line2
prewhat = what
break
def print_result(args):
if args.pid and not args.output:
output = "%d_smaps_analysis.txt" % pid
else:
output = args.output
type = args.type
simple = args.simple
index = -1
if not type == "ALL":
if type in pss_type:
index = pss_type.index(type)
else:
print("Please enter a correct memory type")
return
output_file = open(output, 'w')
if index == -1:
for i,j,m,n,z in zip(pss_type, pssSum_count, pss_count, swapPss_count, type_list):
tmp = "%s : %.3f M" % (i, float(j)/1000)
print(tmp)
output_file.write(tmp)
output_file.write("\n")
tmp = "\tpss: %.3f M" % (float(m) / 1000)
print(tmp)
output_file.write(tmp)
output_file.write("\n")
tmp = "\tswapPss: %.3f M" % (float(n) / 1000)
print(tmp)
output_file.write(tmp)
output_file.write("\n")
if not simple:
count = Counter(z)
for j in count.most_common():
tmp = "\t\t%s : %d kB" % (j[0], j[1])
print(tmp)
output_file.write(tmp)
output_file.write("\n")
else:
tmp = "%s : %.3f M" % (pss_type[index], float(pssSum_count[index]) / 1000)
print(tmp)
output_file.write(tmp)
output_file.write("\n")
tmp = "\tpss: %.3f M" % (float(pss_count[index]) / 1000)
print(tmp)
output_file.write(tmp)
output_file.write("\n")
tmp = "\tswapPss: %.3f M" % (float(swapPss_count[index]) / 1000)
print(tmp)
output_file.write(tmp)
output_file.write("\n")
if not simple:
count = Counter(type_list[index])
for j in count.most_common():
tmp = "\t\t%s : %d kB" % (j[0], j[1])
print(tmp)
output_file.write(tmp)
output_file.write("\n")
if __name__ == "__main__":
args = help()
if args.filename:
if os.path.exists(args.filename):
parse_smaps(args.filename)
print_result(args)
else:
print("smaps is not exist")
elif args.pid:
if args.pid.isdigit():
pid = int(args.pid)
if pid > 0:
check_cmd = "adb shell su root ls /proc/%d/smaps >> /dev/null" % int(pid)
ret = os.system(check_cmd)
if ret == 0:
cmd = "adb shell su root cat /proc/%d/smaps" % int(pid)
smaps_filename = "%d_smaps_file.txt" % pid
ret = os.popen(cmd)
new_file = open(smaps_filename, 'w')
lines = ret.readlines()
for line in lines:
new_file.write(line)
parse_smaps(smaps_filename)
print_result(args)
else:
print("/proc/%d/smaps cannot be accessed" % pid)
else:
print("Please enter a correct pid")
else:
print("Please enter a correct pid")
else:
print("Please provide a pid or a smaps file")