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