| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (C) 2013 The Android Open Source Project | 
|  | 3 | * | 
|  | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | 5 | * you may not use this file except in compliance with the License. | 
|  | 6 | * You may obtain a copy of the License at | 
|  | 7 | * | 
|  | 8 | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | 9 | * | 
|  | 10 | * Unless required by applicable law or agreed to in writing, software | 
|  | 11 | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | 13 | * See the License for the specific language governing permissions and | 
|  | 14 | * limitations under the License. | 
|  | 15 | */ | 
|  | 16 |  | 
|  | 17 | #include <errno.h> | 
|  | 18 | #include <inttypes.h> | 
|  | 19 | #include <pthread.h> | 
|  | 20 | #include <signal.h> | 
|  | 21 | #include <string.h> | 
|  | 22 | #include <sys/types.h> | 
|  | 23 |  | 
|  | 24 | #include <cutils/atomic.h> | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 25 |  | 
| Christopher Ferris | e296091 | 2014-03-07 19:42:19 -0800 | [diff] [blame] | 26 | #include "BacktraceLog.h" | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 27 | #include "BacktraceThread.h" | 
|  | 28 | #include "thread_utils.h" | 
|  | 29 |  | 
|  | 30 | //------------------------------------------------------------------------- | 
|  | 31 | // ThreadEntry implementation. | 
|  | 32 | //------------------------------------------------------------------------- | 
|  | 33 | static ThreadEntry* g_list = NULL; | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 34 | static pthread_mutex_t g_entry_mutex = PTHREAD_MUTEX_INITIALIZER; | 
|  | 35 | static pthread_mutex_t g_sigaction_mutex = PTHREAD_MUTEX_INITIALIZER; | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 36 |  | 
|  | 37 | ThreadEntry::ThreadEntry( | 
|  | 38 | BacktraceThreadInterface* intf, pid_t pid, pid_t tid, size_t num_ignore_frames) | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 39 | : thread_intf(intf), pid(pid), tid(tid), next(NULL), prev(NULL), | 
|  | 40 | state(STATE_WAITING), num_ignore_frames(num_ignore_frames) { | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 41 | } | 
|  | 42 |  | 
|  | 43 | ThreadEntry::~ThreadEntry() { | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 44 | pthread_mutex_lock(&g_entry_mutex); | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 45 | if (g_list == this) { | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 46 | g_list = next; | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 47 | } else { | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 48 | if (next) { | 
|  | 49 | next->prev = prev; | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 50 | } | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 51 | prev->next = next; | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 52 | } | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 53 | pthread_mutex_unlock(&g_entry_mutex); | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 54 |  | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 55 | next = NULL; | 
|  | 56 | prev = NULL; | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 57 | } | 
|  | 58 |  | 
|  | 59 | ThreadEntry* ThreadEntry::AddThreadToUnwind( | 
|  | 60 | BacktraceThreadInterface* intf, pid_t pid, pid_t tid, size_t num_ignore_frames) { | 
|  | 61 | ThreadEntry* entry = new ThreadEntry(intf, pid, tid, num_ignore_frames); | 
|  | 62 |  | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 63 | pthread_mutex_lock(&g_entry_mutex); | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 64 | ThreadEntry* cur_entry = g_list; | 
|  | 65 | while (cur_entry != NULL) { | 
|  | 66 | if (cur_entry->Match(pid, tid)) { | 
|  | 67 | // There is already an entry for this pid/tid, this is bad. | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 68 | BACK_LOGW("Entry for pid %d tid %d already exists.", pid, tid); | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 69 |  | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 70 | pthread_mutex_unlock(&g_entry_mutex); | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 71 | return NULL; | 
|  | 72 | } | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 73 | cur_entry = cur_entry->next; | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 74 | } | 
|  | 75 |  | 
|  | 76 | // Add the entry to the list. | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 77 | entry->next = g_list; | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 78 | if (g_list) { | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 79 | g_list->prev = entry; | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 80 | } | 
|  | 81 | g_list = entry; | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 82 | pthread_mutex_unlock(&g_entry_mutex); | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 83 |  | 
|  | 84 | return entry; | 
|  | 85 | } | 
|  | 86 |  | 
|  | 87 | //------------------------------------------------------------------------- | 
|  | 88 | // BacktraceThread functions. | 
|  | 89 | //------------------------------------------------------------------------- | 
|  | 90 | static void SignalHandler(int n __attribute__((unused)), siginfo_t* siginfo, | 
|  | 91 | void* sigcontext) { | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 92 | if (pthread_mutex_lock(&g_entry_mutex) == 0) { | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 93 | pid_t pid = getpid(); | 
|  | 94 | pid_t tid = gettid(); | 
|  | 95 | ThreadEntry* cur_entry = g_list; | 
|  | 96 | while (cur_entry) { | 
|  | 97 | if (cur_entry->Match(pid, tid)) { | 
|  | 98 | break; | 
|  | 99 | } | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 100 | cur_entry = cur_entry->next; | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 101 | } | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 102 | pthread_mutex_unlock(&g_entry_mutex); | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 103 | if (!cur_entry) { | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 104 | BACK_LOGW("Unable to find pid %d tid %d information", pid, tid); | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 105 | return; | 
|  | 106 | } | 
|  | 107 |  | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 108 | if (android_atomic_acquire_cas(STATE_WAITING, STATE_DUMPING, &cur_entry->state) == 0) { | 
|  | 109 | cur_entry->thread_intf->ThreadUnwind(siginfo, sigcontext, | 
|  | 110 | cur_entry->num_ignore_frames); | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 111 | } | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 112 | android_atomic_release_store(STATE_DONE, &cur_entry->state); | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 113 | } | 
|  | 114 | } | 
|  | 115 |  | 
|  | 116 | BacktraceThread::BacktraceThread( | 
| Christopher Ferris | 9846497 | 2014-01-06 19:16:33 -0800 | [diff] [blame] | 117 | BacktraceImpl* impl, BacktraceThreadInterface* thread_intf, pid_t tid, | 
| Christopher Ferris | 4675682 | 2014-01-14 20:16:30 -0800 | [diff] [blame] | 118 | BacktraceMap* map) | 
|  | 119 | : BacktraceCurrent(impl, map), thread_intf_(thread_intf) { | 
|  | 120 | tid_ = tid; | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 121 | } | 
|  | 122 |  | 
|  | 123 | BacktraceThread::~BacktraceThread() { | 
|  | 124 | } | 
|  | 125 |  | 
|  | 126 | void BacktraceThread::FinishUnwind() { | 
| Christopher Ferris | 4675682 | 2014-01-14 20:16:30 -0800 | [diff] [blame] | 127 | for (std::vector<backtrace_frame_data_t>::iterator it = frames_.begin(); | 
|  | 128 | it != frames_.end(); ++it) { | 
|  | 129 | it->map = FindMap(it->pc); | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 130 |  | 
| Christopher Ferris | 4675682 | 2014-01-14 20:16:30 -0800 | [diff] [blame] | 131 | it->func_offset = 0; | 
|  | 132 | it->func_name = GetFunctionName(it->pc, &it->func_offset); | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 133 | } | 
|  | 134 | } | 
|  | 135 |  | 
|  | 136 | bool BacktraceThread::TriggerUnwindOnThread(ThreadEntry* entry) { | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 137 | entry->state = STATE_WAITING; | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 138 |  | 
|  | 139 | if (tgkill(Pid(), Tid(), SIGURG) != 0) { | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 140 | BACK_LOGW("tgkill failed %s", strerror(errno)); | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 141 | return false; | 
|  | 142 | } | 
|  | 143 |  | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 144 | // Allow up to ten seconds for the dump to start. | 
|  | 145 | int wait_millis = 10000; | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 146 | int32_t state; | 
|  | 147 | while (true) { | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 148 | state = android_atomic_acquire_load(&entry->state); | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 149 | if (state != STATE_WAITING) { | 
|  | 150 | break; | 
|  | 151 | } | 
|  | 152 | if (wait_millis--) { | 
|  | 153 | usleep(1000); | 
|  | 154 | } else { | 
|  | 155 | break; | 
|  | 156 | } | 
|  | 157 | } | 
|  | 158 |  | 
|  | 159 | bool cancelled = false; | 
|  | 160 | if (state == STATE_WAITING) { | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 161 | if (android_atomic_acquire_cas(state, STATE_CANCEL, &entry->state) == 0) { | 
|  | 162 | BACK_LOGW("Cancelled dump of thread %d", entry->tid); | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 163 | state = STATE_CANCEL; | 
|  | 164 | cancelled = true; | 
|  | 165 | } else { | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 166 | state = android_atomic_acquire_load(&entry->state); | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 167 | } | 
|  | 168 | } | 
|  | 169 |  | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 170 | // Wait for at most ten seconds for the cancel or dump to finish. | 
|  | 171 | wait_millis = 10000; | 
|  | 172 | while (android_atomic_acquire_load(&entry->state) != STATE_DONE) { | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 173 | if (wait_millis--) { | 
|  | 174 | usleep(1000); | 
|  | 175 | } else { | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 176 | BACK_LOGW("Didn't finish thread unwind in 60 seconds."); | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 177 | break; | 
|  | 178 | } | 
|  | 179 | } | 
|  | 180 | return !cancelled; | 
|  | 181 | } | 
|  | 182 |  | 
|  | 183 | bool BacktraceThread::Unwind(size_t num_ignore_frames) { | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 184 | ThreadEntry* entry = ThreadEntry::AddThreadToUnwind( | 
|  | 185 | thread_intf_, Pid(), Tid(), num_ignore_frames); | 
|  | 186 | if (!entry) { | 
|  | 187 | return false; | 
|  | 188 | } | 
|  | 189 |  | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 190 | // Prevent multiple threads trying to set the trigger action on different | 
|  | 191 | // threads at the same time. | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 192 | bool retval = false; | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 193 | if (pthread_mutex_lock(&g_sigaction_mutex) == 0) { | 
|  | 194 | struct sigaction act, oldact; | 
|  | 195 | memset(&act, 0, sizeof(act)); | 
|  | 196 | act.sa_sigaction = SignalHandler; | 
|  | 197 | act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK; | 
|  | 198 | sigemptyset(&act.sa_mask); | 
|  | 199 | if (sigaction(SIGURG, &act, &oldact) == 0) { | 
|  | 200 | retval = TriggerUnwindOnThread(entry); | 
|  | 201 | sigaction(SIGURG, &oldact, NULL); | 
|  | 202 | } else { | 
|  | 203 | BACK_LOGW("sigaction failed %s", strerror(errno)); | 
|  | 204 | } | 
|  | 205 | pthread_mutex_unlock(&g_sigaction_mutex); | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 206 | } else { | 
| Christopher Ferris | 8ed4627 | 2013-10-29 15:44:25 -0700 | [diff] [blame] | 207 | BACK_LOGW("unable to acquire sigaction mutex."); | 
| Christopher Ferris | 17e91d4 | 2013-10-21 13:30:52 -0700 | [diff] [blame] | 208 | } | 
|  | 209 |  | 
|  | 210 | if (retval) { | 
|  | 211 | FinishUnwind(); | 
|  | 212 | } | 
|  | 213 | delete entry; | 
|  | 214 |  | 
|  | 215 | return retval; | 
|  | 216 | } |