#!/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 | ./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)