Skip to content

Commit

Permalink
Android: apply some optimizations to the stack symbolization tools.
Browse files Browse the repository at this point in the history
BUG=234973

Review URL: https://chromiumcodereview.appspot.com/18715002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@211068 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
bulach@chromium.org committed Jul 11, 2013
1 parent 115cda5 commit 0ec7947
Show file tree
Hide file tree
Showing 3 changed files with 261 additions and 95 deletions.
100 changes: 96 additions & 4 deletions third_party/android_platform/development/scripts/stack
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,41 @@
"""stack symbolizes native crash dumps."""

import getopt
import glob
import os
import sys

import stack_core
import subprocess
import symbol

import sys

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 " If not specified, will look for the newest lib in out/Debug or"
print " out/Release"
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|x86"
print " the target architecture"
print
Expand All @@ -41,20 +64,78 @@ def PrintUsage():
# 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():
try:
options, arguments = getopt.getopt(sys.argv[1:], "",
["arch=",
["more-info",
"less-info",
"chrome-symbols-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 == "--chrome-symbols-dir":
symbol.CHROME_SYMBOLS_DIR = os.path.join(symbol.CHROME_SYMBOLS_DIR, value)
elif option == "--more-info":
more_info = True
elif option == "--less-info":
more_info = False

if len(arguments) > 1:
PrintUsage()
Expand All @@ -69,8 +150,19 @@ def main():
lines = f.readlines()
f.close()

print "Reading symbols from", symbol.SYMBOLS_DIR
stack_core.ConvertTrace(lines)
rootdir = None
if zip_arg:
rootdir, symbol.SYMBOLS_DIR = UnzipSymbols(zip_arg)

print "Reading Android symbols from", symbol.SYMBOLS_DIR
print "Reading Chrome symbols from", symbol.CHROME_SYMBOLS_DIR
stack_core.ConvertTrace(lines, 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__":
main()
Expand Down
56 changes: 42 additions & 14 deletions third_party/android_platform/development/scripts/stack_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,23 @@ def PrintValueLines(value_lines):
STACK = "[stack]"


def PrintOutput(trace_lines, value_lines):
def PrintOutput(trace_lines, value_lines, more_info):
if trace_lines:
PrintTraceLines(trace_lines)
if value_lines:
PrintValueLines(value_lines)
# TODO(cjhopman): it seems that symbol.SymbolInformation always fails to
# find information for addresses in value_lines in chrome libraries, and so
# value_lines have little value to us and merely clutter the output.
# Since information is sometimes contained in these lines (from system
# libraries), don't completely disable them.
if more_info:
PrintValueLines(value_lines)

def PrintDivider():
print
print "-----------------------------------------------------\n"

def ConvertTrace(lines):
def ConvertTrace(lines, more_info):
"""Convert strings containing native crash to a stack."""
process_info_line = re.compile("(pid: [0-9]+, tid: [0-9]+.*)")
signal_line = re.compile("(signal [0-9]+ \(.*\).*)")
Expand All @@ -77,7 +83,7 @@ def ConvertTrace(lines):
# Or lines from AndroidFeedback crash report system logs like:
# 03-25 00:51:05.520 I/DEBUG ( 65): #00 pc 001cf42e /data/data/com.my.project/lib/libmyproject.so
# Please note the spacing differences.
trace_line = re.compile("(.*)\#([0-9]+)[ \t]+(..)[ \t]+([0-9a-f]{8})[ \t]+([^\r\n \t]*)( \((.*)\))?") # pylint: disable-msg=C6310
trace_line = re.compile("(.*)\#(?P<frame>[0-9]+)[ \t]+(..)[ \t]+(0x)?(?P<address>[0-9a-f]{0,8})[ \t]+(?P<lib>[^\r\n \t]*)(?P<symbol_present> \((?P<symbol_name>.*)\))?") # pylint: disable-msg=C6310
# Examples of matched value lines include:
# bea4170c 8018e4e9 /data/data/com.my.project/lib/libmyproject.so
# bea4170c 8018e4e9 /data/data/com.my.project/lib/libmyproject.so (symbol)
Expand All @@ -97,6 +103,31 @@ def ConvertTrace(lines):
value_lines = []
last_frame = -1

# It is faster to get symbol information with a single call rather than with
# separate calls for each line. Since symbol.SymbolInformation caches results,
# we can extract all the addresses that we will want symbol information for
# from the log and call symbol.SymbolInformation so that the results are
# cached in the following lookups.
code_addresses = {}
for ln in lines:
line = unicode(ln, errors='ignore')
lib, address = None, None

match = trace_line.match(line)
if match:
address, lib = match.group('address', 'lib')

match = value_line.match(line)
if match and not code_line.match(line):
(_0, _1, address, lib, _2, _3) = match.groups()

if lib:
code_addresses.setdefault(lib, set()).add(address)

for lib in code_addresses:
symbol.SymbolInformationForSet(
symbol.TranslateLibPath(lib), code_addresses[lib], more_info)

for ln in lines:
# AndroidFeedback adds zero width spaces into its crash reports. These
# should be removed or the regular expresssions will fail to match.
Expand All @@ -110,7 +141,7 @@ def ConvertTrace(lines):
if process_header or signal_header or register_header or thread_header \
or dalvik_jni_thread_header or dalvik_native_thread_header:
if trace_lines or value_lines:
PrintOutput(trace_lines, value_lines)
PrintOutput(trace_lines, value_lines, more_info)
PrintDivider()
trace_lines = []
value_lines = []
Expand All @@ -130,11 +161,11 @@ def ConvertTrace(lines):
continue
if trace_line.match(line):
match = trace_line.match(line)
(unused_0, frame, unused_1,
code_addr, area, symbol_present, symbol_name) = match.groups()
frame, code_addr, area, symbol_present, symbol_name = match.group(
'frame', 'address', 'lib', 'symbol_present', 'symbol_name')

if frame <= last_frame and (trace_lines or value_lines):
PrintOutput(trace_lines, value_lines)
PrintOutput(trace_lines, value_lines, more_info)
PrintDivider()
trace_lines = []
value_lines = []
Expand All @@ -145,7 +176,7 @@ def ConvertTrace(lines):
else:
# If a calls b which further calls c and c is inlined to b, we want to
# display "a -> b -> c" in the stack trace instead of just "a -> c"
info = symbol.SymbolInformation(area, code_addr)
info = symbol.SymbolInformation(area, code_addr, more_info)
nest_count = len(info) - 1
for (source_symbol, source_location, object_symbol_with_offset) in info:
if not source_symbol:
Expand Down Expand Up @@ -174,7 +205,7 @@ def ConvertTrace(lines):
if area == UNKNOWN or area == HEAP or area == STACK or not area:
value_lines.append((addr, value, "", area))
else:
info = symbol.SymbolInformation(area, value)
info = symbol.SymbolInformation(area, value, more_info)
(source_symbol, source_location, object_symbol_with_offset) = info.pop()
if not source_symbol:
if symbol_present:
Expand All @@ -190,7 +221,4 @@ def ConvertTrace(lines):
object_symbol_with_offset,
source_location))

PrintOutput(trace_lines, value_lines)


# vi: ts=2 sw=2
PrintOutput(trace_lines, value_lines, more_info)
Loading

0 comments on commit 0ec7947

Please sign in to comment.