Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(84)

Side by Side Diff: tools/deep_memory_profiler/dmprof.py

Issue 15035009: Estimates a path in host from a corresponding path in Android device. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | tools/find_runtime_symbols/prepare_symbol_info.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """The deep heap profiler script for Chrome.""" 5 """The deep heap profiler script for Chrome."""
6 6
7 import copy 7 import copy
8 import datetime 8 import datetime
9 import json 9 import json
10 import logging 10 import logging
(...skipping 23 matching lines...) Expand all
34 34
35 BUCKET_ID = 5 35 BUCKET_ID = 5
36 VIRTUAL = 0 36 VIRTUAL = 0
37 COMMITTED = 1 37 COMMITTED = 1
38 ALLOC_COUNT = 2 38 ALLOC_COUNT = 2
39 FREE_COUNT = 3 39 FREE_COUNT = 3
40 NULL_REGEX = re.compile('') 40 NULL_REGEX = re.compile('')
41 41
42 LOGGER = logging.getLogger('dmprof') 42 LOGGER = logging.getLogger('dmprof')
43 POLICIES_JSON_PATH = os.path.join(BASE_PATH, 'policies.json') 43 POLICIES_JSON_PATH = os.path.join(BASE_PATH, 'policies.json')
44 CHROME_SRC_PATH = os.path.join(BASE_PATH, os.pardir, os.pardir)
44 45
45 46
46 # Heap Profile Dump versions 47 # Heap Profile Dump versions
47 48
48 # DUMP_DEEP_[1-4] are obsolete. 49 # DUMP_DEEP_[1-4] are obsolete.
49 # DUMP_DEEP_2+ distinct mmap regions and malloc chunks. 50 # DUMP_DEEP_2+ distinct mmap regions and malloc chunks.
50 # DUMP_DEEP_3+ don't include allocation functions in their stack dumps. 51 # DUMP_DEEP_3+ don't include allocation functions in their stack dumps.
51 # DUMP_DEEP_4+ support comments with '#' and global stats "nonprofiled-*". 52 # DUMP_DEEP_4+ support comments with '#' and global stats "nonprofiled-*".
52 # DUMP_DEEP_[1-2] should be processed by POLICY_DEEP_1. 53 # DUMP_DEEP_[1-2] should be processed by POLICY_DEEP_1.
53 # DUMP_DEEP_[3-4] should be processed by POLICY_DEEP_2 or POLICY_DEEP_3. 54 # DUMP_DEEP_[3-4] should be processed by POLICY_DEEP_2 or POLICY_DEEP_3.
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after
213 files by 'prepare()' with tools/find_runtime_symbols/prepare_symbol_info.py. 214 files by 'prepare()' with tools/find_runtime_symbols/prepare_symbol_info.py.
214 215
215 Binaries are not mandatory to profile. The prepared data sources work in 216 Binaries are not mandatory to profile. The prepared data sources work in
216 place of the binary even if the binary has been overwritten with another 217 place of the binary even if the binary has been overwritten with another
217 binary. 218 binary.
218 219
219 Note that loading the symbol data sources takes a long time. They are often 220 Note that loading the symbol data sources takes a long time. They are often
220 very big. So, the 'dmprof' profiler is designed to use 'SymbolMappingCache' 221 very big. So, the 'dmprof' profiler is designed to use 'SymbolMappingCache'
221 which caches actually used symbols. 222 which caches actually used symbols.
222 """ 223 """
223 def __init__(self, prefix, fake_directories=None): 224 def __init__(self, prefix, alternative_dirs=None):
224 self._prefix = prefix 225 self._prefix = prefix
225 self._prepared_symbol_data_sources_path = None 226 self._prepared_symbol_data_sources_path = None
226 self._loaded_symbol_data_sources = None 227 self._loaded_symbol_data_sources = None
227 self._fake_directories = fake_directories or {} 228 self._alternative_dirs = alternative_dirs or {}
228 229
229 def prepare(self): 230 def prepare(self):
230 """Prepares symbol data sources by extracting mapping from a binary. 231 """Prepares symbol data sources by extracting mapping from a binary.
231 232
232 The prepared symbol data sources are stored in a directory. The directory 233 The prepared symbol data sources are stored in a directory. The directory
233 name is stored in |self._prepared_symbol_data_sources_path|. 234 name is stored in |self._prepared_symbol_data_sources_path|.
234 235
235 Returns: 236 Returns:
236 True if succeeded. 237 True if succeeded.
237 """ 238 """
238 LOGGER.info('Preparing symbol mapping...') 239 LOGGER.info('Preparing symbol mapping...')
239 self._prepared_symbol_data_sources_path, used_tempdir = ( 240 self._prepared_symbol_data_sources_path, used_tempdir = (
240 prepare_symbol_info.prepare_symbol_info( 241 prepare_symbol_info.prepare_symbol_info(
241 self._prefix + '.maps', 242 self._prefix + '.maps',
242 output_dir_path=self._prefix + '.symmap', 243 output_dir_path=self._prefix + '.symmap',
243 fake_directories=self._fake_directories, 244 alternative_dirs=self._alternative_dirs,
244 use_tempdir=True, 245 use_tempdir=True,
245 use_source_file_name=True)) 246 use_source_file_name=True))
246 if self._prepared_symbol_data_sources_path: 247 if self._prepared_symbol_data_sources_path:
247 LOGGER.info(' Prepared symbol mapping.') 248 LOGGER.info(' Prepared symbol mapping.')
248 if used_tempdir: 249 if used_tempdir:
249 LOGGER.warn(' Using a temporary directory for symbol mapping.') 250 LOGGER.warn(' Using a temporary directory for symbol mapping.')
250 LOGGER.warn(' Delete it by yourself.') 251 LOGGER.warn(' Delete it by yourself.')
251 LOGGER.warn(' Or, move the directory by yourself to use it later.') 252 LOGGER.warn(' Or, move the directory by yourself to use it later.')
252 return True 253 return True
253 else: 254 else:
(...skipping 789 matching lines...) Expand 10 before | Expand all | Expand 10 after
1043 1044
1044 def __getitem__(self, index): 1045 def __getitem__(self, index):
1045 return self._dump_list[index] 1046 return self._dump_list[index]
1046 1047
1047 1048
1048 class Command(object): 1049 class Command(object):
1049 """Subclasses are a subcommand for this executable. 1050 """Subclasses are a subcommand for this executable.
1050 1051
1051 See COMMANDS in main(). 1052 See COMMANDS in main().
1052 """ 1053 """
1054 _DEVICE_LIB_PATTERN = re.compile(r'(/data/app-lib/.*-[0-9])/.*')
1055
1053 def __init__(self, usage): 1056 def __init__(self, usage):
1054 self._parser = optparse.OptionParser(usage) 1057 self._parser = optparse.OptionParser(usage)
1055 1058
1056 @staticmethod 1059 @staticmethod
1057 def load_basic_files( 1060 def load_basic_files(
1058 dump_path, multiple, no_dump=False, fake_directories=None): 1061 dump_path, multiple, no_dump=False, alternative_dirs=None):
1059 prefix = Command._find_prefix(dump_path) 1062 prefix = Command._find_prefix(dump_path)
1060 symbol_data_sources = SymbolDataSources(prefix, fake_directories or {}) 1063 # If the target process is estimated to be working on Android, converts
1064 # a path in the Android device to a path estimated to be corresponding in
1065 # the host. Use --alternative-dirs to specify the conversion manually.
1066 if not alternative_dirs:
1067 alternative_dirs = Command._estimate_alternative_dirs(prefix)
1068 if alternative_dirs:
1069 for device, host in alternative_dirs.iteritems():
1070 LOGGER.info('Assuming %s on device as %s on host' % (device, host))
1071 symbol_data_sources = SymbolDataSources(prefix, alternative_dirs)
1061 symbol_data_sources.prepare() 1072 symbol_data_sources.prepare()
1062 bucket_set = BucketSet() 1073 bucket_set = BucketSet()
1063 bucket_set.load(prefix) 1074 bucket_set.load(prefix)
1064 if not no_dump: 1075 if not no_dump:
1065 if multiple: 1076 if multiple:
1066 dump_list = DumpList.load(Command._find_all_dumps(dump_path)) 1077 dump_list = DumpList.load(Command._find_all_dumps(dump_path))
1067 else: 1078 else:
1068 dump = Dump.load(dump_path) 1079 dump = Dump.load(dump_path)
1069 symbol_mapping_cache = SymbolMappingCache() 1080 symbol_mapping_cache = SymbolMappingCache()
1070 with open(prefix + '.cache.function', 'a+') as cache_f: 1081 with open(prefix + '.cache.function', 'a+') as cache_f:
(...skipping 14 matching lines...) Expand all
1085 elif multiple: 1096 elif multiple:
1086 return (bucket_set, dump_list) 1097 return (bucket_set, dump_list)
1087 else: 1098 else:
1088 return (bucket_set, dump) 1099 return (bucket_set, dump)
1089 1100
1090 @staticmethod 1101 @staticmethod
1091 def _find_prefix(path): 1102 def _find_prefix(path):
1092 return re.sub('\.[0-9][0-9][0-9][0-9]\.heap', '', path) 1103 return re.sub('\.[0-9][0-9][0-9][0-9]\.heap', '', path)
1093 1104
1094 @staticmethod 1105 @staticmethod
1106 def _estimate_alternative_dirs(prefix):
1107 """Estimates a path in host from a corresponding path in target device.
1108
1109 For Android, dmprof.py should find symbol information from binaries in
1110 the host instead of the Android device because dmprof.py doesn't run on
1111 the Android device. This method estimates a path in the host
1112 corresponding to a path in the Android device.
1113
1114 Returns:
1115 A dict that maps a path in the Android device to a path in the host.
1116 If a file in "/data/app-lib" is found in /proc/maps, it assumes the
1117 process was running on Android and maps the path to "out/Debug/lib"
1118 in the Chromium directory. An empty dict is returned unless Android.
1119 """
1120 device_lib_path_candidates = set()
1121
1122 with open(prefix + '.maps') as maps_f:
1123 maps = proc_maps.ProcMaps.load(maps_f)
1124 for entry in maps:
1125 matched = Command._DEVICE_LIB_PATTERN.match(entry.as_dict()['name'])
1126 if matched:
1127 device_lib_path_candidates.add(matched.group(1))
1128
1129 if len(device_lib_path_candidates) == 1:
1130 return {device_lib_path_candidates.pop(): os.path.join(
1131 CHROME_SRC_PATH, 'out', 'Debug', 'lib')}
1132 else:
1133 return {}
1134
1135 @staticmethod
1095 def _find_all_dumps(dump_path): 1136 def _find_all_dumps(dump_path):
1096 prefix = Command._find_prefix(dump_path) 1137 prefix = Command._find_prefix(dump_path)
1097 dump_path_list = [dump_path] 1138 dump_path_list = [dump_path]
1098 1139
1099 n = int(dump_path[len(dump_path) - 9 : len(dump_path) - 5]) 1140 n = int(dump_path[len(dump_path) - 9 : len(dump_path) - 5])
1100 n += 1 1141 n += 1
1101 while True: 1142 while True:
1102 p = '%s.%04d.heap' % (prefix, n) 1143 p = '%s.%04d.heap' % (prefix, n)
1103 if os.path.exists(p): 1144 if os.path.exists(p):
1104 dump_path_list.append(p) 1145 dump_path_list.append(p)
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
1197 out.write(frame + ' ') 1238 out.write(frame + ' ')
1198 out.write('\n') 1239 out.write('\n')
1199 1240
1200 1241
1201 class PolicyCommands(Command): 1242 class PolicyCommands(Command):
1202 def __init__(self, command): 1243 def __init__(self, command):
1203 super(PolicyCommands, self).__init__( 1244 super(PolicyCommands, self).__init__(
1204 'Usage: %%prog %s [-p POLICY] <first-dump>' % command) 1245 'Usage: %%prog %s [-p POLICY] <first-dump>' % command)
1205 self._parser.add_option('-p', '--policy', type='string', dest='policy', 1246 self._parser.add_option('-p', '--policy', type='string', dest='policy',
1206 help='profile with POLICY', metavar='POLICY') 1247 help='profile with POLICY', metavar='POLICY')
1207 self._parser.add_option('--fake-directories', dest='fake_directories', 1248 self._parser.add_option('--alternative-dirs', dest='alternative_dirs',
1208 metavar='/path/on/target@/path/on/host[:...]', 1249 metavar='/path/on/target@/path/on/host[:...]',
1209 help='Read files in /path/on/host/ instead of ' 1250 help='Read files in /path/on/host/ instead of '
1210 'files in /path/on/target/.') 1251 'files in /path/on/target/.')
1211 1252
1212 def _set_up(self, sys_argv): 1253 def _set_up(self, sys_argv):
1213 options, args = self._parse_args(sys_argv, 1) 1254 options, args = self._parse_args(sys_argv, 1)
1214 dump_path = args[1] 1255 dump_path = args[1]
1215 fake_directories_dict = {} 1256 alternative_dirs_dict = {}
1216 if options.fake_directories: 1257 if options.alternative_dirs:
1217 for fake_directory_pair in options.fake_directories.split(':'): 1258 for alternative_dir_pair in options.alternative_dirs.split(':'):
1218 target_path, host_path = fake_directory_pair.split('@', 1) 1259 target_path, host_path = alternative_dir_pair.split('@', 1)
1219 fake_directories_dict[target_path] = host_path 1260 alternative_dirs_dict[target_path] = host_path
1220 (bucket_set, dumps) = Command.load_basic_files( 1261 (bucket_set, dumps) = Command.load_basic_files(
1221 dump_path, True, fake_directories=fake_directories_dict) 1262 dump_path, True, alternative_dirs=alternative_dirs_dict)
1222 1263
1223 policy_set = PolicySet.load(Command._parse_policy_list(options.policy)) 1264 policy_set = PolicySet.load(Command._parse_policy_list(options.policy))
1224 return policy_set, dumps, bucket_set 1265 return policy_set, dumps, bucket_set
1225 1266
1226 @staticmethod 1267 @staticmethod
1227 def _apply_policy(dump, policy, bucket_set, first_dump_time): 1268 def _apply_policy(dump, policy, bucket_set, first_dump_time):
1228 """Aggregates the total memory size of each component. 1269 """Aggregates the total memory size of each component.
1229 1270
1230 Iterate through all stacktraces and attribute them to one of the components 1271 Iterate through all stacktraces and attribute them to one of the components
1231 based on the policy. It is important to apply policy in right order. 1272 based on the policy. It is important to apply policy in right order.
(...skipping 538 matching lines...) Expand 10 before | Expand all | Expand 10 after
1770 errorcode = COMMANDS[action]().do(sys.argv) 1811 errorcode = COMMANDS[action]().do(sys.argv)
1771 except ParsingException, e: 1812 except ParsingException, e:
1772 errorcode = 1 1813 errorcode = 1
1773 sys.stderr.write('Exit by parsing error: %s\n' % e) 1814 sys.stderr.write('Exit by parsing error: %s\n' % e)
1774 1815
1775 return errorcode 1816 return errorcode
1776 1817
1777 1818
1778 if __name__ == '__main__': 1819 if __name__ == '__main__':
1779 sys.exit(main()) 1820 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | tools/find_runtime_symbols/prepare_symbol_info.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698