diff --git a/scripts/stack b/scripts/stack index 256d7e977..8e65dba61 100755 --- a/scripts/stack +++ b/scripts/stack @@ -29,7 +29,7 @@ def PrintUsage(): print print " usage: " + sys.argv[0] + " [options] [FILE]" print - print " --arch=arm|x86" + print " --arch=arm|arm64|mips|mips64|x86|x86_64" print " the target architecture" print print " FILE should contain a stack trace in it somewhere" diff --git a/scripts/stack_core.py b/scripts/stack_core.py index 8da01096b..a62afd923 100755 --- a/scripts/stack_core.py +++ b/scripts/stack_core.py @@ -32,7 +32,6 @@ def ConvertTrace(lines): class TraceConverter: process_info_line = re.compile("(pid: [0-9]+, tid: [0-9]+.*)") - abi_line = re.compile("(ABI: \'(.*)\')") revision_line = re.compile("(Revision: \'(.*)\')") signal_line = re.compile("(signal [0-9]+ \(.*\).*)") abort_message_line = re.compile("(Abort message: '.*')") @@ -52,9 +51,6 @@ class TraceConverter: spacing = "" apk_info = dict() - def __init__(self): - self.UpdateAbiRegexes() - register_names = { "arm": "r0|r1|r2|r3|r4|r5|r6|r7|r8|r9|sl|fp|ip|sp|lr|pc|cpsr", "arm64": "x0|x1|x2|x3|x4|x5|x6|x7|x8|x9|x10|x11|x12|x13|x14|x15|x16|x17|x18|x19|x20|x21|x22|x23|x24|x25|x26|x27|x28|x29|x30|sp|pc|pstate", @@ -175,6 +171,9 @@ class TraceConverter: def ConvertTrace(self, lines): lines = map(self.CleanLine, lines) try: + if not symbol.ARCH: + symbol.SetAbi(lines) + self.UpdateAbiRegexes() for line in lines: self.ProcessLine(line) self.PrintOutput(self.trace_lines, self.value_lines) @@ -281,13 +280,11 @@ class TraceConverter: abort_message_header = self.abort_message_line.search(line) thread_header = self.thread_line.search(line) register_header = self.register_line.search(line) - abi_header = self.abi_line.search(line) revision_header = self.revision_line.search(line) dalvik_jni_thread_header = self.dalvik_jni_thread_line.search(line) dalvik_native_thread_header = self.dalvik_native_thread_line.search(line) - if process_header or signal_header or abort_message_header or thread_header or abi_header or \ + if process_header or signal_header or abort_message_header or thread_header or \ register_header or dalvik_jni_thread_header or dalvik_native_thread_header or revision_header: - ret = True if self.trace_lines or self.value_lines: self.PrintOutput(self.trace_lines, self.value_lines) self.PrintDivider() @@ -310,11 +307,7 @@ class TraceConverter: print dalvik_native_thread_header.group(1) if revision_header: print revision_header.group(1) - if abi_header: - print abi_header.group(1) - symbol.ARCH = abi_header.group(2) - self.UpdateAbiRegexes() - return ret + return True trace_line_dict = self.MatchTraceLine(line) if trace_line_dict is not None: ret = True @@ -404,7 +397,10 @@ class TraceConverter: class RegisterPatternTests(unittest.TestCase): def assert_register_matches(self, abi, example_crash, stupid_pattern): tc = TraceConverter() - for line in example_crash.split('\n'): + lines = example_crash.split('\n') + symbol.SetAbi(lines) + tc.UpdateAbiRegexes() + for line in lines: tc.ProcessLine(line) is_register = (re.search(stupid_pattern, line) is not None) matched = (tc.register_line.search(line) is not None) diff --git a/scripts/symbol.py b/scripts/symbol.py index 46465811a..872580860 100755 --- a/scripts/symbol.py +++ b/scripts/symbol.py @@ -44,7 +44,7 @@ def FindSymbolsDir(): SYMBOLS_DIR = FindSymbolsDir() -ARCH = "arm" +ARCH = None # These are private. Do not access them from other modules. @@ -336,6 +336,61 @@ def FormatSymbolWithOffset(symbol, offset): return "%s+%d" % (symbol, offset) +def GetAbiFromToolchain(toolchain_var, bits): + toolchain = os.environ.get(toolchain_var) + if not toolchain: + return None + + toolchain_match = re.search("\/(aarch64|arm|mips|x86)\/", toolchain) + if toolchain_match: + abi = toolchain_match.group(1) + if abi == "aarch64": + return "arm64" + elif bits == 64: + if abi == "x86": + return "x86_64" + elif abi == "mips": + return "mips64" + return abi + return None + + +def SetAbi(lines): + global ARCH + + abi_line = re.compile("ABI: \'(.*)\'") + trace_line = re.compile("\#[0-9]+[ \t]+..[ \t]+([0-9a-f]{8}|[0-9a-f]{16})([ \t]+|$)") + + ARCH = None + for line in lines: + abi_match = abi_line.search(line) + if abi_match: + ARCH = abi_match.group(1) + break + trace_match = trace_line.search(line) + if trace_match: + # Try to guess the arch, we know the bitness. + if len(trace_match.group(1)) == 16: + # 64 bit + # Check for ANDROID_TOOLCHAIN, if it is set, we can figure out the + # arch this way. If this is not set, then default to arm64. + ARCH = GetAbiFromToolchain("ANDROID_TOOLCHAIN", 64) + if not ARCH: + ARCH = "arm64" + else: + # 32 bit + # Check for ANDROID_TOOLCHAIN_2ND_ARCH first, if set, use that. + # If not try ANDROID_TOOLCHAIN to find the arch. + # If this is not set, then default to arm. + ARCH = GetAbiFromToolchain("ANDROID_TOOLCHAIN_2ND_ARCH", 32) + if not ARCH: + ARCH = GetAbiFromToolchain("ANDROID_TOOLCHAIN", 32) + if not ARCH: + ARCH = "arm" + break + if not ARCH: + raise Exception("Could not determine arch from input") + class FindToolchainTests(unittest.TestCase): def assert_toolchain_found(self, abi): @@ -350,6 +405,95 @@ class FindToolchainTests(unittest.TestCase): self.assert_toolchain_found("x86") self.assert_toolchain_found("x86_64") +class SetArchTests(unittest.TestCase): + def test_abi_check(self): + global ARCH + + SetAbi(["ABI: 'arm'"]) + self.assertEqual(ARCH, "arm") + SetAbi(["ABI: 'arm64'"]) + self.assertEqual(ARCH, "arm64") + + SetAbi(["ABI: 'mips'"]) + self.assertEqual(ARCH, "mips") + SetAbi(["ABI: 'mips64'"]) + self.assertEqual(ARCH, "mips64") + + SetAbi(["ABI: 'x86'"]) + self.assertEqual(ARCH, "x86") + SetAbi(["ABI: 'x86_64'"]) + self.assertEqual(ARCH, "x86_64") + + def test_32bit_trace_line_toolchain(self): + global ARCH + + os.environ.clear() + os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/arm/arm-linux-androideabi-4.9/bin" + SetAbi(["#00 pc 000374e0"]) + self.assertEqual(ARCH, "arm") + + os.environ.clear() + os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/mips/arm-linux-androideabi-4.9/bin" + SetAbi(["#00 pc 000374e0"]) + self.assertEqual(ARCH, "mips") + + os.environ.clear() + os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/x86/arm-linux-androideabi-4.9/bin" + SetAbi(["#00 pc 000374e0"]) + self.assertEqual(ARCH, "x86") + + def test_32bit_trace_line_toolchain_2nd(self): + global ARCH + + os.environ.clear() + os.environ["ANDROID_TOOLCHAIN_2ND_ARCH"] = "linux-x86/arm/arm-linux-androideabi-4.9/bin" + os.environ["ANDROID_TOOLCHAIN_ARCH"] = "linux-x86/aarch64/aarch64-linux-android-4.9/bin" + SetAbi(["#00 pc 000374e0"]) + self.assertEqual(ARCH, "arm") + + os.environ.clear() + os.environ["ANDROID_TOOLCHAIN_2ND_ARCH"] = "linux-x86/mips/mips-linux-androideabi-4.9/bin" + os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/unknown/unknown-linux-androideabi-4.9/bin" + SetAbi(["#00 pc 000374e0"]) + self.assertEqual(ARCH, "mips") + + os.environ.clear() + os.environ["ANDROID_TOOLCHAIN_2ND_ARCH"] = "linux-x86/x86/x86-linux-androideabi-4.9/bin" + os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/unknown/unknown-linux-androideabi-4.9/bin" + SetAbi(["#00 pc 000374e0"]) + self.assertEqual(ARCH, "x86") + + def test_64bit_trace_line_toolchain(self): + global ARCH + + os.environ.clear() + os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/aarch/aarch-linux-androideabi-4.9/bin" + SetAbi(["#00 pc 00000000000374e0"]) + self.assertEqual(ARCH, "arm64") + + os.environ.clear() + os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/mips/arm-linux-androideabi-4.9/bin" + SetAbi(["#00 pc 00000000000374e0"]) + self.assertEqual(ARCH, "mips64") + + os.environ.clear() + os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/x86/arm-linux-androideabi-4.9/bin" + SetAbi(["#00 pc 00000000000374e0"]) + self.assertEqual(ARCH, "x86_64") + + def test_default_abis(self): + global ARCH + + os.environ.clear() + SetAbi(["#00 pc 000374e0"]) + self.assertEqual(ARCH, "arm") + SetAbi(["#00 pc 00000000000374e0"]) + self.assertEqual(ARCH, "arm64") + + def test_no_abi(self): + global ARCH + + self.assertRaisesRegexp(Exception, "Could not determine arch from input", SetAbi, []) if __name__ == '__main__': unittest.main()