| #!/usr/bin/env python3 | |
| # | |
| # 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.""" | |
| from __future__ import print_function | |
| import getopt | |
| import glob | |
| import logging | |
| import os | |
| import sys | |
| import stack_core | |
| import subprocess | |
| import symbol | |
| import sys | |
| import zipfile | |
| 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.""" | |
| print("Usage: " + sys.argv[0] + " [options] [FILE]") | |
| print() | |
| print("Find all parts in FILE belonging to stack traces and symbolize") | |
| print("them: copy to stdout augmenting with source file names and line") | |
| print("numbers.") | |
| print() | |
| print("Not providing FILE or setting it to '-' implies reading from stdin.") | |
| print() | |
| print(" -p, --pass-through") | |
| print(" Copy all lines from stdin to stdout in addition to") | |
| print(" symbolizing relevant lines. This way ADB logcat can") | |
| print(" be piped through the tool to symbolize on the fly.") | |
| print() | |
| print(" --symbols-dir=path") | |
| print(" path to the Android OS symbols dir, such as") | |
| print(" =/tmp/out/target/product/dream/symbols") | |
| print() | |
| print(" --chrome-symbols-dir=path") | |
| print(" path to a Chrome symbols dir. E.g.: 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(" --apks-directory=path") | |
| print(" Overrides the default apks directory. Useful if a bundle APKS") | |
| print(" file has been unzipped into a temporary directory.") | |
| print() | |
| print(" --symbols-zip=path") | |
| print(" the path to a symbols zip file, such as") | |
| print(" =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") | |
| print(" will 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-so-file=name") | |
| print(" fallback to given .so file (eg. libmonochrome_64.so) instead") | |
| print(" of libmonochrome.so if we fail to detect the shared lib which") | |
| print(" is loaded from APK, this doesn't work for component build.") | |
| print() | |
| print(" --quiet") | |
| print(" Show less logging") | |
| print() | |
| print(" --verbose") | |
| print(" enable extra logging, particularly for debugging failed") | |
| print(" symbolization") | |
| 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) | |
| logging.info('extracting %s...', symbolfile) | |
| with zipfile.ZipFile(symbolfile, 'r') as zip_ref: | |
| zip_ref.extractall(symdir) | |
| return symdir | |
| def main(argv, test_symbolizer=None): | |
| try: | |
| options, arguments = getopt.getopt(argv, "p", [ | |
| "pass-through", | |
| "flush", | |
| "more-info", | |
| "less-info", | |
| "chrome-symbols-dir=", | |
| "output-directory=", | |
| "apks-directory=", | |
| "symbols-dir=", | |
| "symbols-zip=", | |
| "arch=", | |
| "fallback-so-file=", | |
| "verbose", | |
| "quiet", | |
| "help", | |
| ]) | |
| except getopt.GetoptError: | |
| PrintUsage() | |
| pass_through = False | |
| flush = False | |
| zip_arg = None | |
| more_info = False | |
| fallback_so_file = None | |
| arch_defined = False | |
| apks_directory = None | |
| log_level = logging.INFO | |
| for option, value in options: | |
| if option == "--help": | |
| PrintUsage() | |
| elif option == "--pass-through": | |
| pass_through = True | |
| elif option == "-p": | |
| pass_through = True | |
| elif option == "--flush": | |
| flush = True | |
| elif option == "--symbols-dir": | |
| symbol.SYMBOLS_DIR = os.path.abspath(os.path.expanduser(value)) | |
| elif option == "--symbols-zip": | |
| zip_arg = os.path.abspath(os.path.expanduser(value)) | |
| elif option == "--arch": | |
| symbol.ARCH = value | |
| arch_defined = True | |
| elif option == "--chrome-symbols-dir": | |
| symbol.CHROME_SYMBOLS_DIR = value | |
| elif option == "--output-directory": | |
| constants.SetOutputDirectory(os.path.abspath(value)) | |
| elif option == "--apks-directory": | |
| apks_directory = os.path.abspath(value) | |
| elif option == "--more-info": | |
| more_info = True | |
| elif option == "--less-info": | |
| more_info = False | |
| elif option == "--fallback-so-file": | |
| fallback_so_file = value | |
| elif option == "--verbose": | |
| log_level = logging.DEBUG | |
| elif option == "--quiet": | |
| log_level = logging.WARNING | |
| if len(arguments) > 1: | |
| PrintUsage() | |
| rootdir = None | |
| if zip_arg: | |
| rootdir = UnzipSymbols(zip_arg) | |
| for subdir, dirs, _ in os.walk(rootdir): | |
| if 'lib.unstripped' in dirs: | |
| unzipped_output_dir = subdir | |
| break | |
| constants.SetOutputDirectory(unzipped_output_dir) | |
| logging.basicConfig(level=log_level) | |
| # Do an up-front test that the output directory is known. | |
| if not symbol.CHROME_SYMBOLS_DIR: | |
| constants.CheckOutputDirectory() | |
| logging.info('Reading Android symbols from: %s', | |
| os.path.normpath(symbol.SYMBOLS_DIR)) | |
| chrome_search_path = symbol.GetLibrarySearchPaths() | |
| logging.info('Searching for Chrome symbols from within: %s', ':'.join( | |
| (os.path.normpath(d) for d in chrome_search_path))) | |
| if not arguments or arguments[0] == '-': | |
| logging.info('Reading native crash info from stdin (symbolization starts ' | |
| 'on the first unrelated line or EOF)') | |
| with llvm_symbolizer.LLVMSymbolizer() as symbolizer: | |
| stack_core.StreamingConvertTrace(sys.stdin, {}, more_info, | |
| fallback_so_file, arch_defined, | |
| symbolizer, apks_directory, pass_through, | |
| flush) | |
| else: | |
| logging.info('Searching for native crashes in: %s', | |
| os.path.realpath(arguments[0])) | |
| if pass_through: | |
| logging.error('Processing files in --pass-through mode is not supported') | |
| return 1 | |
| f = open(arguments[0], 'r') | |
| lines = f.readlines() | |
| f.close() | |
| # This used to be required when ELF logical addresses did not align with | |
| # physical addresses, which happened when relocations were converted to APS2 | |
| # format post-link via relocation_packer tool. | |
| load_vaddrs = {} | |
| with llvm_symbolizer.LLVMSymbolizer() as symbolizer: | |
| logging.info('Searching for Chrome symbols from within: %s', ':'.join( | |
| (os.path.normpath(d) for d in chrome_search_path))) | |
| stack_core.ConvertTrace(lines, load_vaddrs, more_info, fallback_so_file, | |
| arch_defined, test_symbolizer or symbolizer, | |
| apks_directory) | |
| if rootdir: | |
| # be a good citizen and clean up...os.rmdir and os.removedirs() don't work | |
| cmd = "rm -rf \"%s\"" % rootdir | |
| logging.info('cleaning up (%s)', cmd) | |
| os.system(cmd) | |
| return 0 | |
| if __name__ == "__main__": | |
| sys.exit(main(sys.argv[1:])) | |
| # vi: ts=2 sw=2 |