1 #!/usr/bin/env python 2 # 3 # Copyright (C) 2013 The Android Open Source Project 4 # 5 # Licensed under the Apache License, Version 2.0 (the "License"); 6 # you may not use this file except in compliance with the License. 7 # You may obtain a copy of the License at 8 # 9 # http://www.apache.org/licenses/LICENSE-2.0 10 # 11 # Unless required by applicable law or agreed to in writing, software 12 # distributed under the License is distributed on an "AS IS" BASIS, 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 # See the License for the specific language governing permissions and 15 # limitations under the License. 16 17 """WebView postprocessor for the go/memdump tool. 18 19 Processes the output of memdump (see go/memdump) aggregating memory usage 20 information for WebView analysis (both classic and chromium). 21 22 Usage: adb shell /path/to/target/memdump <target-PID> | ./memreport.py > out.csv 23 """ 24 25 26 import os 27 import re 28 import sys 29 30 from sets import Set 31 32 33 _ENTRIES = [ 34 ('Total', '.* r... .*'), 35 (' Read-only', '.* r--. .*'), 36 (' Read-write', '.* rw.. .*'), 37 (' Read-write (no x)', '.* rw-. .*'), 38 (' Executable', '.* ..x. .*'), 39 ('Anonymous total', '.* .... .* .*shared_other=[0-9]+ ($|.*dlmalloc.*)'), 40 (' Anonymous executable (JIT)', '.* ..x. .* shared_other=[0-9]+ ($|.*dlmalloc.*)'), 41 (' Anonymous read-write', '.* rw.. .* .*shared_other=[0-9]+ ($|.*dlmalloc.*)'), 42 (' Native heap (dlmalloc)', '.* r... .* /.*dlmalloc.*'), 43 ('File total', '.* .... .* /((?!dev/ashmem/dlmalloc).*)'), 44 (' File executable', '.* ..x. .* /((?!dev/ashmem/dlmalloc).*)'), 45 (' File read-write', '.* rw.. .* /((?!dev/ashmem/dlmalloc).*)'), 46 (' Dalvik', '.* rw.. .* /.*dalvik.*'), 47 (' Dalvik heap', '.* rw.. .* /.*dalvik-heap.*'), 48 (' Ashmem', '.* rw.. .* /dev/ashmem .*'), 49 (' libwebcore.so total', '.* r... .* /.*libwebcore.so'), 50 (' libwebcore.so read-only', '.* r--. .* /.*libwebcore.so'), 51 (' libwebcore.so read-write', '.* rw-. .* /.*libwebcore.so'), 52 (' libwebcore.so executable', '.* r.x. .* /.*libwebcore.so'), 53 (' libwebviewchromium.so total', '.* r... .* /.*libwebviewchromium.so'), 54 (' libwebviewchromium.so read-only', '.* r--. .* /.*libwebviewchromium.so'), 55 (' libwebviewchromium.so read-write', '.* rw-. .* /.*libwebviewchromium.so'), 56 (' libwebviewchromium.so executable', '.* r.x. .* /.*libwebviewchromium.so'), 57 (' Driver mappings', '.* .... .* /dev/\w+$'), 58 (' /dev/maliN total', '.* .... .* /dev/mali.*'), 59 ('OTHER (non file non anon)', '.* .... .*shared_other=[0-9]+ [^/]+'), 60 (' DMA buffers', '.* .... .*shared_other=[0-9]+ .*dmabuf.*'), 61 ] 62 63 64 def _CollectMemoryStats(memdump, region_filters): 65 processes = [] 66 mem_usage_for_regions = None 67 regexps = {} 68 for region_filter in region_filters: 69 regexps[region_filter] = re.compile(region_filter) 70 for line in memdump: 71 if 'PID=' in line: 72 mem_usage_for_regions = {} 73 processes.append(mem_usage_for_regions) 74 continue 75 matched_regions = Set([]) 76 for region_filter in region_filters: 77 if regexps[region_filter].match(line.rstrip('\r\n')): 78 matched_regions.add(region_filter) 79 if not region_filter in mem_usage_for_regions: 80 mem_usage_for_regions[region_filter] = { 81 'private_unevictable': 0, 82 'private': 0, 83 'shared_app': 0.0, 84 'shared_other_unevictable': 0, 85 'shared_other': 0, 86 } 87 for matched_region in matched_regions: 88 mem_usage = mem_usage_for_regions[matched_region] 89 for key in mem_usage: 90 for token in line.split(' '): 91 if (key+'=') in token: 92 field = token.split('=')[1] 93 if key != 'shared_app': 94 mem_usage[key] += int(field) 95 else: # shared_app=[\d,\d...] 96 array = eval(field) 97 for i in xrange(len(array)): 98 mem_usage[key] += float(array[i]) / (i + 2) 99 break 100 return processes 101 102 103 def _ConvertMemoryField(field): 104 return str(field / (1024)) 105 106 107 def _DumpCSV(processes_stats): 108 total_map = {} 109 i = 0 110 for process in processes_stats: 111 i += 1 112 print ',private,private_unevictable,shared_other,shared_other_unevictable,' 113 for (k, v) in _ENTRIES: 114 header_column = k + ',' if 'NOHEADER' not in os.environ else ',' 115 if not v in process: 116 print header_column + '0,0,0,0,' 117 continue 118 if not v in total_map: 119 total_map[v] = 0 120 total_map[v] += process[v]['private'] + process[v]['shared_app'] 121 print ( 122 header_column + 123 _ConvertMemoryField(process[v]['private']) + ',' + 124 _ConvertMemoryField(process[v]['private_unevictable']) + ',' + 125 _ConvertMemoryField(process[v]['shared_other']) + ',' + 126 _ConvertMemoryField(process[v]['shared_other_unevictable']) + ',' 127 ) 128 129 130 def main(argv): 131 _DumpCSV(_CollectMemoryStats(sys.stdin, [value for (key, value) in _ENTRIES])) 132 133 134 if __name__ == '__main__': 135 main(sys.argv) 136