1# Copyright 2017 The Chromium Authors 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import logging 6import os 7import re 8import tempfile 9import time 10 11from devil.utils import cmd_helper 12from pylib import constants 13from pylib.constants import host_paths 14from .expensive_line_transformer import ExpensiveLineTransformer 15from .expensive_line_transformer import ExpensiveLineTransformerPool 16 17_STACK_TOOL = os.path.join(host_paths.ANDROID_PLATFORM_DEVELOPMENT_SCRIPTS_PATH, 18 'stack') 19_MINIMUM_TIMEOUT = 10.0 20_PER_LINE_TIMEOUT = .005 # Should be able to process 200 lines per second. 21_PROCESS_START_TIMEOUT = 20.0 22_MAX_RESTARTS = 4 # Should be plenty unless tool is crashing on start-up. 23_POOL_SIZE = 1 24_PASSTHROUH_ON_FAILURE = True 25ABI_REG = re.compile('ABI: \'(.+?)\'') 26 27 28def _DeviceAbiToArch(device_abi): 29 # The order of this list is significant to find the more specific match 30 # (e.g., arm64) before the less specific (e.g., arm). 31 arches = ['arm64', 'arm', 'x86_64', 'x86_64', 'x86', 'mips'] 32 for arch in arches: 33 if arch in device_abi: 34 return arch 35 raise RuntimeError('Unknown device ABI: %s' % device_abi) 36 37 38class Symbolizer: 39 """A helper class to symbolize stack.""" 40 41 def __init__(self, apk_under_test=None): 42 self._apk_under_test = apk_under_test 43 self._time_spent_symbolizing = 0 44 45 46 def __del__(self): 47 self.CleanUp() 48 49 50 def CleanUp(self): 51 """Clean up the temporary directory of apk libs.""" 52 if self._time_spent_symbolizing > 0: 53 logging.info( 54 'Total time spent symbolizing: %.2fs', self._time_spent_symbolizing) 55 56 57 def ExtractAndResolveNativeStackTraces(self, data_to_symbolize, 58 device_abi, include_stack=True): 59 """Run the stack tool for given input. 60 61 Args: 62 data_to_symbolize: a list of strings to symbolize. 63 include_stack: boolean whether to include stack data in output. 64 device_abi: the default ABI of the device which generated the tombstone. 65 66 Yields: 67 A string for each line of resolved stack output. 68 """ 69 if not os.path.exists(_STACK_TOOL): 70 logging.warning('%s missing. Unable to resolve native stack traces.', 71 _STACK_TOOL) 72 return 73 74 arch = _DeviceAbiToArch(device_abi) 75 if not arch: 76 logging.warning('No device_abi can be found.') 77 return 78 79 cmd = [_STACK_TOOL, '--arch', arch, '--output-directory', 80 constants.GetOutDirectory(), '--more-info'] 81 env = dict(os.environ) 82 env['PYTHONDONTWRITEBYTECODE'] = '1' 83 with tempfile.NamedTemporaryFile(mode='w') as f: 84 f.write('\n'.join(data_to_symbolize)) 85 f.flush() 86 start = time.time() 87 try: 88 _, output = cmd_helper.GetCmdStatusAndOutput(cmd + [f.name], env=env) 89 finally: 90 self._time_spent_symbolizing += time.time() - start 91 for line in output.splitlines(): 92 if not include_stack and 'Stack Data:' in line: 93 break 94 yield line 95 96 97class PassThroughSymbolizer(ExpensiveLineTransformer): 98 def __init__(self, device_abi): 99 self._command = None 100 super().__init__(_PROCESS_START_TIMEOUT, _MINIMUM_TIMEOUT, 101 _PER_LINE_TIMEOUT) 102 if not os.path.exists(_STACK_TOOL): 103 logging.warning('%s: %s missing. Unable to resolve native stack traces.', 104 PassThroughSymbolizer.name, _STACK_TOOL) 105 return 106 arch = _DeviceAbiToArch(device_abi) 107 if not arch: 108 logging.warning('%s: No device_abi can be found.', 109 PassThroughSymbolizer.name) 110 return 111 self._command = [ 112 _STACK_TOOL, '--arch', arch, '--output-directory', 113 constants.GetOutDirectory(), '--more-info', '--pass-through', '--flush', 114 '--quiet', '-' 115 ] 116 self.start() 117 118 @property 119 def name(self): 120 return "symbolizer" 121 122 @property 123 def command(self): 124 return self._command 125 126 127class PassThroughSymbolizerPool(ExpensiveLineTransformerPool): 128 def __init__(self, device_abi): 129 self._device_abi = device_abi 130 super().__init__(_MAX_RESTARTS, _POOL_SIZE, _PASSTHROUH_ON_FAILURE) 131 132 def CreateTransformer(self): 133 return PassThroughSymbolizer(self._device_abi) 134 135 @property 136 def name(self): 137 return "symbolizer-pool" 138