blob: 47a1bf5f5dab4f13c15324ef9b7fa3aacbcc4777 [file] [log] [blame]
Jeff Brown053b8652012-06-06 16:25:03 -07001/*
2 * Copyright (C) 2012 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 <stddef.h>
18#include <stdbool.h>
19#include <stdlib.h>
20#include <signal.h>
21#include <string.h>
22#include <stdio.h>
23#include <fcntl.h>
24#include <errno.h>
25#include <dirent.h>
26#include <time.h>
27#include <sys/ptrace.h>
28#include <sys/stat.h>
29
30#include <private/android_filesystem_config.h>
31
32#include <cutils/logger.h>
33#include <cutils/properties.h>
34
35#include <corkscrew/demangle.h>
36#include <corkscrew/backtrace.h>
37
rpcraigf1186f32012-07-19 09:38:06 -040038#include <selinux/android.h>
rpcraigf1186f32012-07-19 09:38:06 -040039
Jeff Brown053b8652012-06-06 16:25:03 -070040#include "machine.h"
41#include "tombstone.h"
42#include "utility.h"
43
44#define STACK_DEPTH 32
45#define STACK_WORDS 16
46
47#define MAX_TOMBSTONES 10
48#define TOMBSTONE_DIR "/data/tombstones"
49
50#define typecheck(x,y) { \
51 typeof(x) __dummy1; \
52 typeof(y) __dummy2; \
53 (void)(&__dummy1 == &__dummy2); }
54
55
56static bool signal_has_address(int sig) {
57 switch (sig) {
58 case SIGILL:
59 case SIGFPE:
60 case SIGSEGV:
61 case SIGBUS:
62 return true;
63 default:
64 return false;
65 }
66}
67
68static const char *get_signame(int sig)
69{
70 switch(sig) {
71 case SIGILL: return "SIGILL";
72 case SIGABRT: return "SIGABRT";
73 case SIGBUS: return "SIGBUS";
74 case SIGFPE: return "SIGFPE";
75 case SIGSEGV: return "SIGSEGV";
76 case SIGPIPE: return "SIGPIPE";
Chris Dearman231e3c82012-08-10 17:06:20 -070077#ifdef SIGSTKFLT
Jeff Brown053b8652012-06-06 16:25:03 -070078 case SIGSTKFLT: return "SIGSTKFLT";
Chris Dearman231e3c82012-08-10 17:06:20 -070079#endif
Jeff Brown053b8652012-06-06 16:25:03 -070080 case SIGSTOP: return "SIGSTOP";
81 default: return "?";
82 }
83}
84
85static const char *get_sigcode(int signo, int code)
86{
87 switch (signo) {
88 case SIGILL:
89 switch (code) {
90 case ILL_ILLOPC: return "ILL_ILLOPC";
91 case ILL_ILLOPN: return "ILL_ILLOPN";
92 case ILL_ILLADR: return "ILL_ILLADR";
93 case ILL_ILLTRP: return "ILL_ILLTRP";
94 case ILL_PRVOPC: return "ILL_PRVOPC";
95 case ILL_PRVREG: return "ILL_PRVREG";
96 case ILL_COPROC: return "ILL_COPROC";
97 case ILL_BADSTK: return "ILL_BADSTK";
98 }
99 break;
100 case SIGBUS:
101 switch (code) {
102 case BUS_ADRALN: return "BUS_ADRALN";
103 case BUS_ADRERR: return "BUS_ADRERR";
104 case BUS_OBJERR: return "BUS_OBJERR";
105 }
106 break;
107 case SIGFPE:
108 switch (code) {
109 case FPE_INTDIV: return "FPE_INTDIV";
110 case FPE_INTOVF: return "FPE_INTOVF";
111 case FPE_FLTDIV: return "FPE_FLTDIV";
112 case FPE_FLTOVF: return "FPE_FLTOVF";
113 case FPE_FLTUND: return "FPE_FLTUND";
114 case FPE_FLTRES: return "FPE_FLTRES";
115 case FPE_FLTINV: return "FPE_FLTINV";
116 case FPE_FLTSUB: return "FPE_FLTSUB";
117 }
118 break;
119 case SIGSEGV:
120 switch (code) {
121 case SEGV_MAPERR: return "SEGV_MAPERR";
122 case SEGV_ACCERR: return "SEGV_ACCERR";
123 }
124 break;
125 }
126 return "?";
127}
128
129static void dump_build_info(log_t* log)
130{
131 char fingerprint[PROPERTY_VALUE_MAX];
132
133 property_get("ro.build.fingerprint", fingerprint, "unknown");
134
135 _LOG(log, false, "Build fingerprint: '%s'\n", fingerprint);
136}
137
138static void dump_fault_addr(log_t* log, pid_t tid, int sig)
139{
140 siginfo_t si;
141
142 memset(&si, 0, sizeof(si));
143 if(ptrace(PTRACE_GETSIGINFO, tid, 0, &si)){
144 _LOG(log, false, "cannot get siginfo: %s\n", strerror(errno));
145 } else if (signal_has_address(sig)) {
146 _LOG(log, false, "signal %d (%s), code %d (%s), fault addr %08x\n",
147 sig, get_signame(sig),
148 si.si_code, get_sigcode(sig, si.si_code),
149 (uintptr_t) si.si_addr);
150 } else {
151 _LOG(log, false, "signal %d (%s), code %d (%s), fault addr --------\n",
152 sig, get_signame(sig), si.si_code, get_sigcode(sig, si.si_code));
153 }
154}
155
156static void dump_thread_info(log_t* log, pid_t pid, pid_t tid, bool at_fault) {
157 char path[64];
158 char threadnamebuf[1024];
159 char* threadname = NULL;
160 FILE *fp;
161
162 snprintf(path, sizeof(path), "/proc/%d/comm", tid);
163 if ((fp = fopen(path, "r"))) {
164 threadname = fgets(threadnamebuf, sizeof(threadnamebuf), fp);
165 fclose(fp);
166 if (threadname) {
167 size_t len = strlen(threadname);
168 if (len && threadname[len - 1] == '\n') {
169 threadname[len - 1] = '\0';
170 }
171 }
172 }
173
174 if (at_fault) {
175 char procnamebuf[1024];
176 char* procname = NULL;
177
178 snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
179 if ((fp = fopen(path, "r"))) {
180 procname = fgets(procnamebuf, sizeof(procnamebuf), fp);
181 fclose(fp);
182 }
183
184 _LOG(log, false, "pid: %d, tid: %d, name: %s >>> %s <<<\n", pid, tid,
185 threadname ? threadname : "UNKNOWN",
186 procname ? procname : "UNKNOWN");
187 } else {
188 _LOG(log, true, "pid: %d, tid: %d, name: %s\n", pid, tid,
189 threadname ? threadname : "UNKNOWN");
190 }
191}
192
193static void dump_backtrace(const ptrace_context_t* context __attribute((unused)),
194 log_t* log, pid_t tid __attribute((unused)), bool at_fault,
195 const backtrace_frame_t* backtrace, size_t frames) {
196 _LOG(log, !at_fault, "\nbacktrace:\n");
197
198 backtrace_symbol_t backtrace_symbols[STACK_DEPTH];
199 get_backtrace_symbols_ptrace(context, backtrace, frames, backtrace_symbols);
200 for (size_t i = 0; i < frames; i++) {
201 char line[MAX_BACKTRACE_LINE_LENGTH];
202 format_backtrace_line(i, &backtrace[i], &backtrace_symbols[i],
203 line, MAX_BACKTRACE_LINE_LENGTH);
204 _LOG(log, !at_fault, " %s\n", line);
205 }
206 free_backtrace_symbols(backtrace_symbols, frames);
207}
208
209static void dump_stack_segment(const ptrace_context_t* context, log_t* log, pid_t tid,
210 bool only_in_tombstone, uintptr_t* sp, size_t words, int label) {
211 for (size_t i = 0; i < words; i++) {
212 uint32_t stack_content;
213 if (!try_get_word_ptrace(tid, *sp, &stack_content)) {
214 break;
215 }
216
217 const map_info_t* mi;
218 const symbol_t* symbol;
219 find_symbol_ptrace(context, stack_content, &mi, &symbol);
220
221 if (symbol) {
222 char* demangled_name = demangle_symbol_name(symbol->name);
223 const char* symbol_name = demangled_name ? demangled_name : symbol->name;
224 uint32_t offset = stack_content - (mi->start + symbol->start);
225 if (!i && label >= 0) {
226 if (offset) {
227 _LOG(log, only_in_tombstone, " #%02d %08x %08x %s (%s+%u)\n",
228 label, *sp, stack_content, mi ? mi->name : "", symbol_name, offset);
229 } else {
230 _LOG(log, only_in_tombstone, " #%02d %08x %08x %s (%s)\n",
231 label, *sp, stack_content, mi ? mi->name : "", symbol_name);
232 }
233 } else {
234 if (offset) {
235 _LOG(log, only_in_tombstone, " %08x %08x %s (%s+%u)\n",
236 *sp, stack_content, mi ? mi->name : "", symbol_name, offset);
237 } else {
238 _LOG(log, only_in_tombstone, " %08x %08x %s (%s)\n",
239 *sp, stack_content, mi ? mi->name : "", symbol_name);
240 }
241 }
242 free(demangled_name);
243 } else {
244 if (!i && label >= 0) {
245 _LOG(log, only_in_tombstone, " #%02d %08x %08x %s\n",
246 label, *sp, stack_content, mi ? mi->name : "");
247 } else {
248 _LOG(log, only_in_tombstone, " %08x %08x %s\n",
249 *sp, stack_content, mi ? mi->name : "");
250 }
251 }
252
253 *sp += sizeof(uint32_t);
254 }
255}
256
257static void dump_stack(const ptrace_context_t* context, log_t* log, pid_t tid, bool at_fault,
258 const backtrace_frame_t* backtrace, size_t frames) {
259 bool have_first = false;
260 size_t first, last;
261 for (size_t i = 0; i < frames; i++) {
262 if (backtrace[i].stack_top) {
263 if (!have_first) {
264 have_first = true;
265 first = i;
266 }
267 last = i;
268 }
269 }
270 if (!have_first) {
271 return;
272 }
273
274 _LOG(log, !at_fault, "\nstack:\n");
275
276 // Dump a few words before the first frame.
277 bool only_in_tombstone = !at_fault;
278 uintptr_t sp = backtrace[first].stack_top - STACK_WORDS * sizeof(uint32_t);
279 dump_stack_segment(context, log, tid, only_in_tombstone, &sp, STACK_WORDS, -1);
280
281 // Dump a few words from all successive frames.
282 // Only log the first 3 frames, put the rest in the tombstone.
283 for (size_t i = first; i <= last; i++) {
284 const backtrace_frame_t* frame = &backtrace[i];
285 if (sp != frame->stack_top) {
286 _LOG(log, only_in_tombstone, " ........ ........\n");
287 sp = frame->stack_top;
288 }
289 if (i - first == 3) {
290 only_in_tombstone = true;
291 }
292 if (i == last) {
293 dump_stack_segment(context, log, tid, only_in_tombstone, &sp, STACK_WORDS, i);
294 if (sp < frame->stack_top + frame->stack_size) {
295 _LOG(log, only_in_tombstone, " ........ ........\n");
296 }
297 } else {
298 size_t words = frame->stack_size / sizeof(uint32_t);
299 if (words == 0) {
300 words = 1;
301 } else if (words > STACK_WORDS) {
302 words = STACK_WORDS;
303 }
304 dump_stack_segment(context, log, tid, only_in_tombstone, &sp, words, i);
305 }
306 }
307}
308
309static void dump_backtrace_and_stack(const ptrace_context_t* context, log_t* log, pid_t tid,
310 bool at_fault) {
311 backtrace_frame_t backtrace[STACK_DEPTH];
312 ssize_t frames = unwind_backtrace_ptrace(tid, context, backtrace, 0, STACK_DEPTH);
313 if (frames > 0) {
314 dump_backtrace(context, log, tid, at_fault, backtrace, frames);
315 dump_stack(context, log, tid, at_fault, backtrace, frames);
316 }
317}
318
319static void dump_nearby_maps(const ptrace_context_t* context, log_t* log, pid_t tid) {
320 siginfo_t si;
321 memset(&si, 0, sizeof(si));
322 if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si)) {
323 _LOG(log, false, "cannot get siginfo for %d: %s\n",
324 tid, strerror(errno));
325 return;
326 }
327 if (!signal_has_address(si.si_signo)) {
328 return;
329 }
330
331 uintptr_t addr = (uintptr_t) si.si_addr;
332 addr &= ~0xfff; /* round to 4K page boundary */
333 if (addr == 0) { /* null-pointer deref */
334 return;
335 }
336
337 _LOG(log, false, "\nmemory map around fault addr %08x:\n", (int)si.si_addr);
338
339 /*
340 * Search for a match, or for a hole where the match would be. The list
341 * is backward from the file content, so it starts at high addresses.
342 */
Jeff Brown053b8652012-06-06 16:25:03 -0700343 map_info_t* map = context->map_info_list;
344 map_info_t *next = NULL;
345 map_info_t *prev = NULL;
346 while (map != NULL) {
347 if (addr >= map->start && addr < map->end) {
Jeff Brown053b8652012-06-06 16:25:03 -0700348 next = map->next;
349 break;
350 } else if (addr >= map->end) {
351 /* map would be between "prev" and this entry */
352 next = map;
353 map = NULL;
354 break;
355 }
356
357 prev = map;
358 map = map->next;
359 }
360
361 /*
362 * Show "next" then "match" then "prev" so that the addresses appear in
363 * ascending order (like /proc/pid/maps).
364 */
365 if (next != NULL) {
366 _LOG(log, false, " %08x-%08x %s\n", next->start, next->end, next->name);
367 } else {
368 _LOG(log, false, " (no map below)\n");
369 }
370 if (map != NULL) {
371 _LOG(log, false, " %08x-%08x %s\n", map->start, map->end, map->name);
372 } else {
373 _LOG(log, false, " (no map for address)\n");
374 }
375 if (prev != NULL) {
376 _LOG(log, false, " %08x-%08x %s\n", prev->start, prev->end, prev->name);
377 } else {
378 _LOG(log, false, " (no map above)\n");
379 }
380}
381
382static void dump_thread(const ptrace_context_t* context, log_t* log, pid_t tid, bool at_fault,
383 int* total_sleep_time_usec) {
384 wait_for_stop(tid, total_sleep_time_usec);
385
386 dump_registers(context, log, tid, at_fault);
387 dump_backtrace_and_stack(context, log, tid, at_fault);
388 if (at_fault) {
389 dump_memory_and_code(context, log, tid, at_fault);
390 dump_nearby_maps(context, log, tid);
391 }
392}
393
394/* Return true if some thread is not detached cleanly */
395static bool dump_sibling_thread_report(const ptrace_context_t* context,
396 log_t* log, pid_t pid, pid_t tid, int* total_sleep_time_usec) {
397 char task_path[64];
398 snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
399
400 DIR* d = opendir(task_path);
401 /* Bail early if cannot open the task directory */
402 if (d == NULL) {
403 XLOG("Cannot open /proc/%d/task\n", pid);
404 return false;
405 }
406
407 bool detach_failed = false;
Elliott Hughesc463d2c2012-10-26 16:47:09 -0700408 struct dirent* de;
409 while ((de = readdir(d)) != NULL) {
Jeff Brown053b8652012-06-06 16:25:03 -0700410 /* Ignore "." and ".." */
411 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
412 continue;
413 }
414
415 /* The main thread at fault has been handled individually */
416 char* end;
417 pid_t new_tid = strtoul(de->d_name, &end, 10);
418 if (*end || new_tid == tid) {
419 continue;
420 }
421
422 /* Skip this thread if cannot ptrace it */
423 if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0) {
424 continue;
425 }
426
427 _LOG(log, true, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
428 dump_thread_info(log, pid, new_tid, false);
429 dump_thread(context, log, new_tid, false, total_sleep_time_usec);
430
431 if (ptrace(PTRACE_DETACH, new_tid, 0, 0) != 0) {
432 LOG("ptrace detach from %d failed: %s\n", new_tid, strerror(errno));
433 detach_failed = true;
434 }
435 }
436
437 closedir(d);
438 return detach_failed;
439}
440
441/*
442 * Reads the contents of the specified log device, filters out the entries
443 * that don't match the specified pid, and writes them to the tombstone file.
444 *
445 * If "tailOnly" is set, we only print the last few lines.
446 */
447static void dump_log_file(log_t* log, pid_t pid, const char* filename,
448 bool tailOnly)
449{
450 bool first = true;
451
452 /* circular buffer, for "tailOnly" mode */
453 const int kShortLogMaxLines = 5;
454 const int kShortLogLineLen = 256;
455 char shortLog[kShortLogMaxLines][kShortLogLineLen];
456 int shortLogCount = 0;
457 int shortLogNext = 0;
458
459 int logfd = open(filename, O_RDONLY | O_NONBLOCK);
460 if (logfd < 0) {
461 XLOG("Unable to open %s: %s\n", filename, strerror(errno));
462 return;
463 }
464
465 union {
466 unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1];
467 struct logger_entry entry;
468 } log_entry;
469
470 while (true) {
471 ssize_t actual = read(logfd, log_entry.buf, LOGGER_ENTRY_MAX_LEN);
472 if (actual < 0) {
473 if (errno == EINTR) {
474 /* interrupted by signal, retry */
475 continue;
476 } else if (errno == EAGAIN) {
477 /* non-blocking EOF; we're done */
478 break;
479 } else {
480 _LOG(log, true, "Error while reading log: %s\n",
481 strerror(errno));
482 break;
483 }
484 } else if (actual == 0) {
485 _LOG(log, true, "Got zero bytes while reading log: %s\n",
486 strerror(errno));
487 break;
488 }
489
490 /*
491 * NOTE: if you XLOG something here, this will spin forever,
492 * because you will be writing as fast as you're reading. Any
493 * high-frequency debug diagnostics should just be written to
494 * the tombstone file.
495 */
496
497 struct logger_entry* entry = &log_entry.entry;
498
499 if (entry->pid != (int32_t) pid) {
500 /* wrong pid, ignore */
501 continue;
502 }
503
504 if (first) {
505 _LOG(log, true, "--------- %slog %s\n",
506 tailOnly ? "tail end of " : "", filename);
507 first = false;
508 }
509
510 /*
511 * Msg format is: <priority:1><tag:N>\0<message:N>\0
512 *
513 * We want to display it in the same format as "logcat -v threadtime"
514 * (although in this case the pid is redundant).
515 *
516 * TODO: scan for line breaks ('\n') and display each text line
517 * on a separate line, prefixed with the header, like logcat does.
518 */
519 static const char* kPrioChars = "!.VDIWEFS";
520 unsigned char prio = entry->msg[0];
521 char* tag = entry->msg + 1;
522 char* msg = tag + strlen(tag) + 1;
523
524 /* consume any trailing newlines */
525 char* eatnl = msg + strlen(msg) - 1;
526 while (eatnl >= msg && *eatnl == '\n') {
527 *eatnl-- = '\0';
528 }
529
530 char prioChar = (prio < strlen(kPrioChars) ? kPrioChars[prio] : '?');
531
532 char timeBuf[32];
533 time_t sec = (time_t) entry->sec;
534 struct tm tmBuf;
535 struct tm* ptm;
536 ptm = localtime_r(&sec, &tmBuf);
537 strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
538
539 if (tailOnly) {
540 snprintf(shortLog[shortLogNext], kShortLogLineLen,
541 "%s.%03d %5d %5d %c %-8s: %s",
542 timeBuf, entry->nsec / 1000000, entry->pid, entry->tid,
543 prioChar, tag, msg);
544 shortLogNext = (shortLogNext + 1) % kShortLogMaxLines;
545 shortLogCount++;
546 } else {
547 _LOG(log, true, "%s.%03d %5d %5d %c %-8s: %s\n",
548 timeBuf, entry->nsec / 1000000, entry->pid, entry->tid,
549 prioChar, tag, msg);
550 }
551 }
552
553 if (tailOnly) {
554 int i;
555
556 /*
557 * If we filled the buffer, we want to start at "next", which has
558 * the oldest entry. If we didn't, we want to start at zero.
559 */
560 if (shortLogCount < kShortLogMaxLines) {
561 shortLogNext = 0;
562 } else {
563 shortLogCount = kShortLogMaxLines; /* cap at window size */
564 }
565
566 for (i = 0; i < shortLogCount; i++) {
567 _LOG(log, true, "%s\n", shortLog[shortLogNext]);
568 shortLogNext = (shortLogNext + 1) % kShortLogMaxLines;
569 }
570 }
571
572 close(logfd);
573}
574
575/*
576 * Dumps the logs generated by the specified pid to the tombstone, from both
577 * "system" and "main" log devices. Ideally we'd interleave the output.
578 */
579static void dump_logs(log_t* log, pid_t pid, bool tailOnly)
580{
581 dump_log_file(log, pid, "/dev/log/system", tailOnly);
582 dump_log_file(log, pid, "/dev/log/main", tailOnly);
583}
584
585/*
586 * Dumps all information about the specified pid to the tombstone.
587 */
588static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal,
589 bool dump_sibling_threads, int* total_sleep_time_usec)
590{
591 /* don't copy log messages to tombstone unless this is a dev device */
592 char value[PROPERTY_VALUE_MAX];
593 property_get("ro.debuggable", value, "0");
594 bool want_logs = (value[0] == '1');
595
596 _LOG(log, false,
597 "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
598 dump_build_info(log);
599 dump_thread_info(log, pid, tid, true);
600 if(signal) {
601 dump_fault_addr(log, tid, signal);
602 }
603
604 ptrace_context_t* context = load_ptrace_context(tid);
605 dump_thread(context, log, tid, true, total_sleep_time_usec);
606
607 if (want_logs) {
608 dump_logs(log, pid, true);
609 }
610
611 bool detach_failed = false;
612 if (dump_sibling_threads) {
613 detach_failed = dump_sibling_thread_report(context, log, pid, tid, total_sleep_time_usec);
614 }
615
616 free_ptrace_context(context);
617
618 if (want_logs) {
619 dump_logs(log, pid, false);
620 }
621 return detach_failed;
622}
623
624/*
625 * find_and_open_tombstone - find an available tombstone slot, if any, of the
626 * form tombstone_XX where XX is 00 to MAX_TOMBSTONES-1, inclusive. If no
627 * file is available, we reuse the least-recently-modified file.
628 *
629 * Returns the path of the tombstone file, allocated using malloc(). Caller must free() it.
630 */
631static char* find_and_open_tombstone(int* fd)
632{
633 unsigned long mtime = ULONG_MAX;
634 struct stat sb;
635
636 /*
637 * XXX: Our stat.st_mtime isn't time_t. If it changes, as it probably ought
638 * to, our logic breaks. This check will generate a warning if that happens.
639 */
640 typecheck(mtime, sb.st_mtime);
641
642 /*
643 * In a single wolf-like pass, find an available slot and, in case none
644 * exist, find and record the least-recently-modified file.
645 */
646 char path[128];
647 int oldest = 0;
648 for (int i = 0; i < MAX_TOMBSTONES; i++) {
649 snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", i);
650
651 if (!stat(path, &sb)) {
652 if (sb.st_mtime < mtime) {
653 oldest = i;
654 mtime = sb.st_mtime;
655 }
656 continue;
657 }
658 if (errno != ENOENT)
659 continue;
660
661 *fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0600);
662 if (*fd < 0)
663 continue; /* raced ? */
664
665 fchown(*fd, AID_SYSTEM, AID_SYSTEM);
666 return strdup(path);
667 }
668
669 /* we didn't find an available file, so we clobber the oldest one */
670 snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", oldest);
671 *fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
672 if (*fd < 0) {
673 LOG("failed to open tombstone file '%s': %s\n", path, strerror(errno));
674 return NULL;
675 }
676 fchown(*fd, AID_SYSTEM, AID_SYSTEM);
677 return strdup(path);
678}
679
680char* engrave_tombstone(pid_t pid, pid_t tid, int signal,
681 bool dump_sibling_threads, bool quiet, bool* detach_failed,
682 int* total_sleep_time_usec) {
683 mkdir(TOMBSTONE_DIR, 0755);
684 chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM);
685
rpcraigf1186f32012-07-19 09:38:06 -0400686 if (selinux_android_restorecon(TOMBSTONE_DIR) == -1) {
687 *detach_failed = false;
688 return NULL;
689 }
rpcraigf1186f32012-07-19 09:38:06 -0400690
Jeff Brown053b8652012-06-06 16:25:03 -0700691 int fd;
692 char* path = find_and_open_tombstone(&fd);
693 if (!path) {
694 *detach_failed = false;
695 return NULL;
696 }
697
698 log_t log;
699 log.tfd = fd;
700 log.quiet = quiet;
701 *detach_failed = dump_crash(&log, pid, tid, signal, dump_sibling_threads,
702 total_sleep_time_usec);
703
704 close(fd);
705 return path;
706}