/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ThreadEntry.h" namespace unwindstack { static void SignalLogOnly(int, siginfo_t*, void*) { android::base::ErrnoRestorer restore; Log::AsyncSafe("pid %d, tid %d: Received a spurious thread signal\n", getpid(), static_cast(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); // 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. // The number indicates that we are waiting for the second Wake() call // overall which is made by the thread requesting an unwind. if (entry->Wait(WAIT_FOR_UNWIND_TO_COMPLETE)) { // 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. Log::AsyncSafe("Timed out waiting for unwind thread to indicate it completed."); } } ThreadUnwinder::ThreadUnwinder(size_t max_frames, Maps* maps) : UnwinderFromPid(max_frames, getpid(), Regs::CurrentArch(), maps) {} 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 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::AsyncSafe("sigaction failed: %s", strerror(errno)); ThreadEntry::Remove(entry); last_error_.code = ERROR_SYSTEM_CALL; return nullptr; } if (tgkill(getpid(), tid, signal) != 0) { // Do not emit an error message, this might be expected. Set the // error and let the caller decide. 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; } // Wait for the thread to get the ucontext. The number indicates // that we are waiting for the first Wake() call made by the thread. bool wait_completed = entry->Wait(WAIT_FOR_UCONTEXT); if (wait_completed) { return entry; } if (old_action.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. 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); } // Check to see if the thread has disappeared. if (tgkill(getpid(), tid, 0) == -1 && errno == ESRCH) { last_error_.code = ERROR_THREAD_DOES_NOT_EXIST; } else { last_error_.code = ERROR_THREAD_TIMEOUT; Log::AsyncSafe("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* initial_map_names_to_skip, const std::vector* 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::CreateFromUcontext(Regs::CurrentArch(), entry->GetUcontext())); SetRegs(regs.get()); UnwinderFromPid::Unwind(initial_map_names_to_skip, map_suffixes_to_ignore); // 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(WAIT_FOR_THREAD_TO_RESTART)) { // Send a warning, but do not mark as a failure to unwind. Log::AsyncSafe("Timed out waiting for signal handler to indicate it finished."); } ThreadEntry::Remove(entry); } } // namespace unwindstack