Merge "Find abi before trying to decode the stack."
diff --git a/scripts/stack b/scripts/stack
index 256d7e9..8e65dba 100755
--- a/scripts/stack
+++ b/scripts/stack
@@ -29,7 +29,7 @@
   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 8da0109..a62afd9 100755
--- a/scripts/stack_core.py
+++ b/scripts/stack_core.py
@@ -32,7 +32,6 @@
 
 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 @@
   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 @@
   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 @@
     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 @@
         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 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 4646581..8725808 100755
--- a/scripts/symbol.py
+++ b/scripts/symbol.py
@@ -44,7 +44,7 @@
 
 SYMBOLS_DIR = FindSymbolsDir()
 
-ARCH = "arm"
+ARCH = None
 
 
 # These are private. Do not access them from other modules.
@@ -336,6 +336,61 @@
   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 @@
     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()