From c3c861957934bc7e2f3cd97b768ae30bd828f591 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Fri, 29 Aug 2014 13:49:57 -0700 Subject: [PATCH] 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 --- scripts/stack_core.py | 14 ++++---- scripts/symbol.py | 80 +++++++++++++++++++++++++++---------------- 2 files changed, 57 insertions(+), 37 deletions(-) diff --git a/scripts/stack_core.py b/scripts/stack_core.py index 1eb0c29d5..9b7ff2300 100755 --- a/scripts/stack_core.py +++ b/scripts/stack_core.py @@ -44,6 +44,7 @@ class TraceConverter: value_lines = [] last_frame = -1 width = "{8}" + spacing = "" def __init__(self): self.UpdateAbiRegexes() @@ -59,8 +60,10 @@ class TraceConverter: def UpdateAbiRegexes(self): if symbol.ARCH == "arm64" or symbol.ARCH == "mips64" or symbol.ARCH == "x86_64": self.width = "{16}" + self.spacing = " " else: self.width = "{8}" + self.spacing = "" 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): """Print back trace.""" 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 "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: (addr, symbol_with_offset, location) = tl 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)) print 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: (addr, value, symbol_with_offset, location) = vl print " %8s %8s %s %s" % (addr, value, symbol_with_offset.ljust(maxlen), location) @@ -256,12 +256,12 @@ class TraceConverter: class RegisterPatternTests(unittest.TestCase): def assert_register_matches(self, abi, example_crash, stupid_pattern): tc = TraceConverter() - symbol.ARCH = abi - tc.UpdateAbiRegexes() for line in example_crash.split('\n'): + tc.ProcessLine(line) is_register = (re.search(stupid_pattern, line) is not None) matched = (tc.register_line.search(line) is not None) self.assertEquals(matched, is_register, line) + tc.PrintOutput(tc.trace_lines, tc.value_lines) def test_arm_registers(self): self.assert_register_matches("arm", example_crashes.arm, '\\b(r0|r4|r8|ip)\\b') diff --git a/scripts/symbol.py b/scripts/symbol.py index 5669b9db2..a211c7ce0 100755 --- a/scripts/symbol.py +++ b/scripts/symbol.py @@ -23,6 +23,7 @@ import glob import os import re import subprocess +import unittest ANDROID_BUILD_TOP = os.environ["ANDROID_BUILD_TOP"] if not ANDROID_BUILD_TOP: @@ -44,7 +45,11 @@ SYMBOLS_DIR = FindSymbolsDir() 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): """Return a fully-qualified path to the specified tool""" @@ -52,42 +57,38 @@ def ToolPath(tool, toolchain=None): toolchain = FindToolchain() return glob.glob(os.path.join(toolchain, "*-" + tool))[0] + def FindToolchain(): - """Returns the toolchain matching ARCH. Assumes that you're lunched - such that the necessary toolchain is either your primary or secondary. - TODO: we could make this 'just work' for most users by just globbing the - newest toolchains for every architecture out of prebuilts/, but other - parts of this tool assume you're lunched correctly anyway.""" - global TOOLCHAIN - if TOOLCHAIN is not None: - return TOOLCHAIN + """Returns the toolchain matching ARCH.""" + global _CACHED_TOOLCHAIN, _CACHED_TOOLCHAIN_ARCH + if _CACHED_TOOLCHAIN is not None and _CACHED_TOOLCHAIN_ARCH == ARCH: + return _CACHED_TOOLCHAIN # We use slightly different names from GCC, and there's only one toolchain - # for x86/x86_64. - gcc_arch = ARCH - if gcc_arch == "arm64": - gcc_arch = "aarch64" - elif gcc_arch == "mips": - gcc_arch = "mipsel" - elif gcc_arch == "x86": - gcc_arch = "x86_64" + # for x86/x86_64. Note that these are the names of the top-level directory + # rather than the _different_ names used lower down the directory hierarchy! + gcc_dir = ARCH + if gcc_dir == "arm64": + gcc_dir = "aarch64" + elif gcc_dir == "mips64": + gcc_dir = "mips" + elif gcc_dir == "x86_64": + gcc_dir = "x86" - tc1 = os.environ["ANDROID_TOOLCHAIN"] - tc2 = os.environ["ANDROID_TOOLCHAIN_2ND_ARCH"] + available_toolchains = glob.glob("%s/prebuilts/gcc/linux-x86/%s/*-linux-*/bin/" % (ANDROID_BUILD_TOP, gcc_dir)) + if len(available_toolchains) == 0: + raise Exception("Could not find tool chain for %s" % (ARCH)) - if ("/" + gcc_arch + "-linux-") in tc1: - toolchain = tc1 - elif ("/" + gcc_arch + "-linux-") in tc2: - toolchain = tc2 - else: - raise Exception("Could not find tool chain for %s" % (gcc_arch)) + toolchain = sorted(available_toolchains)[-1] if not os.path.exists(ToolPath("addr2line", toolchain)): raise Exception("No addr2line for %s" % (toolchain)) - TOOLCHAIN = toolchain - print "Using toolchain from: %s" % TOOLCHAIN - return TOOLCHAIN + _CACHED_TOOLCHAIN = toolchain + _CACHED_TOOLCHAIN_ARCH = ARCH + print "Using %s toolchain from: %s" % (_CACHED_TOOLCHAIN_ARCH, _CACHED_TOOLCHAIN) + return _CACHED_TOOLCHAIN + def SymbolInformation(lib, addr): """Look up symbol information about an address. @@ -179,7 +180,6 @@ def CallAddr2LineForSet(lib, unique_addrs): if not lib: return None - symbols = SYMBOLS_DIR + lib if not os.path.exists(symbols): return None @@ -227,11 +227,11 @@ def StripPC(addr): The stripped program counter address. """ global ARCH - if ARCH == "arm": return addr & ~1 return addr + def CallObjdumpForSet(lib, unique_addrs): """Use objdump to find out the names of the containing functions. @@ -326,7 +326,27 @@ def CallCppFilt(mangled_symbol): process.stdout.close() return demangled_symbol + def FormatSymbolWithOffset(symbol, offset): if offset == 0: return symbol 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()