mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
256 lines
7.7 KiB
Python
Executable File
256 lines
7.7 KiB
Python
Executable File
#!/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 os
|
|
import re
|
|
import sys
|
|
|
|
import stack_core
|
|
import subprocess
|
|
import symbol
|
|
|
|
_DEFAULT_SYMROOT = '/tmp/symbols'
|
|
_DEFAULT_BUILD_DIR = 'out/android_Debug'
|
|
_DEFAULT_NDK_DIR = 'third_party/android_tools/ndk'
|
|
|
|
|
|
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, this is generally for system level"
|
|
print " symbols"
|
|
print
|
|
print " --build-dir=path"
|
|
print " the path to a directory containing Mojo symbols (can be"
|
|
print " absolute or relative to src), such as =out/android_Debug"
|
|
|
|
print " --ndk-dir=path"
|
|
print " the path to a directory containing Android NDK"
|
|
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 " 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 GetBasenameFromMojoApp(url):
|
|
"""Used by GetSymbolMapping() to extract the basename from the location the
|
|
mojo app was downloaded from. The location is a URL, e.g.
|
|
http://foo/bar/x.so."""
|
|
index = url.rfind('/')
|
|
return url[(index + 1):] if index != -1 else url
|
|
|
|
|
|
def GetSymboledNameForMojoApp(path):
|
|
"""Used by GetSymbolMapping to get the non-stripped library name for an
|
|
installed mojo app."""
|
|
# e.g. tracing.mojo -> libtracing_library.so
|
|
name, ext = os.path.splitext(path)
|
|
if ext != '.mojo':
|
|
return path
|
|
return 'lib%s_library.so' % name
|
|
|
|
|
|
def GetSymbolMapping(lines):
|
|
"""Returns a mapping (dictionary) from download file to .so."""
|
|
regex = re.compile('Caching mojo app (\S+) at (\S+)')
|
|
mappings = {}
|
|
for line in lines:
|
|
result = regex.search(line)
|
|
if result:
|
|
url = GetBasenameFromMojoApp(result.group(1))
|
|
mappings[result.group(2)] = GetSymboledNameForMojoApp(url)
|
|
return mappings
|
|
|
|
|
|
def _LowestAncestorContainingRelpath(dir_path, relpath):
|
|
"""Returns the lowest ancestor dir of |dir_path| that contains |relpath|.
|
|
"""
|
|
cur_dir_path = os.path.abspath(dir_path)
|
|
while True:
|
|
if os.path.exists(os.path.join(cur_dir_path, relpath)):
|
|
return cur_dir_path
|
|
|
|
next_dir_path = os.path.dirname(cur_dir_path)
|
|
if next_dir_path != cur_dir_path:
|
|
cur_dir_path = next_dir_path
|
|
else:
|
|
return None
|
|
|
|
|
|
def _GuessDir(relpath):
|
|
"""Returns absolute path to location |relpath| in the lowest ancestor of this
|
|
file that contains it."""
|
|
lowest_ancestor = _LowestAncestorContainingRelpath(
|
|
os.path.dirname(__file__), relpath)
|
|
if not lowest_ancestor:
|
|
return None
|
|
return os.path.join(lowest_ancestor, relpath)
|
|
|
|
|
|
|
|
def main():
|
|
try:
|
|
options, arguments = getopt.getopt(sys.argv[1:], "",
|
|
["more-info",
|
|
"less-info",
|
|
"build-dir=",
|
|
"ndk-dir=",
|
|
"symbols-dir=",
|
|
"symbols-zip=",
|
|
"arch=",
|
|
"help"])
|
|
except getopt.GetoptError, unused_error:
|
|
PrintUsage()
|
|
|
|
zip_arg = None
|
|
more_info = False
|
|
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 == "--build-dir":
|
|
symbol.BUILD_DIR = value
|
|
elif option == "--ndk-dir":
|
|
symbol.NDK_DIR = value
|
|
elif option == "--more-info":
|
|
more_info = True
|
|
elif option == "--less-info":
|
|
more_info = False
|
|
|
|
if not symbol.BUILD_DIR:
|
|
guess = _GuessDir(_DEFAULT_BUILD_DIR)
|
|
if not guess:
|
|
print "Couldn't find the build directory, please pass --build-dir."
|
|
return 1
|
|
print "Inferring the build directory path as " + guess
|
|
symbol.BUILD_DIR = guess
|
|
|
|
if not symbol.NDK_DIR:
|
|
guess = _GuessDir(_DEFAULT_NDK_DIR)
|
|
if not guess:
|
|
print "Couldn't find the Android NDK, please pass --ndk-dir."
|
|
return 1
|
|
print "Inferring the Android NDK path as " + guess
|
|
symbol.NDK_DIR = guess
|
|
|
|
|
|
if len(arguments) > 1:
|
|
PrintUsage()
|
|
|
|
if not arguments or arguments[0] == "-":
|
|
print "Reading native crash info from stdin"
|
|
f = sys.stdin
|
|
else:
|
|
print "Searching for native crashes in %s" % 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 symbol.SYMBOLS_DIR:
|
|
print "Reading Android symbols from", symbol.SYMBOLS_DIR
|
|
|
|
print "Reading Mojo symbols from", symbol.BUILD_DIR
|
|
stack_core.ConvertTrace(lines, more_info, GetSymbolMapping(lines))
|
|
|
|
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__":
|
|
main()
|
|
|
|
# vi: ts=2 sw=2
|