File: stack_symbolizer.py

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (137 lines) | stat: -rw-r--r-- 4,290 bytes parent folder | download | duplicates (11)
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"