blob: 6c3641ecd2996d2988b177376561a0296dd4127e [file] [log] [blame]
Christopher Ferris17e91d42013-10-21 13:30:52 -07001/*
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>
25#include <cutils/log.h>
26
27#include "BacktraceThread.h"
28#include "thread_utils.h"
29
30//-------------------------------------------------------------------------
31// ThreadEntry implementation.
32//-------------------------------------------------------------------------
33static ThreadEntry* g_list = NULL;
34static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
35
36ThreadEntry::ThreadEntry(
37 BacktraceThreadInterface* intf, pid_t pid, pid_t tid, size_t num_ignore_frames)
38 : thread_intf_(intf), pid_(pid), tid_(tid), next_(NULL), prev_(NULL),
39 state_(STATE_WAITING), num_ignore_frames_(num_ignore_frames) {
40}
41
42ThreadEntry::~ThreadEntry() {
43 pthread_mutex_lock(&g_mutex);
44 if (g_list == this) {
45 g_list = next_;
46 } else {
47 if (next_) {
48 next_->prev_ = prev_;
49 }
50 prev_->next_ = next_;
51 }
52 pthread_mutex_unlock(&g_mutex);
53
54 next_ = NULL;
55 prev_ = NULL;
56}
57
58ThreadEntry* ThreadEntry::AddThreadToUnwind(
59 BacktraceThreadInterface* intf, pid_t pid, pid_t tid, size_t num_ignore_frames) {
60 ThreadEntry* entry = new ThreadEntry(intf, pid, tid, num_ignore_frames);
61
62 pthread_mutex_lock(&g_mutex);
63 ThreadEntry* cur_entry = g_list;
64 while (cur_entry != NULL) {
65 if (cur_entry->Match(pid, tid)) {
66 // There is already an entry for this pid/tid, this is bad.
67 ALOGW("%s::%s(): Entry for pid %d tid %d already exists.\n",
68 __FILE__, __FUNCTION__, pid, tid);
69
70 pthread_mutex_unlock(&g_mutex);
71 return NULL;
72 }
73 cur_entry = cur_entry->next_;
74 }
75
76 // Add the entry to the list.
77 entry->next_ = g_list;
78 if (g_list) {
79 g_list->prev_ = entry;
80 }
81 g_list = entry;
82 pthread_mutex_unlock(&g_mutex);
83
84 return entry;
85}
86
87//-------------------------------------------------------------------------
88// BacktraceThread functions.
89//-------------------------------------------------------------------------
90static void SignalHandler(int n __attribute__((unused)), siginfo_t* siginfo,
91 void* sigcontext) {
92 if (pthread_mutex_lock(&g_mutex) == 0) {
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 }
100 cur_entry = cur_entry->next_;
101 }
102 pthread_mutex_unlock(&g_mutex);
103 if (!cur_entry) {
104 ALOGW("%s::%s(): Unable to find pid %d tid %d information\n",
105 __FILE__, __FUNCTION__, pid, tid);
106 return;
107 }
108
109 if (android_atomic_acquire_cas(STATE_WAITING, STATE_DUMPING, &cur_entry->state_) == 0) {
110 cur_entry->thread_intf_->ThreadUnwind(siginfo, sigcontext,
111 cur_entry->num_ignore_frames_);
112 }
113 android_atomic_release_store(STATE_DONE, &cur_entry->state_);
114 }
115}
116
117BacktraceThread::BacktraceThread(
118 BacktraceImpl* impl, BacktraceThreadInterface* thread_intf, pid_t tid)
119 : BacktraceCurrent(impl), thread_intf_(thread_intf) {
120 backtrace_.tid = tid;
121}
122
123BacktraceThread::~BacktraceThread() {
124}
125
126void BacktraceThread::FinishUnwind() {
127 for (size_t i = 0; i < NumFrames(); i++) {
128 backtrace_frame_data_t* frame = &backtrace_.frames[i];
129
130 frame->map_offset = 0;
131 uintptr_t map_start;
132 frame->map_name = GetMapName(frame->pc, &map_start);
133 if (frame->map_name) {
134 frame->map_offset = frame->pc - map_start;
135 }
136
137 frame->func_offset = 0;
138 std::string func_name = GetFunctionName(frame->pc, &frame->func_offset);
139 if (!func_name.empty()) {
140 frame->func_name = strdup(func_name.c_str());
141 }
142 }
143}
144
145bool BacktraceThread::TriggerUnwindOnThread(ThreadEntry* entry) {
146 entry->state_ = STATE_WAITING;
147
148 if (tgkill(Pid(), Tid(), SIGURG) != 0) {
149 ALOGW("%s::%s(): tgkill failed %s\n", __FILE__, __FUNCTION__, strerror(errno));
150 return false;
151 }
152
153 // Allow up to a second for the dump to occur.
154 int wait_millis = 1000;
155 int32_t state;
156 while (true) {
157 state = android_atomic_acquire_load(&entry->state_);
158 if (state != STATE_WAITING) {
159 break;
160 }
161 if (wait_millis--) {
162 usleep(1000);
163 } else {
164 break;
165 }
166 }
167
168 bool cancelled = false;
169 if (state == STATE_WAITING) {
170 if (android_atomic_acquire_cas(state, STATE_CANCEL, &entry->state_) == 0) {
171 ALOGW("%s::%s(): Cancelled dump of thread %d\n", __FILE__, __FUNCTION__,
172 entry->tid_);
173 state = STATE_CANCEL;
174 cancelled = true;
175 } else {
176 state = android_atomic_acquire_load(&entry->state_);
177 }
178 }
179
180 // Wait for at most one minute for the dump to finish.
181 wait_millis = 60000;
182 while (android_atomic_acquire_load(&entry->state_) != STATE_DONE) {
183 if (wait_millis--) {
184 usleep(1000);
185 } else {
186 ALOGW("%s::%s(): Didn't finish thread unwind in 60 seconds.\n",
187 __FILE__, __FUNCTION__);
188 break;
189 }
190 }
191 return !cancelled;
192}
193
194bool BacktraceThread::Unwind(size_t num_ignore_frames) {
195 if (!thread_intf_->Init()) {
196 return false;
197 }
198
199 ThreadEntry* entry = ThreadEntry::AddThreadToUnwind(
200 thread_intf_, Pid(), Tid(), num_ignore_frames);
201 if (!entry) {
202 return false;
203 }
204
205 bool retval = false;
206 struct sigaction act, oldact;
207 memset(&act, 0, sizeof(act));
208 act.sa_sigaction = SignalHandler;
209 act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
210 sigemptyset(&act.sa_mask);
211 if (sigaction(SIGURG, &act, &oldact) == 0) {
212 retval = TriggerUnwindOnThread(entry);
213 sigaction(SIGURG, &oldact, NULL);
214 } else {
215 ALOGW("%s::%s(): sigaction failed %s\n", __FILE__, __FUNCTION__, strerror(errno));
216 }
217
218 if (retval) {
219 FinishUnwind();
220 }
221 delete entry;
222
223 return retval;
224}