/* * Copyright (C) 2013 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 "BacktraceLog.h" #include "BacktraceThread.h" #include "thread_utils.h" //------------------------------------------------------------------------- // 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( 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) { } ThreadEntry::~ThreadEntry() { pthread_mutex_lock(&g_entry_mutex); if (g_list == this) { g_list = next; } else { if (next) { next->prev = prev; } prev->next = next; } 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); return entry; } //------------------------------------------------------------------------- // 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; } 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); } } BacktraceThread::BacktraceThread( BacktraceImpl* impl, BacktraceThreadInterface* thread_intf, pid_t tid, BacktraceMap* map) : BacktraceCurrent(impl, map), thread_intf_(thread_intf) { tid_ = tid; } BacktraceThread::~BacktraceThread() { } void BacktraceThread::FinishUnwind() { for (std::vector::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(), SIGURG) != 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; } // 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(SIGURG, &act, &oldact) == 0) { retval = TriggerUnwindOnThread(entry); sigaction(SIGURG, &oldact, NULL); } else { BACK_LOGW("sigaction failed %s", strerror(errno)); } pthread_mutex_unlock(&g_sigaction_mutex); } else { BACK_LOGW("unable to acquire sigaction mutex."); } if (retval) { FinishUnwind(); } delete entry; return retval; }