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