Revert of Remove OneClickSigninHelper since it is no longer used. (patchset #5 id...
[chromium-blink-merge.git] / tools / deep_memory_profiler / lib / subcommand.py
blob36d0fe3e3d10fec333e09e3e112bc606a5759cc4
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.
5 import logging
6 import optparse
7 import os
8 import re
10 from lib.bucket import BucketSet
11 from lib.dump import Dump, DumpList
12 from lib.symbol import SymbolDataSources, SymbolMappingCache, SymbolFinder
13 from lib.symbol import procfs
14 from lib.symbol import FUNCTION_SYMBOLS, SOURCEFILE_SYMBOLS, TYPEINFO_SYMBOLS
17 LOGGER = logging.getLogger('dmprof')
19 BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
20 CHROME_SRC_PATH = os.path.join(BASE_PATH, os.pardir, os.pardir)
23 class SubCommand(object):
24 """Subclasses are a subcommand for this executable.
26 See COMMANDS in main() in dmprof.py.
27 """
28 _DEVICE_BINDIRS = ['/data/data/', '/data/app-lib/', '/data/local/tmp']
30 def __init__(self, usage):
31 self._parser = optparse.OptionParser(usage)
33 @staticmethod
34 def load_basic_files(dump_path, multiple, alternative_dirs=None):
35 prefix = SubCommand._find_prefix(dump_path)
36 # If the target process is estimated to be working on Android, converts
37 # a path in the Android device to a path estimated to be corresponding in
38 # the host. Use --alternative-dirs to specify the conversion manually.
39 if not alternative_dirs:
40 alternative_dirs = SubCommand._estimate_alternative_dirs(prefix)
41 if alternative_dirs:
42 for device, host in alternative_dirs.iteritems():
43 LOGGER.info('Assuming %s on device as %s on host' % (device, host))
44 symbol_data_sources = SymbolDataSources(prefix, alternative_dirs)
45 symbol_data_sources.prepare()
46 bucket_set = BucketSet()
47 bucket_set.load(prefix)
48 if multiple:
49 dump_list = DumpList.load(SubCommand._find_all_dumps(dump_path))
50 else:
51 dump = Dump.load(dump_path)
52 symbol_mapping_cache = SymbolMappingCache()
53 with open(prefix + '.cache.function', 'a+') as cache_f:
54 symbol_mapping_cache.update(
55 FUNCTION_SYMBOLS, bucket_set,
56 SymbolFinder(FUNCTION_SYMBOLS, symbol_data_sources), cache_f)
57 with open(prefix + '.cache.typeinfo', 'a+') as cache_f:
58 symbol_mapping_cache.update(
59 TYPEINFO_SYMBOLS, bucket_set,
60 SymbolFinder(TYPEINFO_SYMBOLS, symbol_data_sources), cache_f)
61 with open(prefix + '.cache.sourcefile', 'a+') as cache_f:
62 symbol_mapping_cache.update(
63 SOURCEFILE_SYMBOLS, bucket_set,
64 SymbolFinder(SOURCEFILE_SYMBOLS, symbol_data_sources), cache_f)
65 bucket_set.symbolize(symbol_mapping_cache)
66 if multiple:
67 return (bucket_set, dump_list)
68 else:
69 return (bucket_set, dump)
71 @staticmethod
72 def _find_prefix(path):
73 return re.sub('\.[0-9][0-9][0-9][0-9]\.heap', '', path)
75 @staticmethod
76 def _estimate_alternative_dirs(prefix):
77 """Estimates a path in host from a corresponding path in target device.
79 For Android, dmprof.py should find symbol information from binaries in
80 the host instead of the Android device because dmprof.py doesn't run on
81 the Android device. This method estimates a path in the host
82 corresponding to a path in the Android device.
84 Returns:
85 A dict that maps a path in the Android device to a path in the host.
86 If a file in SubCommand._DEVICE_BINDIRS is found in /proc/maps, it
87 assumes the process was running on Android and maps the path to
88 "out/Debug/lib" in the Chromium directory. An empty dict is returned
89 unless Android.
90 """
91 device_lib_path_candidates = set()
93 with open(prefix + '.maps') as maps_f:
94 maps = procfs.ProcMaps.load_file(maps_f)
95 for entry in maps:
96 name = entry.as_dict()['name']
97 if any([base_dir in name for base_dir in SubCommand._DEVICE_BINDIRS]):
98 device_lib_path_candidates.add(os.path.dirname(name))
100 if len(device_lib_path_candidates) == 1:
101 return {device_lib_path_candidates.pop(): os.path.join(
102 CHROME_SRC_PATH, 'out', 'Debug', 'lib')}
103 else:
104 return {}
106 @staticmethod
107 def _find_all_dumps(dump_path):
108 prefix = SubCommand._find_prefix(dump_path)
109 dump_path_list = [dump_path]
111 n = int(dump_path[len(dump_path) - 9 : len(dump_path) - 5])
112 n += 1
113 skipped = 0
114 while True:
115 p = '%s.%04d.heap' % (prefix, n)
116 if os.path.exists(p) and os.stat(p).st_size:
117 dump_path_list.append(p)
118 else:
119 if skipped > 10:
120 break
121 skipped += 1
122 n += 1
124 return dump_path_list
126 @staticmethod
127 def _find_all_buckets(dump_path):
128 prefix = SubCommand._find_prefix(dump_path)
129 bucket_path_list = []
131 n = 0
132 while True:
133 path = '%s.%04d.buckets' % (prefix, n)
134 if not os.path.exists(path):
135 if n > 10:
136 break
137 n += 1
138 continue
139 bucket_path_list.append(path)
140 n += 1
142 return bucket_path_list
144 def _parse_args(self, sys_argv, required):
145 options, args = self._parser.parse_args(sys_argv)
146 if len(args) < required + 1:
147 self._parser.error('needs %d argument(s).\n' % required)
148 return None
149 return (options, args)
151 @staticmethod
152 def _parse_policy_list(options_policy):
153 if options_policy:
154 return options_policy.split(',')
155 else:
156 return None