|  | /* | 
|  | * 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 <errno.h> | 
|  | #include <inttypes.h> | 
|  | #include <pthread.h> | 
|  | #include <signal.h> | 
|  | #include <string.h> | 
|  | #include <sys/types.h> | 
|  |  | 
|  | #include <cutils/atomic.h> | 
|  |  | 
|  | #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<backtrace_frame_data_t>::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; | 
|  | } |