1# Copyright 2013 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import logging 6import os 7 8from lib.symbol import FUNCTION_SYMBOLS, SOURCEFILE_SYMBOLS, TYPEINFO_SYMBOLS 9 10 11LOGGER = logging.getLogger('dmprof') 12 13# Indexes in dumped heap profile dumps. 14VIRTUAL, COMMITTED, ALLOC_COUNT, FREE_COUNT, _, BUCKET_ID = range(6) 15 16 17class Bucket(object): 18 """Represents a bucket, which is a unit of memory block classification.""" 19 20 def __init__(self, stacktrace, allocator_type, typeinfo, typeinfo_name): 21 self._stacktrace = stacktrace 22 self._allocator_type = allocator_type 23 self._typeinfo = typeinfo 24 self._typeinfo_name = typeinfo_name 25 26 self._symbolized_stackfunction = stacktrace 27 self._symbolized_joined_stackfunction = '' 28 self._symbolized_stacksourcefile = stacktrace 29 self._symbolized_joined_stacksourcefile = '' 30 self._symbolized_typeinfo = typeinfo_name 31 32 self.component_cache = '' 33 34 def __str__(self): 35 result = [] 36 result.append(self._allocator_type) 37 if self._symbolized_typeinfo == 'no typeinfo': 38 result.append('tno_typeinfo') 39 else: 40 result.append('t' + self._symbolized_typeinfo) 41 result.append('n' + self._typeinfo_name) 42 result.extend(['%s(@%s)' % (function, sourcefile) 43 for function, sourcefile 44 in zip(self._symbolized_stackfunction, 45 self._symbolized_stacksourcefile)]) 46 return ' '.join(result) 47 48 def symbolize(self, symbol_mapping_cache): 49 """Makes a symbolized stacktrace and typeinfo with |symbol_mapping_cache|. 50 51 Args: 52 symbol_mapping_cache: A SymbolMappingCache object. 53 """ 54 # TODO(dmikurube): Fill explicitly with numbers if symbol not found. 55 self._symbolized_stackfunction = [ 56 symbol_mapping_cache.lookup(FUNCTION_SYMBOLS, address) 57 for address in self._stacktrace] 58 self._symbolized_joined_stackfunction = ' '.join( 59 self._symbolized_stackfunction) 60 self._symbolized_stacksourcefile = [ 61 symbol_mapping_cache.lookup(SOURCEFILE_SYMBOLS, address) 62 for address in self._stacktrace] 63 self._symbolized_joined_stacksourcefile = ' '.join( 64 self._symbolized_stacksourcefile) 65 if not self._typeinfo: 66 self._symbolized_typeinfo = 'no typeinfo' 67 else: 68 self._symbolized_typeinfo = symbol_mapping_cache.lookup( 69 TYPEINFO_SYMBOLS, self._typeinfo) 70 if not self._symbolized_typeinfo: 71 self._symbolized_typeinfo = 'no typeinfo' 72 73 def clear_component_cache(self): 74 self.component_cache = '' 75 76 @property 77 def stacktrace(self): 78 return self._stacktrace 79 80 @property 81 def allocator_type(self): 82 return self._allocator_type 83 84 @property 85 def typeinfo(self): 86 return self._typeinfo 87 88 @property 89 def typeinfo_name(self): 90 return self._typeinfo_name 91 92 @property 93 def symbolized_stackfunction(self): 94 return self._symbolized_stackfunction 95 96 @property 97 def symbolized_joined_stackfunction(self): 98 return self._symbolized_joined_stackfunction 99 100 @property 101 def symbolized_stacksourcefile(self): 102 return self._symbolized_stacksourcefile 103 104 @property 105 def symbolized_joined_stacksourcefile(self): 106 return self._symbolized_joined_stacksourcefile 107 108 @property 109 def symbolized_typeinfo(self): 110 return self._symbolized_typeinfo 111 112 113class BucketSet(object): 114 """Represents a set of bucket.""" 115 def __init__(self): 116 self._buckets = {} 117 self._code_addresses = set() 118 self._typeinfo_addresses = set() 119 120 def load(self, prefix): 121 """Loads all related bucket files. 122 123 Args: 124 prefix: A prefix string for bucket file names. 125 """ 126 LOGGER.info('Loading bucket files.') 127 128 n = 0 129 skipped = 0 130 while True: 131 path = '%s.%04d.buckets' % (prefix, n) 132 if not os.path.exists(path) or not os.stat(path).st_size: 133 if skipped > 10: 134 break 135 n += 1 136 skipped += 1 137 continue 138 LOGGER.info(' %s' % path) 139 with open(path, 'r') as f: 140 self._load_file(f) 141 n += 1 142 skipped = 0 143 144 def _load_file(self, bucket_f): 145 for line in bucket_f: 146 words = line.split() 147 typeinfo = None 148 typeinfo_name = '' 149 stacktrace_begin = 2 150 for index, word in enumerate(words): 151 if index < 2: 152 continue 153 if word[0] == 't': 154 typeinfo = int(word[1:], 16) 155 self._typeinfo_addresses.add(typeinfo) 156 elif word[0] == 'n': 157 typeinfo_name = word[1:] 158 else: 159 stacktrace_begin = index 160 break 161 stacktrace = [int(address, 16) for address in words[stacktrace_begin:]] 162 for frame in stacktrace: 163 self._code_addresses.add(frame) 164 self._buckets[int(words[0])] = Bucket( 165 stacktrace, words[1], typeinfo, typeinfo_name) 166 167 def __iter__(self): 168 for bucket_id, bucket_content in self._buckets.iteritems(): 169 yield bucket_id, bucket_content 170 171 def __getitem__(self, bucket_id): 172 return self._buckets[bucket_id] 173 174 def get(self, bucket_id): 175 return self._buckets.get(bucket_id) 176 177 def symbolize(self, symbol_mapping_cache): 178 for bucket_content in self._buckets.itervalues(): 179 bucket_content.symbolize(symbol_mapping_cache) 180 181 def clear_component_cache(self): 182 for bucket_content in self._buckets.itervalues(): 183 bucket_content.clear_component_cache() 184 185 def iter_addresses(self, symbol_type): 186 if symbol_type in [FUNCTION_SYMBOLS, SOURCEFILE_SYMBOLS]: 187 for function in self._code_addresses: 188 yield function 189 else: 190 for function in self._typeinfo_addresses: 191 yield function 192