#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>
+#include <cutils/threads.h>
+
#include "BacktraceCurrent.h"
#include "BacktraceLog.h"
#include "ThreadEntry.h"
}
bool BacktraceCurrent::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
+ if (GetMap() == nullptr) {
+ // Without a map object, we can't do anything.
+ return false;
+ }
+
if (ucontext) {
return UnwindFromContext(num_ignore_frames, ucontext);
}
static pthread_mutex_t g_sigaction_mutex = PTHREAD_MUTEX_INITIALIZER;
+static void SignalLogOnly(int, siginfo_t*, void*) {
+ BACK_LOGE("pid %d, tid %d: Received a spurious signal %d\n", getpid(), gettid(), THREAD_SIGNAL);
+}
+
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());
+ BACK_LOGE("pid %d, tid %d entry not found", getpid(), gettid());
return;
}
// the thread run ahead causing problems.
// The number indicates that we are waiting for the second Wake() call
// overall which is made by the thread requesting an unwind.
- entry->Wait(2);
-
- ThreadEntry::Remove(entry);
+ if (entry->Wait(2)) {
+ // Do not remove the entry here because that can result in a deadlock
+ // if the code cannot properly send a signal to the thread under test.
+ entry->Wake();
+ } else {
+ // At this point, it is possible that entry has been freed, so just exit.
+ BACK_LOGE("Timed out waiting for unwind thread to indicate it completed.");
+ }
}
bool BacktraceCurrent::UnwindThread(size_t num_ignore_frames) {
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();
+ BACK_LOGE("sigaction failed: %s", strerror(errno));
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));
+ BACK_LOGE("tgkill %d failed: %s", Tid(), strerror(errno));
sigaction(THREAD_SIGNAL, &oldact, nullptr);
- entry->Unlock();
ThreadEntry::Remove(entry);
pthread_mutex_unlock(&g_sigaction_mutex);
return false;
// Wait for the thread to get the ucontext. The number indicates
// that we are waiting for the first Wake() call made by the thread.
- entry->Wait(1);
-
+ bool wait_completed = entry->Wait(1);
+
+ if (!wait_completed && oldact.sa_sigaction == nullptr) {
+ // If the wait failed, it could be that the signal could not be delivered
+ // within the timeout. Add a signal handler that's simply going to log
+ // something so that we don't crash if the signal eventually gets
+ // delivered. Only do this if there isn't already an action set up.
+ memset(&act, 0, sizeof(act));
+ act.sa_sigaction = SignalLogOnly;
+ act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
+ sigemptyset(&act.sa_mask);
+ sigaction(THREAD_SIGNAL, &act, nullptr);
+ } else {
+ sigaction(THREAD_SIGNAL, &oldact, nullptr);
+ }
// After the thread has received the signal, allow other unwinders to
// continue.
- sigaction(THREAD_SIGNAL, &oldact, nullptr);
pthread_mutex_unlock(&g_sigaction_mutex);
- bool unwind_done = UnwindFromContext(num_ignore_frames, entry->GetUcontext());
+ bool unwind_done = false;
+ if (wait_completed) {
+ unwind_done = UnwindFromContext(num_ignore_frames, entry->GetUcontext());
- // Tell the signal handler to exit and release the entry.
- entry->Wake();
+ // Tell the signal handler to exit and release the entry.
+ entry->Wake();
+
+ // Wait for the thread to indicate it is done with the ThreadEntry.
+ if (!entry->Wait(3)) {
+ // Send a warning, but do not mark as a failure to unwind.
+ BACK_LOGW("Timed out waiting for signal handler to indicate it finished.");
+ }
+ } else {
+ BACK_LOGE("Timed out waiting for signal handler to get ucontext data.");
+ }
+
+ ThreadEntry::Remove(entry);
return unwind_done;
}