tombstone.c revision f1186f3e980f5ebcc3380d8a7f746bf8a45e3531
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    case SIGSTKFLT:  return "SIGSTKFLT";
80    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     */
343    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) {
348            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;
408    struct dirent debuf;
409    struct dirent *de;
410    while (!readdir_r(d, &debuf, &de) && de) {
411        /* Ignore "." and ".." */
412        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
413            continue;
414        }
415
416        /* The main thread at fault has been handled individually */
417        char* end;
418        pid_t new_tid = strtoul(de->d_name, &end, 10);
419        if (*end || new_tid == tid) {
420            continue;
421        }
422
423        /* Skip this thread if cannot ptrace it */
424        if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0) {
425            continue;
426        }
427
428        _LOG(log, true, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
429        dump_thread_info(log, pid, new_tid, false);
430        dump_thread(context, log, new_tid, false, total_sleep_time_usec);
431
432        if (ptrace(PTRACE_DETACH, new_tid, 0, 0) != 0) {
433            LOG("ptrace detach from %d failed: %s\n", new_tid, strerror(errno));
434            detach_failed = true;
435        }
436    }
437
438    closedir(d);
439    return detach_failed;
440}
441
442/*
443 * Reads the contents of the specified log device, filters out the entries
444 * that don't match the specified pid, and writes them to the tombstone file.
445 *
446 * If "tailOnly" is set, we only print the last few lines.
447 */
448static void dump_log_file(log_t* log, pid_t pid, const char* filename,
449    bool tailOnly)
450{
451    bool first = true;
452
453    /* circular buffer, for "tailOnly" mode */
454    const int kShortLogMaxLines = 5;
455    const int kShortLogLineLen = 256;
456    char shortLog[kShortLogMaxLines][kShortLogLineLen];
457    int shortLogCount = 0;
458    int shortLogNext = 0;
459
460    int logfd = open(filename, O_RDONLY | O_NONBLOCK);
461    if (logfd < 0) {
462        XLOG("Unable to open %s: %s\n", filename, strerror(errno));
463        return;
464    }
465
466    union {
467        unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1];
468        struct logger_entry entry;
469    } log_entry;
470
471    while (true) {
472        ssize_t actual = read(logfd, log_entry.buf, LOGGER_ENTRY_MAX_LEN);
473        if (actual < 0) {
474            if (errno == EINTR) {
475                /* interrupted by signal, retry */
476                continue;
477            } else if (errno == EAGAIN) {
478                /* non-blocking EOF; we're done */
479                break;
480            } else {
481                _LOG(log, true, "Error while reading log: %s\n",
482                    strerror(errno));
483                break;
484            }
485        } else if (actual == 0) {
486            _LOG(log, true, "Got zero bytes while reading log: %s\n",
487                strerror(errno));
488            break;
489        }
490
491        /*
492         * NOTE: if you XLOG something here, this will spin forever,
493         * because you will be writing as fast as you're reading.  Any
494         * high-frequency debug diagnostics should just be written to
495         * the tombstone file.
496         */
497
498        struct logger_entry* entry = &log_entry.entry;
499
500        if (entry->pid != (int32_t) pid) {
501            /* wrong pid, ignore */
502            continue;
503        }
504
505        if (first) {
506            _LOG(log, true, "--------- %slog %s\n",
507                tailOnly ? "tail end of " : "", filename);
508            first = false;
509        }
510
511        /*
512         * Msg format is: <priority:1><tag:N>\0<message:N>\0
513         *
514         * We want to display it in the same format as "logcat -v threadtime"
515         * (although in this case the pid is redundant).
516         *
517         * TODO: scan for line breaks ('\n') and display each text line
518         * on a separate line, prefixed with the header, like logcat does.
519         */
520        static const char* kPrioChars = "!.VDIWEFS";
521        unsigned char prio = entry->msg[0];
522        char* tag = entry->msg + 1;
523        char* msg = tag + strlen(tag) + 1;
524
525        /* consume any trailing newlines */
526        char* eatnl = msg + strlen(msg) - 1;
527        while (eatnl >= msg && *eatnl == '\n') {
528            *eatnl-- = '\0';
529        }
530
531        char prioChar = (prio < strlen(kPrioChars) ? kPrioChars[prio] : '?');
532
533        char timeBuf[32];
534        time_t sec = (time_t) entry->sec;
535        struct tm tmBuf;
536        struct tm* ptm;
537        ptm = localtime_r(&sec, &tmBuf);
538        strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
539
540        if (tailOnly) {
541            snprintf(shortLog[shortLogNext], kShortLogLineLen,
542                "%s.%03d %5d %5d %c %-8s: %s",
543                timeBuf, entry->nsec / 1000000, entry->pid, entry->tid,
544                prioChar, tag, msg);
545            shortLogNext = (shortLogNext + 1) % kShortLogMaxLines;
546            shortLogCount++;
547        } else {
548            _LOG(log, true, "%s.%03d %5d %5d %c %-8s: %s\n",
549                timeBuf, entry->nsec / 1000000, entry->pid, entry->tid,
550                prioChar, tag, msg);
551        }
552    }
553
554    if (tailOnly) {
555        int i;
556
557        /*
558         * If we filled the buffer, we want to start at "next", which has
559         * the oldest entry.  If we didn't, we want to start at zero.
560         */
561        if (shortLogCount < kShortLogMaxLines) {
562            shortLogNext = 0;
563        } else {
564            shortLogCount = kShortLogMaxLines;  /* cap at window size */
565        }
566
567        for (i = 0; i < shortLogCount; i++) {
568            _LOG(log, true, "%s\n", shortLog[shortLogNext]);
569            shortLogNext = (shortLogNext + 1) % kShortLogMaxLines;
570        }
571    }
572
573    close(logfd);
574}
575
576/*
577 * Dumps the logs generated by the specified pid to the tombstone, from both
578 * "system" and "main" log devices.  Ideally we'd interleave the output.
579 */
580static void dump_logs(log_t* log, pid_t pid, bool tailOnly)
581{
582    dump_log_file(log, pid, "/dev/log/system", tailOnly);
583    dump_log_file(log, pid, "/dev/log/main", tailOnly);
584}
585
586/*
587 * Dumps all information about the specified pid to the tombstone.
588 */
589static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal,
590        bool dump_sibling_threads, int* total_sleep_time_usec)
591{
592    /* don't copy log messages to tombstone unless this is a dev device */
593    char value[PROPERTY_VALUE_MAX];
594    property_get("ro.debuggable", value, "0");
595    bool want_logs = (value[0] == '1');
596
597    _LOG(log, false,
598            "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
599    dump_build_info(log);
600    dump_thread_info(log, pid, tid, true);
601    if(signal) {
602        dump_fault_addr(log, tid, signal);
603    }
604
605    ptrace_context_t* context = load_ptrace_context(tid);
606    dump_thread(context, log, tid, true, total_sleep_time_usec);
607
608    if (want_logs) {
609        dump_logs(log, pid, true);
610    }
611
612    bool detach_failed = false;
613    if (dump_sibling_threads) {
614        detach_failed = dump_sibling_thread_report(context, log, pid, tid, total_sleep_time_usec);
615    }
616
617    free_ptrace_context(context);
618
619    if (want_logs) {
620        dump_logs(log, pid, false);
621    }
622    return detach_failed;
623}
624
625/*
626 * find_and_open_tombstone - find an available tombstone slot, if any, of the
627 * form tombstone_XX where XX is 00 to MAX_TOMBSTONES-1, inclusive. If no
628 * file is available, we reuse the least-recently-modified file.
629 *
630 * Returns the path of the tombstone file, allocated using malloc().  Caller must free() it.
631 */
632static char* find_and_open_tombstone(int* fd)
633{
634    unsigned long mtime = ULONG_MAX;
635    struct stat sb;
636
637    /*
638     * XXX: Our stat.st_mtime isn't time_t. If it changes, as it probably ought
639     * to, our logic breaks. This check will generate a warning if that happens.
640     */
641    typecheck(mtime, sb.st_mtime);
642
643    /*
644     * In a single wolf-like pass, find an available slot and, in case none
645     * exist, find and record the least-recently-modified file.
646     */
647    char path[128];
648    int oldest = 0;
649    for (int i = 0; i < MAX_TOMBSTONES; i++) {
650        snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", i);
651
652        if (!stat(path, &sb)) {
653            if (sb.st_mtime < mtime) {
654                oldest = i;
655                mtime = sb.st_mtime;
656            }
657            continue;
658        }
659        if (errno != ENOENT)
660            continue;
661
662        *fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0600);
663        if (*fd < 0)
664            continue;   /* raced ? */
665
666        fchown(*fd, AID_SYSTEM, AID_SYSTEM);
667        return strdup(path);
668    }
669
670    /* we didn't find an available file, so we clobber the oldest one */
671    snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", oldest);
672    *fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
673    if (*fd < 0) {
674        LOG("failed to open tombstone file '%s': %s\n", path, strerror(errno));
675        return NULL;
676    }
677    fchown(*fd, AID_SYSTEM, AID_SYSTEM);
678    return strdup(path);
679}
680
681char* engrave_tombstone(pid_t pid, pid_t tid, int signal,
682        bool dump_sibling_threads, bool quiet, bool* detach_failed,
683        int* total_sleep_time_usec) {
684    mkdir(TOMBSTONE_DIR, 0755);
685    chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM);
686
687#ifdef HAVE_SELINUX
688    if (selinux_android_restorecon(TOMBSTONE_DIR) == -1) {
689        *detach_failed = false;
690        return NULL;
691    }
692#endif
693
694    int fd;
695    char* path = find_and_open_tombstone(&fd);
696    if (!path) {
697        *detach_failed = false;
698        return NULL;
699    }
700
701    log_t log;
702    log.tfd = fd;
703    log.quiet = quiet;
704    *detach_failed = dump_crash(&log, pid, tid, signal, dump_sibling_threads,
705            total_sleep_time_usec);
706
707    close(fd);
708    return path;
709}
710