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.
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.
28 _DEVICE_BINDIRS
= ['/data/data/', '/data/app-lib/', '/data/local/tmp']
30 def __init__(self
, usage
):
31 self
._parser
= optparse
.OptionParser(usage
)
35 dump_path
, multiple
, no_dump
=False, alternative_dirs
=None):
36 prefix
= SubCommand
._find
_prefix
(dump_path
)
37 # If the target process is estimated to be working on Android, converts
38 # a path in the Android device to a path estimated to be corresponding in
39 # the host. Use --alternative-dirs to specify the conversion manually.
40 if not alternative_dirs
:
41 alternative_dirs
= SubCommand
._estimate
_alternative
_dirs
(prefix
)
43 for device
, host
in alternative_dirs
.iteritems():
44 LOGGER
.info('Assuming %s on device as %s on host' % (device
, host
))
45 symbol_data_sources
= SymbolDataSources(prefix
, alternative_dirs
)
46 symbol_data_sources
.prepare()
47 bucket_set
= BucketSet()
48 bucket_set
.load(prefix
)
51 dump_list
= DumpList
.load(SubCommand
._find
_all
_dumps
(dump_path
))
53 dump
= Dump
.load(dump_path
)
54 symbol_mapping_cache
= SymbolMappingCache()
55 with
open(prefix
+ '.cache.function', 'a+') as cache_f
:
56 symbol_mapping_cache
.update(
57 FUNCTION_SYMBOLS
, bucket_set
,
58 SymbolFinder(FUNCTION_SYMBOLS
, symbol_data_sources
), cache_f
)
59 with
open(prefix
+ '.cache.typeinfo', 'a+') as cache_f
:
60 symbol_mapping_cache
.update(
61 TYPEINFO_SYMBOLS
, bucket_set
,
62 SymbolFinder(TYPEINFO_SYMBOLS
, symbol_data_sources
), cache_f
)
63 with
open(prefix
+ '.cache.sourcefile', 'a+') as cache_f
:
64 symbol_mapping_cache
.update(
65 SOURCEFILE_SYMBOLS
, bucket_set
,
66 SymbolFinder(SOURCEFILE_SYMBOLS
, symbol_data_sources
), cache_f
)
67 bucket_set
.symbolize(symbol_mapping_cache
)
71 return (bucket_set
, dump_list
)
73 return (bucket_set
, dump
)
76 def _find_prefix(path
):
77 return re
.sub('\.[0-9][0-9][0-9][0-9]\.heap', '', path
)
80 def _estimate_alternative_dirs(prefix
):
81 """Estimates a path in host from a corresponding path in target device.
83 For Android, dmprof.py should find symbol information from binaries in
84 the host instead of the Android device because dmprof.py doesn't run on
85 the Android device. This method estimates a path in the host
86 corresponding to a path in the Android device.
89 A dict that maps a path in the Android device to a path in the host.
90 If a file in SubCommand._DEVICE_BINDIRS is found in /proc/maps, it
91 assumes the process was running on Android and maps the path to
92 "out/Debug/lib" in the Chromium directory. An empty dict is returned
95 device_lib_path_candidates
= set()
97 with
open(prefix
+ '.maps') as maps_f
:
98 maps
= procfs
.ProcMaps
.load_file(maps_f
)
100 name
= entry
.as_dict()['name']
101 if any([base_dir
in name
for base_dir
in SubCommand
._DEVICE
_BINDIRS
]):
102 device_lib_path_candidates
.add(os
.path
.dirname(name
))
104 if len(device_lib_path_candidates
) == 1:
105 return {device_lib_path_candidates
.pop(): os
.path
.join(
106 CHROME_SRC_PATH
, 'out', 'Debug', 'lib')}
111 def _find_all_dumps(dump_path
):
112 prefix
= SubCommand
._find
_prefix
(dump_path
)
113 dump_path_list
= [dump_path
]
115 n
= int(dump_path
[len(dump_path
) - 9 : len(dump_path
) - 5])
119 p
= '%s.%04d.heap' % (prefix
, n
)
120 if os
.path
.exists(p
) and os
.stat(p
).st_size
:
121 dump_path_list
.append(p
)
128 return dump_path_list
131 def _find_all_buckets(dump_path
):
132 prefix
= SubCommand
._find
_prefix
(dump_path
)
133 bucket_path_list
= []
137 path
= '%s.%04d.buckets' % (prefix
, n
)
138 if not os
.path
.exists(path
):
143 bucket_path_list
.append(path
)
146 return bucket_path_list
148 def _parse_args(self
, sys_argv
, required
):
149 options
, args
= self
._parser
.parse_args(sys_argv
)
150 if len(args
) < required
+ 1:
151 self
._parser
.error('needs %d argument(s).\n' % required
)
153 return (options
, args
)
156 def _parse_policy_list(options_policy
):
158 return options_policy
.split(',')