Merge "Rewrite unwind thread handling."
This commit is contained in:
commit
3a088524cb
11 changed files with 281 additions and 270 deletions
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <ucontext.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -64,7 +65,7 @@ public:
|
|||
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.
|
||||
|
|
|
@ -116,6 +116,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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.");
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in a new issue