1#!/usr/bin/env python
2#
3# Copyright (C) 2013 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""stack symbolizes native crash dumps."""
18
19import getopt
20import glob
21import os
22import sys
23
24import stack_core
25import subprocess
26import symbol
27import sys
28
29DEFAULT_SYMROOT='/tmp/symbols'
30
31def PrintUsage():
32  """Print usage and exit with error."""
33  # pylint: disable-msg=C6310
34  print
35  print "  usage: " + sys.argv[0] + " [options] [FILE]"
36  print
37  print "  --symbols-dir=path"
38  print "       the path to a symbols dir, such as =/tmp/out/target/product/dream/symbols"
39  print
40  print "  --chrome-symbols-dir=path"
41  print "       the path to a Chrome symbols dir (can be absolute or relative"
42  print "       to src), such as =out/Debug/lib"
43  print "       If not specified, will look for the newest lib in out/Debug or"
44  print "       out/Release"
45  print
46  print "  --symbols-zip=path"
47  print "       the path to a symbols zip file, such as =dream-symbols-12345.zip"
48  print
49  print "  --more-info"
50  print "  --less-info"
51  print "       Change the level of detail in the output."
52  print "       --more-info is slower and more verbose, but more functions will"
53  print "       be fully qualified with namespace/classname and have full"
54  print "       argument information. Also, the 'stack data' section will be"
55  print "       printed."
56  print
57  print "  --arch=arm|arm64|x86_64|x86|mips"
58  print "       the target architecture"
59  print
60  print "  FILE should contain a stack trace in it somewhere"
61  print "       the tool will find that and re-print it with"
62  print "       source files and line numbers.  If you don't"
63  print "       pass FILE, or if file is -, it reads from"
64  print "       stdin."
65  print
66  # pylint: enable-msg=C6310
67  sys.exit(1)
68
69def UnzipSymbols(symbolfile, symdir=None):
70  """Unzips a file to DEFAULT_SYMROOT and returns the unzipped location.
71
72  Args:
73    symbolfile: The .zip file to unzip
74    symdir: Optional temporary directory to use for extraction
75
76  Returns:
77    A tuple containing (the directory into which the zip file was unzipped,
78    the path to the "symbols" directory in the unzipped file).  To clean
79    up, the caller can delete the first element of the tuple.
80
81  Raises:
82    SymbolDownloadException: When the unzip fails.
83  """
84  if not symdir:
85    symdir = "%s/%s" % (DEFAULT_SYMROOT, hash(symbolfile))
86  if not os.path.exists(symdir):
87    os.makedirs(symdir)
88
89  print "extracting %s..." % symbolfile
90  saveddir = os.getcwd()
91  os.chdir(symdir)
92  try:
93    unzipcode = subprocess.call(["unzip", "-qq", "-o", symbolfile])
94    if unzipcode > 0:
95      os.remove(symbolfile)
96      raise SymbolDownloadException("failed to extract symbol files (%s)."
97                                    % symbolfile)
98  finally:
99    os.chdir(saveddir)
100
101  android_symbols = glob.glob("%s/out/target/product/*/symbols" % symdir)
102  if android_symbols:
103    return (symdir, android_symbols[0])
104  else:
105    # This is a zip of Chrome symbols, so symbol.CHROME_SYMBOLS_DIR needs to be
106    # updated to point here.
107    symbol.CHROME_SYMBOLS_DIR = symdir
108    return (symdir, symdir)
109
110
111def main():
112  try:
113    options, arguments = getopt.getopt(sys.argv[1:], "",
114                                       ["more-info",
115                                        "less-info",
116                                        "chrome-symbols-dir=",
117                                        "symbols-dir=",
118                                        "symbols-zip=",
119                                        "arch=",
120                                        "help"])
121  except getopt.GetoptError, unused_error:
122    PrintUsage()
123
124  zip_arg = None
125  more_info = False
126  for option, value in options:
127    if option == "--help":
128      PrintUsage()
129    elif option == "--symbols-dir":
130      symbol.SYMBOLS_DIR = os.path.expanduser(value)
131    elif option == "--symbols-zip":
132      zip_arg = os.path.expanduser(value)
133    elif option == "--arch":
134      symbol.ARCH = value
135    elif option == "--chrome-symbols-dir":
136      symbol.CHROME_SYMBOLS_DIR = os.path.join(symbol.CHROME_SYMBOLS_DIR, value)
137    elif option == "--more-info":
138      more_info = True
139    elif option == "--less-info":
140      more_info = False
141
142  if len(arguments) > 1:
143    PrintUsage()
144
145  if not arguments or arguments[0] == "-":
146    print "Reading native crash info from stdin"
147    f = sys.stdin
148  else:
149    print "Searching for native crashes in %s" % arguments[0]
150    f = open(arguments[0], "r")
151
152  lines = f.readlines()
153  f.close()
154
155  rootdir = None
156  if zip_arg:
157    rootdir, symbol.SYMBOLS_DIR = UnzipSymbols(zip_arg)
158
159  print "Reading Android symbols from", symbol.SYMBOLS_DIR
160  print "Reading Chrome symbols from", symbol.CHROME_SYMBOLS_DIR
161  stack_core.ConvertTrace(lines, more_info)
162
163  if rootdir:
164    # be a good citizen and clean up...os.rmdir and os.removedirs() don't work
165    cmd = "rm -rf \"%s\"" % rootdir
166    print "\ncleaning up (%s)" % cmd
167    os.system(cmd)
168
169if __name__ == "__main__":
170  main()
171
172# vi: ts=2 sw=2
173