am bf1b4574: Fix multithreaded dex2oat on Mac OS.

* commit 'bf1b4574dc681d49696571c59033e8c63583a029':
  Fix multithreaded dex2oat on Mac OS.
diff --git a/build/Android.common.mk b/build/Android.common.mk
index 57baee7..f7678fe 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -79,12 +79,15 @@
 ART_HOST_NON_DEBUG_CFLAGS := $(art_non_debug_cflags)
 ART_TARGET_NON_DEBUG_CFLAGS := $(art_non_debug_cflags)
 
-# TODO: move -fkeep-inline-functions to art_debug_cflags when target gcc > 4.4
+# TODO: move -fkeep-inline-functions to art_debug_cflags when target gcc > 4.4 (and -lsupc++)
 ART_HOST_DEBUG_CFLAGS := $(art_debug_cflags) -fkeep-inline-functions
+ART_HOST_DEBUG_LDLIBS := -lsupc++
 
 ifneq ($(HOST_OS),linux)
   # Some Mac OS pthread header files are broken with -fkeep-inline-functions.
   ART_HOST_DEBUG_CFLAGS := $(filter-out -fkeep-inline-functions,$(ART_HOST_DEBUG_CFLAGS))
+  # Mac OS doesn't have libsupc++.
+  ART_HOST_DEBUG_LDLIBS := $(filter-out -lsupc++,$(ART_HOST_DEBUG_LDLIBS))
 endif
 
 ART_TARGET_DEBUG_CFLAGS := $(art_debug_cflags)
diff --git a/build/Android.libart-compiler-llvm.mk b/build/Android.libart-compiler-llvm.mk
index 05b6080..4172f21 100644
--- a/build/Android.libart-compiler-llvm.mk
+++ b/build/Android.libart-compiler-llvm.mk
@@ -131,10 +131,10 @@
     libLLVMSupport \
     librsloader
   LOCAL_SHARED_LIBRARIES := liblog libnativehelper
+  LOCAL_SHARED_LIBRARIES += libcorkscrew # native stack trace support
   ifeq ($$(art_target_or_host),target)
     LOCAL_SHARED_LIBRARIES += libcutils libstlport libz libdl
     LOCAL_SHARED_LIBRARIES += libdynamic_annotations # tsan support
-    LOCAL_SHARED_LIBRARIES += libcorkscrew # native stack trace support
   else # host
     LOCAL_STATIC_LIBRARIES += libcutils
     LOCAL_SHARED_LIBRARIES += libz-host
diff --git a/build/Android.libart.mk b/build/Android.libart.mk
index c7a85ef..4b882b6 100644
--- a/build/Android.libart.mk
+++ b/build/Android.libart.mk
@@ -79,6 +79,7 @@
       LOCAL_CFLAGS += $(ART_TARGET_DEBUG_CFLAGS)
     else # host
       LOCAL_CFLAGS += $(ART_HOST_DEBUG_CFLAGS)
+      LOCAL_LDLIBS += $(ART_HOST_DEBUG_LDLIBS)
       LOCAL_STATIC_LIBRARIES := libgtest_host
     endif
   else
@@ -102,7 +103,10 @@
     LOCAL_STATIC_LIBRARIES += libcutils
     LOCAL_SHARED_LIBRARIES += libz-host
     LOCAL_SHARED_LIBRARIES += libdynamic_annotations-host # tsan support
-    LOCAL_LDLIBS := -ldl -lpthread
+    LOCAL_LDLIBS += -ldl -lpthread
+    ifneq ($(HOST_OS),darwin)
+      LOCAL_SHARED_LIBRARIES += libcorkscrew # native stack trace support
+    endif
     ifeq ($(HOST_OS),linux)
       LOCAL_LDLIBS += -lrt
     endif
diff --git a/src/compiler.cc b/src/compiler.cc
index d833d63..578c0af 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -316,13 +316,14 @@
       compiler_(NULL),
       compiler_context_(NULL),
       jni_compiler_(NULL),
-      create_invoke_stub_(NULL)
-#if defined(ART_USE_LLVM_COMPILER)
-    , compiler_enable_auto_elf_loading_(NULL),
+#if !defined(ART_USE_LLVM_COMPILER)
+      create_invoke_stub_(NULL) {
+#else
+      create_invoke_stub_(NULL),
+      compiler_enable_auto_elf_loading_(NULL),
       compiler_get_method_code_addr_(NULL),
-      compiler_get_method_invoke_stub_addr_(NULL)
+      compiler_get_method_invoke_stub_addr_(NULL) {
 #endif
-{
   std::string compiler_so_name(MakeCompilerSoName(instruction_set_));
   compiler_library_ = dlopen(compiler_so_name.c_str(), RTLD_LAZY);
   if (compiler_library_ == NULL) {
diff --git a/src/compiler/codegen/GenInvoke.cc b/src/compiler/codegen/GenInvoke.cc
index 6d3c240..2217059 100644
--- a/src/compiler/codegen/GenInvoke.cc
+++ b/src/compiler/codegen/GenInvoke.cc
@@ -667,7 +667,7 @@
   oatFreeTemp(cUnit, regPtr);
   storeValue(cUnit, rlDest, rlResult);
   if (rangeCheck) {
-    launchPad->operands[2] = NULL;  // no resumption
+    launchPad->operands[2] = 0;  // no resumption
     launchPad->operands[3] = (uintptr_t)bb;
   }
   // Record that we've already inlined & null checked
@@ -852,10 +852,10 @@
   //TUNING: check if rlCmp.sRegLow is already null checked
   LIR* launchPad = rawLIR(cUnit, 0, kPseudoIntrinsicRetry, (int)mir, type);
   oatInsertGrowableList(cUnit, &cUnit->intrinsicLaunchpads,
-              (intptr_t)launchPad);
+                        (intptr_t)launchPad);
   opCmpImmBranch(cUnit, kCondEq, regCmp, 0, launchPad);
   opReg(cUnit, kOpBlx, rTgt);
-  launchPad->operands[2] = NULL;  // No return possible
+  launchPad->operands[2] = 0;  // No return possible
   launchPad->operands[3] = (uintptr_t)bb;
   // Record that we've already inlined & null checked
   mir->optimizationFlags |= (MIR_INLINED | MIR_IGNORE_NULL_CHECK);
diff --git a/src/compiler/codegen/mips/Mips32/Factory.cc b/src/compiler/codegen/mips/Mips32/Factory.cc
index 8b6f185..c884f8c 100644
--- a/src/compiler/codegen/mips/Mips32/Factory.cc
+++ b/src/compiler/codegen/mips/Mips32/Factory.cc
@@ -430,7 +430,6 @@
                       int rIndex, int rSrc, int scale, OpSize size)
 {
   LIR *first = NULL;
-  LIR *res;
   MipsOpCode opcode = kMipsNop;
   int rNewIndex = rIndex;
   int tReg = oatAllocTemp(cUnit);
@@ -459,23 +458,23 @@
       opcode = kMipsFswc1;
       break;
 #endif
-    case kWord:
-      opcode = kMipsSw;
-      break;
-    case kUnsignedHalf:
-    case kSignedHalf:
-      opcode = kMipsSh;
-      break;
-    case kUnsignedByte:
-    case kSignedByte:
-      opcode = kMipsSb;
-      break;
-    default:
-      LOG(FATAL) << "Bad case in storeBaseIndexed";
-  }
-  res = newLIR3(cUnit, opcode, rSrc, 0, tReg);
-  oatFreeTemp(cUnit, rNewIndex);
-  return first;
+      case kWord:
+        opcode = kMipsSw;
+        break;
+      case kUnsignedHalf:
+      case kSignedHalf:
+        opcode = kMipsSh;
+        break;
+      case kUnsignedByte:
+      case kSignedByte:
+        opcode = kMipsSb;
+        break;
+      default:
+        LOG(FATAL) << "Bad case in storeBaseIndexed";
+    }
+    newLIR3(cUnit, opcode, rSrc, 0, tReg);
+    oatFreeTemp(cUnit, rNewIndex);
+    return first;
 }
 
 LIR *loadMultiple(CompilationUnit *cUnit, int rBase, int rMask)
diff --git a/src/compiler/codegen/x86/X86/Factory.cc b/src/compiler/codegen/x86/X86/Factory.cc
index 6f11709..66e7028 100644
--- a/src/compiler/codegen/x86/X86/Factory.cc
+++ b/src/compiler/codegen/x86/X86/Factory.cc
@@ -530,15 +530,12 @@
                           int rSrc, int rSrcHi,
                           OpSize size, int sReg) {
   LIR *store = NULL;
-  LIR *store2 = NULL;
   bool isArray = rIndex != INVALID_REG;
   bool pair = false;
-  bool is64bit = false;
   X86OpCode opcode = kX86Nop;
   switch (size) {
     case kLong:
     case kDouble:
-      is64bit = true;
       if (FPREG(rSrc)) {
         opcode = isArray ? kX86MovsdAR : kX86MovsdMR;
         if (DOUBLEREG(rSrc)) {
@@ -582,8 +579,7 @@
       store = newLIR3(cUnit, opcode, rBase, displacement + LOWORD_OFFSET, rSrc);
     } else {
       store = newLIR3(cUnit, opcode, rBase, displacement + LOWORD_OFFSET, rSrc);
-      store2 = newLIR3(cUnit, opcode, rBase, displacement + HIWORD_OFFSET,
-                       rSrcHi);
+      newLIR3(cUnit, opcode, rBase, displacement + HIWORD_OFFSET, rSrcHi);
     }
   } else {
     if (!pair) {
@@ -592,8 +588,8 @@
     } else {
       store = newLIR5(cUnit, opcode, rBase, rIndex, scale,
                       displacement + LOWORD_OFFSET, rSrc);
-      store2 = newLIR5(cUnit, opcode, rBase, rIndex, scale,
-                       displacement + HIWORD_OFFSET, rSrcHi);
+      newLIR5(cUnit, opcode, rBase, rIndex, scale,
+              displacement + HIWORD_OFFSET, rSrcHi);
     }
   }
 
diff --git a/src/dex2oat.cc b/src/dex2oat.cc
index 660115c..7008f3b 100644
--- a/src/dex2oat.cc
+++ b/src/dex2oat.cc
@@ -211,7 +211,7 @@
     UniquePtr<SirtRef<ClassLoader> > class_loader(new SirtRef<ClassLoader>(NULL));
     if (class_loader.get() == NULL) {
       LOG(ERROR) << "Failed to create SirtRef for class loader";
-      return false;
+      return NULL;
     }
 
     if (!boot_image_option.empty()) {
diff --git a/src/jdwp/jdwp_handler.cc b/src/jdwp/jdwp_handler.cc
index 73c70cb..cf1e188 100644
--- a/src/jdwp/jdwp_handler.cc
+++ b/src/jdwp/jdwp_handler.cc
@@ -1016,9 +1016,7 @@
  * Get the monitor that the thread is waiting on.
  */
 static JdwpError handleTR_CurrentContendedMonitor(JdwpState*, const uint8_t* buf, int, ExpandBuf*) {
-  ObjectId threadId;
-
-  threadId = ReadObjectId(&buf);
+  ReadObjectId(&buf);  // threadId
 
   // TODO: create an Object to represent the monitor (we're currently
   // just using a raw Monitor struct in the VM)
@@ -1143,8 +1141,7 @@
 }
 
 static JdwpError handleCLR_VisibleClasses(JdwpState*, const uint8_t* buf, int, ExpandBuf* pReply) {
-  ObjectId classLoaderObject;
-  classLoaderObject = ReadObjectId(&buf);
+  ReadObjectId(&buf);  // classLoaderObject
   // TODO: we should only return classes which have the given class loader as a defining or
   // initiating loader. The former would be easy; the latter is hard, because we don't have
   // any such notion.
diff --git a/src/jdwp/jdwp_socket.cc b/src/jdwp/jdwp_socket.cc
index 9ff50f5..01eba12 100644
--- a/src/jdwp/jdwp_socket.cc
+++ b/src/jdwp/jdwp_socket.cc
@@ -558,7 +558,6 @@
   JdwpReqHeader hdr;
   uint32_t length, id;
   uint8_t flags, cmdSet, cmd;
-  uint16_t error;
   bool reply;
   int dataLen;
 
@@ -571,7 +570,7 @@
   flags = Read1(&buf);
   if ((flags & kJDWPFlagReply) != 0) {
     reply = true;
-    error = Read2BE(&buf);
+    Read2BE(&buf);  // error
   } else {
     reply = false;
     cmdSet = Read1(&buf);
diff --git a/src/native/dalvik_system_Zygote.cc b/src/native/dalvik_system_Zygote.cc
index b4f69fb..8ced7e4 100644
--- a/src/native/dalvik_system_Zygote.cc
+++ b/src/native/dalvik_system_Zygote.cc
@@ -36,6 +36,10 @@
 #include <sys/prctl.h>
 #endif
 
+#if defined(__linux__)
+#include <sys/personality.h>
+#endif
+
 namespace art {
 
 static pid_t gSystemServerPid = 0;
@@ -130,53 +134,77 @@
 
 // Calls POSIX setgroups() using the int[] object as an argument.
 // A NULL argument is tolerated.
-static int SetGids(JNIEnv* env, jintArray javaGids) {
+static void SetGids(JNIEnv* env, jintArray javaGids) {
   if (javaGids == NULL) {
-    return 0;
+    return;
   }
 
   COMPILE_ASSERT(sizeof(gid_t) == sizeof(jint), sizeof_gid_and_jint_are_differerent);
   ScopedIntArrayRO gids(env, javaGids);
-  if (gids.get() == NULL) {
-    return -1;
+  CHECK(gids.get() != NULL);
+  int rc = setgroups(gids.size(), reinterpret_cast<const gid_t*>(&gids[0]));
+  if (rc == -1) {
+    PLOG(FATAL) << "setgroups failed";
   }
-  return setgroups(gids.size(), (const gid_t *) &gids[0]);
 }
 
 // Sets the resource limits via setrlimit(2) for the values in the
 // two-dimensional array of integers that's passed in. The second dimension
 // contains a tuple of length 3: (resource, rlim_cur, rlim_max). NULL is
 // treated as an empty array.
-//
-// -1 is returned on error.
-static int SetRLimits(JNIEnv* env, jobjectArray javaRlimits) {
+static void SetRLimits(JNIEnv* env, jobjectArray javaRlimits) {
   if (javaRlimits == NULL) {
-    return 0;
+    return;
   }
 
   rlimit rlim;
   memset(&rlim, 0, sizeof(rlim));
 
-  for (int i = 0; i < env->GetArrayLength(javaRlimits); i++) {
+  for (int i = 0; i < env->GetArrayLength(javaRlimits); ++i) {
     ScopedLocalRef<jobject> javaRlimitObject(env, env->GetObjectArrayElement(javaRlimits, i));
     ScopedIntArrayRO javaRlimit(env, reinterpret_cast<jintArray>(javaRlimitObject.get()));
     if (javaRlimit.size() != 3) {
-      LOG(ERROR) << "rlimits array must have a second dimension of size 3";
-      return -1;
+      LOG(FATAL) << "rlimits array must have a second dimension of size 3";
     }
 
     rlim.rlim_cur = javaRlimit[1];
     rlim.rlim_max = javaRlimit[2];
 
-    int err = setrlimit(javaRlimit[0], &rlim);
-    if (err < 0) {
-      return -1;
+    int rc = setrlimit(javaRlimit[0], &rlim);
+    if (rc == -1) {
+      PLOG(FATAL) << "setrlimit(" << javaRlimit[0] << ", "
+                  << "{" << rlim.rlim_cur << ", " << rlim.rlim_max << "}) failed";
     }
   }
-  return 0;
 }
 
 #if defined(HAVE_ANDROID_OS)
+
+// The debug malloc library needs to know whether it's the zygote or a child.
+extern "C" int gMallocLeakZygoteChild;
+
+static void EnableDebugger() {
+  // To let a non-privileged gdbserver attach to this
+  // process, we must set our dumpable flag.
+  if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) {
+    PLOG(ERROR) << "prctl(PR_SET_DUMPABLE) failed for pid " << getpid();
+  }
+  // We don't want core dumps, though, so set the core dump size to 0.
+  rlimit rl;
+  rl.rlim_cur = 0;
+  rl.rlim_max = RLIM_INFINITY;
+  if (setrlimit(RLIMIT_CORE, &rl) == -1) {
+    PLOG(ERROR) << "setrlimit(RLIMIT_CORE) failed for pid " << getpid();
+  }
+}
+
+static void EnableKeepCapabilities() {
+  int rc = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
+  if (rc == -1) {
+    PLOG(FATAL) << "prctl(PR_SET_KEEPCAPS) failed";
+  }
+}
+
 static void SetCapabilities(int64_t permitted, int64_t effective) {
   __user_cap_header_struct capheader;
   __user_cap_data_struct capdata;
@@ -194,8 +222,23 @@
     PLOG(FATAL) << "capset(" << permitted << ", " << effective << ") failed";
   }
 }
+
+static void SetSchedulerPolicy() {
+  errno = -set_sched_policy(0, SP_DEFAULT);
+  if (errno != 0) {
+    PLOG(FATAL) << "set_sched_policy(0, SP_DEFAULT) failed";
+  }
+}
+
 #else
+
+static int gMallocLeakZygoteChild = 0;
+
+static void EnableDebugger() {}
+static void EnableKeepCapabilities() {}
 static void SetCapabilities(int64_t, int64_t) {}
+static void SetSchedulerPolicy() {}
+
 #endif
 
 static void EnableDebugFeatures(uint32_t debug_flags) {
@@ -228,26 +271,9 @@
   }
 
   Dbg::SetJdwpAllowed((debug_flags & DEBUG_ENABLE_DEBUGGER) != 0);
-#ifdef HAVE_ANDROID_OS
   if ((debug_flags & DEBUG_ENABLE_DEBUGGER) != 0) {
-    /* To let a non-privileged gdbserver attach to this
-     * process, we must set its dumpable bit flag. However
-     * we are not interested in generating a coredump in
-     * case of a crash, so also set the coredump size to 0
-     * to disable that
-     */
-    if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) {
-      PLOG(ERROR) << "could not set dumpable bit flag for pid " << getpid();
-    } else {
-      rlimit rl;
-      rl.rlim_cur = 0;
-      rl.rlim_max = RLIM_INFINITY;
-      if (setrlimit(RLIMIT_CORE, &rl) < 0) {
-        PLOG(ERROR) << "could not disable core file generation for pid " << getpid();
-      }
-    }
+    EnableDebugger();
   }
-#endif
   debug_flags &= ~DEBUG_ENABLE_DEBUGGER;
 
   // These two are for backwards compatibility with Dalvik.
@@ -259,10 +285,6 @@
   }
 }
 
-#ifdef HAVE_ANDROID_OS
-extern "C" int gMallocLeakZygoteChild;
-#endif
-
 // Utility routine to fork zygote and specialize the child process.
 static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
                                      jint debug_flags, jobjectArray javaRlimits,
@@ -282,52 +304,41 @@
   pid_t pid = fork();
 
   if (pid == 0) {
-    // The child process
-
-#ifdef HAVE_ANDROID_OS
+    // The child process.
     gMallocLeakZygoteChild = 1;
 
-    // keep caps across UID change, unless we're staying root */
+    // Keep capabilities across UID change, unless we're staying root.
     if (uid != 0) {
-      int err = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
-      if (err < 0) {
-        PLOG(FATAL) << "cannot PR_SET_KEEPCAPS";
-      }
-    }
-#endif // HAVE_ANDROID_OS
-
-    int err = SetGids(env, javaGids);
-    if (err < 0) {
-        PLOG(FATAL) << "setgroups failed";
+      EnableKeepCapabilities();
     }
 
-    err = SetRLimits(env, javaRlimits);
-    if (err < 0) {
-      PLOG(FATAL) << "setrlimit failed";
-    }
+    SetGids(env, javaGids);
 
-    err = setgid(gid);
-    if (err < 0) {
+    SetRLimits(env, javaRlimits);
+
+    int rc = setgid(gid);
+    if (rc == -1) {
       PLOG(FATAL) << "setgid(" << gid << ") failed";
     }
 
-    err = setuid(uid);
-    if (err < 0) {
+    rc = setuid(uid);
+    if (rc == -1) {
       PLOG(FATAL) << "setuid(" << uid << ") failed";
     }
 
-    SetCapabilities(permittedCapabilities, effectiveCapabilities);
-
-#if 1
-    UNIMPLEMENTED(WARNING) << "enable this code when cutils/sched_policy.h has SP_DEFAULT";
-#else
-    err = set_sched_policy(0, SP_DEFAULT);
-    if (err < 0) {
-      errno = -err;
-      PLOG(FATAL) << "set_sched_policy(0, SP_DEFAULT) failed";
+#if defined(__linux__)
+    // Work around ARM kernel ASLR lossage (http://b/5817320).
+    int old_personality = personality(0xffffffff);
+    int new_personality = personality(old_personality | ADDR_NO_RANDOMIZE);
+    if (new_personality == -1) {
+      PLOG(WARNING) << "personality(" << new_personality << ") failed";
     }
 #endif
 
+    SetCapabilities(permittedCapabilities, effectiveCapabilities);
+
+    SetSchedulerPolicy();
+
     // Our system thread ID, etc, has changed so reset Thread state.
     self->InitAfterFork();
 
diff --git a/src/native/java_lang_Runtime.cc b/src/native/java_lang_Runtime.cc
index 1b5520b..4789f4e 100644
--- a/src/native/java_lang_Runtime.cc
+++ b/src/native/java_lang_Runtime.cc
@@ -32,11 +32,8 @@
   Runtime::Current()->GetHeap()->CollectGarbage(false);
 }
 
-static void Runtime_nativeExit(JNIEnv*, jclass, jint status, jboolean isExit) {
-  // isExit is true for System.exit and false for System.halt.
-  if (isExit) {
-    Runtime::Current()->CallExitHook(status);
-  }
+static void Runtime_nativeExit(JNIEnv*, jclass, jint status) {
+  Runtime::Current()->CallExitHook(status);
   exit(status);
 }
 
@@ -80,7 +77,7 @@
     NATIVE_METHOD(Runtime, freeMemory, "()J"),
     NATIVE_METHOD(Runtime, gc, "()V"),
     NATIVE_METHOD(Runtime, maxMemory, "()J"),
-    NATIVE_METHOD(Runtime, nativeExit, "(IZ)V"),
+    NATIVE_METHOD(Runtime, nativeExit, "(I)V"),
     NATIVE_METHOD(Runtime, nativeLoad, "(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/String;"),
     NATIVE_METHOD(Runtime, totalMemory, "()J"),
 };
diff --git a/src/oat/jni/arm/jni_internal_arm.cc b/src/oat/jni/arm/jni_internal_arm.cc
index 2227742..78c3903 100644
--- a/src/oat/jni/arm/jni_internal_arm.cc
+++ b/src/oat/jni/arm/jni_internal_arm.cc
@@ -71,12 +71,7 @@
 
   // Can either get 3 or 2 arguments into registers
   size_t reg_bytes = (is_static ? 3 : 2) * kPointerSize;
-  // Bytes passed by stack
-  size_t stack_bytes;
-  if (num_arg_array_bytes > reg_bytes) {
-    stack_bytes = num_arg_array_bytes - reg_bytes;
-  } else {
-    stack_bytes = 0;
+  if (num_arg_array_bytes <= reg_bytes) {
     reg_bytes = num_arg_array_bytes;
   }
 
diff --git a/src/oat/runtime/support_stubs.cc b/src/oat/runtime/support_stubs.cc
index ac5d6f9..2a46c8b 100644
--- a/src/oat/runtime/support_stubs.cc
+++ b/src/oat/runtime/support_stubs.cc
@@ -89,8 +89,10 @@
   bool is_static;
   bool is_virtual;
   uint32_t dex_method_idx;
+#if !defined(__i386__)
   const char* shorty;
   uint32_t shorty_len;
+#endif
   if (type == Runtime::kUnknownMethod) {
     DCHECK(called->IsRuntimeMethod());
     // less two as return address may span into next dex instruction
@@ -109,15 +111,19 @@
            (instr_code == Instruction::INVOKE_DIRECT_RANGE));
     DecodedInstruction dec_insn(instr);
     dex_method_idx = dec_insn.vB;
+#if !defined(__i386__)
     shorty = linker->MethodShorty(dex_method_idx, caller, &shorty_len);
+#endif
   } else {
     DCHECK(!called->IsRuntimeMethod());
     is_static = type == Runtime::kStaticMethod;
     is_virtual = false;
     dex_method_idx = called->GetDexMethodIndex();
+#if !defined(__i386__)
     MethodHelper mh(called);
     shorty = mh.GetShorty();
     shorty_len = mh.GetShortyLength();
+#endif
   }
 #if !defined(__i386__)
   // Discover shorty (avoid GCs)
diff --git a/src/oat_writer.cc b/src/oat_writer.cc
index 4d11237..ffdb0c8 100644
--- a/src/oat_writer.cc
+++ b/src/oat_writer.cc
@@ -258,8 +258,10 @@
                                     uint32_t method_idx, const DexFile* dex_file) {
   // derived from CompiledMethod if available
   uint32_t code_offset = 0;
+#if defined(ART_USE_LLVM_COMPILER)
   uint16_t code_elf_idx = static_cast<uint16_t>(-1u);
   uint16_t code_elf_func_idx = static_cast<uint16_t>(-1u);
+#endif
   uint32_t frame_size_in_bytes = kStackAlignment;
   uint32_t core_spill_mask = 0;
   uint32_t fp_spill_mask = 0;
@@ -268,15 +270,19 @@
   uint32_t gc_map_offset = 0;
   // derived from CompiledInvokeStub if available
   uint32_t invoke_stub_offset = 0;
+#if defined(ART_USE_LLVM_COMPILER)
   uint16_t invoke_stub_elf_idx = static_cast<uint16_t>(-1u);
   uint16_t invoke_stub_elf_func_idx = static_cast<uint16_t>(-1u);
+#endif
 
   CompiledMethod* compiled_method =
       compiler_->GetCompiledMethod(Compiler::MethodReference(dex_file, method_idx));
   if (compiled_method != NULL) {
     if (compiled_method->IsExecutableInElf()) {
+#if defined(ART_USE_LLVM_COMPILER)
       code_elf_idx = compiled_method->GetElfIndex();
       code_elf_func_idx = compiled_method->GetElfFuncIndex();
+#endif
       frame_size_in_bytes = compiled_method->GetFrameSizeInBytes();
     } else {
       offset = compiled_method->AlignCode(offset);
@@ -359,8 +365,10 @@
   const CompiledInvokeStub* compiled_invoke_stub = compiler_->FindInvokeStub(is_static, shorty);
   if (compiled_invoke_stub != NULL) {
     if (compiled_invoke_stub->IsExecutableInElf()) {
+#if defined(ART_USE_LLVM_COMPILER)
       invoke_stub_elf_idx = compiled_invoke_stub->GetElfIndex();
       invoke_stub_elf_func_idx = compiled_invoke_stub->GetElfFuncIndex();
+#endif
     } else {
       offset = CompiledMethod::AlignCode(offset, compiler_->GetInstructionSet());
       DCHECK_ALIGNED(offset, kArmAlignment);
@@ -600,10 +608,6 @@
   const CompiledMethod* compiled_method =
       compiler_->GetCompiledMethod(Compiler::MethodReference(&dex_file, method_idx));
 
-  uint32_t frame_size_in_bytes = 0;
-  uint32_t core_spill_mask = 0;
-  uint32_t fp_spill_mask = 0;
-
   OatMethodOffsets method_offsets =
       oat_classes_[oat_class_index]->method_offsets_[class_def_method_index];
 
@@ -647,9 +651,6 @@
         code_offset += code_size;
       }
       DCHECK_CODE_OFFSET();
-      frame_size_in_bytes = compiled_method->GetFrameSizeInBytes();
-      core_spill_mask = compiled_method->GetCoreSpillMask();
-      fp_spill_mask = compiled_method->GetFpSpillMask();
 
       const std::vector<uint32_t>& mapping_table = compiled_method->GetMappingTable();
       size_t mapping_table_size = mapping_table.size() * sizeof(mapping_table[0]);
diff --git a/src/runtime_linux.cc b/src/runtime_linux.cc
index e2c806d..1717bc9 100644
--- a/src/runtime_linux.cc
+++ b/src/runtime_linux.cc
@@ -16,97 +16,18 @@
 
 #include "runtime.h"
 
-#include <cxxabi.h>
-#include <execinfo.h>
 #include <signal.h>
 #include <string.h>
 
 #include "logging.h"
 #include "stringprintf.h"
+#include "utils.h"
 
 namespace art {
 
-static std::string Demangle(const std::string& mangled_name) {
-  if (mangled_name.empty()) {
-    return "??";
-  }
-
-  // http://gcc.gnu.org/onlinedocs/libstdc++/manual/ext_demangling.html
-  int status;
-  char* name(abi::__cxa_demangle(mangled_name.c_str(), NULL, NULL, &status));
-  if (name != NULL) {
-    std::string result(name);
-    free(name);
-    return result;
-  }
-
-  return mangled_name;
-}
-
 struct Backtrace {
   void Dump(std::ostream& os) {
-    // Get the raw stack frames.
-    size_t MAX_STACK_FRAMES = 128;
-    void* frames[MAX_STACK_FRAMES];
-    size_t frame_count = backtrace(frames, MAX_STACK_FRAMES);
-    if (frame_count == 0) {
-      os << "--- backtrace(3) returned no frames";
-      return;
-    }
-
-    // Turn them into something human-readable with symbols.
-    char** symbols = backtrace_symbols(frames, frame_count);
-    if (symbols == NULL) {
-      os << "--- backtrace_symbols(3) failed";
-      return;
-    }
-
-
-    // Parse the backtrace strings and demangle, so we can produce output like this:
-    // ]    #00 art::Runtime::Abort(char const*, int)+0x15b [0xf770dd51] (libartd.so)
-    for (size_t i = 0; i < frame_count; ++i) {
-      std::string text(symbols[i]);
-      std::string filename("???");
-      std::string function_name;
-
-#if defined(__APPLE__)
-      // backtrace_symbols(3) gives us lines like this on Mac OS:
-      // "0   libartd.dylib                       0x001cd29a _ZN3art9Backtrace4DumpERSo + 40>"
-      // "3   ???                                 0xffffffff 0x0 + 4294967295>"
-      text.erase(0, 4);
-      size_t index = text.find(' ');
-      filename = text.substr(0, index);
-      text.erase(0, 40 - 4);
-      index = text.find(' ');
-      std::string address(text.substr(0, index));
-      text.erase(0, index + 1);
-      index = text.find(' ');
-      function_name = Demangle(text.substr(0, index));
-      text.erase(0, index);
-      text += " [" + address + "]";
-#else
-      // backtrace_symbols(3) gives us lines like this on Linux:
-      // "/usr/local/google/home/enh/a1/out/host/linux-x86/bin/../lib/libartd.so(_ZN3art7Runtime5AbortEPKci+0x15b) [0xf76c5af3]"
-      // "[0xf7b62057]"
-      size_t index = text.find('(');
-      if (index != std::string::npos) {
-        filename = text.substr(0, index);
-        text.erase(0, index + 1);
-
-        index = text.find_first_of("+)");
-        function_name = Demangle(text.substr(0, index));
-        text.erase(0, index);
-        index = text.find(')');
-        text.erase(index, 1);
-      }
-#endif
-
-      const char* last_slash = strrchr(filename.c_str(), '/');
-      const char* so_name = (last_slash == NULL) ? filename.c_str() : last_slash + 1;
-      os << StringPrintf("\t#%02zd ", i) << function_name << text << " (" << so_name << ")\n";
-    }
-
-    free(symbols);
+    DumpNativeStack(os, GetTid(), "\t", true);
   }
 };
 
@@ -213,7 +134,9 @@
     os << '\n';
 
     DumpRegister32(os, "eip", context->__ss.__eip);
+    os << "                   ";
     DumpRegister32(os, "eflags", context->__ss.__eflags);
+    DumpX86Flags(os, context->__ss.__eflags);
     os << '\n';
 
     DumpRegister32(os, "cs",  context->__ss.__cs);
@@ -237,7 +160,9 @@
     os << '\n';
 
     DumpRegister32(os, "eip", context.gregs[REG_EIP]);
+    os << "                   ";
     DumpRegister32(os, "eflags", context.gregs[REG_EFL]);
+    DumpX86Flags(os, context.gregs[REG_EFL]);
     os << '\n';
 
     DumpRegister32(os, "cs",  context.gregs[REG_CS]);
@@ -254,6 +179,38 @@
     os << StringPrintf(" %6s: 0x%08x", name, value);
   }
 
+  void DumpX86Flags(std::ostream& os, uint32_t flags) {
+    os << " [";
+    if ((flags & (1 << 0)) != 0) {
+      os << " CF";
+    }
+    if ((flags & (1 << 2)) != 0) {
+      os << " PF";
+    }
+    if ((flags & (1 << 4)) != 0) {
+      os << " AF";
+    }
+    if ((flags & (1 << 6)) != 0) {
+      os << " ZF";
+    }
+    if ((flags & (1 << 7)) != 0) {
+      os << " SF";
+    }
+    if ((flags & (1 << 8)) != 0) {
+      os << " TF";
+    }
+    if ((flags & (1 << 9)) != 0) {
+      os << " IF";
+    }
+    if ((flags & (1 << 10)) != 0) {
+      os << " DF";
+    }
+    if ((flags & (1 << 11)) != 0) {
+      os << " OF";
+    }
+    os << " ]";
+  }
+
   mcontext_t& context;
 };
 
diff --git a/src/stack.cc b/src/stack.cc
index 5dda584..d724a72 100644
--- a/src/stack.cc
+++ b/src/stack.cc
@@ -102,13 +102,8 @@
  */
 int Frame::GetVRegOffset(const DexFile::CodeItem* code_item,
                          uint32_t core_spills, uint32_t fp_spills,
-                         size_t frame_size, int reg)
-{
-#if defined(ART_USE_LLVM_COMPILER)
-  LOG(FATAL) << "LLVM compiler don't support this function";
-  return 0;
-#else
-  DCHECK_EQ( frame_size & (kStackAlignment - 1), 0U);
+                         size_t frame_size, int reg) {
+  DCHECK_EQ(frame_size & (kStackAlignment - 1), 0U);
   int num_spills = __builtin_popcount(core_spills) + __builtin_popcount(fp_spills) + 1 /* filler */;
   int num_ins = code_item->ins_size_;
   int num_regs = code_item->registers_size_ - num_ins;
@@ -122,7 +117,6 @@
   } else {
     return frame_size + ((reg - num_regs) * sizeof(uint32_t)) + sizeof(uint32_t); // Dalvik in
   }
-#endif
 }
 
 uint32_t Frame::GetVReg(const DexFile::CodeItem* code_item, uint32_t core_spills,
diff --git a/src/stringpiece.h b/src/stringpiece.h
index 7299120..7ff826d 100644
--- a/src/stringpiece.h
+++ b/src/stringpiece.h
@@ -30,6 +30,7 @@
 
 #include <string.h>
 #include <algorithm>
+#include <cstddef>
 #include <iosfwd>
 #include <string>
 
diff --git a/src/thread.cc b/src/thread.cc
index fe38c8e..c5677c9 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -29,9 +29,9 @@
 #include <iostream>
 #include <list>
 
-#include "debugger.h"
 #include "class_linker.h"
 #include "class_loader.h"
+#include "debugger.h"
 #include "heap.h"
 #include "jni_internal.h"
 #include "monitor.h"
@@ -41,8 +41,8 @@
 #include "reflection.h"
 #include "runtime.h"
 #include "runtime_support.h"
-#include "ScopedLocalRef.h"
 #include "scoped_jni_thread_state.h"
+#include "ScopedLocalRef.h"
 #include "shadow_frame.h"
 #include "space.h"
 #include "stack.h"
@@ -110,24 +110,9 @@
 }
 
 void Thread::InitAfterFork() {
+  // One thread (us) survived the fork, but we have a new tid so we need to
+  // update the value stashed in this Thread*.
   InitTid();
-
-#if defined(__BIONIC__)
-  // Work around a bionic bug.
-  struct bionic_pthread_internal_t {
-    void*  next;
-    void** pref;
-    pthread_attr_t attr;
-    pid_t kernel_id;
-    // et cetera. we just need 'kernel_id' so we can stop here.
-  };
-  bionic_pthread_internal_t* self = reinterpret_cast<bionic_pthread_internal_t*>(pthread_self());
-  if (self->kernel_id == tid_) {
-    // TODO: if you see this logging, you can remove this code!
-    LOG(INFO) << "*** this tree doesn't have the bionic pthread kernel_id bug";
-  }
-  self->kernel_id = tid_;
-#endif
 }
 
 void* Thread::CreateCallback(void* arg) {
@@ -631,35 +616,13 @@
 void Thread::DumpStack(std::ostream& os) const {
   // If we're currently in native code, dump that stack before dumping the managed stack.
   if (GetState() == kNative || GetState() == kVmWait) {
-    DumpKernelStack(os);
-    DumpNativeStack(os);
+    DumpKernelStack(os, GetTid(), "  kernel: ", false);
+    DumpNativeStack(os, GetTid(), "  native: ", false);
   }
   StackDumpVisitor dumper(os, this);
   WalkStack(&dumper);
 }
 
-#if !defined(__APPLE__)
-void Thread::DumpKernelStack(std::ostream& os) const {
-  std::string kernel_stack_filename(StringPrintf("/proc/self/task/%d/stack", GetTid()));
-  std::string kernel_stack;
-  if (!ReadFileToString(kernel_stack_filename, &kernel_stack)) {
-    os << "  (couldn't read " << kernel_stack_filename << ")";
-  }
-
-  std::vector<std::string> kernel_stack_frames;
-  Split(kernel_stack, '\n', kernel_stack_frames);
-  // We skip the last stack frame because it's always equivalent to "[<ffffffff>] 0xffffffff",
-  // which looking at the source appears to be the kernel's way of saying "that's all, folks!".
-  kernel_stack_frames.pop_back();
-  for (size_t i = 0; i < kernel_stack_frames.size(); ++i) {
-    os << "  kernel: " << kernel_stack_frames[i] << "\n";
-  }
-}
-#else
-// TODO: can we get the kernel stack on Mac OS?
-void Thread::DumpKernelStack(std::ostream&) const {}
-#endif
-
 void Thread::SetStateWithoutSuspendCheck(ThreadState new_state) {
   volatile void* raw = reinterpret_cast<volatile void*>(&state_);
   volatile int32_t* addr = reinterpret_cast<volatile int32_t*>(raw);
diff --git a/src/thread.h b/src/thread.h
index 91ea568..c429ed5 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -486,8 +486,6 @@
 
   void DumpState(std::ostream& os) const;
   void DumpStack(std::ostream& os) const;
-  void DumpKernelStack(std::ostream& os) const;
-  void DumpNativeStack(std::ostream& os) const;
 
   // Out-of-line conveniences for debugging in gdb.
   static Thread* CurrentFromGdb(); // Like Thread::Current.
diff --git a/src/thread_android.cc b/src/thread_android.cc
index 4d982b1..f86d0f4 100644
--- a/src/thread_android.cc
+++ b/src/thread_android.cc
@@ -21,7 +21,6 @@
 #include <limits.h>
 #include <errno.h>
 
-#include <corkscrew/backtrace.h>
 #include <cutils/sched_policy.h>
 #include <utils/threads.h>
 
@@ -29,23 +28,22 @@
 
 namespace art {
 
-/*
- * Conversion map for "nice" values.
- *
- * We use Android thread priority constants to be consistent with the rest
- * of the system.  In some cases adjacent entries may overlap.
- */
+// Conversion map for "nice" values.
+//
+// We use Android thread priority constants to be consistent with the rest
+// of the system.  In some cases adjacent entries may overlap.
+//
 static const int kNiceValues[10] = {
-  ANDROID_PRIORITY_LOWEST,                /* 1 (MIN_PRIORITY) */
+  ANDROID_PRIORITY_LOWEST,                // 1 (MIN_PRIORITY)
   ANDROID_PRIORITY_BACKGROUND + 6,
   ANDROID_PRIORITY_BACKGROUND + 3,
   ANDROID_PRIORITY_BACKGROUND,
-  ANDROID_PRIORITY_NORMAL,                /* 5 (NORM_PRIORITY) */
+  ANDROID_PRIORITY_NORMAL,                // 5 (NORM_PRIORITY)
   ANDROID_PRIORITY_NORMAL - 2,
   ANDROID_PRIORITY_NORMAL - 4,
   ANDROID_PRIORITY_URGENT_DISPLAY + 3,
   ANDROID_PRIORITY_URGENT_DISPLAY + 2,
-  ANDROID_PRIORITY_URGENT_DISPLAY         /* 10 (MAX_PRIORITY) */
+  ANDROID_PRIORITY_URGENT_DISPLAY         // 10 (MAX_PRIORITY)
 };
 
 void Thread::SetNativePriority(int newPriority) {
@@ -89,29 +87,4 @@
   return managed_priority;
 }
 
-void Thread::DumpNativeStack(std::ostream& os) const {
-  const size_t MAX_DEPTH = 32;
-  UniquePtr<backtrace_frame_t[]> backtrace(new backtrace_frame_t[MAX_DEPTH]);
-  ssize_t frame_count = unwind_backtrace_thread(GetTid(), backtrace.get(), 0, MAX_DEPTH);
-  if (frame_count == -1) {
-    os << "  (unwind_backtrace_thread failed for thread " << GetTid() << ".)";
-    return;
-  } else if (frame_count == 0) {
-    os << "  (no native stack frames)";
-    return;
-  }
-
-  UniquePtr<backtrace_symbol_t[]> backtrace_symbols(new backtrace_symbol_t[frame_count]);
-  get_backtrace_symbols(backtrace.get(), frame_count, backtrace_symbols.get());
-
-  for (size_t i = 0; i < static_cast<size_t>(frame_count); ++i) {
-    char line[MAX_BACKTRACE_LINE_LENGTH];
-    format_backtrace_line(i, &backtrace[i], &backtrace_symbols[i],
-                          line, MAX_BACKTRACE_LINE_LENGTH);
-    os << "  " << line << "\n";
-  }
-
-  free_backtrace_symbols(backtrace_symbols.get(), frame_count);
-}
-
 }  // namespace art
diff --git a/src/thread_linux.cc b/src/thread_linux.cc
index 8eb8813..6f4b75d 100644
--- a/src/thread_linux.cc
+++ b/src/thread_linux.cc
@@ -18,10 +18,6 @@
 
 namespace art {
 
-void Thread::DumpNativeStack(std::ostream&) const {
-  // TODO: use libcorkscrew; backtrace(3) only works for the calling thread.
-}
-
 void Thread::SetNativePriority(int) {
   // Do nothing.
 }
diff --git a/src/utils.cc b/src/utils.cc
index 4e35cd2..7dea110 100644
--- a/src/utils.cc
+++ b/src/utils.cc
@@ -41,9 +41,16 @@
 #if defined(__APPLE__)
 #include "AvailabilityMacros.h" // For MAC_OS_X_VERSION_MAX_ALLOWED
 #include <sys/syscall.h>
+
+#include <cxxabi.h> // For DumpNativeStack.
+#include <execinfo.h> // For DumpNativeStack.
+
 #endif
 
 #if defined(__linux__)
+#include <corkscrew/backtrace.h> // For DumpNativeStack.
+#include <corkscrew/demangle.h> // For DumpNativeStack.
+
 #include <linux/unistd.h>
 #endif
 
@@ -906,6 +913,207 @@
   return "";
 }
 
+#if defined(__APPLE__)
+
+static std::string Demangle(const std::string& mangled_name) {
+  if (mangled_name.empty()) {
+    return "??";
+  }
+
+  // http://gcc.gnu.org/onlinedocs/libstdc++/manual/ext_demangling.html
+  int status;
+  char* name(abi::__cxa_demangle(mangled_name.c_str(), NULL, NULL, &status));
+  if (name != NULL) {
+    std::string result(name);
+    free(name);
+    return result;
+  }
+
+  return mangled_name;
+}
+
+// TODO: port libcorkscrew to Mac OS (or find an equivalent).
+void DumpNativeStack(std::ostream& os, pid_t tid, const char* prefix, bool include_count) {
+  if (tid != GetTid()) {
+    // backtrace(3) only works for the current thread.
+    return;
+  }
+
+  // Get the raw stack frames.
+  size_t MAX_STACK_FRAMES = 128;
+  void* frames[MAX_STACK_FRAMES];
+  size_t frame_count = backtrace(frames, MAX_STACK_FRAMES);
+  if (frame_count == 0) {
+    os << prefix << "--- backtrace(3) returned no frames";
+    return;
+  }
+
+  // Turn them into something human-readable with symbols.
+  char** symbols = backtrace_symbols(frames, frame_count);
+  if (symbols == NULL) {
+    os << prefix << "--- backtrace_symbols(3) failed";
+    return;
+  }
+
+  // Parse the backtrace strings and demangle, so we can produce output like this:
+  // ]    #00 art::Runtime::Abort(char const*, int)+0x15b [0xf770dd51] (libartd.so)
+  for (size_t i = 0; i < frame_count; ++i) {
+    std::string text(symbols[i]);
+    std::string filename("???");
+    std::string function_name;
+
+    // backtrace_symbols(3) gives us lines like this on Mac OS:
+    // "0   libartd.dylib                       0x001cd29a _ZN3art9Backtrace4DumpERSo + 40>"
+    // "3   ???                                 0xffffffff 0x0 + 4294967295>"
+    text.erase(0, 4);
+    size_t index = text.find(' ');
+    filename = text.substr(0, index);
+    text.erase(0, 40 - 4);
+    index = text.find(' ');
+    std::string address(text.substr(0, index));
+    text.erase(0, index + 1);
+    index = text.find(' ');
+    function_name = Demangle(text.substr(0, index));
+    text.erase(0, index);
+    text += " [" + address + "]";
+
+    const char* last_slash = strrchr(filename.c_str(), '/');
+    const char* so_name = (last_slash == NULL) ? filename.c_str() : last_slash + 1;
+    os << prefix;
+    if (include_count) {
+      os << StringPrintf("\t#%02zd ", i);
+    }
+    os << function_name << text << " (" << so_name << ")\n";
+  }
+
+  free(symbols);
+}
+
+// TODO: is there any way to get the kernel stack on Mac OS?
+void DumpKernelStack(std::ostream&, pid_t, const char*, bool) {}
+
+#else
+
+static const char* CleanMapName(const backtrace_symbol_t* symbol) {
+  const char* map_name = symbol->map_name;
+  if (map_name == NULL) {
+    map_name = "???";
+  }
+  // Turn "/usr/local/google/home/enh/clean-dalvik-dev/out/host/linux-x86/lib/libartd.so"
+  // into "libartd.so".
+  const char* last_slash = strrchr(map_name, '/');
+  if (last_slash != NULL) {
+    map_name = last_slash + 1;
+  }
+  return map_name;
+}
+
+static void FindSymbolInElf(const backtrace_frame_t* frame, const backtrace_symbol_t* symbol,
+                            std::string& symbol_name, uint32_t& pc_offset) {
+  symbol_table_t* symbol_table = NULL;
+  if (symbol->map_name != NULL) {
+    symbol_table = load_symbol_table(symbol->map_name);
+  }
+  const symbol_t* elf_symbol = NULL;
+  if (symbol_table != NULL) {
+    elf_symbol = find_symbol(symbol_table, symbol->relative_pc);
+    if (elf_symbol == NULL) {
+      elf_symbol = find_symbol(symbol_table, frame->absolute_pc);
+    }
+  }
+  if (elf_symbol != NULL) {
+    const char* demangled_symbol_name = demangle_symbol_name(elf_symbol->name);
+    if (demangled_symbol_name != NULL) {
+      symbol_name = demangled_symbol_name;
+    } else {
+      symbol_name = elf_symbol->name;
+    }
+    pc_offset = frame->absolute_pc - elf_symbol->start;
+  } else {
+    symbol_name = "???";
+  }
+  free_symbol_table(symbol_table);
+}
+
+void DumpNativeStack(std::ostream& os, pid_t tid, const char* prefix, bool include_count) {
+  const size_t MAX_DEPTH = 32;
+  UniquePtr<backtrace_frame_t[]> frames(new backtrace_frame_t[MAX_DEPTH]);
+  ssize_t frame_count = unwind_backtrace_thread(tid, frames.get(), 0, MAX_DEPTH);
+  if (frame_count == -1) {
+    os << prefix << "(unwind_backtrace_thread failed for thread " << tid << ".)";
+    return;
+  } else if (frame_count == 0) {
+    os << prefix << "(no native stack frames)";
+    return;
+  }
+
+  UniquePtr<backtrace_symbol_t[]> backtrace_symbols(new backtrace_symbol_t[frame_count]);
+  get_backtrace_symbols(frames.get(), frame_count, backtrace_symbols.get());
+
+  for (size_t i = 0; i < static_cast<size_t>(frame_count); ++i) {
+    const backtrace_frame_t* frame = &frames[i];
+    const backtrace_symbol_t* symbol = &backtrace_symbols[i];
+
+    // We produce output like this:
+    // ]    #00 unwind_backtrace_thread+536 [0x55d75bb8] (libcorkscrew.so)
+
+    std::string symbol_name;
+    uint32_t pc_offset = 0;
+    if (symbol->demangled_name != NULL) {
+      symbol_name = symbol->demangled_name;
+      pc_offset = symbol->relative_pc - symbol->relative_symbol_addr;
+    } else if (symbol->symbol_name != NULL) {
+      symbol_name = symbol->symbol_name;
+      pc_offset = symbol->relative_pc - symbol->relative_symbol_addr;
+    } else {
+      // dladdr(3) didn't find a symbol; maybe it's static? Look in the ELF file...
+      FindSymbolInElf(frame, symbol, symbol_name, pc_offset);
+    }
+
+    os << prefix;
+    if (include_count) {
+      os << StringPrintf("#%02zd ", i);
+    }
+    os << symbol_name;
+    if (pc_offset != 0) {
+      os << "+" << pc_offset;
+    }
+    os << StringPrintf(" [%p] (%s)\n",
+                       reinterpret_cast<void*>(frame->absolute_pc), CleanMapName(symbol));
+  }
+
+  free_backtrace_symbols(backtrace_symbols.get(), frame_count);
+}
+
+void DumpKernelStack(std::ostream& os, pid_t tid, const char* prefix, bool include_count) {
+  std::string kernel_stack_filename(StringPrintf("/proc/self/task/%d/stack", tid));
+  std::string kernel_stack;
+  if (!ReadFileToString(kernel_stack_filename, &kernel_stack)) {
+    os << "  (couldn't read " << kernel_stack_filename << ")";
+  }
+
+  std::vector<std::string> kernel_stack_frames;
+  Split(kernel_stack, '\n', kernel_stack_frames);
+  // We skip the last stack frame because it's always equivalent to "[<ffffffff>] 0xffffffff",
+  // which looking at the source appears to be the kernel's way of saying "that's all, folks!".
+  kernel_stack_frames.pop_back();
+  for (size_t i = 0; i < kernel_stack_frames.size(); ++i) {
+    // Turn "[<ffffffff8109156d>] futex_wait_queue_me+0xcd/0x110" into "futex_wait_queue_me+0xcd/0x110".
+    const char* text = kernel_stack_frames[i].c_str();
+    const char* close_bracket = strchr(text, ']');
+    if (close_bracket != NULL) {
+      text = close_bracket + 2;
+    }
+    os << prefix;
+    if (include_count) {
+      os << StringPrintf("#%02zd ", i);
+    }
+    os << text << "\n";
+  }
+}
+
+#endif
+
 const char* GetAndroidRoot() {
   const char* android_root = getenv("ANDROID_ROOT");
   if (android_root == NULL) {
diff --git a/src/utils.h b/src/utils.h
index a26358a..e1c00ac 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -280,6 +280,12 @@
 // implementation-defined limit.
 void SetThreadName(const char* thread_name);
 
+// Dumps the native stack for thread 'tid' to 'os'.
+void DumpNativeStack(std::ostream& os, pid_t tid, const char* prefix = "", bool include_count = true);
+
+// Dumps the kernel stack for thread 'tid' to 'os'. Note that this is only available on linux-x86.
+void DumpKernelStack(std::ostream& os, pid_t tid, const char* prefix = "", bool include_count = true);
+
 // Find $ANDROID_ROOT, /system, or abort
 const char* GetAndroidRoot();