17dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch# Copyright 2013 The Chromium Authors. All rights reserved. 27dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch# Use of this source code is governed by a BSD-style license that can be 37dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch# found in the LICENSE file. 47dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 57dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochimport logging 67dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochimport optparse 77dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochimport os 87dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochimport re 97dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 107dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochfrom lib.bucket import BucketSet 117dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochfrom lib.dump import Dump, DumpList 127dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochfrom lib.symbol import SymbolDataSources, SymbolMappingCache, SymbolFinder 13a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)from lib.symbol import procfs 147dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochfrom lib.symbol import FUNCTION_SYMBOLS, SOURCEFILE_SYMBOLS, TYPEINFO_SYMBOLS 157dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 167dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 177dbb3d5cf0c15f500944d211057644d6a2f37371Ben MurdochLOGGER = logging.getLogger('dmprof') 187dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 197dbb3d5cf0c15f500944d211057644d6a2f37371Ben MurdochBASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 207dbb3d5cf0c15f500944d211057644d6a2f37371Ben MurdochCHROME_SRC_PATH = os.path.join(BASE_PATH, os.pardir, os.pardir) 217dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 227dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 237dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochclass SubCommand(object): 247dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch """Subclasses are a subcommand for this executable. 257dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 267dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch See COMMANDS in main() in dmprof.py. 277dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch """ 287dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch _DEVICE_BINDIRS = ['/data/data/', '/data/app-lib/', '/data/local/tmp'] 297dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 307dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch def __init__(self, usage): 317dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch self._parser = optparse.OptionParser(usage) 327dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 337dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch @staticmethod 346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) def load_basic_files(dump_path, multiple, alternative_dirs=None): 357dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch prefix = SubCommand._find_prefix(dump_path) 367dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch # If the target process is estimated to be working on Android, converts 377dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch # a path in the Android device to a path estimated to be corresponding in 387dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch # the host. Use --alternative-dirs to specify the conversion manually. 397dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch if not alternative_dirs: 407dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch alternative_dirs = SubCommand._estimate_alternative_dirs(prefix) 417dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch if alternative_dirs: 427dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch for device, host in alternative_dirs.iteritems(): 437dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch LOGGER.info('Assuming %s on device as %s on host' % (device, host)) 447dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch symbol_data_sources = SymbolDataSources(prefix, alternative_dirs) 457dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch symbol_data_sources.prepare() 467dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch bucket_set = BucketSet() 477dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch bucket_set.load(prefix) 486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if multiple: 496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) dump_list = DumpList.load(SubCommand._find_all_dumps(dump_path)) 506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) else: 516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) dump = Dump.load(dump_path) 527dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch symbol_mapping_cache = SymbolMappingCache() 537dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch with open(prefix + '.cache.function', 'a+') as cache_f: 547dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch symbol_mapping_cache.update( 557dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch FUNCTION_SYMBOLS, bucket_set, 567dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch SymbolFinder(FUNCTION_SYMBOLS, symbol_data_sources), cache_f) 577dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch with open(prefix + '.cache.typeinfo', 'a+') as cache_f: 587dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch symbol_mapping_cache.update( 597dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch TYPEINFO_SYMBOLS, bucket_set, 607dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch SymbolFinder(TYPEINFO_SYMBOLS, symbol_data_sources), cache_f) 617dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch with open(prefix + '.cache.sourcefile', 'a+') as cache_f: 627dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch symbol_mapping_cache.update( 637dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch SOURCEFILE_SYMBOLS, bucket_set, 647dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch SymbolFinder(SOURCEFILE_SYMBOLS, symbol_data_sources), cache_f) 657dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch bucket_set.symbolize(symbol_mapping_cache) 666e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if multiple: 677dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch return (bucket_set, dump_list) 687dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch else: 697dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch return (bucket_set, dump) 707dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 717dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch @staticmethod 727dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch def _find_prefix(path): 737dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch return re.sub('\.[0-9][0-9][0-9][0-9]\.heap', '', path) 747dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 757dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch @staticmethod 767dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch def _estimate_alternative_dirs(prefix): 777dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch """Estimates a path in host from a corresponding path in target device. 787dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 797dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch For Android, dmprof.py should find symbol information from binaries in 807dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch the host instead of the Android device because dmprof.py doesn't run on 817dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch the Android device. This method estimates a path in the host 827dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch corresponding to a path in the Android device. 837dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 847dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch Returns: 857dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch A dict that maps a path in the Android device to a path in the host. 867dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch If a file in SubCommand._DEVICE_BINDIRS is found in /proc/maps, it 877dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch assumes the process was running on Android and maps the path to 887dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch "out/Debug/lib" in the Chromium directory. An empty dict is returned 897dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch unless Android. 907dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch """ 917dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch device_lib_path_candidates = set() 927dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 937dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch with open(prefix + '.maps') as maps_f: 94a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) maps = procfs.ProcMaps.load_file(maps_f) 957dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch for entry in maps: 967dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch name = entry.as_dict()['name'] 977dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch if any([base_dir in name for base_dir in SubCommand._DEVICE_BINDIRS]): 987dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch device_lib_path_candidates.add(os.path.dirname(name)) 997dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 1007dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch if len(device_lib_path_candidates) == 1: 1017dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch return {device_lib_path_candidates.pop(): os.path.join( 1027dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch CHROME_SRC_PATH, 'out', 'Debug', 'lib')} 1037dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch else: 1047dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch return {} 1057dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 1067dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch @staticmethod 1077dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch def _find_all_dumps(dump_path): 1087dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch prefix = SubCommand._find_prefix(dump_path) 1097dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch dump_path_list = [dump_path] 1107dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 1117dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch n = int(dump_path[len(dump_path) - 9 : len(dump_path) - 5]) 1127dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch n += 1 1137dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch skipped = 0 1147dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch while True: 1157dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch p = '%s.%04d.heap' % (prefix, n) 1167dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch if os.path.exists(p) and os.stat(p).st_size: 1177dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch dump_path_list.append(p) 1187dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch else: 1197dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch if skipped > 10: 1207dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch break 1217dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch skipped += 1 1227dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch n += 1 1237dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 1247dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch return dump_path_list 1257dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 1267dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch @staticmethod 1277dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch def _find_all_buckets(dump_path): 1287dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch prefix = SubCommand._find_prefix(dump_path) 1297dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch bucket_path_list = [] 1307dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 1317dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch n = 0 1327dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch while True: 1337dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch path = '%s.%04d.buckets' % (prefix, n) 1347dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch if not os.path.exists(path): 1357dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch if n > 10: 1367dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch break 1377dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch n += 1 1387dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch continue 1397dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch bucket_path_list.append(path) 1407dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch n += 1 1417dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 1427dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch return bucket_path_list 1437dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 1447dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch def _parse_args(self, sys_argv, required): 1457dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch options, args = self._parser.parse_args(sys_argv) 1467dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch if len(args) < required + 1: 1477dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch self._parser.error('needs %d argument(s).\n' % required) 1487dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch return None 1497dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch return (options, args) 1507dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 1517dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch @staticmethod 1527dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch def _parse_policy_list(options_policy): 1537dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch if options_policy: 1547dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch return options_policy.split(',') 1557dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch else: 1567dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch return None 157