blob: 364fca5e8ee00a625efa32747c62d7dcb11e0b82 [file] [log] [blame]
Josh Gaoe73c9322017-02-08 16:06:26 -08001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
Josh Gaoe1aa0ca2017-03-01 17:23:22 -080029#include <dirent.h>
30#include <fcntl.h>
31#include <poll.h>
32#include <pthread.h>
Josh Gaoe73c9322017-02-08 16:06:26 -080033#include <stddef.h>
34#include <sys/ucontext.h>
Josh Gaoe1aa0ca2017-03-01 17:23:22 -080035#include <syscall.h>
Josh Gaoe73c9322017-02-08 16:06:26 -080036#include <unistd.h>
37
Josh Gaoe1aa0ca2017-03-01 17:23:22 -080038#include <atomic>
Josh Gao2b2ae0c2017-08-21 14:31:17 -070039#include <memory>
Josh Gaoe1aa0ca2017-03-01 17:23:22 -080040
41#include <android-base/file.h>
42#include <android-base/unique_fd.h>
Christopher Ferrisac225782017-04-25 11:23:10 -070043#include <async_safe/log.h>
Josh Gao2b2ae0c2017-08-21 14:31:17 -070044#include <backtrace/BacktraceMap.h>
45#include <unwindstack/Memory.h>
46#include <unwindstack/Regs.h>
Josh Gaoe1aa0ca2017-03-01 17:23:22 -080047
48#include "debuggerd/handler.h"
Narayan Kamath2d377cd2017-05-10 10:58:59 +010049#include "tombstoned/tombstoned.h"
50#include "util.h"
Josh Gaoe1aa0ca2017-03-01 17:23:22 -080051
Josh Gaoc3706662017-08-29 13:08:32 -070052#include "libdebuggerd/backtrace.h"
53#include "libdebuggerd/tombstone.h"
Josh Gaoe73c9322017-02-08 16:06:26 -080054
Josh Gaoe1aa0ca2017-03-01 17:23:22 -080055using android::base::unique_fd;
Josh Gao2b2ae0c2017-08-21 14:31:17 -070056using unwindstack::Regs;
Josh Gaoe1aa0ca2017-03-01 17:23:22 -080057
Josh Gaoc531ed62018-01-24 14:23:42 -080058extern "C" bool __linker_enable_fallback_allocator();
Josh Gaoe1aa0ca2017-03-01 17:23:22 -080059extern "C" void __linker_disable_fallback_allocator();
60
61// This is incredibly sketchy to do inside of a signal handler, especially when libbacktrace
62// uses the C++ standard library throughout, but this code runs in the linker, so we'll be using
63// the linker's malloc instead of the libc one. Switch it out for a replacement, just in case.
64//
65// This isn't the default method of dumping because it can fail in cases such as address space
66// exhaustion.
67static void debuggerd_fallback_trace(int output_fd, ucontext_t* ucontext) {
Josh Gaoc531ed62018-01-24 14:23:42 -080068 if (!__linker_enable_fallback_allocator()) {
69 async_safe_format_log(ANDROID_LOG_ERROR, "libc", "fallback allocator already in use");
70 return;
71 }
72
Josh Gao2b2ae0c2017-08-21 14:31:17 -070073 {
74 std::unique_ptr<Regs> regs;
75
76 ThreadInfo thread;
77 thread.pid = getpid();
78 thread.tid = gettid();
79 thread.thread_name = get_thread_name(gettid());
80 thread.registers.reset(Regs::CreateFromUcontext(Regs::CurrentArch(), ucontext));
81
82 // TODO: Create this once and store it in a global?
83 std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid()));
84 dump_backtrace_thread(output_fd, map.get(), thread);
85 }
Josh Gaoe1aa0ca2017-03-01 17:23:22 -080086 __linker_disable_fallback_allocator();
87}
88
89static void debuggerd_fallback_tombstone(int output_fd, ucontext_t* ucontext, siginfo_t* siginfo,
90 void* abort_message) {
Josh Gaoc531ed62018-01-24 14:23:42 -080091 if (!__linker_enable_fallback_allocator()) {
92 async_safe_format_log(ANDROID_LOG_ERROR, "libc", "fallback allocator already in use");
93 return;
94 }
95
Josh Gaoe1aa0ca2017-03-01 17:23:22 -080096 engrave_tombstone_ucontext(output_fd, reinterpret_cast<uintptr_t>(abort_message), siginfo,
97 ucontext);
98 __linker_disable_fallback_allocator();
99}
100
101static void iterate_siblings(bool (*callback)(pid_t, int), int output_fd) {
102 pid_t current_tid = gettid();
103 char buf[BUFSIZ];
104 snprintf(buf, sizeof(buf), "/proc/%d/task", current_tid);
105 DIR* dir = opendir(buf);
106
107 if (!dir) {
Christopher Ferrisac225782017-04-25 11:23:10 -0700108 async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open %s: %s", buf, strerror(errno));
Josh Gaoe1aa0ca2017-03-01 17:23:22 -0800109 return;
110 }
111
112 struct dirent* ent;
113 while ((ent = readdir(dir))) {
114 char* end;
115 long tid = strtol(ent->d_name, &end, 10);
116 if (end == ent->d_name || *end != '\0') {
117 continue;
118 }
119
120 if (tid != current_tid) {
121 callback(tid, output_fd);
122 }
123 }
124 closedir(dir);
125}
126
Josh Gaoc531ed62018-01-24 14:23:42 -0800127static bool forward_output(int src_fd, int dst_fd, pid_t expected_tid) {
Josh Gaoe1aa0ca2017-03-01 17:23:22 -0800128 // Make sure the thread actually got the signal.
129 struct pollfd pfd = {
130 .fd = src_fd, .events = POLLIN,
131 };
132
133 // Wait for up to a second for output to start flowing.
134 if (poll(&pfd, 1, 1000) != 1) {
135 return false;
136 }
137
Josh Gaoc531ed62018-01-24 14:23:42 -0800138 pid_t tid;
139 if (TEMP_FAILURE_RETRY(read(src_fd, &tid, sizeof(tid))) != sizeof(tid)) {
140 async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to read tid");
141 return false;
142 }
143
144 if (tid != expected_tid) {
145 async_safe_format_log(ANDROID_LOG_ERROR, "libc", "received tid %d, expected %d", tid,
146 expected_tid);
147 return false;
148 }
149
Josh Gaoe1aa0ca2017-03-01 17:23:22 -0800150 while (true) {
151 char buf[512];
152 ssize_t rc = TEMP_FAILURE_RETRY(read(src_fd, buf, sizeof(buf)));
153 if (rc == 0) {
154 return true;
155 } else if (rc < 0) {
156 return false;
157 }
158
159 if (!android::base::WriteFully(dst_fd, buf, rc)) {
160 // We failed to write to tombstoned, but there's not much we can do.
161 // Keep reading from src_fd to keep things going.
162 continue;
163 }
164 }
165}
166
Josh Gaoc531ed62018-01-24 14:23:42 -0800167struct __attribute__((__packed__)) packed_thread_output {
168 int32_t tid;
169 int32_t fd;
170};
171
172static uint64_t pack_thread_fd(pid_t tid, int fd) {
173 packed_thread_output packed = {.tid = tid, .fd = fd};
174 uint64_t result;
175 static_assert(sizeof(packed) == sizeof(result));
176 memcpy(&result, &packed, sizeof(packed));
177 return result;
178}
179
180static std::pair<pid_t, int> unpack_thread_fd(uint64_t value) {
181 packed_thread_output result;
182 memcpy(&result, &value, sizeof(value));
183 return std::make_pair(result.tid, result.fd);
184}
185
Josh Gaoe1aa0ca2017-03-01 17:23:22 -0800186static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
Josh Gaoc531ed62018-01-24 14:23:42 -0800187 static std::atomic<uint64_t> trace_output(pack_thread_fd(-1, -1));
Josh Gaoe1aa0ca2017-03-01 17:23:22 -0800188
189 if (info->si_value.sival_int == ~0) {
190 // Asked to dump by the original signal recipient.
Josh Gaoc531ed62018-01-24 14:23:42 -0800191 uint64_t val = trace_output.load();
192 auto [tid, fd] = unpack_thread_fd(val);
193 if (tid != gettid()) {
194 // We received some other thread's info request?
195 async_safe_format_log(ANDROID_LOG_ERROR, "libc",
196 "thread %d received output fd for thread %d?", gettid(), tid);
197 return;
198 }
Josh Gaoe1aa0ca2017-03-01 17:23:22 -0800199
Josh Gaoc531ed62018-01-24 14:23:42 -0800200 if (!trace_output.compare_exchange_strong(val, pack_thread_fd(-1, -1))) {
201 // Presumably, the timeout in forward_output expired, and the main thread moved on.
202 // If this happened, the main thread closed our fd for us, so just return.
203 async_safe_format_log(ANDROID_LOG_ERROR, "libc", "cmpxchg for thread %d failed", gettid());
204 return;
205 }
206
207 // Write our tid to the output fd to let the main thread know that we're working.
208 if (TEMP_FAILURE_RETRY(write(fd, &tid, sizeof(tid))) == sizeof(tid)) {
209 debuggerd_fallback_trace(fd, ucontext);
210 } else {
211 async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to write to output fd");
212 }
213
214 close(fd);
Josh Gaoe1aa0ca2017-03-01 17:23:22 -0800215 return;
216 }
217
218 // Only allow one thread to perform a trace at a time.
219 static pthread_mutex_t trace_mutex = PTHREAD_MUTEX_INITIALIZER;
220 int ret = pthread_mutex_trylock(&trace_mutex);
221 if (ret != 0) {
Christopher Ferrisac225782017-04-25 11:23:10 -0700222 async_safe_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_try_lock failed: %s",
223 strerror(ret));
Josh Gaoe1aa0ca2017-03-01 17:23:22 -0800224 return;
225 }
226
227 // Fetch output fd from tombstoned.
228 unique_fd tombstone_socket, output_fd;
Narayan Kamatha73df602017-05-24 15:07:25 +0100229 if (!tombstoned_connect(getpid(), &tombstone_socket, &output_fd, kDebuggerdNativeBacktrace)) {
Josh Gaoe1aa0ca2017-03-01 17:23:22 -0800230 goto exit;
231 }
232
233 dump_backtrace_header(output_fd.get());
234
235 // Dump our own stack.
236 debuggerd_fallback_trace(output_fd.get(), ucontext);
237
238 // Send a signal to all of our siblings, asking them to dump their stack.
239 iterate_siblings(
Josh Gao2b2ae0c2017-08-21 14:31:17 -0700240 [](pid_t tid, int output_fd) {
241 // Use a pipe, to be able to detect situations where the thread gracefully exits before
242 // receiving our signal.
243 unique_fd pipe_read, pipe_write;
244 if (!Pipe(&pipe_read, &pipe_write)) {
245 async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to create pipe: %s",
246 strerror(errno));
247 return false;
248 }
Josh Gaoe1aa0ca2017-03-01 17:23:22 -0800249
Josh Gaoc531ed62018-01-24 14:23:42 -0800250 uint64_t expected = pack_thread_fd(-1, -1);
251 if (!trace_output.compare_exchange_strong(expected,
252 pack_thread_fd(tid, pipe_write.release()))) {
253 auto [tid, fd] = unpack_thread_fd(expected);
254 async_safe_format_log(ANDROID_LOG_ERROR, "libc",
255 "thread %d is already outputting to fd %d?", tid, fd);
256 return false;
257 }
Josh Gaoe1aa0ca2017-03-01 17:23:22 -0800258
Josh Gao2b2ae0c2017-08-21 14:31:17 -0700259 siginfo_t siginfo = {};
260 siginfo.si_code = SI_QUEUE;
261 siginfo.si_value.sival_int = ~0;
262 siginfo.si_pid = getpid();
263 siginfo.si_uid = getuid();
Josh Gaoe1aa0ca2017-03-01 17:23:22 -0800264
Josh Gao2b2ae0c2017-08-21 14:31:17 -0700265 if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, DEBUGGER_SIGNAL, &siginfo) != 0) {
266 async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s",
267 tid, strerror(errno));
268 return false;
269 }
Josh Gaoe1aa0ca2017-03-01 17:23:22 -0800270
Josh Gaoc531ed62018-01-24 14:23:42 -0800271 bool success = forward_output(pipe_read.get(), output_fd, tid);
272 if (!success) {
273 async_safe_format_log(ANDROID_LOG_ERROR, "libc",
274 "timeout expired while waiting for thread %d to dump", tid);
275 }
276
277 // Regardless of whether the poll succeeds, check to see if the thread took fd ownership.
278 uint64_t post_wait = trace_output.exchange(pack_thread_fd(-1, -1));
279 if (post_wait != pack_thread_fd(-1, -1)) {
280 auto [tid, fd] = unpack_thread_fd(post_wait);
281 if (fd != -1) {
282 async_safe_format_log(ANDROID_LOG_ERROR, "libc", "closing fd %d for thread %d", fd, tid);
283 close(fd);
284 }
Josh Gao2b2ae0c2017-08-21 14:31:17 -0700285 }
Josh Gaoe1aa0ca2017-03-01 17:23:22 -0800286
Josh Gao2b2ae0c2017-08-21 14:31:17 -0700287 return true;
288 },
289 output_fd.get());
Josh Gaoe1aa0ca2017-03-01 17:23:22 -0800290
291 dump_backtrace_footer(output_fd.get());
292 tombstoned_notify_completion(tombstone_socket.get());
293
294exit:
295 pthread_mutex_unlock(&trace_mutex);
296}
297
298static void crash_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_message) {
Josh Gao2b2ae0c2017-08-21 14:31:17 -0700299 // Only allow one thread to handle a crash at a time (this can happen multiple times without
300 // exit, since tombstones can be requested without a real crash happening.)
Josh Gaoe1aa0ca2017-03-01 17:23:22 -0800301 static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER;
302 int ret = pthread_mutex_lock(&crash_mutex);
303 if (ret != 0) {
Christopher Ferrisac225782017-04-25 11:23:10 -0700304 async_safe_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret));
Josh Gaoe1aa0ca2017-03-01 17:23:22 -0800305 return;
306 }
307
308 unique_fd tombstone_socket, output_fd;
Narayan Kamatha73df602017-05-24 15:07:25 +0100309 bool tombstoned_connected =
310 tombstoned_connect(getpid(), &tombstone_socket, &output_fd, kDebuggerdTombstone);
Josh Gaoe1aa0ca2017-03-01 17:23:22 -0800311 debuggerd_fallback_tombstone(output_fd.get(), ucontext, info, abort_message);
312 if (tombstoned_connected) {
313 tombstoned_notify_completion(tombstone_socket.get());
314 }
Josh Gao2b2ae0c2017-08-21 14:31:17 -0700315
316 pthread_mutex_unlock(&crash_mutex);
Josh Gaoe1aa0ca2017-03-01 17:23:22 -0800317}
318
319extern "C" void debuggerd_fallback_handler(siginfo_t* info, ucontext_t* ucontext,
320 void* abort_message) {
Josh Gao2b2ae0c2017-08-21 14:31:17 -0700321 if (info->si_signo == DEBUGGER_SIGNAL && info->si_value.sival_int != 0) {
Josh Gaoe1aa0ca2017-03-01 17:23:22 -0800322 return trace_handler(info, ucontext);
323 } else {
324 return crash_handler(info, ucontext, abort_message);
325 }
Josh Gaoe73c9322017-02-08 16:06:26 -0800326}