#!/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 sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, os.pardir, os.pardir, 'tools', 'python')) import llvm_symbolizer DEFAULT_SYMROOT='/tmp/symbols' # 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.unstripped" 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 " --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 " --fallback-monochrome" print " fallback to monochrome instead of chrome if fail to detect" print " shared lib which is loaded from APK, this doesn't work for" print " component build." 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=", "output-directory=", "symbols-dir=", "symbols-zip=", "packed-lib=", "arch=", "fallback-monochrome", "verbose", "help"]) except getopt.GetoptError, unused_error: PrintUsage() zip_arg = None more_info = False fallback_monochrome = False arch_defined = False packed_libs = [] 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 arch_defined = True elif option == "--chrome-symbols-dir": symbol.CHROME_SYMBOLS_DIR = os.path.join(constants.DIR_SOURCE_ROOT, value) elif option == "--output-directory": constants.SetOutputDirectory(value) elif option == "--packed-lib": packed_libs.append(os.path.expanduser(value)) elif option == "--more-info": more_info = True elif option == "--less-info": more_info = False elif option == "--fallback-monochrome": fallback_monochrome = True elif option == "--verbose": logging.basicConfig(level=logging.DEBUG) elif option in ( '--packed-relocation-adjustments', '--no-packed-relocation-adjustments'): print ('--[no-]packed-relocation-adjustments options are deprecated. ' 'Specify packed libs directory instead.') 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 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) version = stack_libs.GetTargetAndroidVersionNumber(lines) if version is None: print ("Unknown Android release, " "consider passing --packed-lib.") elif version < _ANDROID_M_MAJOR_VERSION and not packed_libs: print ("Pre-M Android release detected, " "but --packed-lib not specified. Stack symbolization may fail.") if (version is None or version < _ANDROID_M_MAJOR_VERSION) and packed_libs: load_vaddrs = stack_libs.GetLoadVaddrs(stripped_libs=packed_libs) else: load_vaddrs = {} print ("Reading Android symbols from: " + os.path.normpath(symbol.SYMBOLS_DIR)) chrome_search_path = symbol.GetLibrarySearchPaths() with llvm_symbolizer.LLVMSymbolizer() as symbolizer: 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, fallback_monochrome, arch_defined, symbolizer) 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