summaryrefslogtreecommitdiffstats
path: root/third_party/android_platform/development/scripts/stack
blob: 59d7ebfde794c04566fd95983e0c231f8154d766 (plain)
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
#!/usr/bin/env python
#
# Copyright (C) 2013 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""stack symbolizes native crash dumps."""

import getopt
import glob
import logging
import os
import sys

import stack_core
import stack_libs
import subprocess
import symbol
import sys

sys.path.insert(0, os.path.join(os.path.dirname(__file__),
                                os.pardir, os.pardir, os.pardir, os.pardir,
                                'build', 'android'))
from pylib import constants


DEFAULT_SYMROOT='/tmp/symbols'
DEFAULT_APK_DIR='chrome_apk'
# From: https://source.android.com/source/build-numbers.html
_ANDROID_M_MAJOR_VERSION=6

def PrintUsage():
  """Print usage and exit with error."""
  # pylint: disable-msg=C6310
  print
  print "  usage: " + sys.argv[0] + " [options] [FILE]"
  print
  print "  --symbols-dir=path"
  print "       the path to a symbols dir, such as =/tmp/out/target/product/dream/symbols"
  print
  print "  --chrome-symbols-dir=path"
  print "       the path to a Chrome symbols dir (can be absolute or relative"
  print "       to src), such as =out/Debug/lib"
  print
  print "  --output-directory=path"
  print "       the path to the build output directory, such as out/Debug."
  print "       Ignored if --chrome-symbols-dir is passed."
  print
  print "  --packed-relocation-adjustments"
  print "  --no-packed-relocation-adjustments"
  print "       turn packed relocation adjustment on and off (default is off)"
  print "       If running on pre-M Android and the stack trace appears to"
  print "       make no sense, try turning this feature on."
  print
  print "  --chrome-apk-dir=path"
  print "       the path to the APK staging dir (can be absolute or relative"
  print "       to src), such as =out/Debug/chrome_apk"
  print "       If not specified, uses =|chrome-symbols-dir|/../chrome_apk"
  print "       Parses libraries here to find data for adjusting debuggerd"
  print "       tombstones where relocations are packed."
  print "       Enable/disable with --[no-]packed-relocation-adjustments."
  print
  print "  --symbols-zip=path"
  print "       the path to a symbols zip file, such as =dream-symbols-12345.zip"
  print
  print "  --more-info"
  print "  --less-info"
  print "       Change the level of detail in the output."
  print "       --more-info is slower and more verbose, but more functions will"
  print "       be fully qualified with namespace/classname and have full"
  print "       argument information. Also, the 'stack data' section will be"
  print "       printed."
  print
  print "  --arch=arm|arm64|x64|x86|mips"
  print "       the target architecture"
  print
  print "  --verbose"
  print "       enable extra logging, particularly for debugging failed symbolization"
  print
  print "  FILE should contain a stack trace in it somewhere"
  print "       the tool will find that and re-print it with"
  print "       source files and line numbers.  If you don't"
  print "       pass FILE, or if file is -, it reads from"
  print "       stdin."
  print
  # pylint: enable-msg=C6310
  sys.exit(1)

def UnzipSymbols(symbolfile, symdir=None):
  """Unzips a file to DEFAULT_SYMROOT and returns the unzipped location.

  Args:
    symbolfile: The .zip file to unzip
    symdir: Optional temporary directory to use for extraction

  Returns:
    A tuple containing (the directory into which the zip file was unzipped,
    the path to the "symbols" directory in the unzipped file).  To clean
    up, the caller can delete the first element of the tuple.

  Raises:
    SymbolDownloadException: When the unzip fails.
  """
  if not symdir:
    symdir = "%s/%s" % (DEFAULT_SYMROOT, hash(symbolfile))
  if not os.path.exists(symdir):
    os.makedirs(symdir)

  print "extracting %s..." % symbolfile
  saveddir = os.getcwd()
  os.chdir(symdir)
  try:
    unzipcode = subprocess.call(["unzip", "-qq", "-o", symbolfile])
    if unzipcode > 0:
      os.remove(symbolfile)
      raise SymbolDownloadException("failed to extract symbol files (%s)."
                                    % symbolfile)
  finally:
    os.chdir(saveddir)

  android_symbols = glob.glob("%s/out/target/product/*/symbols" % symdir)
  if android_symbols:
    return (symdir, android_symbols[0])
  else:
    # This is a zip of Chrome symbols, so symbol.CHROME_SYMBOLS_DIR needs to be
    # updated to point here.
    symbol.CHROME_SYMBOLS_DIR = symdir
    return (symdir, symdir)


def main(argv):
  try:
    options, arguments = getopt.getopt(argv, "",
                                       ["packed-relocation-adjustments",
                                        "no-packed-relocation-adjustments",
                                        "more-info",
                                        "less-info",
                                        "chrome-symbols-dir=",
                                        "chrome-apk-dir=",
                                        "output-directory=",
                                        "symbols-dir=",
                                        "symbols-zip=",
                                        "arch=",
                                        "verbose",
                                        "help"])
  except getopt.GetoptError, unused_error:
    PrintUsage()

  zip_arg = None
  more_info = False
  chrome_apk_dir = None
  packed_relocation_adjustments = "unknown"
  for option, value in options:
    if option == "--help":
      PrintUsage()
    elif option == "--symbols-dir":
      symbol.SYMBOLS_DIR = os.path.expanduser(value)
    elif option == "--symbols-zip":
      zip_arg = os.path.expanduser(value)
    elif option == "--arch":
      symbol.ARCH = value
    elif option == "--chrome-symbols-dir":
      symbol.CHROME_SYMBOLS_DIR = os.path.join(symbol.CHROME_SRC, value)
    elif option == "--chrome-apk-dir":
      chrome_apk_dir = os.path.join(symbol.CHROME_SRC, value)
    elif option == "--output-directory":
      constants.SetOutputDirectory(value)
    elif option == "--packed-relocation-adjustments":
      packed_relocation_adjustments = True
    elif option == "--no-packed-relocation-adjustments":
      packed_relocation_adjustments = False
    elif option == "--more-info":
      more_info = True
    elif option == "--less-info":
      more_info = False
    elif option == "--verbose":
      logging.basicConfig(level=logging.DEBUG)

  if len(arguments) > 1:
    PrintUsage()

  # Do an up-front test that the output directory is known.
  if not symbol.CHROME_SYMBOLS_DIR:
    constants.CheckOutputDirectory()

  if not chrome_apk_dir:
    # TODO(agrieve): This directory doesn't exist with GN.
    probable_dir = os.path.join(constants.GetOutDirectory(), DEFAULT_APK_DIR)
    if os.path.exists(probable_dir):
      chrome_apk_dir = probable_dir

  if not arguments or arguments[0] == "-":
    print "Reading native crash info from stdin"
    f = sys.stdin
  else:
    print "Searching for native crashes in: " + os.path.realpath(arguments[0])
    f = open(arguments[0], "r")

  lines = f.readlines()
  f.close()

  rootdir = None
  if zip_arg:
    rootdir, symbol.SYMBOLS_DIR = UnzipSymbols(zip_arg)

  if packed_relocation_adjustments == "unknown":
    if chrome_apk_dir:
      version = stack_libs.GetTargetAndroidVersionNumber(lines)
      if version == None:
        packed_relocation_adjustments = False
        print ("Unknown Android release, "
               + "consider --[no-]packed-relocation-adjustments options")
      elif version >= _ANDROID_M_MAJOR_VERSION:
        packed_relocation_adjustments = False
      else:
        packed_relocation_adjustments = True
        print ("Pre-M Android release detected, "
               + "added --packed-relocation-adjustments option")
    else:
      packed_relocation_adjustments = False

  if packed_relocation_adjustments and not chrome_apk_dir:
    packed_relocation_adjustments = False
    print ("No APK directory given or defaulted, "
           + "--packed-relocation-adjustments option ignored")

  if packed_relocation_adjustments:
    print ("Reading Chrome APK library data from: "
           + os.path.normpath(chrome_apk_dir))
    load_vaddrs = stack_libs.GetLoadVaddrs(chrome_apk_dir)
  else:
    load_vaddrs = {}

  print ("Reading Android symbols from: "
         + os.path.normpath(symbol.SYMBOLS_DIR))
  chrome_search_path = symbol.GetLibrarySearchPaths()
  print ("Searching for Chrome symbols from within: "
         + ':'.join((os.path.normpath(d) for d in chrome_search_path)))
  stack_core.ConvertTrace(lines, load_vaddrs, more_info)

  if rootdir:
    # be a good citizen and clean up...os.rmdir and os.removedirs() don't work
    cmd = "rm -rf \"%s\"" % rootdir
    print "\ncleaning up (%s)" % cmd
    os.system(cmd)

if __name__ == "__main__":
  sys.exit(main(sys.argv[1:]))

# vi: ts=2 sw=2