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
)
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
)
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
)
49 dump_list
= DumpList
.load(SubCommand
._find
_all
_dumps
(dump_path
))
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
)
67 return (bucket_set
, dump_list
)
69 return (bucket_set
, dump
)
72 def _find_prefix(path
):
73 return re
.sub('\.[0-9][0-9][0-9][0-9]\.heap', '', path
)
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.
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
91 device_lib_path_candidates
= set()
93 with
open(prefix
+ '.maps') as maps_f
:
94 maps
= procfs
.ProcMaps
.load_file(maps_f
)
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')}
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])
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
)
124 return dump_path_list
127 def _find_all_buckets(dump_path
):
128 prefix
= SubCommand
._find
_prefix
(dump_path
)
129 bucket_path_list
= []
133 path
= '%s.%04d.buckets' % (prefix
, n
)
134 if not os
.path
.exists(path
):
139 bucket_path_list
.append(path
)
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
)
149 return (options
, args
)
152 def _parse_policy_list(options_policy
):
154 return options_policy
.split(',')