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