diff options
Diffstat (limited to 'libbacktrace')
-rwxr-xr-x | libbacktrace/Android.mk | 3 | ||||
-rw-r--r-- | libbacktrace/BacktraceImpl.cpp | 4 | ||||
-rwxr-xr-x | libbacktrace/BacktraceImpl.h | 3 | ||||
-rw-r--r-- | libbacktrace/BacktraceThread.cpp | 270 | ||||
-rw-r--r-- | libbacktrace/BacktraceThread.h | 93 | ||||
-rwxr-xr-x | libbacktrace/UnwindCurrent.cpp | 85 | ||||
-rw-r--r-- | libbacktrace/UnwindCurrent.h | 14 | ||||
-rw-r--r-- | libbacktrace/UnwindPtrace.cpp | 7 | ||||
-rw-r--r-- | libbacktrace/UnwindPtrace.h | 2 | ||||
-rw-r--r-- | libbacktrace/backtrace_test.cpp | 43 |
10 files changed, 267 insertions, 257 deletions
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk index a7305da..9c1b011 100755 --- a/libbacktrace/Android.mk +++ b/libbacktrace/Android.mk @@ -118,6 +118,9 @@ backtrace_test_shared_libraries := \ 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 @@ Backtrace::~Backtrace() { } } -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( - 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) { +ThreadEntry* ThreadEntry::list_ = NULL; +pthread_mutex_t ThreadEntry::list_mutex_ = PTHREAD_MUTEX_INITIALIZER; + +// 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; - } else { - if (next) { - next->prev = prev; +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; } - prev->next = next; + entry = entry->next_; } - pthread_mutex_unlock(&g_entry_mutex); - next = NULL; - prev = NULL; + if (!entry) { + if (create) { + entry = new ThreadEntry(pid, tid); + } + } else { + entry->ref_count_++; + } + pthread_mutex_unlock(&ThreadEntry::list_mutex_); + + return entry; } -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); +void ThreadEntry::Remove(ThreadEntry* entry) { + pthread_mutex_unlock(&entry->mutex_); - 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_lock(&ThreadEntry::list_mutex_); + if (--entry->ref_count_ == 0) { + delete entry; + } + pthread_mutex_unlock(&ThreadEntry::list_mutex_); +} - pthread_mutex_unlock(&g_entry_mutex); - return NULL; +// 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_; } - cur_entry = cur_entry->next; + prev_->next_ = next_; } - // Add the entry to the list. - entry->next = g_list; - if (g_list) { - g_list->prev = entry; + 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)); } - g_list = entry; - pthread_mutex_unlock(&g_entry_mutex); +} - return entry; +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::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) { + if (ucontext) { + // Unwind using an already existing ucontext. + return impl_->Unwind(num_ignore_frames, ucontext); } -} -bool BacktraceThread::TriggerUnwindOnThread(ThreadEntry* entry) { - entry->state = STATE_WAITING; - - if (tgkill(Pid(), Tid(), THREAD_SIGNAL) != 0) { - BACK_LOGW("tgkill failed %s", strerror(errno)); + // Prevent multiple threads trying to set the trigger action on different + // threads at the same time. + if (pthread_mutex_lock(&g_sigaction_mutex) < 0) { + BACK_LOGW("sigaction 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; - } + 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); + return false; } - 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); - } + 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; } - // 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; -} + // Wait for the thread to get the ucontext. + entry->Wait(0); -bool BacktraceThread::Unwind(size_t num_ignore_frames) { - ThreadEntry* entry = ThreadEntry::AddThreadToUnwind( - thread_intf_, Pid(), Tid(), num_ignore_frames); - if (!entry) { - return false; - } + // After the thread has received the signal, allow other unwinders to + // continue. + sigaction(THREAD_SIGNAL, &oldact, NULL); + pthread_mutex_unlock(&g_sigaction_mutex); - // 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)); - } - pthread_mutex_unlock(&g_sigaction_mutex); - } else { - BACK_LOGW("unable to acquire sigaction mutex."); - } + bool unwind_done = impl_->Unwind(num_ignore_frames, entry->GetUcontext()); - if (retval) { - FinishUnwind(); - } - delete entry; + // Tell the signal handler to exit and release the entry. + entry->Wake(); - return retval; + 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 @@ enum state_e { #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); - ~ThreadEntry(); + static void Remove(ThreadEntry* entry); - bool Match(pid_t chk_pid, pid_t chk_tid) { return (chk_pid == pid && chk_tid == tid); } + inline void CopyUcontext(ucontext_t* ucontext) { + memcpy(&ucontext_, ucontext, sizeof(ucontext_)); + } - static ThreadEntry* AddThreadToUnwind( - BacktraceThreadInterface* thread_intf, pid_t pid, pid_t tid, - size_t num_ignored_frames); + void Wake(); - BacktraceThreadInterface* thread_intf; - pid_t pid; - pid_t tid; - ThreadEntry* next; - ThreadEntry* prev; - int32_t state; - int num_ignore_frames; -}; + void Wait(int); -// Interface class that does not contain any local storage, only defines -// virtual functions to be defined by subclasses. -class BacktraceThreadInterface { -public: - virtual ~BacktraceThreadInterface() { } + inline void Lock() { + pthread_mutex_lock(&mutex_); + // Reset the futex value in case of multiple unwinds of the same thread. + futex_ = 0; + } - virtual void ThreadUnwind( - siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) = 0; -}; + inline void Unlock() { + pthread_mutex_unlock(&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); - virtual ~BacktraceThread(); + inline ucontext_t* GetUcontext() { return &ucontext_; } - virtual bool Unwind(size_t num_ignore_frames); +private: + ThreadEntry(pid_t pid, pid_t tid); + ~ThreadEntry(); - virtual void ThreadUnwind( - siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) { - thread_intf_->ThreadUnwind(siginfo, sigcontext, num_ignore_frames); - } + bool Match(pid_t chk_pid, pid_t chk_tid) { return (chk_pid == pid_ && chk_tid == tid_); } -private: - virtual bool TriggerUnwindOnThread(ThreadEntry* entry); + pid_t pid_; + pid_t tid_; + int futex_; + int ref_count_; + pthread_mutex_t mutex_; + ThreadEntry* next_; + ThreadEntry* prev_; + ucontext_t ucontext_; - virtual void FinishUnwind(); + static ThreadEntry* list_; + static pthread_mutex_t list_mutex_; +}; + +class BacktraceThread : public BacktraceCurrent { +public: + BacktraceThread(BacktraceImpl* impl, pid_t tid, BacktraceMap* map); + virtual ~BacktraceThread(); - 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() { 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 @@ bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool within_hand 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* CreateCurrentObj(BacktraceMap* map) { } 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 @@ public: 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 @@ UnwindPtrace::~UnwindPtrace() { } } -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 @@ public: 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) { } } +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) { |