#include <errno.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
#include <android-base/errno_restorer.h>
#include <android-base/threads.h>
#include <unwindstack/Log.h>
#include <unwindstack/Regs.h>
#include <unwindstack/Unwinder.h>
#include "ThreadEntry.h"
namespace unwindstack {
static void SignalLogOnly(int, siginfo_t*, void*) {
android::base::ErrnoRestorer restore;
log_async_safe("pid %d, tid %d: Received a spurious thread signal\n", getpid(),
static_cast<int>(android::base::GetThreadId()));
}
static void SignalHandler(int, siginfo_t*, void* sigcontext) {
android::base::ErrnoRestorer restore;
ThreadEntry* entry = ThreadEntry::Get(android::base::GetThreadId(), false);
if (!entry) {
return;
}
entry->CopyUcontextFromSigcontext(sigcontext);
entry->Wake();
if (entry->Wait(WAIT_FOR_UNWIND_TO_COMPLETE)) {
entry->Wake();
} else {
log_async_safe("Timed out waiting for unwind thread to indicate it completed.");
}
}
ThreadUnwinder::ThreadUnwinder(size_t max_frames)
: UnwinderFromPid(max_frames, getpid(), Regs::CurrentArch()) {}
ThreadUnwinder::ThreadUnwinder(size_t max_frames, const ThreadUnwinder* unwinder)
: UnwinderFromPid(max_frames, getpid(), Regs::CurrentArch()) {
process_memory_ = unwinder->process_memory_;
maps_ = unwinder->maps_;
jit_debug_ = unwinder->jit_debug_;
dex_files_ = unwinder->dex_files_;
initted_ = unwinder->initted_;
}
ThreadEntry* ThreadUnwinder::SendSignalToThread(int signal, pid_t tid) {
static std::mutex action_mutex;
std::lock_guard<std::mutex> guard(action_mutex);
ThreadEntry* entry = ThreadEntry::Get(tid);
entry->Lock();
struct sigaction new_action = {.sa_sigaction = SignalHandler,
.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK};
struct sigaction old_action = {};
sigemptyset(&new_action.sa_mask);
if (sigaction(signal, &new_action, &old_action) != 0) {
log_async_safe("sigaction failed: %s", strerror(errno));
ThreadEntry::Remove(entry);
last_error_.code = ERROR_SYSTEM_CALL;
return nullptr;
}
if (tgkill(getpid(), tid, signal) != 0) {
if (errno == ESRCH) {
last_error_.code = ERROR_THREAD_DOES_NOT_EXIST;
} else {
last_error_.code = ERROR_SYSTEM_CALL;
}
sigaction(signal, &old_action, nullptr);
ThreadEntry::Remove(entry);
return nullptr;
}
bool wait_completed = entry->Wait(WAIT_FOR_UCONTEXT);
if (wait_completed) {
return entry;
}
if (old_action.sa_sigaction == nullptr) {
struct sigaction log_action = {.sa_sigaction = SignalLogOnly,
.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK};
sigemptyset(&log_action.sa_mask);
sigaction(signal, &log_action, nullptr);
} else {
sigaction(signal, &old_action, nullptr);
}
if (tgkill(getpid(), tid, 0) == -1 && errno == ESRCH) {
last_error_.code = ERROR_THREAD_DOES_NOT_EXIST;
} else {
last_error_.code = ERROR_THREAD_TIMEOUT;
log_async_safe("Timed out waiting for signal handler to get ucontext data.");
}
ThreadEntry::Remove(entry);
return nullptr;
}
void ThreadUnwinder::UnwindWithSignal(int signal, pid_t tid,
const std::vector<std::string>* initial_map_names_to_skip,
const std::vector<std::string>* map_suffixes_to_ignore) {
ClearErrors();
if (tid == pid_) {
last_error_.code = ERROR_UNSUPPORTED;
return;
}
if (!Init()) {
return;
}
ThreadEntry* entry = SendSignalToThread(signal, tid);
if (entry == nullptr) {
return;
}
std::unique_ptr<Regs> regs(Regs::CreateFromUcontext(Regs::CurrentArch(), entry->GetUcontext()));
SetRegs(regs.get());
UnwinderFromPid::Unwind(initial_map_names_to_skip, map_suffixes_to_ignore);
entry->Wake();
if (!entry->Wait(WAIT_FOR_THREAD_TO_RESTART)) {
log_async_safe("Timed out waiting for signal handler to indicate it finished.");
}
ThreadEntry::Remove(entry);
}
}