135 lines
4.9 KiB
Python
Executable file
135 lines
4.9 KiB
Python
Executable file
#!/usr/bin/env python
|
|
#
|
|
# Copyright (C) 2013 The Android Open Source Project
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
"""WebView postprocessor for the go/memdump tool.
|
|
|
|
Processes the output of memdump (see go/memdump) aggregating memory usage
|
|
information for WebView analysis (both classic and chromium).
|
|
|
|
Usage: adb shell /path/to/target/memdump <target-PID> | ./memreport.py > out.csv
|
|
"""
|
|
|
|
|
|
import os
|
|
import re
|
|
import sys
|
|
|
|
from sets import Set
|
|
|
|
|
|
_ENTRIES = [
|
|
('Total', '.* r... .*'),
|
|
(' Read-only', '.* r--. .*'),
|
|
(' Read-write', '.* rw.. .*'),
|
|
(' Read-write (no x)', '.* rw-. .*'),
|
|
(' Executable', '.* ..x. .*'),
|
|
('Anonymous total', '.* .... .* .*shared_other=[0-9]+ ($|.*dlmalloc.*)'),
|
|
(' Anonymous executable (JIT)', '.* ..x. .* shared_other=[0-9]+ ($|.*dlmalloc.*)'),
|
|
(' Anonymous read-write', '.* rw.. .* .*shared_other=[0-9]+ ($|.*dlmalloc.*)'),
|
|
(' Native heap (dlmalloc)', '.* r... .* /.*dlmalloc.*'),
|
|
('File total', '.* .... .* /((?!dev/ashmem/dlmalloc).*)'),
|
|
(' File executable', '.* ..x. .* /((?!dev/ashmem/dlmalloc).*)'),
|
|
(' File read-write', '.* rw.. .* /((?!dev/ashmem/dlmalloc).*)'),
|
|
(' Dalvik', '.* rw.. .* /.*dalvik.*'),
|
|
(' Dalvik heap', '.* rw.. .* /.*dalvik-heap.*'),
|
|
(' Ashmem', '.* rw.. .* /dev/ashmem .*'),
|
|
(' libwebcore.so total', '.* r... .* /.*libwebcore.so'),
|
|
(' libwebcore.so read-only', '.* r--. .* /.*libwebcore.so'),
|
|
(' libwebcore.so read-write', '.* rw-. .* /.*libwebcore.so'),
|
|
(' libwebcore.so executable', '.* r.x. .* /.*libwebcore.so'),
|
|
(' libwebviewchromium.so total', '.* r... .* /.*libwebviewchromium.so'),
|
|
(' libwebviewchromium.so read-only', '.* r--. .* /.*libwebviewchromium.so'),
|
|
(' libwebviewchromium.so read-write', '.* rw-. .* /.*libwebviewchromium.so'),
|
|
(' libwebviewchromium.so executable', '.* r.x. .* /.*libwebviewchromium.so'),
|
|
(' Driver mappings', '.* .... .* /dev/\w+$'),
|
|
(' /dev/maliN total', '.* .... .* /dev/mali.*'),
|
|
('OTHER (non file non anon)', '.* .... .*shared_other=[0-9]+ [^/]+'),
|
|
(' DMA buffers', '.* .... .*shared_other=[0-9]+ .*dmabuf.*'),
|
|
]
|
|
|
|
|
|
def _CollectMemoryStats(memdump, region_filters):
|
|
processes = []
|
|
mem_usage_for_regions = None
|
|
regexps = {}
|
|
for region_filter in region_filters:
|
|
regexps[region_filter] = re.compile(region_filter)
|
|
for line in memdump:
|
|
if 'PID=' in line:
|
|
mem_usage_for_regions = {}
|
|
processes.append(mem_usage_for_regions)
|
|
continue
|
|
matched_regions = Set([])
|
|
for region_filter in region_filters:
|
|
if regexps[region_filter].match(line.rstrip('\r\n')):
|
|
matched_regions.add(region_filter)
|
|
if not region_filter in mem_usage_for_regions:
|
|
mem_usage_for_regions[region_filter] = {
|
|
'private_unevictable': 0,
|
|
'private': 0,
|
|
'shared_app': 0.0,
|
|
'shared_other_unevictable': 0,
|
|
'shared_other': 0,
|
|
}
|
|
for matched_region in matched_regions:
|
|
mem_usage = mem_usage_for_regions[matched_region]
|
|
for key in mem_usage:
|
|
for token in line.split(' '):
|
|
if (key+'=') in token:
|
|
field = token.split('=')[1]
|
|
if key != 'shared_app':
|
|
mem_usage[key] += int(field)
|
|
else: # shared_app=[\d,\d...]
|
|
array = eval(field)
|
|
for i in xrange(len(array)):
|
|
mem_usage[key] += float(array[i]) / (i + 2)
|
|
break
|
|
return processes
|
|
|
|
|
|
def _ConvertMemoryField(field):
|
|
return str(field / (1024))
|
|
|
|
|
|
def _DumpCSV(processes_stats):
|
|
total_map = {}
|
|
i = 0
|
|
for process in processes_stats:
|
|
i += 1
|
|
print ',private,private_unevictable,shared_other,shared_other_unevictable,'
|
|
for (k, v) in _ENTRIES:
|
|
header_column = k + ',' if 'NOHEADER' not in os.environ else ','
|
|
if not v in process:
|
|
print header_column + '0,0,0,0,'
|
|
continue
|
|
if not v in total_map:
|
|
total_map[v] = 0
|
|
total_map[v] += process[v]['private'] + process[v]['shared_app']
|
|
print (
|
|
header_column +
|
|
_ConvertMemoryField(process[v]['private']) + ',' +
|
|
_ConvertMemoryField(process[v]['private_unevictable']) + ',' +
|
|
_ConvertMemoryField(process[v]['shared_other']) + ',' +
|
|
_ConvertMemoryField(process[v]['shared_other_unevictable']) + ','
|
|
)
|
|
|
|
|
|
def main(argv):
|
|
_DumpCSV(_CollectMemoryStats(sys.stdin, [value for (key, value) in _ENTRIES]))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main(sys.argv)
|