am 3adbe79d: am 66e31dc5: am 3a088524: Merge "Rewrite unwind thread handling."

* commit '3adbe79d2f14665ff55a9b0db245abebc105658d':
  Rewrite unwind thread handling.
diff --git a/include/backtrace/Backtrace.h b/include/backtrace/Backtrace.h
index 3c3a482..561dfeb 100644
--- a/include/backtrace/Backtrace.h
+++ b/include/backtrace/Backtrace.h
@@ -19,6 +19,7 @@
 
 #include <inttypes.h>
 #include <stdint.h>
+#include <ucontext.h>
 
 #include <string>
 #include <vector>
@@ -64,7 +65,7 @@
   virtual ~Backtrace();
 
   // Get the current stack trace and store in the backtrace_ structure.
-  virtual bool Unwind(size_t num_ignore_frames);
+  virtual bool Unwind(size_t num_ignore_frames, ucontext_t* context = NULL);
 
   // Get the function name and offset into the function given the pc.
   // If the string is empty, then no valid function name was found.
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk
index 7fe52ef..fa79221 100755
--- a/libbacktrace/Android.mk
+++ b/libbacktrace/Android.mk
@@ -116,6 +116,9 @@
 backtrace_test_shared_libraries_target := \
 	libcutils \
 
+backtrace_test_static_libraries_host := \
+	libcutils \
+
 module := backtrace_test
 module_tag := debug
 build_type := target
diff --git a/libbacktrace/BacktraceImpl.cpp b/libbacktrace/BacktraceImpl.cpp
index 05007d9..d7ac0b2 100644
--- a/libbacktrace/BacktraceImpl.cpp
+++ b/libbacktrace/BacktraceImpl.cpp
@@ -55,8 +55,8 @@
   }
 }
 
-bool Backtrace::Unwind(size_t num_ignore_frames) {
-  return impl_->Unwind(num_ignore_frames);
+bool Backtrace::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
+  return impl_->Unwind(num_ignore_frames, ucontext);
 }
 
 extern "C" char* __cxa_demangle(const char* mangled, char* buf, size_t* len,
diff --git a/libbacktrace/BacktraceImpl.h b/libbacktrace/BacktraceImpl.h
index 7b31c38..8ed64b3 100755
--- a/libbacktrace/BacktraceImpl.h
+++ b/libbacktrace/BacktraceImpl.h
@@ -21,12 +21,13 @@
 #include <backtrace/BacktraceMap.h>
 
 #include <sys/types.h>
+#include <ucontext.h>
 
 class BacktraceImpl {
 public:
   virtual ~BacktraceImpl() { }
 
-  virtual bool Unwind(size_t num_ignore_frames) = 0;
+  virtual bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext) = 0;
 
   // The name returned is not demangled, Backtrace::GetFunctionName()
   // takes care of demangling the name.
diff --git a/libbacktrace/BacktraceThread.cpp b/libbacktrace/BacktraceThread.cpp
index e0bab24..018d51f 100644
--- a/libbacktrace/BacktraceThread.cpp
+++ b/libbacktrace/BacktraceThread.cpp
@@ -16,10 +16,15 @@
 
 #include <errno.h>
 #include <inttypes.h>
+#include <limits.h>
+#include <linux/futex.h>
 #include <pthread.h>
 #include <signal.h>
 #include <string.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
 #include <sys/types.h>
+#include <ucontext.h>
 
 #include <cutils/atomic.h>
 
@@ -27,190 +32,173 @@
 #include "BacktraceThread.h"
 #include "thread_utils.h"
 
+static inline int futex(volatile int* uaddr, int op, int val, const struct timespec* ts, volatile int* uaddr2, int val3) {
+  return syscall(__NR_futex, uaddr, op, val, ts, uaddr2, val3);
+}
+
 //-------------------------------------------------------------------------
 // ThreadEntry implementation.
 //-------------------------------------------------------------------------
-static ThreadEntry* g_list = NULL;
-static pthread_mutex_t g_entry_mutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_mutex_t g_sigaction_mutex = PTHREAD_MUTEX_INITIALIZER;
+ThreadEntry* ThreadEntry::list_ = NULL;
+pthread_mutex_t ThreadEntry::list_mutex_ = PTHREAD_MUTEX_INITIALIZER;
 
-ThreadEntry::ThreadEntry(
-    BacktraceThreadInterface* intf, pid_t pid, pid_t tid, size_t num_ignore_frames)
-    : thread_intf(intf), pid(pid), tid(tid), next(NULL), prev(NULL),
-      state(STATE_WAITING), num_ignore_frames(num_ignore_frames) {
+// Assumes that ThreadEntry::list_mutex_ has already been locked before
+// creating a ThreadEntry object.
+ThreadEntry::ThreadEntry(pid_t pid, pid_t tid)
+    : pid_(pid), tid_(tid), futex_(0), ref_count_(1), mutex_(PTHREAD_MUTEX_INITIALIZER), next_(ThreadEntry::list_), prev_(NULL) {
+  // Add ourselves to the list.
+  if (ThreadEntry::list_) {
+    ThreadEntry::list_->prev_ = this;
+  }
+  ThreadEntry::list_ = this;
 }
 
-ThreadEntry::~ThreadEntry() {
-  pthread_mutex_lock(&g_entry_mutex);
-  if (g_list == this) {
-    g_list = next;
+ThreadEntry* ThreadEntry::Get(pid_t pid, pid_t tid, bool create) {
+  pthread_mutex_lock(&ThreadEntry::list_mutex_);
+  ThreadEntry* entry = list_;
+  while (entry != NULL) {
+    if (entry->Match(pid, tid)) {
+      break;
+    }
+    entry = entry->next_;
+  }
+
+  if (!entry) {
+    if (create) {
+      entry = new ThreadEntry(pid, tid);
+    }
   } else {
-    if (next) {
-      next->prev = prev;
-    }
-    prev->next = next;
+    entry->ref_count_++;
   }
-  pthread_mutex_unlock(&g_entry_mutex);
-
-  next = NULL;
-  prev = NULL;
-}
-
-ThreadEntry* ThreadEntry::AddThreadToUnwind(
-    BacktraceThreadInterface* intf, pid_t pid, pid_t tid, size_t num_ignore_frames) {
-  ThreadEntry* entry = new ThreadEntry(intf, pid, tid, num_ignore_frames);
-
-  pthread_mutex_lock(&g_entry_mutex);
-  ThreadEntry* cur_entry = g_list;
-  while (cur_entry != NULL) {
-    if (cur_entry->Match(pid, tid)) {
-      // There is already an entry for this pid/tid, this is bad.
-      BACK_LOGW("Entry for pid %d tid %d already exists.", pid, tid);
-
-      pthread_mutex_unlock(&g_entry_mutex);
-      return NULL;
-    }
-    cur_entry = cur_entry->next;
-  }
-
-  // Add the entry to the list.
-  entry->next = g_list;
-  if (g_list) {
-    g_list->prev = entry;
-  }
-  g_list = entry;
-  pthread_mutex_unlock(&g_entry_mutex);
+  pthread_mutex_unlock(&ThreadEntry::list_mutex_);
 
   return entry;
 }
 
+void ThreadEntry::Remove(ThreadEntry* entry) {
+  pthread_mutex_unlock(&entry->mutex_);
+
+  pthread_mutex_lock(&ThreadEntry::list_mutex_);
+  if (--entry->ref_count_ == 0) {
+    delete entry;
+  }
+  pthread_mutex_unlock(&ThreadEntry::list_mutex_);
+}
+
+// Assumes that ThreadEntry::list_mutex_ has already been locked before
+// deleting a ThreadEntry object.
+ThreadEntry::~ThreadEntry() {
+  if (list_ == this) {
+    list_ = next_;
+  } else {
+    if (next_) {
+      next_->prev_ = prev_;
+    }
+    prev_->next_ = next_;
+  }
+
+  next_ = NULL;
+  prev_ = NULL;
+}
+
+void ThreadEntry::Wait(int value) {
+  timespec ts;
+  ts.tv_sec = 10;
+  ts.tv_nsec = 0;
+  errno = 0;
+  futex(&futex_, FUTEX_WAIT, value, &ts, NULL, 0);
+  if (errno != 0 && errno != EWOULDBLOCK) {
+    BACK_LOGW("futex wait failed, futex = %d: %s", futex_, strerror(errno));
+  }
+}
+
+void ThreadEntry::Wake() {
+  futex_++;
+  futex(&futex_, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
+}
+
 //-------------------------------------------------------------------------
 // BacktraceThread functions.
 //-------------------------------------------------------------------------
-static void SignalHandler(int n __attribute__((unused)), siginfo_t* siginfo,
-                          void* sigcontext) {
-  if (pthread_mutex_lock(&g_entry_mutex) == 0) {
-    pid_t pid = getpid();
-    pid_t tid = gettid();
-    ThreadEntry* cur_entry = g_list;
-    while (cur_entry) {
-      if (cur_entry->Match(pid, tid)) {
-        break;
-      }
-      cur_entry = cur_entry->next;
-    }
-    pthread_mutex_unlock(&g_entry_mutex);
-    if (!cur_entry) {
-      BACK_LOGW("Unable to find pid %d tid %d information", pid, tid);
-      return;
-    }
+static pthread_mutex_t g_sigaction_mutex = PTHREAD_MUTEX_INITIALIZER;
 
-    if (android_atomic_acquire_cas(STATE_WAITING, STATE_DUMPING, &cur_entry->state) == 0) {
-      cur_entry->thread_intf->ThreadUnwind(siginfo, sigcontext,
-                                           cur_entry->num_ignore_frames);
-    }
-    android_atomic_release_store(STATE_DONE, &cur_entry->state);
+static void SignalHandler(int, siginfo_t*, void* sigcontext) {
+  ThreadEntry* entry = ThreadEntry::Get(getpid(), gettid(), false);
+  if (!entry) {
+    BACK_LOGW("Unable to find pid %d tid %d information", getpid(), gettid());
+    return;
   }
+
+  entry->CopyUcontext(reinterpret_cast<ucontext_t*>(sigcontext));
+
+  // Indicate the ucontext is now valid.
+  entry->Wake();
+
+  // Pause the thread until the unwind is complete. This avoids having
+  // the thread run ahead causing problems.
+  entry->Wait(1);
+
+  ThreadEntry::Remove(entry);
 }
 
-BacktraceThread::BacktraceThread(
-    BacktraceImpl* impl, BacktraceThreadInterface* thread_intf, pid_t tid,
-    BacktraceMap* map)
-    : BacktraceCurrent(impl, map), thread_intf_(thread_intf) {
+BacktraceThread::BacktraceThread(BacktraceImpl* impl, pid_t tid, BacktraceMap* map)
+    : BacktraceCurrent(impl, map) {
   tid_ = tid;
 }
 
 BacktraceThread::~BacktraceThread() {
 }
 
-void BacktraceThread::FinishUnwind() {
-  for (std::vector<backtrace_frame_data_t>::iterator it = frames_.begin();
-       it != frames_.end(); ++it) {
-    it->map = FindMap(it->pc);
-
-    it->func_offset = 0;
-    it->func_name = GetFunctionName(it->pc, &it->func_offset);
-  }
-}
-
-bool BacktraceThread::TriggerUnwindOnThread(ThreadEntry* entry) {
-  entry->state = STATE_WAITING;
-
-  if (tgkill(Pid(), Tid(), THREAD_SIGNAL) != 0) {
-    BACK_LOGW("tgkill failed %s", strerror(errno));
-    return false;
-  }
-
-  // Allow up to ten seconds for the dump to start.
-  int wait_millis = 10000;
-  int32_t state;
-  while (true) {
-    state = android_atomic_acquire_load(&entry->state);
-    if (state != STATE_WAITING) {
-      break;
-    }
-    if (wait_millis--) {
-      usleep(1000);
-    } else {
-      break;
-    }
-  }
-
-  bool cancelled = false;
-  if (state == STATE_WAITING) {
-    if (android_atomic_acquire_cas(state, STATE_CANCEL, &entry->state) == 0) {
-      BACK_LOGW("Cancelled dump of thread %d", entry->tid);
-      state = STATE_CANCEL;
-      cancelled = true;
-    } else {
-      state = android_atomic_acquire_load(&entry->state);
-    }
-  }
-
-  // Wait for at most ten seconds for the cancel or dump to finish.
-  wait_millis = 10000;
-  while (android_atomic_acquire_load(&entry->state) != STATE_DONE) {
-    if (wait_millis--) {
-      usleep(1000);
-    } else {
-      BACK_LOGW("Didn't finish thread unwind in 60 seconds.");
-      break;
-    }
-  }
-  return !cancelled;
-}
-
-bool BacktraceThread::Unwind(size_t num_ignore_frames) {
-  ThreadEntry* entry = ThreadEntry::AddThreadToUnwind(
-      thread_intf_, Pid(), Tid(), num_ignore_frames);
-  if (!entry) {
-    return false;
+bool BacktraceThread::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
+  if (ucontext) {
+    // Unwind using an already existing ucontext.
+    return impl_->Unwind(num_ignore_frames, ucontext);
   }
 
   // Prevent multiple threads trying to set the trigger action on different
   // threads at the same time.
-  bool retval = false;
-  if (pthread_mutex_lock(&g_sigaction_mutex) == 0) {
-    struct sigaction act, oldact;
-    memset(&act, 0, sizeof(act));
-    act.sa_sigaction = SignalHandler;
-    act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
-    sigemptyset(&act.sa_mask);
-    if (sigaction(THREAD_SIGNAL, &act, &oldact) == 0) {
-      retval = TriggerUnwindOnThread(entry);
-      sigaction(THREAD_SIGNAL, &oldact, NULL);
-    } else {
-      BACK_LOGW("sigaction failed %s", strerror(errno));
-    }
+  if (pthread_mutex_lock(&g_sigaction_mutex) < 0) {
+    BACK_LOGW("sigaction failed: %s", strerror(errno));
+    return false;
+  }
+
+  ThreadEntry* entry = ThreadEntry::Get(Pid(), Tid());
+  entry->Lock();
+
+  struct sigaction act, oldact;
+  memset(&act, 0, sizeof(act));
+  act.sa_sigaction = SignalHandler;
+  act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
+  sigemptyset(&act.sa_mask);
+  if (sigaction(THREAD_SIGNAL, &act, &oldact) != 0) {
+    BACK_LOGW("sigaction failed %s", strerror(errno));
+    entry->Unlock();
+    ThreadEntry::Remove(entry);
     pthread_mutex_unlock(&g_sigaction_mutex);
-  } else {
-    BACK_LOGW("unable to acquire sigaction mutex.");
+    return false;
   }
 
-  if (retval) {
-    FinishUnwind();
+  if (tgkill(Pid(), Tid(), THREAD_SIGNAL) != 0) {
+    BACK_LOGW("tgkill %d failed: %s", Tid(), strerror(errno));
+    sigaction(THREAD_SIGNAL, &oldact, NULL);
+    entry->Unlock();
+    ThreadEntry::Remove(entry);
+    pthread_mutex_unlock(&g_sigaction_mutex);
+    return false;
   }
-  delete entry;
 
-  return retval;
+  // Wait for the thread to get the ucontext.
+  entry->Wait(0);
+
+  // After the thread has received the signal, allow other unwinders to
+  // continue.
+  sigaction(THREAD_SIGNAL, &oldact, NULL);
+  pthread_mutex_unlock(&g_sigaction_mutex);
+
+  bool unwind_done = impl_->Unwind(num_ignore_frames, entry->GetUcontext());
+
+  // Tell the signal handler to exit and release the entry.
+  entry->Wake();
+
+  return unwind_done;
 }
diff --git a/libbacktrace/BacktraceThread.h b/libbacktrace/BacktraceThread.h
index 9310a44..a75a807 100644
--- a/libbacktrace/BacktraceThread.h
+++ b/libbacktrace/BacktraceThread.h
@@ -18,18 +18,14 @@
 #define _LIBBACKTRACE_BACKTRACE_THREAD_H
 
 #include <inttypes.h>
+#include <pthread.h>
 #include <signal.h>
+#include <string.h>
 #include <sys/types.h>
+#include <ucontext.h>
 
 #include "BacktraceImpl.h"
 
-enum state_e {
-  STATE_WAITING = 0,
-  STATE_DUMPING,
-  STATE_DONE,
-  STATE_CANCEL,
-};
-
 // The signal used to cause a thread to dump the stack.
 #if defined(__GLIBC__)
 // GLIBC reserves __SIGRTMIN signals, so use SIGRTMIN to avoid errors.
@@ -38,62 +34,57 @@
 #define THREAD_SIGNAL (__SIGRTMIN+1)
 #endif
 
-class BacktraceThreadInterface;
+class ThreadEntry {
+public:
+  static ThreadEntry* Get(pid_t pid, pid_t tid, bool create = true);
 
-struct ThreadEntry {
-  ThreadEntry(
-      BacktraceThreadInterface* impl, pid_t pid, pid_t tid,
-      size_t num_ignore_frames);
+  static void Remove(ThreadEntry* entry);
+
+  inline void CopyUcontext(ucontext_t* ucontext) {
+    memcpy(&ucontext_, ucontext, sizeof(ucontext_));
+  }
+
+  void Wake();
+
+  void Wait(int);
+
+  inline void Lock() {
+    pthread_mutex_lock(&mutex_);
+    // Reset the futex value in case of multiple unwinds of the same thread.
+    futex_ = 0;
+  }
+
+  inline void Unlock() {
+    pthread_mutex_unlock(&mutex_);
+  }
+
+  inline ucontext_t* GetUcontext() { return &ucontext_; }
+
+private:
+  ThreadEntry(pid_t pid, pid_t tid);
   ~ThreadEntry();
 
-  bool Match(pid_t chk_pid, pid_t chk_tid) { return (chk_pid == pid && chk_tid == tid); }
+  bool Match(pid_t chk_pid, pid_t chk_tid) { return (chk_pid == pid_ && chk_tid == tid_); }
 
-  static ThreadEntry* AddThreadToUnwind(
-      BacktraceThreadInterface* thread_intf, pid_t pid, pid_t tid,
-      size_t num_ignored_frames);
+  pid_t pid_;
+  pid_t tid_;
+  int futex_;
+  int ref_count_;
+  pthread_mutex_t mutex_;
+  ThreadEntry* next_;
+  ThreadEntry* prev_;
+  ucontext_t ucontext_;
 
-  BacktraceThreadInterface* thread_intf;
-  pid_t pid;
-  pid_t tid;
-  ThreadEntry* next;
-  ThreadEntry* prev;
-  int32_t state;
-  int num_ignore_frames;
-};
-
-// Interface class that does not contain any local storage, only defines
-// virtual functions to be defined by subclasses.
-class BacktraceThreadInterface {
-public:
-  virtual ~BacktraceThreadInterface() { }
-
-  virtual void ThreadUnwind(
-      siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) = 0;
+  static ThreadEntry* list_;
+  static pthread_mutex_t list_mutex_;
 };
 
 class BacktraceThread : public BacktraceCurrent {
 public:
-  // impl and thread_intf should point to the same object, this allows
-  // the compiler to catch if an implementation does not properly
-  // subclass both.
-  BacktraceThread(
-      BacktraceImpl* impl, BacktraceThreadInterface* thread_intf, pid_t tid,
-      BacktraceMap* map);
+  BacktraceThread(BacktraceImpl* impl, pid_t tid, BacktraceMap* map);
   virtual ~BacktraceThread();
 
-  virtual bool Unwind(size_t num_ignore_frames);
-
-  virtual void ThreadUnwind(
-      siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) {
-    thread_intf_->ThreadUnwind(siginfo, sigcontext, num_ignore_frames);
-  }
-
-private:
-  virtual bool TriggerUnwindOnThread(ThreadEntry* entry);
-
-  virtual void FinishUnwind();
-
-  BacktraceThreadInterface* thread_intf_;
+  virtual bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext);
 };
 
 #endif // _LIBBACKTRACE_BACKTRACE_THREAD_H
diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp
index 67d372a..e5f7c3d 100755
--- a/libbacktrace/UnwindCurrent.cpp
+++ b/libbacktrace/UnwindCurrent.cpp
@@ -24,6 +24,7 @@
 #include <libunwind.h>
 
 #include "BacktraceLog.h"
+#include "BacktraceThread.h"
 #include "UnwindCurrent.h"
 #include "UnwindMap.h"
 
@@ -36,15 +37,45 @@
 UnwindCurrent::~UnwindCurrent() {
 }
 
-bool UnwindCurrent::Unwind(size_t num_ignore_frames) {
-  int ret = unw_getcontext(&context_);
-  if (ret < 0) {
-    BACK_LOGW("unw_getcontext failed %d", ret);
-    return false;
+bool UnwindCurrent::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
+  if (!ucontext) {
+    int ret = unw_getcontext(&context_);
+    if (ret < 0) {
+      BACK_LOGW("unw_getcontext failed %d", ret);
+      return false;
+    }
+  }
+  else {
+    GetUnwContextFromUcontext(ucontext);
   }
   return UnwindFromContext(num_ignore_frames, false);
 }
 
+void UnwindCurrent::GetUnwContextFromUcontext(const ucontext_t* ucontext) {
+  unw_tdep_context_t* unw_context = reinterpret_cast<unw_tdep_context_t*>(&context_);
+
+#if defined(__arm__)
+  unw_context->regs[0] = ucontext->uc_mcontext.arm_r0;
+  unw_context->regs[1] = ucontext->uc_mcontext.arm_r1;
+  unw_context->regs[2] = ucontext->uc_mcontext.arm_r2;
+  unw_context->regs[3] = ucontext->uc_mcontext.arm_r3;
+  unw_context->regs[4] = ucontext->uc_mcontext.arm_r4;
+  unw_context->regs[5] = ucontext->uc_mcontext.arm_r5;
+  unw_context->regs[6] = ucontext->uc_mcontext.arm_r6;
+  unw_context->regs[7] = ucontext->uc_mcontext.arm_r7;
+  unw_context->regs[8] = ucontext->uc_mcontext.arm_r8;
+  unw_context->regs[9] = ucontext->uc_mcontext.arm_r9;
+  unw_context->regs[10] = ucontext->uc_mcontext.arm_r10;
+  unw_context->regs[11] = ucontext->uc_mcontext.arm_fp;
+  unw_context->regs[12] = ucontext->uc_mcontext.arm_ip;
+  unw_context->regs[13] = ucontext->uc_mcontext.arm_sp;
+  unw_context->regs[14] = ucontext->uc_mcontext.arm_lr;
+  unw_context->regs[15] = ucontext->uc_mcontext.arm_pc;
+#else
+  unw_context->uc_mcontext = ucontext->uc_mcontext;
+#endif
+}
+
 std::string UnwindCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
   *offset = 0;
   char buf[512];
@@ -122,47 +153,6 @@
   return true;
 }
 
-void UnwindCurrent::ExtractContext(void* sigcontext) {
-  unw_tdep_context_t* context = reinterpret_cast<unw_tdep_context_t*>(&context_);
-  const ucontext_t* uc = reinterpret_cast<const ucontext_t*>(sigcontext);
-
-#if defined(__arm__)
-  context->regs[0] = uc->uc_mcontext.arm_r0;
-  context->regs[1] = uc->uc_mcontext.arm_r1;
-  context->regs[2] = uc->uc_mcontext.arm_r2;
-  context->regs[3] = uc->uc_mcontext.arm_r3;
-  context->regs[4] = uc->uc_mcontext.arm_r4;
-  context->regs[5] = uc->uc_mcontext.arm_r5;
-  context->regs[6] = uc->uc_mcontext.arm_r6;
-  context->regs[7] = uc->uc_mcontext.arm_r7;
-  context->regs[8] = uc->uc_mcontext.arm_r8;
-  context->regs[9] = uc->uc_mcontext.arm_r9;
-  context->regs[10] = uc->uc_mcontext.arm_r10;
-  context->regs[11] = uc->uc_mcontext.arm_fp;
-  context->regs[12] = uc->uc_mcontext.arm_ip;
-  context->regs[13] = uc->uc_mcontext.arm_sp;
-  context->regs[14] = uc->uc_mcontext.arm_lr;
-  context->regs[15] = uc->uc_mcontext.arm_pc;
-#else
-  context->uc_mcontext = uc->uc_mcontext;
-#endif
-}
-
-//-------------------------------------------------------------------------
-// UnwindThread functions.
-//-------------------------------------------------------------------------
-UnwindThread::UnwindThread() {
-}
-
-UnwindThread::~UnwindThread() {
-}
-
-void UnwindThread::ThreadUnwind(
-    siginfo_t* /*siginfo*/, void* sigcontext, size_t num_ignore_frames) {
-  ExtractContext(sigcontext);
-  UnwindFromContext(num_ignore_frames, true);
-}
-
 //-------------------------------------------------------------------------
 // C++ object creation function.
 //-------------------------------------------------------------------------
@@ -171,6 +161,5 @@
 }
 
 Backtrace* CreateThreadObj(pid_t tid, BacktraceMap* map) {
-  UnwindThread* thread_obj = new UnwindThread();
-  return new BacktraceThread(thread_obj, thread_obj, tid, map);
+  return new BacktraceThread(new UnwindCurrent(), tid, map);
 }
diff --git a/libbacktrace/UnwindCurrent.h b/libbacktrace/UnwindCurrent.h
index 41080c7..2375e6e 100644
--- a/libbacktrace/UnwindCurrent.h
+++ b/libbacktrace/UnwindCurrent.h
@@ -20,7 +20,6 @@
 #include <string>
 
 #include "BacktraceImpl.h"
-#include "BacktraceThread.h"
 
 #define UNW_LOCAL_ONLY
 #include <libunwind.h>
@@ -30,25 +29,16 @@
   UnwindCurrent();
   virtual ~UnwindCurrent();
 
-  virtual bool Unwind(size_t num_ignore_frames);
+  virtual bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext);
 
   virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
 
   bool UnwindFromContext(size_t num_ignore_frames, bool within_handler);
 
-  void ExtractContext(void* sigcontext);
+  void GetUnwContextFromUcontext(const ucontext_t* context);
 
 protected:
   unw_context_t context_;
 };
 
-class UnwindThread : public UnwindCurrent, public BacktraceThreadInterface {
-public:
-  UnwindThread();
-  virtual ~UnwindThread();
-
-  virtual void ThreadUnwind(
-      siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames);
-};
-
 #endif // _LIBBACKTRACE_UNWIND_CURRENT_H
diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp
index 5ca7e60..63b4f16 100644
--- a/libbacktrace/UnwindPtrace.cpp
+++ b/libbacktrace/UnwindPtrace.cpp
@@ -45,7 +45,12 @@
   }
 }
 
-bool UnwindPtrace::Unwind(size_t num_ignore_frames) {
+bool UnwindPtrace::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
+  if (ucontext) {
+    BACK_LOGW("Unwinding from a specified context not supported yet.");
+    return false;
+  }
+
   addr_space_ = unw_create_addr_space(&_UPT_accessors, 0);
   if (!addr_space_) {
     BACK_LOGW("unw_create_addr_space failed.");
diff --git a/libbacktrace/UnwindPtrace.h b/libbacktrace/UnwindPtrace.h
index 1e82117..2fb7967 100644
--- a/libbacktrace/UnwindPtrace.h
+++ b/libbacktrace/UnwindPtrace.h
@@ -28,7 +28,7 @@
   UnwindPtrace();
   virtual ~UnwindPtrace();
 
-  virtual bool Unwind(size_t num_ignore_frames);
+  virtual bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext);
 
   virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
 
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 9744922..ed6b211 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -615,6 +615,49 @@
   }
 }
 
+TEST(libbacktrace, thread_multiple_dump_same_thread) {
+  pthread_attr_t attr;
+  pthread_attr_init(&attr);
+  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+  thread_t runner;
+  runner.tid = 0;
+  runner.state = 0;
+  ASSERT_TRUE(pthread_create(&runner.threadId, &attr, ThreadMaxRun, &runner) == 0);
+
+  // Wait for tids to be set.
+  ASSERT_TRUE(WaitForNonZero(&runner.state, 10));
+
+  // Start all of the dumpers at once, they will spin until they are signalled
+  // to begin their dump run.
+  int32_t dump_now = 0;
+  // Dump the same thread NUM_THREADS simultaneously.
+  std::vector<dump_thread_t> dumpers(NUM_THREADS);
+  for (size_t i = 0; i < NUM_THREADS; i++) {
+    dumpers[i].thread.tid = runner.tid;
+    dumpers[i].thread.state = 0;
+    dumpers[i].done = 0;
+    dumpers[i].now = &dump_now;
+
+    ASSERT_TRUE(pthread_create(&dumpers[i].thread.threadId, &attr, ThreadDump, &dumpers[i]) == 0);
+  }
+
+  // Start all of the dumpers going at once.
+  android_atomic_acquire_store(1, &dump_now);
+
+  for (size_t i = 0; i < NUM_THREADS; i++) {
+    ASSERT_TRUE(WaitForNonZero(&dumpers[i].done, 100));
+
+    ASSERT_TRUE(dumpers[i].backtrace != NULL);
+    VerifyMaxDump(dumpers[i].backtrace);
+
+    delete dumpers[i].backtrace;
+    dumpers[i].backtrace = NULL;
+  }
+
+  // Tell the runner thread to exit its infinite loop.
+  android_atomic_acquire_store(0, &runner.state);
+}
+
 // This test is for UnwindMaps that should share the same map cursor when
 // multiple maps are created for the current process at the same time.
 TEST(libbacktrace, simultaneous_maps) {