Make 'stack' able to find toolchains regardless of lunch.

This is needed by the internal stack decoding website, which
will be pulling symbols from the build servers anyway (so doesn't
need to be correctly lunched).

Bug: 16734486
Change-Id: I9190065148c0d4bb4eacec28bcc062fa95798917
This commit is contained in:
Elliott Hughes
2014-08-29 13:49:57 -07:00
parent 4123e0886b
commit c3c8619579
2 changed files with 57 additions and 37 deletions

View File

@@ -44,6 +44,7 @@ class TraceConverter:
value_lines = [] value_lines = []
last_frame = -1 last_frame = -1
width = "{8}" width = "{8}"
spacing = ""
def __init__(self): def __init__(self):
self.UpdateAbiRegexes() self.UpdateAbiRegexes()
@@ -59,8 +60,10 @@ class TraceConverter:
def UpdateAbiRegexes(self): def UpdateAbiRegexes(self):
if symbol.ARCH == "arm64" or symbol.ARCH == "mips64" or symbol.ARCH == "x86_64": if symbol.ARCH == "arm64" or symbol.ARCH == "mips64" or symbol.ARCH == "x86_64":
self.width = "{16}" self.width = "{16}"
self.spacing = " "
else: else:
self.width = "{8}" self.width = "{8}"
self.spacing = ""
self.register_line = re.compile("(([ ]*\\b(" + self.register_names[symbol.ARCH] + ")\\b +[0-9a-f]" + self.width + "){2,5})") self.register_line = re.compile("(([ ]*\\b(" + self.register_names[symbol.ARCH] + ")\\b +[0-9a-f]" + self.width + "){2,5})")
@@ -104,12 +107,9 @@ class TraceConverter:
def PrintTraceLines(self, trace_lines): def PrintTraceLines(self, trace_lines):
"""Print back trace.""" """Print back trace."""
maxlen = max(map(lambda tl: len(tl[1]), trace_lines)) maxlen = max(map(lambda tl: len(tl[1]), trace_lines))
spacing = ""
if symbol.ARCH == "arm64" or symbol.ARCH == "mips64" or symbol.ARCH == "x86_64":
spacing = " "
print print
print "Stack Trace:" print "Stack Trace:"
print " RELADDR " + spacing + "FUNCTION".ljust(maxlen) + " FILE:LINE" print " RELADDR " + self.spacing + "FUNCTION".ljust(maxlen) + " FILE:LINE"
for tl in self.trace_lines: for tl in self.trace_lines:
(addr, symbol_with_offset, location) = tl (addr, symbol_with_offset, location) = tl
print " %8s %s %s" % (addr, symbol_with_offset.ljust(maxlen), location) print " %8s %s %s" % (addr, symbol_with_offset.ljust(maxlen), location)
@@ -120,7 +120,7 @@ class TraceConverter:
maxlen = max(map(lambda tl: len(tl[2]), self.value_lines)) maxlen = max(map(lambda tl: len(tl[2]), self.value_lines))
print print
print "Stack Data:" print "Stack Data:"
print " ADDR VALUE " + "FUNCTION".ljust(maxlen) + " FILE:LINE" print " ADDR " + self.spacing + "VALUE " + "FUNCTION".ljust(maxlen) + " FILE:LINE"
for vl in self.value_lines: for vl in self.value_lines:
(addr, value, symbol_with_offset, location) = vl (addr, value, symbol_with_offset, location) = vl
print " %8s %8s %s %s" % (addr, value, symbol_with_offset.ljust(maxlen), location) print " %8s %8s %s %s" % (addr, value, symbol_with_offset.ljust(maxlen), location)
@@ -256,12 +256,12 @@ class TraceConverter:
class RegisterPatternTests(unittest.TestCase): class RegisterPatternTests(unittest.TestCase):
def assert_register_matches(self, abi, example_crash, stupid_pattern): def assert_register_matches(self, abi, example_crash, stupid_pattern):
tc = TraceConverter() tc = TraceConverter()
symbol.ARCH = abi
tc.UpdateAbiRegexes()
for line in example_crash.split('\n'): for line in example_crash.split('\n'):
tc.ProcessLine(line)
is_register = (re.search(stupid_pattern, line) is not None) is_register = (re.search(stupid_pattern, line) is not None)
matched = (tc.register_line.search(line) is not None) matched = (tc.register_line.search(line) is not None)
self.assertEquals(matched, is_register, line) self.assertEquals(matched, is_register, line)
tc.PrintOutput(tc.trace_lines, tc.value_lines)
def test_arm_registers(self): def test_arm_registers(self):
self.assert_register_matches("arm", example_crashes.arm, '\\b(r0|r4|r8|ip)\\b') self.assert_register_matches("arm", example_crashes.arm, '\\b(r0|r4|r8|ip)\\b')

View File

@@ -23,6 +23,7 @@ import glob
import os import os
import re import re
import subprocess import subprocess
import unittest
ANDROID_BUILD_TOP = os.environ["ANDROID_BUILD_TOP"] ANDROID_BUILD_TOP = os.environ["ANDROID_BUILD_TOP"]
if not ANDROID_BUILD_TOP: if not ANDROID_BUILD_TOP:
@@ -44,7 +45,11 @@ SYMBOLS_DIR = FindSymbolsDir()
ARCH = "arm" ARCH = "arm"
TOOLCHAIN = None
# These are private. Do not access them from other modules.
_CACHED_TOOLCHAIN = None
_CACHED_TOOLCHAIN_ARCH = None
def ToolPath(tool, toolchain=None): def ToolPath(tool, toolchain=None):
"""Return a fully-qualified path to the specified tool""" """Return a fully-qualified path to the specified tool"""
@@ -52,42 +57,38 @@ def ToolPath(tool, toolchain=None):
toolchain = FindToolchain() toolchain = FindToolchain()
return glob.glob(os.path.join(toolchain, "*-" + tool))[0] return glob.glob(os.path.join(toolchain, "*-" + tool))[0]
def FindToolchain(): def FindToolchain():
"""Returns the toolchain matching ARCH. Assumes that you're lunched """Returns the toolchain matching ARCH."""
such that the necessary toolchain is either your primary or secondary. global _CACHED_TOOLCHAIN, _CACHED_TOOLCHAIN_ARCH
TODO: we could make this 'just work' for most users by just globbing the if _CACHED_TOOLCHAIN is not None and _CACHED_TOOLCHAIN_ARCH == ARCH:
newest toolchains for every architecture out of prebuilts/, but other return _CACHED_TOOLCHAIN
parts of this tool assume you're lunched correctly anyway."""
global TOOLCHAIN
if TOOLCHAIN is not None:
return TOOLCHAIN
# We use slightly different names from GCC, and there's only one toolchain # We use slightly different names from GCC, and there's only one toolchain
# for x86/x86_64. # for x86/x86_64. Note that these are the names of the top-level directory
gcc_arch = ARCH # rather than the _different_ names used lower down the directory hierarchy!
if gcc_arch == "arm64": gcc_dir = ARCH
gcc_arch = "aarch64" if gcc_dir == "arm64":
elif gcc_arch == "mips": gcc_dir = "aarch64"
gcc_arch = "mipsel" elif gcc_dir == "mips64":
elif gcc_arch == "x86": gcc_dir = "mips"
gcc_arch = "x86_64" elif gcc_dir == "x86_64":
gcc_dir = "x86"
tc1 = os.environ["ANDROID_TOOLCHAIN"] available_toolchains = glob.glob("%s/prebuilts/gcc/linux-x86/%s/*-linux-*/bin/" % (ANDROID_BUILD_TOP, gcc_dir))
tc2 = os.environ["ANDROID_TOOLCHAIN_2ND_ARCH"] if len(available_toolchains) == 0:
raise Exception("Could not find tool chain for %s" % (ARCH))
if ("/" + gcc_arch + "-linux-") in tc1: toolchain = sorted(available_toolchains)[-1]
toolchain = tc1
elif ("/" + gcc_arch + "-linux-") in tc2:
toolchain = tc2
else:
raise Exception("Could not find tool chain for %s" % (gcc_arch))
if not os.path.exists(ToolPath("addr2line", toolchain)): if not os.path.exists(ToolPath("addr2line", toolchain)):
raise Exception("No addr2line for %s" % (toolchain)) raise Exception("No addr2line for %s" % (toolchain))
TOOLCHAIN = toolchain _CACHED_TOOLCHAIN = toolchain
print "Using toolchain from: %s" % TOOLCHAIN _CACHED_TOOLCHAIN_ARCH = ARCH
return TOOLCHAIN print "Using %s toolchain from: %s" % (_CACHED_TOOLCHAIN_ARCH, _CACHED_TOOLCHAIN)
return _CACHED_TOOLCHAIN
def SymbolInformation(lib, addr): def SymbolInformation(lib, addr):
"""Look up symbol information about an address. """Look up symbol information about an address.
@@ -179,7 +180,6 @@ def CallAddr2LineForSet(lib, unique_addrs):
if not lib: if not lib:
return None return None
symbols = SYMBOLS_DIR + lib symbols = SYMBOLS_DIR + lib
if not os.path.exists(symbols): if not os.path.exists(symbols):
return None return None
@@ -227,11 +227,11 @@ def StripPC(addr):
The stripped program counter address. The stripped program counter address.
""" """
global ARCH global ARCH
if ARCH == "arm": if ARCH == "arm":
return addr & ~1 return addr & ~1
return addr return addr
def CallObjdumpForSet(lib, unique_addrs): def CallObjdumpForSet(lib, unique_addrs):
"""Use objdump to find out the names of the containing functions. """Use objdump to find out the names of the containing functions.
@@ -326,7 +326,27 @@ def CallCppFilt(mangled_symbol):
process.stdout.close() process.stdout.close()
return demangled_symbol return demangled_symbol
def FormatSymbolWithOffset(symbol, offset): def FormatSymbolWithOffset(symbol, offset):
if offset == 0: if offset == 0:
return symbol return symbol
return "%s+%d" % (symbol, offset) return "%s+%d" % (symbol, offset)
class FindToolchainTests(unittest.TestCase):
def assert_toolchain_found(self, abi):
global ARCH
ARCH = abi
FindToolchain() # Will throw on failure.
def test_toolchains_found(self):
self.assert_toolchain_found("arm")
self.assert_toolchain_found("arm64")
self.assert_toolchain_found("mips")
self.assert_toolchain_found("x86")
self.assert_toolchain_found("x86_64")
if __name__ == '__main__':
unittest.main()