/* * 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 #include "BacktraceThread.h" #include "thread_utils.h" //------------------------------------------------------------------------- // ThreadEntry implementation. //------------------------------------------------------------------------- static ThreadEntry* g_list = NULL; static pthread_mutex_t g_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_mutex); if (g_list == this) { g_list = next_; } else { if (next_) { next_->prev_ = prev_; } prev_->next_ = next_; } pthread_mutex_unlock(&g_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_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. ALOGW("%s::%s(): Entry for pid %d tid %d already exists.\n", __FILE__, __FUNCTION__, pid, tid); pthread_mutex_unlock(&g_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_mutex); return entry; } //------------------------------------------------------------------------- // BacktraceThread functions. //------------------------------------------------------------------------- static void SignalHandler(int n __attribute__((unused)), siginfo_t* siginfo, void* sigcontext) { if (pthread_mutex_lock(&g_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_mutex); if (!cur_entry) { ALOGW("%s::%s(): Unable to find pid %d tid %d information\n", __FILE__, __FUNCTION__, 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) : BacktraceCurrent(impl), thread_intf_(thread_intf) { backtrace_.tid = tid; } BacktraceThread::~BacktraceThread() { } void BacktraceThread::FinishUnwind() { for (size_t i = 0; i < NumFrames(); i++) { backtrace_frame_data_t* frame = &backtrace_.frames[i]; frame->map_offset = 0; uintptr_t map_start; frame->map_name = GetMapName(frame->pc, &map_start); if (frame->map_name) { frame->map_offset = frame->pc - map_start; } frame->func_offset = 0; std::string func_name = GetFunctionName(frame->pc, &frame->func_offset); if (!func_name.empty()) { frame->func_name = strdup(func_name.c_str()); } } } bool BacktraceThread::TriggerUnwindOnThread(ThreadEntry* entry) { entry->state_ = STATE_WAITING; if (tgkill(Pid(), Tid(), SIGURG) != 0) { ALOGW("%s::%s(): tgkill failed %s\n", __FILE__, __FUNCTION__, strerror(errno)); return false; } // Allow up to a second for the dump to occur. int wait_millis = 1000; 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) { ALOGW("%s::%s(): Cancelled dump of thread %d\n", __FILE__, __FUNCTION__, entry->tid_); state = STATE_CANCEL; cancelled = true; } else { state = android_atomic_acquire_load(&entry->state_); } } // Wait for at most one minute for the dump to finish. wait_millis = 60000; while (android_atomic_acquire_load(&entry->state_) != STATE_DONE) { if (wait_millis--) { usleep(1000); } else { ALOGW("%s::%s(): Didn't finish thread unwind in 60 seconds.\n", __FILE__, __FUNCTION__); break; } } return !cancelled; } bool BacktraceThread::Unwind(size_t num_ignore_frames) { if (!thread_intf_->Init()) { return false; } ThreadEntry* entry = ThreadEntry::AddThreadToUnwind( thread_intf_, Pid(), Tid(), num_ignore_frames); if (!entry) { return false; } bool retval = false; 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 { ALOGW("%s::%s(): sigaction failed %s\n", __FILE__, __FUNCTION__, strerror(errno)); } if (retval) { FinishUnwind(); } delete entry; return retval; }