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. 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); return false; } 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 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; }
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); }