Merge "Enable tracing on user builds" into jb-dev
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk
index f127363..2a516fb 100644
--- a/debuggerd/Android.mk
+++ b/debuggerd/Android.mk
@@ -5,9 +5,15 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES:= debuggerd.c utility.c getevent.c $(TARGET_ARCH)/machine.c
+LOCAL_SRC_FILES:= \
+	backtrace.c \
+	debuggerd.c \
+	getevent.c \
+	tombstone.c \
+	utility.c \
+	$(TARGET_ARCH)/machine.c
 
-LOCAL_CFLAGS := -Wall -Werror -std=gnu99
+LOCAL_CFLAGS := -Wall -Wno-unused-parameter -std=gnu99
 LOCAL_MODULE := debuggerd
 
 ifeq ($(ARCH_ARM_HAVE_VFP),true)
diff --git a/debuggerd/arm/machine.c b/debuggerd/arm/machine.c
index ca45c9b..1c2e13f 100644
--- a/debuggerd/arm/machine.c
+++ b/debuggerd/arm/machine.c
@@ -15,23 +15,17 @@
 ** limitations under the License.
 */
 
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
 #include <stdio.h>
 #include <errno.h>
-#include <signal.h>
-#include <pthread.h>
-#include <fcntl.h>
 #include <sys/types.h>
-#include <dirent.h>
-
 #include <sys/ptrace.h>
-#include <sys/wait.h>
-#include <sys/exec_elf.h>
-#include <sys/stat.h>
 
-#include <cutils/sockets.h>
-#include <cutils/properties.h>
+#include <corkscrew/ptrace.h>
 
-#include <linux/input.h>
 #include <linux/user.h>
 
 #include "../utility.h"
@@ -48,11 +42,72 @@
 #endif
 #endif
 
+static void dump_memory(log_t* log, pid_t tid, uintptr_t addr, bool at_fault) {
+    char code_buffer[64];       /* actual 8+1+((8+1)*4) + 1 == 45 */
+    char ascii_buffer[32];      /* actual 16 + 1 == 17 */
+    uintptr_t p, end;
+
+    p = addr & ~3;
+    p -= 32;
+    if (p > addr) {
+        /* catch underflow */
+        p = 0;
+    }
+    end = p + 80;
+    /* catch overflow; 'end - p' has to be multiples of 16 */
+    while (end < p)
+        end -= 16;
+
+    /* Dump the code around PC as:
+     *  addr     contents                             ascii
+     *  00008d34 ef000000 e8bd0090 e1b00000 512fff1e  ............../Q
+     *  00008d44 ea00b1f9 e92d0090 e3a070fc ef000000  ......-..p......
+     */
+    while (p < end) {
+        char* asc_out = ascii_buffer;
+
+        sprintf(code_buffer, "%08x ", p);
+
+        int i;
+        for (i = 0; i < 4; i++) {
+            /*
+             * If we see (data == -1 && errno != 0), we know that the ptrace
+             * call failed, probably because we're dumping memory in an
+             * unmapped or inaccessible page.  I don't know if there's
+             * value in making that explicit in the output -- it likely
+             * just complicates parsing and clarifies nothing for the
+             * enlightened reader.
+             */
+            long data = ptrace(PTRACE_PEEKTEXT, tid, (void*)p, NULL);
+            sprintf(code_buffer + strlen(code_buffer), "%08lx ", data);
+
+            int j;
+            for (j = 0; j < 4; j++) {
+                /*
+                 * Our isprint() allows high-ASCII characters that display
+                 * differently (often badly) in different viewers, so we
+                 * just use a simpler test.
+                 */
+                char val = (data >> (j*8)) & 0xff;
+                if (val >= 0x20 && val < 0x7f) {
+                    *asc_out++ = val;
+                } else {
+                    *asc_out++ = '.';
+                }
+            }
+            p += 4;
+        }
+        *asc_out = '\0';
+        _LOG(log, !at_fault, "    %s %s\n", code_buffer, ascii_buffer);
+    }
+}
+
 /*
  * If configured to do so, dump memory around *all* registers
  * for the crashing thread.
  */
-static void dump_memory_and_code(int tfd, pid_t tid, bool at_fault) {
+void dump_memory_and_code(const ptrace_context_t* context __attribute((unused)),
+        log_t* log, pid_t tid, bool at_fault) {
     struct pt_regs regs;
     if(ptrace(PTRACE_GETREGS, tid, 0, &regs)) {
         return;
@@ -73,38 +128,38 @@
                 continue;
             }
 
-            _LOG(tfd, false, "\nmemory near %.2s:\n", &REG_NAMES[reg * 2]);
-            dump_memory(tfd, tid, addr, at_fault);
+            _LOG(log, false, "\nmemory near %.2s:\n", &REG_NAMES[reg * 2]);
+            dump_memory(log, tid, addr, at_fault);
         }
     }
 
-    _LOG(tfd, !at_fault, "\ncode around pc:\n");
-    dump_memory(tfd, tid, (uintptr_t)regs.ARM_pc, at_fault);
+    _LOG(log, !at_fault, "\ncode around pc:\n");
+    dump_memory(log, tid, (uintptr_t)regs.ARM_pc, at_fault);
 
     if (regs.ARM_pc != regs.ARM_lr) {
-        _LOG(tfd, !at_fault, "\ncode around lr:\n");
-        dump_memory(tfd, tid, (uintptr_t)regs.ARM_lr, at_fault);
+        _LOG(log, !at_fault, "\ncode around lr:\n");
+        dump_memory(log, tid, (uintptr_t)regs.ARM_lr, at_fault);
     }
 }
 
 void dump_registers(const ptrace_context_t* context __attribute((unused)),
-        int tfd, pid_t tid, bool at_fault)
+        log_t* log, pid_t tid, bool at_fault)
 {
     struct pt_regs r;
     bool only_in_tombstone = !at_fault;
 
     if(ptrace(PTRACE_GETREGS, tid, 0, &r)) {
-        _LOG(tfd, only_in_tombstone, "cannot get registers: %s\n", strerror(errno));
+        _LOG(log, only_in_tombstone, "cannot get registers: %s\n", strerror(errno));
         return;
     }
 
-    _LOG(tfd, only_in_tombstone, "    r0 %08x  r1 %08x  r2 %08x  r3 %08x\n",
+    _LOG(log, only_in_tombstone, "    r0 %08x  r1 %08x  r2 %08x  r3 %08x\n",
             (uint32_t)r.ARM_r0, (uint32_t)r.ARM_r1, (uint32_t)r.ARM_r2, (uint32_t)r.ARM_r3);
-    _LOG(tfd, only_in_tombstone, "    r4 %08x  r5 %08x  r6 %08x  r7 %08x\n",
+    _LOG(log, only_in_tombstone, "    r4 %08x  r5 %08x  r6 %08x  r7 %08x\n",
             (uint32_t)r.ARM_r4, (uint32_t)r.ARM_r5, (uint32_t)r.ARM_r6, (uint32_t)r.ARM_r7);
-    _LOG(tfd, only_in_tombstone, "    r8 %08x  r9 %08x  sl %08x  fp %08x\n",
+    _LOG(log, only_in_tombstone, "    r8 %08x  r9 %08x  sl %08x  fp %08x\n",
             (uint32_t)r.ARM_r8, (uint32_t)r.ARM_r9, (uint32_t)r.ARM_r10, (uint32_t)r.ARM_fp);
-    _LOG(tfd, only_in_tombstone, "    ip %08x  sp %08x  lr %08x  pc %08x  cpsr %08x\n",
+    _LOG(log, only_in_tombstone, "    ip %08x  sp %08x  lr %08x  pc %08x  cpsr %08x\n",
             (uint32_t)r.ARM_ip, (uint32_t)r.ARM_sp, (uint32_t)r.ARM_lr,
             (uint32_t)r.ARM_pc, (uint32_t)r.ARM_cpsr);
 
@@ -113,25 +168,14 @@
     int i;
 
     if(ptrace(PTRACE_GETVFPREGS, tid, 0, &vfp_regs)) {
-        _LOG(tfd, only_in_tombstone, "cannot get registers: %s\n", strerror(errno));
+        _LOG(log, only_in_tombstone, "cannot get registers: %s\n", strerror(errno));
         return;
     }
 
     for (i = 0; i < NUM_VFP_REGS; i += 2) {
-        _LOG(tfd, only_in_tombstone, "    d%-2d %016llx  d%-2d %016llx\n",
+        _LOG(log, only_in_tombstone, "    d%-2d %016llx  d%-2d %016llx\n",
                 i, vfp_regs.fpregs[i], i+1, vfp_regs.fpregs[i+1]);
     }
-    _LOG(tfd, only_in_tombstone, "    scr %08lx\n", vfp_regs.fpscr);
+    _LOG(log, only_in_tombstone, "    scr %08lx\n", vfp_regs.fpscr);
 #endif
 }
-
-void dump_thread(const ptrace_context_t* context, int tfd, pid_t tid, bool at_fault) {
-    dump_registers(context, tfd, tid, at_fault);
-
-    dump_backtrace_and_stack(context, tfd, tid, at_fault);
-
-    if (at_fault) {
-        dump_memory_and_code(tfd, tid, at_fault);
-        dump_nearby_maps(context, tfd, tid);
-    }
-}
diff --git a/debuggerd/backtrace.c b/debuggerd/backtrace.c
new file mode 100644
index 0000000..62f7f32
--- /dev/null
+++ b/debuggerd/backtrace.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+#include <limits.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ptrace.h>
+
+#include <corkscrew/backtrace.h>
+
+#include "tombstone.h"
+#include "utility.h"
+
+#define STACK_DEPTH 32
+
+static void dump_process_header(log_t* log, pid_t pid) {
+    char path[PATH_MAX];
+    char procnamebuf[1024];
+    char* procname = NULL;
+    FILE* fp;
+
+    snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
+    if ((fp = fopen(path, "r"))) {
+        procname = fgets(procnamebuf, sizeof(procnamebuf), fp);
+        fclose(fp);
+    }
+
+    time_t t = time(NULL);
+    struct tm tm;
+    localtime_r(&t, &tm);
+    char timestr[64];
+    strftime(timestr, sizeof(timestr), "%F %T", &tm);
+    _LOG(log, false, "\n\n----- pid %d at %s -----\n", pid, timestr);
+
+    if (procname) {
+        _LOG(log, false, "Cmd line: %s\n", procname);
+    }
+}
+
+static void dump_process_footer(log_t* log, pid_t pid) {
+    _LOG(log, false, "\n----- end %d -----\n", pid);
+}
+
+static void dump_thread(log_t* log, pid_t tid, ptrace_context_t* context, bool attached,
+        bool* detach_failed, int* total_sleep_time_usec) {
+    char path[PATH_MAX];
+    char threadnamebuf[1024];
+    char* threadname = NULL;
+    FILE* fp;
+
+    snprintf(path, sizeof(path), "/proc/%d/comm", tid);
+    if ((fp = fopen(path, "r"))) {
+        threadname = fgets(threadnamebuf, sizeof(threadnamebuf), fp);
+        fclose(fp);
+        if (threadname) {
+            size_t len = strlen(threadname);
+            if (len && threadname[len - 1] == '\n') {
+                threadname[len - 1] = '\0';
+            }
+        }
+    }
+
+    _LOG(log, false, "\n\"%s\" sysTid=%d\n", threadname ? threadname : "<unknown>", tid);
+
+    if (!attached && ptrace(PTRACE_ATTACH, tid, 0, 0) < 0) {
+        _LOG(log, false, "Could not attach to thread: %s\n", strerror(errno));
+        return;
+    }
+
+    wait_for_stop(tid, total_sleep_time_usec);
+
+    backtrace_frame_t backtrace[STACK_DEPTH];
+    ssize_t frames = unwind_backtrace_ptrace(tid, context, backtrace, 0, STACK_DEPTH);
+    if (frames <= 0) {
+        _LOG(log, false, "Could not obtain stack trace for thread.\n");
+    } else {
+        backtrace_symbol_t backtrace_symbols[STACK_DEPTH];
+        get_backtrace_symbols_ptrace(context, backtrace, frames, backtrace_symbols);
+        for (size_t i = 0; i < (size_t)frames; i++) {
+            char line[MAX_BACKTRACE_LINE_LENGTH];
+            format_backtrace_line(i, &backtrace[i], &backtrace_symbols[i],
+                    line, MAX_BACKTRACE_LINE_LENGTH);
+            _LOG(log, false, "  %s\n", line);
+        }
+        free_backtrace_symbols(backtrace_symbols, frames);
+    }
+
+    if (!attached && ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {
+        LOG("ptrace detach from %d failed: %s\n", tid, strerror(errno));
+        *detach_failed = true;
+    }
+}
+
+void dump_backtrace(int fd, pid_t pid, pid_t tid, bool* detach_failed,
+        int* total_sleep_time_usec) {
+    log_t log;
+    log.tfd = fd;
+    log.quiet = true;
+
+    ptrace_context_t* context = load_ptrace_context(tid);
+    dump_process_header(&log, pid);
+    dump_thread(&log, tid, context, true, detach_failed, total_sleep_time_usec);
+
+    char task_path[64];
+    snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
+    DIR* d = opendir(task_path);
+    if (d) {
+        struct dirent debuf;
+        struct dirent *de;
+        while (!readdir_r(d, &debuf, &de) && de) {
+            if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
+                continue;
+            }
+
+            char* end;
+            pid_t new_tid = strtoul(de->d_name, &end, 10);
+            if (*end || new_tid == tid) {
+                continue;
+            }
+
+            dump_thread(&log, new_tid, context, false, detach_failed, total_sleep_time_usec);
+        }
+        closedir(d);
+    }
+
+    dump_process_footer(&log, pid);
+    free_ptrace_context(context);
+}
diff --git a/debuggerd/backtrace.h b/debuggerd/backtrace.h
new file mode 100644
index 0000000..ec7d20f
--- /dev/null
+++ b/debuggerd/backtrace.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DEBUGGERD_BACKTRACE_H
+#define _DEBUGGERD_BACKTRACE_H
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include <corkscrew/ptrace.h>
+
+/* Dumps a backtrace using a format similar to what Dalvik uses so that the result
+ * can be intermixed in a bug report. */
+void dump_backtrace(int fd, pid_t pid, pid_t tid, bool* detach_failed,
+        int* total_sleep_time_usec);
+
+#endif // _DEBUGGERD_BACKTRACE_H
diff --git a/debuggerd/debuggerd.c b/debuggerd/debuggerd.c
index 662683a..8009631 100644
--- a/debuggerd/debuggerd.c
+++ b/debuggerd/debuggerd.c
@@ -23,6 +23,7 @@
 #include <fcntl.h>
 #include <sys/types.h>
 #include <dirent.h>
+#include <time.h>
 
 #include <sys/ptrace.h>
 #include <sys/wait.h>
@@ -34,6 +35,7 @@
 #include <cutils/logd.h>
 #include <cutils/logger.h>
 #include <cutils/properties.h>
+#include <cutils/debugger.h>
 
 #include <corkscrew/backtrace.h>
 
@@ -41,432 +43,16 @@
 
 #include <private/android_filesystem_config.h>
 
+#include "backtrace.h"
 #include "getevent.h"
-#include "machine.h"
+#include "tombstone.h"
 #include "utility.h"
 
-#define ANDROID_LOG_INFO 4
-
-static void dump_build_info(int tfd)
-{
-    char fingerprint[PROPERTY_VALUE_MAX];
-
-    property_get("ro.build.fingerprint", fingerprint, "unknown");
-
-    _LOG(tfd, false, "Build fingerprint: '%s'\n", fingerprint);
-}
-
-static const char *get_signame(int sig)
-{
-    switch(sig) {
-    case SIGILL:     return "SIGILL";
-    case SIGABRT:    return "SIGABRT";
-    case SIGBUS:     return "SIGBUS";
-    case SIGFPE:     return "SIGFPE";
-    case SIGSEGV:    return "SIGSEGV";
-    case SIGPIPE:    return "SIGPIPE";
-    case SIGSTKFLT:  return "SIGSTKFLT";
-    case SIGSTOP:    return "SIGSTOP";
-    default:         return "?";
-    }
-}
-
-static const char *get_sigcode(int signo, int code)
-{
-    switch (signo) {
-    case SIGILL:
-        switch (code) {
-        case ILL_ILLOPC: return "ILL_ILLOPC";
-        case ILL_ILLOPN: return "ILL_ILLOPN";
-        case ILL_ILLADR: return "ILL_ILLADR";
-        case ILL_ILLTRP: return "ILL_ILLTRP";
-        case ILL_PRVOPC: return "ILL_PRVOPC";
-        case ILL_PRVREG: return "ILL_PRVREG";
-        case ILL_COPROC: return "ILL_COPROC";
-        case ILL_BADSTK: return "ILL_BADSTK";
-        }
-        break;
-    case SIGBUS:
-        switch (code) {
-        case BUS_ADRALN: return "BUS_ADRALN";
-        case BUS_ADRERR: return "BUS_ADRERR";
-        case BUS_OBJERR: return "BUS_OBJERR";
-        }
-        break;
-    case SIGFPE:
-        switch (code) {
-        case FPE_INTDIV: return "FPE_INTDIV";
-        case FPE_INTOVF: return "FPE_INTOVF";
-        case FPE_FLTDIV: return "FPE_FLTDIV";
-        case FPE_FLTOVF: return "FPE_FLTOVF";
-        case FPE_FLTUND: return "FPE_FLTUND";
-        case FPE_FLTRES: return "FPE_FLTRES";
-        case FPE_FLTINV: return "FPE_FLTINV";
-        case FPE_FLTSUB: return "FPE_FLTSUB";
-        }
-        break;
-    case SIGSEGV:
-        switch (code) {
-        case SEGV_MAPERR: return "SEGV_MAPERR";
-        case SEGV_ACCERR: return "SEGV_ACCERR";
-        }
-        break;
-    }
-    return "?";
-}
-
-static void dump_fault_addr(int tfd, pid_t tid, int sig)
-{
-    siginfo_t si;
-
-    memset(&si, 0, sizeof(si));
-    if(ptrace(PTRACE_GETSIGINFO, tid, 0, &si)){
-        _LOG(tfd, false, "cannot get siginfo: %s\n", strerror(errno));
-    } else if (signal_has_address(sig)) {
-        _LOG(tfd, false, "signal %d (%s), code %d (%s), fault addr %08x\n",
-             sig, get_signame(sig),
-             si.si_code, get_sigcode(sig, si.si_code),
-             (uintptr_t) si.si_addr);
-    } else {
-        _LOG(tfd, false, "signal %d (%s), code %d (%s), fault addr --------\n",
-             sig, get_signame(sig), si.si_code, get_sigcode(sig, si.si_code));
-    }
-}
-
-static void dump_crash_banner(int tfd, pid_t pid, pid_t tid, int sig)
-{
-    char data[1024];
-    char *x = 0;
-    FILE *fp;
-
-    sprintf(data, "/proc/%d/cmdline", pid);
-    fp = fopen(data, "r");
-    if(fp) {
-        x = fgets(data, 1024, fp);
-        fclose(fp);
-    }
-
-    _LOG(tfd, false,
-            "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
-    dump_build_info(tfd);
-    _LOG(tfd, false, "pid: %d, tid: %d  >>> %s <<<\n",
-         pid, tid, x ? x : "UNKNOWN");
-
-    if(sig) {
-        dump_fault_addr(tfd, tid, sig);
-    }
-}
-
-/* Return true if some thread is not detached cleanly */
-static bool dump_sibling_thread_report(const ptrace_context_t* context,
-        int tfd, pid_t pid, pid_t tid) {
-    char task_path[64];
-    snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
-
-    DIR* d = opendir(task_path);
-    /* Bail early if cannot open the task directory */
-    if (d == NULL) {
-        XLOG("Cannot open /proc/%d/task\n", pid);
-        return false;
-    }
-
-    bool detach_failed = false;
-    struct dirent *de;
-    while ((de = readdir(d)) != NULL) {
-        pid_t new_tid;
-        /* Ignore "." and ".." */
-        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
-            continue;
-        }
-
-        new_tid = atoi(de->d_name);
-        /* The main thread at fault has been handled individually */
-        if (new_tid == tid) {
-            continue;
-        }
-
-        /* Skip this thread if cannot ptrace it */
-        if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0) {
-            continue;
-        }
-
-        _LOG(tfd, true, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
-        _LOG(tfd, true, "pid: %d, tid: %d\n", pid, new_tid);
-
-        dump_thread(context, tfd, new_tid, false);
-
-        if (ptrace(PTRACE_DETACH, new_tid, 0, 0) != 0) {
-            LOG("ptrace detach from %d failed: %s\n", new_tid, strerror(errno));
-            detach_failed = true;
-        }
-    }
-
-    closedir(d);
-    return detach_failed;
-}
-
-/*
- * Reads the contents of the specified log device, filters out the entries
- * that don't match the specified pid, and writes them to the tombstone file.
- *
- * If "tailOnly" is set, we only print the last few lines.
- */
-static void dump_log_file(int tfd, pid_t pid, const char* filename,
-    bool tailOnly)
-{
-    bool first = true;
-
-    /* circular buffer, for "tailOnly" mode */
-    const int kShortLogMaxLines = 5;
-    const int kShortLogLineLen = 256;
-    char shortLog[kShortLogMaxLines][kShortLogLineLen];
-    int shortLogCount = 0;
-    int shortLogNext = 0;
-
-    int logfd = open(filename, O_RDONLY | O_NONBLOCK);
-    if (logfd < 0) {
-        XLOG("Unable to open %s: %s\n", filename, strerror(errno));
-        return;
-    }
-
-    union {
-        unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1];
-        struct logger_entry entry;
-    } log_entry;
-
-    while (true) {
-        ssize_t actual = read(logfd, log_entry.buf, LOGGER_ENTRY_MAX_LEN);
-        if (actual < 0) {
-            if (errno == EINTR) {
-                /* interrupted by signal, retry */
-                continue;
-            } else if (errno == EAGAIN) {
-                /* non-blocking EOF; we're done */
-                break;
-            } else {
-                _LOG(tfd, true, "Error while reading log: %s\n",
-                    strerror(errno));
-                break;
-            }
-        } else if (actual == 0) {
-            _LOG(tfd, true, "Got zero bytes while reading log: %s\n",
-                strerror(errno));
-            break;
-        }
-
-        /*
-         * NOTE: if you XLOG something here, this will spin forever,
-         * because you will be writing as fast as you're reading.  Any
-         * high-frequency debug diagnostics should just be written to
-         * the tombstone file.
-         */
-
-        struct logger_entry* entry = &log_entry.entry;
-
-        if (entry->pid != (int32_t) pid) {
-            /* wrong pid, ignore */
-            continue;
-        }
-
-        if (first) {
-            _LOG(tfd, true, "--------- %slog %s\n",
-                tailOnly ? "tail end of " : "", filename);
-            first = false;
-        }
-
-        /*
-         * Msg format is: <priority:1><tag:N>\0<message:N>\0
-         *
-         * We want to display it in the same format as "logcat -v threadtime"
-         * (although in this case the pid is redundant).
-         *
-         * TODO: scan for line breaks ('\n') and display each text line
-         * on a separate line, prefixed with the header, like logcat does.
-         */
-        static const char* kPrioChars = "!.VDIWEFS";
-        unsigned char prio = entry->msg[0];
-        char* tag = entry->msg + 1;
-        char* msg = tag + strlen(tag) + 1;
-
-        /* consume any trailing newlines */
-        char* eatnl = msg + strlen(msg) - 1;
-        while (eatnl >= msg && *eatnl == '\n') {
-            *eatnl-- = '\0';
-        }
-
-        char prioChar = (prio < strlen(kPrioChars) ? kPrioChars[prio] : '?');
-
-        char timeBuf[32];
-        time_t sec = (time_t) entry->sec;
-        struct tm tmBuf;
-        struct tm* ptm;
-        ptm = localtime_r(&sec, &tmBuf);
-        strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
-
-        if (tailOnly) {
-            snprintf(shortLog[shortLogNext], kShortLogLineLen,
-                "%s.%03d %5d %5d %c %-8s: %s",
-                timeBuf, entry->nsec / 1000000, entry->pid, entry->tid,
-                prioChar, tag, msg);
-            shortLogNext = (shortLogNext + 1) % kShortLogMaxLines;
-            shortLogCount++;
-        } else {
-            _LOG(tfd, true, "%s.%03d %5d %5d %c %-8s: %s\n",
-                timeBuf, entry->nsec / 1000000, entry->pid, entry->tid,
-                prioChar, tag, msg);
-        }
-    }
-
-    if (tailOnly) {
-        int i;
-
-        /*
-         * If we filled the buffer, we want to start at "next", which has
-         * the oldest entry.  If we didn't, we want to start at zero.
-         */
-        if (shortLogCount < kShortLogMaxLines) {
-            shortLogNext = 0;
-        } else {
-            shortLogCount = kShortLogMaxLines;  /* cap at window size */
-        }
-
-        for (i = 0; i < shortLogCount; i++) {
-            _LOG(tfd, true, "%s\n", shortLog[shortLogNext]);
-            shortLogNext = (shortLogNext + 1) % kShortLogMaxLines;
-        }
-    }
-
-    close(logfd);
-}
-
-/*
- * Dumps the logs generated by the specified pid to the tombstone, from both
- * "system" and "main" log devices.  Ideally we'd interleave the output.
- */
-static void dump_logs(int tfd, pid_t pid, bool tailOnly)
-{
-    dump_log_file(tfd, pid, "/dev/log/system", tailOnly);
-    dump_log_file(tfd, pid, "/dev/log/main", tailOnly);
-}
-
-/*
- * Dumps all information about the specified pid to the tombstone.
- */
-static bool dump_crash(int tfd, pid_t pid, pid_t tid, int signal,
-        bool dump_sibling_threads)
-{
-    /* don't copy log messages to tombstone unless this is a dev device */
-    char value[PROPERTY_VALUE_MAX];
-    property_get("ro.debuggable", value, "0");
-    bool wantLogs = (value[0] == '1');
-
-    dump_crash_banner(tfd, pid, tid, signal);
-
-    ptrace_context_t* context = load_ptrace_context(tid);
-
-    dump_thread(context, tfd, tid, true);
-
-    if (wantLogs) {
-        dump_logs(tfd, pid, true);
-    }
-
-    bool detach_failed = false;
-    if (dump_sibling_threads) {
-        detach_failed = dump_sibling_thread_report(context, tfd, pid, tid);
-    }
-
-    free_ptrace_context(context);
-
-    if (wantLogs) {
-        dump_logs(tfd, pid, false);
-    }
-    return detach_failed;
-}
-
-#define MAX_TOMBSTONES	10
-
-#define typecheck(x,y) {    \
-    typeof(x) __dummy1;     \
-    typeof(y) __dummy2;     \
-    (void)(&__dummy1 == &__dummy2); }
-
-#define TOMBSTONE_DIR	"/data/tombstones"
-
-/*
- * find_and_open_tombstone - find an available tombstone slot, if any, of the
- * form tombstone_XX where XX is 00 to MAX_TOMBSTONES-1, inclusive. If no
- * file is available, we reuse the least-recently-modified file.
- *
- * Returns the path of the tombstone file, allocated using malloc().  Caller must free() it.
- */
-static char* find_and_open_tombstone(int* fd)
-{
-    unsigned long mtime = ULONG_MAX;
-    struct stat sb;
-
-    /*
-     * XXX: Our stat.st_mtime isn't time_t. If it changes, as it probably ought
-     * to, our logic breaks. This check will generate a warning if that happens.
-     */
-    typecheck(mtime, sb.st_mtime);
-
-    /*
-     * In a single wolf-like pass, find an available slot and, in case none
-     * exist, find and record the least-recently-modified file.
-     */
-    char path[128];
-    int oldest = 0;
-    for (int i = 0; i < MAX_TOMBSTONES; i++) {
-        snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", i);
-
-        if (!stat(path, &sb)) {
-            if (sb.st_mtime < mtime) {
-                oldest = i;
-                mtime = sb.st_mtime;
-            }
-            continue;
-        }
-        if (errno != ENOENT)
-            continue;
-
-        *fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0600);
-        if (*fd < 0)
-            continue;	/* raced ? */
-
-        fchown(*fd, AID_SYSTEM, AID_SYSTEM);
-        return strdup(path);
-    }
-
-    /* we didn't find an available file, so we clobber the oldest one */
-    snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", oldest);
-    *fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
-    if (*fd < 0) {
-        LOG("failed to open tombstone file '%s': %s\n", path, strerror(errno));
-        return NULL;
-    }
-    fchown(*fd, AID_SYSTEM, AID_SYSTEM);
-    return strdup(path);
-}
-
-/* Return true if some thread is not detached cleanly */
-static char* engrave_tombstone(pid_t pid, pid_t tid, int signal, bool dump_sibling_threads,
-        bool* detach_failed)
-{
-    mkdir(TOMBSTONE_DIR, 0755);
-    chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM);
-
-    int fd;
-    char* path = find_and_open_tombstone(&fd);
-    if (!path) {
-        *detach_failed = false;
-        return NULL;
-    }
-
-    *detach_failed = dump_crash(fd, pid, tid, signal, dump_sibling_threads);
-
-    close(fd);
-    return path;
-}
+typedef struct {
+    debugger_action_t action;
+    pid_t pid, tid;
+    uid_t uid, gid;
+} debugger_request_t;
 
 static int
 write_string(const char* file, const char* string)
@@ -598,50 +184,7 @@
     return fields == 7 ? 0 : -1;
 }
 
-static int wait_for_signal(pid_t tid, int* total_sleep_time_usec) {
-    const int sleep_time_usec = 200000;         /* 0.2 seconds */
-    const int max_total_sleep_usec = 3000000;   /* 3 seconds */
-    for (;;) {
-        int status;
-        pid_t n = waitpid(tid, &status, __WALL | WNOHANG);
-        if (n < 0) {
-            if(errno == EAGAIN) continue;
-            LOG("waitpid failed: %s\n", strerror(errno));
-            return -1;
-        } else if (n > 0) {
-            XLOG("waitpid: n=%d status=%08x\n", n, status);
-            if (WIFSTOPPED(status)) {
-                return WSTOPSIG(status);
-            } else {
-                LOG("unexpected waitpid response: n=%d, status=%08x\n", n, status);
-                return -1;
-            }
-        }
-
-        if (*total_sleep_time_usec > max_total_sleep_usec) {
-            LOG("timed out waiting for tid=%d to die\n", tid);
-            return -1;
-        }
-
-        /* not ready yet */
-        XLOG("not ready yet\n");
-        usleep(sleep_time_usec);
-        *total_sleep_time_usec += sleep_time_usec;
-    }
-}
-
-enum {
-    REQUEST_TYPE_CRASH,
-    REQUEST_TYPE_DUMP,
-};
-
-typedef struct {
-    int type;
-    pid_t pid, tid;
-    uid_t uid, gid;
-} request_t;
-
-static int read_request(int fd, request_t* out_request) {
+static int read_request(int fd, debugger_request_t* out_request) {
     struct ucred cr;
     int len = sizeof(cr);
     int status = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
@@ -663,20 +206,37 @@
         return -1;
     }
 
-    status = TEMP_FAILURE_RETRY(read(fd, &out_request->tid, sizeof(pid_t)));
+    debugger_msg_t msg;
+    status = TEMP_FAILURE_RETRY(read(fd, &msg, sizeof(msg)));
     if (status < 0) {
         LOG("read failure? %s\n", strerror(errno));
         return -1;
     }
-    if (status != sizeof(pid_t)) {
+    if (status != sizeof(msg)) {
         LOG("invalid crash request of size %d\n", status);
         return -1;
     }
 
-    if (out_request->tid < 0 && cr.uid == 0) {
-        /* Root can ask us to attach to any process and dump it explicitly. */
-        out_request->type = REQUEST_TYPE_DUMP;
-        out_request->tid = -out_request->tid;
+    out_request->action = msg.action;
+    out_request->tid = msg.tid;
+    out_request->pid = cr.pid;
+    out_request->uid = cr.uid;
+    out_request->gid = cr.gid;
+
+    if (msg.action == DEBUGGER_ACTION_CRASH) {
+        /* Ensure that the tid reported by the crashing process is valid. */
+        char buf[64];
+        struct stat s;
+        snprintf(buf, sizeof buf, "/proc/%d/task/%d", out_request->pid, out_request->tid);
+        if(stat(buf, &s)) {
+            LOG("tid %d does not exist in pid %d. ignoring debug request\n",
+                    out_request->tid, out_request->pid);
+            return -1;
+        }
+    } else if (cr.uid == 0
+            || (cr.uid == AID_SYSTEM && msg.action == DEBUGGER_ACTION_DUMP_BACKTRACE)) {
+        /* Only root or system can ask us to attach to any process and dump it explicitly.
+         * However, system is only allowed to collect backtraces but cannot dump tombstones. */
         status = get_process_info(out_request->tid, &out_request->pid,
                 &out_request->uid, &out_request->gid);
         if (status < 0) {
@@ -684,28 +244,15 @@
                     out_request->tid);
             return -1;
         }
-        return 0;
-    }
-
-    /* Ensure that the tid reported by the crashing process is valid. */
-    out_request->type = REQUEST_TYPE_CRASH;
-    out_request->pid = cr.pid;
-    out_request->uid = cr.uid;
-    out_request->gid = cr.gid;
-
-    char buf[64];
-    struct stat s;
-    snprintf(buf, sizeof buf, "/proc/%d/task/%d", out_request->pid, out_request->tid);
-    if(stat(buf, &s)) {
-        LOG("tid %d does not exist in pid %d. ignoring debug request\n",
-                out_request->tid, out_request->pid);
+    } else {
+        /* No one else is not allowed to dump arbitrary processes. */
         return -1;
     }
     return 0;
 }
 
-static bool should_attach_gdb(request_t* request) {
-    if (request->type == REQUEST_TYPE_CRASH) {
+static bool should_attach_gdb(debugger_request_t* request) {
+    if (request->action == DEBUGGER_ACTION_CRASH) {
         char value[PROPERTY_VALUE_MAX];
         property_get("debug.db.uid", value, "-1");
         int debug_uid = atoi(value);
@@ -717,7 +264,7 @@
 static void handle_request(int fd) {
     XLOG("handle_request(%d)\n", fd);
 
-    request_t request;
+    debugger_request_t request;
     int status = read_request(fd, &request);
     if (!status) {
         XLOG("BOOM: pid=%d uid=%d gid=%d tid=%d\n",
@@ -739,13 +286,12 @@
         } else {
             bool detach_failed = false;
             bool attach_gdb = should_attach_gdb(&request);
-            char response = 0;
-            if (TEMP_FAILURE_RETRY(write(fd, &response, 1)) != 1) {
+            if (TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) {
                 LOG("failed responding to client: %s\n", strerror(errno));
             } else {
                 char* tombstone_path = NULL;
 
-                if (request.type != REQUEST_TYPE_DUMP) {
+                if (request.action == DEBUGGER_ACTION_CRASH) {
                     close(fd);
                     fd = -1;
                 }
@@ -759,10 +305,15 @@
 
                     switch (signal) {
                     case SIGSTOP:
-                        if (request.type == REQUEST_TYPE_DUMP) {
-                            XLOG("stopped -- dumping\n");
+                        if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
+                            XLOG("stopped -- dumping to tombstone\n");
                             tombstone_path = engrave_tombstone(request.pid, request.tid,
-                                    signal, true, &detach_failed);
+                                    signal, true, true, &detach_failed,
+                                    &total_sleep_time_usec);
+                        } else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) {
+                            XLOG("stopped -- dumping to fd\n");
+                            dump_backtrace(fd, request.pid, request.tid, &detach_failed,
+                                    &total_sleep_time_usec);
                         } else {
                             XLOG("stopped -- continuing\n");
                             status = ptrace(PTRACE_CONT, request.tid, 0, 0);
@@ -791,7 +342,8 @@
                         /* don't dump sibling threads when attaching to GDB because it
                          * makes the process less reliable, apparently... */
                         tombstone_path = engrave_tombstone(request.pid, request.tid,
-                                signal, !attach_gdb, &detach_failed);
+                                signal, !attach_gdb, false, &detach_failed,
+                                &total_sleep_time_usec);
                         break;
                     }
 
@@ -803,7 +355,7 @@
                     break;
                 }
 
-                if (request.type == REQUEST_TYPE_DUMP) {
+                if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
                     if (tombstone_path) {
                         write(fd, tombstone_path, strlen(tombstone_path));
                     }
@@ -888,7 +440,7 @@
     act.sa_flags = SA_NOCLDWAIT;
     sigaction(SIGCHLD, &act, 0);
 
-    s = socket_local_server("android:debuggerd",
+    s = socket_local_server(DEBUGGER_SOCKET_NAME,
             ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
     if(s < 0) return 1;
     fcntl(s, F_SETFD, FD_CLOEXEC);
@@ -915,47 +467,56 @@
     return 0;
 }
 
-static int do_explicit_dump(pid_t tid) {
+static int do_explicit_dump(pid_t tid, bool dump_backtrace) {
     fprintf(stdout, "Sending request to dump task %d.\n", tid);
 
-    int fd = socket_local_client("android:debuggerd",
-            ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
-    if (fd < 0) {
-        fputs("Error opening local socket to debuggerd.\n", stderr);
-        return 1;
-    }
-
-    pid_t request = -tid;
-    write(fd, &request, sizeof(pid_t));
-    if (read(fd, &request, 1) != 1) {
-        /* did not get expected reply, debuggerd must have closed the socket */
-        fputs("Error sending request.  Did not receive reply from debuggerd.\n", stderr);
+    if (dump_backtrace) {
+        fflush(stdout);
+        if (dump_backtrace_to_file(tid, fileno(stdout)) < 0) {
+            fputs("Error dumping backtrace.\n", stderr);
+            return 1;
+        }
     } else {
         char tombstone_path[PATH_MAX];
-        ssize_t n = read(fd, &tombstone_path, sizeof(tombstone_path) - 1);
-        if (n <= 0) {
-            fputs("Error dumping process.  Check log for details.\n", stderr);
-        } else {
-            tombstone_path[n] = '\0';
-            fprintf(stderr, "Tombstone written to: %s\n", tombstone_path);
+        if (dump_tombstone(tid, tombstone_path, sizeof(tombstone_path)) < 0) {
+            fputs("Error dumping tombstone.\n", stderr);
+            return 1;
         }
+        fprintf(stderr, "Tombstone written to: %s\n", tombstone_path);
     }
-
-    close(fd);
     return 0;
 }
 
+static void usage() {
+    fputs("Usage: -b [<tid>]\n"
+            "  -b dump backtrace to console, otherwise dump full tombstone file\n"
+            "\n"
+            "If tid specified, sends a request to debuggerd to dump that task.\n"
+            "Otherwise, starts the debuggerd server.\n", stderr);
+}
+
 int main(int argc, char** argv) {
-    if (argc == 2) {
-        pid_t tid = atoi(argv[1]);
-        if (!tid) {
-            fputs("Usage: [<tid>]\n"
-                    "\n"
-                    "If tid specified, sends a request to debuggerd to dump that task.\n"
-                    "Otherwise, starts the debuggerd server.\n", stderr);
+    if (argc == 1) {
+        return do_server();
+    }
+
+    bool dump_backtrace = false;
+    bool have_tid = false;
+    pid_t tid = 0;
+    for (int i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], "-b")) {
+            dump_backtrace = true;
+        } else if (!have_tid) {
+            tid = atoi(argv[i]);
+            have_tid = true;
+        } else {
+            usage();
             return 1;
         }
-        return do_explicit_dump(tid);
     }
-    return do_server();
+    if (!have_tid) {
+        usage();
+        return 1;
+    }
+    return do_explicit_dump(tid, dump_backtrace);
 }
diff --git a/debuggerd/machine.h b/debuggerd/machine.h
index 6049b69..1619dd3 100644
--- a/debuggerd/machine.h
+++ b/debuggerd/machine.h
@@ -17,9 +17,15 @@
 #ifndef _DEBUGGERD_MACHINE_H
 #define _DEBUGGERD_MACHINE_H
 
-#include <corkscrew/backtrace.h>
+#include <stddef.h>
+#include <stdbool.h>
 #include <sys/types.h>
 
-void dump_thread(const ptrace_context_t* context, int tfd, pid_t tid, bool at_fault);
+#include <corkscrew/ptrace.h>
+
+#include "utility.h"
+
+void dump_memory_and_code(const ptrace_context_t* context, log_t* log, pid_t tid, bool at_fault);
+void dump_registers(const ptrace_context_t* context, log_t* log, pid_t tid, bool at_fault);
 
 #endif // _DEBUGGERD_MACHINE_H
diff --git a/debuggerd/tombstone.c b/debuggerd/tombstone.c
new file mode 100644
index 0000000..2e92846
--- /dev/null
+++ b/debuggerd/tombstone.c
@@ -0,0 +1,700 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <dirent.h>
+#include <time.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+
+#include <private/android_filesystem_config.h>
+
+#include <cutils/logger.h>
+#include <cutils/properties.h>
+
+#include <corkscrew/demangle.h>
+#include <corkscrew/backtrace.h>
+
+#include "machine.h"
+#include "tombstone.h"
+#include "utility.h"
+
+#define STACK_DEPTH 32
+#define STACK_WORDS 16
+
+#define MAX_TOMBSTONES  10
+#define TOMBSTONE_DIR   "/data/tombstones"
+
+#define typecheck(x,y) {    \
+    typeof(x) __dummy1;     \
+    typeof(y) __dummy2;     \
+    (void)(&__dummy1 == &__dummy2); }
+
+
+static bool signal_has_address(int sig) {
+    switch (sig) {
+        case SIGILL:
+        case SIGFPE:
+        case SIGSEGV:
+        case SIGBUS:
+            return true;
+        default:
+            return false;
+    }
+}
+
+static const char *get_signame(int sig)
+{
+    switch(sig) {
+    case SIGILL:     return "SIGILL";
+    case SIGABRT:    return "SIGABRT";
+    case SIGBUS:     return "SIGBUS";
+    case SIGFPE:     return "SIGFPE";
+    case SIGSEGV:    return "SIGSEGV";
+    case SIGPIPE:    return "SIGPIPE";
+    case SIGSTKFLT:  return "SIGSTKFLT";
+    case SIGSTOP:    return "SIGSTOP";
+    default:         return "?";
+    }
+}
+
+static const char *get_sigcode(int signo, int code)
+{
+    switch (signo) {
+    case SIGILL:
+        switch (code) {
+        case ILL_ILLOPC: return "ILL_ILLOPC";
+        case ILL_ILLOPN: return "ILL_ILLOPN";
+        case ILL_ILLADR: return "ILL_ILLADR";
+        case ILL_ILLTRP: return "ILL_ILLTRP";
+        case ILL_PRVOPC: return "ILL_PRVOPC";
+        case ILL_PRVREG: return "ILL_PRVREG";
+        case ILL_COPROC: return "ILL_COPROC";
+        case ILL_BADSTK: return "ILL_BADSTK";
+        }
+        break;
+    case SIGBUS:
+        switch (code) {
+        case BUS_ADRALN: return "BUS_ADRALN";
+        case BUS_ADRERR: return "BUS_ADRERR";
+        case BUS_OBJERR: return "BUS_OBJERR";
+        }
+        break;
+    case SIGFPE:
+        switch (code) {
+        case FPE_INTDIV: return "FPE_INTDIV";
+        case FPE_INTOVF: return "FPE_INTOVF";
+        case FPE_FLTDIV: return "FPE_FLTDIV";
+        case FPE_FLTOVF: return "FPE_FLTOVF";
+        case FPE_FLTUND: return "FPE_FLTUND";
+        case FPE_FLTRES: return "FPE_FLTRES";
+        case FPE_FLTINV: return "FPE_FLTINV";
+        case FPE_FLTSUB: return "FPE_FLTSUB";
+        }
+        break;
+    case SIGSEGV:
+        switch (code) {
+        case SEGV_MAPERR: return "SEGV_MAPERR";
+        case SEGV_ACCERR: return "SEGV_ACCERR";
+        }
+        break;
+    }
+    return "?";
+}
+
+static void dump_build_info(log_t* log)
+{
+    char fingerprint[PROPERTY_VALUE_MAX];
+
+    property_get("ro.build.fingerprint", fingerprint, "unknown");
+
+    _LOG(log, false, "Build fingerprint: '%s'\n", fingerprint);
+}
+
+static void dump_fault_addr(log_t* log, pid_t tid, int sig)
+{
+    siginfo_t si;
+
+    memset(&si, 0, sizeof(si));
+    if(ptrace(PTRACE_GETSIGINFO, tid, 0, &si)){
+        _LOG(log, false, "cannot get siginfo: %s\n", strerror(errno));
+    } else if (signal_has_address(sig)) {
+        _LOG(log, false, "signal %d (%s), code %d (%s), fault addr %08x\n",
+             sig, get_signame(sig),
+             si.si_code, get_sigcode(sig, si.si_code),
+             (uintptr_t) si.si_addr);
+    } else {
+        _LOG(log, false, "signal %d (%s), code %d (%s), fault addr --------\n",
+             sig, get_signame(sig), si.si_code, get_sigcode(sig, si.si_code));
+    }
+}
+
+static void dump_thread_info(log_t* log, pid_t pid, pid_t tid, bool at_fault) {
+    char path[64];
+    char threadnamebuf[1024];
+    char* threadname = NULL;
+    FILE *fp;
+
+    snprintf(path, sizeof(path), "/proc/%d/comm", tid);
+    if ((fp = fopen(path, "r"))) {
+        threadname = fgets(threadnamebuf, sizeof(threadnamebuf), fp);
+        fclose(fp);
+        if (threadname) {
+            size_t len = strlen(threadname);
+            if (len && threadname[len - 1] == '\n') {
+                threadname[len - 1] = '\0';
+            }
+        }
+    }
+
+    if (at_fault) {
+        char procnamebuf[1024];
+        char* procname = NULL;
+
+        snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
+        if ((fp = fopen(path, "r"))) {
+            procname = fgets(procnamebuf, sizeof(procnamebuf), fp);
+            fclose(fp);
+        }
+
+        _LOG(log, false, "pid: %d, tid: %d, name: %s  >>> %s <<<\n", pid, tid,
+                threadname ? threadname : "UNKNOWN",
+                procname ? procname : "UNKNOWN");
+    } else {
+        _LOG(log, true, "pid: %d, tid: %d, name: %s\n", pid, tid,
+                threadname ? threadname : "UNKNOWN");
+    }
+}
+
+static void dump_backtrace(const ptrace_context_t* context __attribute((unused)),
+        log_t* log, pid_t tid __attribute((unused)), bool at_fault,
+        const backtrace_frame_t* backtrace, size_t frames) {
+    _LOG(log, !at_fault, "\nbacktrace:\n");
+
+    backtrace_symbol_t backtrace_symbols[STACK_DEPTH];
+    get_backtrace_symbols_ptrace(context, backtrace, frames, backtrace_symbols);
+    for (size_t i = 0; i < frames; i++) {
+        char line[MAX_BACKTRACE_LINE_LENGTH];
+        format_backtrace_line(i, &backtrace[i], &backtrace_symbols[i],
+                line, MAX_BACKTRACE_LINE_LENGTH);
+        _LOG(log, !at_fault, "    %s\n", line);
+    }
+    free_backtrace_symbols(backtrace_symbols, frames);
+}
+
+static void dump_stack_segment(const ptrace_context_t* context, log_t* log, pid_t tid,
+        bool only_in_tombstone, uintptr_t* sp, size_t words, int label) {
+    for (size_t i = 0; i < words; i++) {
+        uint32_t stack_content;
+        if (!try_get_word_ptrace(tid, *sp, &stack_content)) {
+            break;
+        }
+
+        const map_info_t* mi;
+        const symbol_t* symbol;
+        find_symbol_ptrace(context, stack_content, &mi, &symbol);
+
+        if (symbol) {
+            char* demangled_name = demangle_symbol_name(symbol->name);
+            const char* symbol_name = demangled_name ? demangled_name : symbol->name;
+            uint32_t offset = stack_content - (mi->start + symbol->start);
+            if (!i && label >= 0) {
+                if (offset) {
+                    _LOG(log, only_in_tombstone, "    #%02d  %08x  %08x  %s (%s+%u)\n",
+                            label, *sp, stack_content, mi ? mi->name : "", symbol_name, offset);
+                } else {
+                    _LOG(log, only_in_tombstone, "    #%02d  %08x  %08x  %s (%s)\n",
+                            label, *sp, stack_content, mi ? mi->name : "", symbol_name);
+                }
+            } else {
+                if (offset) {
+                    _LOG(log, only_in_tombstone, "         %08x  %08x  %s (%s+%u)\n",
+                            *sp, stack_content, mi ? mi->name : "", symbol_name, offset);
+                } else {
+                    _LOG(log, only_in_tombstone, "         %08x  %08x  %s (%s)\n",
+                            *sp, stack_content, mi ? mi->name : "", symbol_name);
+                }
+            }
+            free(demangled_name);
+        } else {
+            if (!i && label >= 0) {
+                _LOG(log, only_in_tombstone, "    #%02d  %08x  %08x  %s\n",
+                        label, *sp, stack_content, mi ? mi->name : "");
+            } else {
+                _LOG(log, only_in_tombstone, "         %08x  %08x  %s\n",
+                        *sp, stack_content, mi ? mi->name : "");
+            }
+        }
+
+        *sp += sizeof(uint32_t);
+    }
+}
+
+static void dump_stack(const ptrace_context_t* context, log_t* log, pid_t tid, bool at_fault,
+        const backtrace_frame_t* backtrace, size_t frames) {
+    bool have_first = false;
+    size_t first, last;
+    for (size_t i = 0; i < frames; i++) {
+        if (backtrace[i].stack_top) {
+            if (!have_first) {
+                have_first = true;
+                first = i;
+            }
+            last = i;
+        }
+    }
+    if (!have_first) {
+        return;
+    }
+
+    _LOG(log, !at_fault, "\nstack:\n");
+
+    // Dump a few words before the first frame.
+    bool only_in_tombstone = !at_fault;
+    uintptr_t sp = backtrace[first].stack_top - STACK_WORDS * sizeof(uint32_t);
+    dump_stack_segment(context, log, tid, only_in_tombstone, &sp, STACK_WORDS, -1);
+
+    // Dump a few words from all successive frames.
+    // Only log the first 3 frames, put the rest in the tombstone.
+    for (size_t i = first; i <= last; i++) {
+        const backtrace_frame_t* frame = &backtrace[i];
+        if (sp != frame->stack_top) {
+            _LOG(log, only_in_tombstone, "         ........  ........\n");
+            sp = frame->stack_top;
+        }
+        if (i - first == 3) {
+            only_in_tombstone = true;
+        }
+        if (i == last) {
+            dump_stack_segment(context, log, tid, only_in_tombstone, &sp, STACK_WORDS, i);
+            if (sp < frame->stack_top + frame->stack_size) {
+                _LOG(log, only_in_tombstone, "         ........  ........\n");
+            }
+        } else {
+            size_t words = frame->stack_size / sizeof(uint32_t);
+            if (words == 0) {
+                words = 1;
+            } else if (words > STACK_WORDS) {
+                words = STACK_WORDS;
+            }
+            dump_stack_segment(context, log, tid, only_in_tombstone, &sp, words, i);
+        }
+    }
+}
+
+static void dump_backtrace_and_stack(const ptrace_context_t* context, log_t* log, pid_t tid,
+        bool at_fault) {
+    backtrace_frame_t backtrace[STACK_DEPTH];
+    ssize_t frames = unwind_backtrace_ptrace(tid, context, backtrace, 0, STACK_DEPTH);
+    if (frames > 0) {
+        dump_backtrace(context, log, tid, at_fault, backtrace, frames);
+        dump_stack(context, log, tid, at_fault, backtrace, frames);
+    }
+}
+
+static void dump_nearby_maps(const ptrace_context_t* context, log_t* log, pid_t tid) {
+    siginfo_t si;
+    memset(&si, 0, sizeof(si));
+    if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si)) {
+        _LOG(log, false, "cannot get siginfo for %d: %s\n",
+                tid, strerror(errno));
+        return;
+    }
+    if (!signal_has_address(si.si_signo)) {
+        return;
+    }
+
+    uintptr_t addr = (uintptr_t) si.si_addr;
+    addr &= ~0xfff;     /* round to 4K page boundary */
+    if (addr == 0) {    /* null-pointer deref */
+        return;
+    }
+
+    _LOG(log, false, "\nmemory map around fault addr %08x:\n", (int)si.si_addr);
+
+    /*
+     * Search for a match, or for a hole where the match would be.  The list
+     * is backward from the file content, so it starts at high addresses.
+     */
+    bool found = false;
+    map_info_t* map = context->map_info_list;
+    map_info_t *next = NULL;
+    map_info_t *prev = NULL;
+    while (map != NULL) {
+        if (addr >= map->start && addr < map->end) {
+            found = true;
+            next = map->next;
+            break;
+        } else if (addr >= map->end) {
+            /* map would be between "prev" and this entry */
+            next = map;
+            map = NULL;
+            break;
+        }
+
+        prev = map;
+        map = map->next;
+    }
+
+    /*
+     * Show "next" then "match" then "prev" so that the addresses appear in
+     * ascending order (like /proc/pid/maps).
+     */
+    if (next != NULL) {
+        _LOG(log, false, "    %08x-%08x %s\n", next->start, next->end, next->name);
+    } else {
+        _LOG(log, false, "    (no map below)\n");
+    }
+    if (map != NULL) {
+        _LOG(log, false, "    %08x-%08x %s\n", map->start, map->end, map->name);
+    } else {
+        _LOG(log, false, "    (no map for address)\n");
+    }
+    if (prev != NULL) {
+        _LOG(log, false, "    %08x-%08x %s\n", prev->start, prev->end, prev->name);
+    } else {
+        _LOG(log, false, "    (no map above)\n");
+    }
+}
+
+static void dump_thread(const ptrace_context_t* context, log_t* log, pid_t tid, bool at_fault,
+        int* total_sleep_time_usec) {
+    wait_for_stop(tid, total_sleep_time_usec);
+
+    dump_registers(context, log, tid, at_fault);
+    dump_backtrace_and_stack(context, log, tid, at_fault);
+    if (at_fault) {
+        dump_memory_and_code(context, log, tid, at_fault);
+        dump_nearby_maps(context, log, tid);
+    }
+}
+
+/* Return true if some thread is not detached cleanly */
+static bool dump_sibling_thread_report(const ptrace_context_t* context,
+        log_t* log, pid_t pid, pid_t tid, int* total_sleep_time_usec) {
+    char task_path[64];
+    snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
+
+    DIR* d = opendir(task_path);
+    /* Bail early if cannot open the task directory */
+    if (d == NULL) {
+        XLOG("Cannot open /proc/%d/task\n", pid);
+        return false;
+    }
+
+    bool detach_failed = false;
+    struct dirent debuf;
+    struct dirent *de;
+    while (!readdir_r(d, &debuf, &de) && de) {
+        /* Ignore "." and ".." */
+        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
+            continue;
+        }
+
+        /* The main thread at fault has been handled individually */
+        char* end;
+        pid_t new_tid = strtoul(de->d_name, &end, 10);
+        if (*end || new_tid == tid) {
+            continue;
+        }
+
+        /* Skip this thread if cannot ptrace it */
+        if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0) {
+            continue;
+        }
+
+        _LOG(log, true, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
+        dump_thread_info(log, pid, new_tid, false);
+        dump_thread(context, log, new_tid, false, total_sleep_time_usec);
+
+        if (ptrace(PTRACE_DETACH, new_tid, 0, 0) != 0) {
+            LOG("ptrace detach from %d failed: %s\n", new_tid, strerror(errno));
+            detach_failed = true;
+        }
+    }
+
+    closedir(d);
+    return detach_failed;
+}
+
+/*
+ * Reads the contents of the specified log device, filters out the entries
+ * that don't match the specified pid, and writes them to the tombstone file.
+ *
+ * If "tailOnly" is set, we only print the last few lines.
+ */
+static void dump_log_file(log_t* log, pid_t pid, const char* filename,
+    bool tailOnly)
+{
+    bool first = true;
+
+    /* circular buffer, for "tailOnly" mode */
+    const int kShortLogMaxLines = 5;
+    const int kShortLogLineLen = 256;
+    char shortLog[kShortLogMaxLines][kShortLogLineLen];
+    int shortLogCount = 0;
+    int shortLogNext = 0;
+
+    int logfd = open(filename, O_RDONLY | O_NONBLOCK);
+    if (logfd < 0) {
+        XLOG("Unable to open %s: %s\n", filename, strerror(errno));
+        return;
+    }
+
+    union {
+        unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1];
+        struct logger_entry entry;
+    } log_entry;
+
+    while (true) {
+        ssize_t actual = read(logfd, log_entry.buf, LOGGER_ENTRY_MAX_LEN);
+        if (actual < 0) {
+            if (errno == EINTR) {
+                /* interrupted by signal, retry */
+                continue;
+            } else if (errno == EAGAIN) {
+                /* non-blocking EOF; we're done */
+                break;
+            } else {
+                _LOG(log, true, "Error while reading log: %s\n",
+                    strerror(errno));
+                break;
+            }
+        } else if (actual == 0) {
+            _LOG(log, true, "Got zero bytes while reading log: %s\n",
+                strerror(errno));
+            break;
+        }
+
+        /*
+         * NOTE: if you XLOG something here, this will spin forever,
+         * because you will be writing as fast as you're reading.  Any
+         * high-frequency debug diagnostics should just be written to
+         * the tombstone file.
+         */
+
+        struct logger_entry* entry = &log_entry.entry;
+
+        if (entry->pid != (int32_t) pid) {
+            /* wrong pid, ignore */
+            continue;
+        }
+
+        if (first) {
+            _LOG(log, true, "--------- %slog %s\n",
+                tailOnly ? "tail end of " : "", filename);
+            first = false;
+        }
+
+        /*
+         * Msg format is: <priority:1><tag:N>\0<message:N>\0
+         *
+         * We want to display it in the same format as "logcat -v threadtime"
+         * (although in this case the pid is redundant).
+         *
+         * TODO: scan for line breaks ('\n') and display each text line
+         * on a separate line, prefixed with the header, like logcat does.
+         */
+        static const char* kPrioChars = "!.VDIWEFS";
+        unsigned char prio = entry->msg[0];
+        char* tag = entry->msg + 1;
+        char* msg = tag + strlen(tag) + 1;
+
+        /* consume any trailing newlines */
+        char* eatnl = msg + strlen(msg) - 1;
+        while (eatnl >= msg && *eatnl == '\n') {
+            *eatnl-- = '\0';
+        }
+
+        char prioChar = (prio < strlen(kPrioChars) ? kPrioChars[prio] : '?');
+
+        char timeBuf[32];
+        time_t sec = (time_t) entry->sec;
+        struct tm tmBuf;
+        struct tm* ptm;
+        ptm = localtime_r(&sec, &tmBuf);
+        strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
+
+        if (tailOnly) {
+            snprintf(shortLog[shortLogNext], kShortLogLineLen,
+                "%s.%03d %5d %5d %c %-8s: %s",
+                timeBuf, entry->nsec / 1000000, entry->pid, entry->tid,
+                prioChar, tag, msg);
+            shortLogNext = (shortLogNext + 1) % kShortLogMaxLines;
+            shortLogCount++;
+        } else {
+            _LOG(log, true, "%s.%03d %5d %5d %c %-8s: %s\n",
+                timeBuf, entry->nsec / 1000000, entry->pid, entry->tid,
+                prioChar, tag, msg);
+        }
+    }
+
+    if (tailOnly) {
+        int i;
+
+        /*
+         * If we filled the buffer, we want to start at "next", which has
+         * the oldest entry.  If we didn't, we want to start at zero.
+         */
+        if (shortLogCount < kShortLogMaxLines) {
+            shortLogNext = 0;
+        } else {
+            shortLogCount = kShortLogMaxLines;  /* cap at window size */
+        }
+
+        for (i = 0; i < shortLogCount; i++) {
+            _LOG(log, true, "%s\n", shortLog[shortLogNext]);
+            shortLogNext = (shortLogNext + 1) % kShortLogMaxLines;
+        }
+    }
+
+    close(logfd);
+}
+
+/*
+ * Dumps the logs generated by the specified pid to the tombstone, from both
+ * "system" and "main" log devices.  Ideally we'd interleave the output.
+ */
+static void dump_logs(log_t* log, pid_t pid, bool tailOnly)
+{
+    dump_log_file(log, pid, "/dev/log/system", tailOnly);
+    dump_log_file(log, pid, "/dev/log/main", tailOnly);
+}
+
+/*
+ * Dumps all information about the specified pid to the tombstone.
+ */
+static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal,
+        bool dump_sibling_threads, int* total_sleep_time_usec)
+{
+    /* don't copy log messages to tombstone unless this is a dev device */
+    char value[PROPERTY_VALUE_MAX];
+    property_get("ro.debuggable", value, "0");
+    bool want_logs = (value[0] == '1');
+
+    _LOG(log, false,
+            "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
+    dump_build_info(log);
+    dump_thread_info(log, pid, tid, true);
+    if(signal) {
+        dump_fault_addr(log, tid, signal);
+    }
+
+    ptrace_context_t* context = load_ptrace_context(tid);
+    dump_thread(context, log, tid, true, total_sleep_time_usec);
+
+    if (want_logs) {
+        dump_logs(log, pid, true);
+    }
+
+    bool detach_failed = false;
+    if (dump_sibling_threads) {
+        detach_failed = dump_sibling_thread_report(context, log, pid, tid, total_sleep_time_usec);
+    }
+
+    free_ptrace_context(context);
+
+    if (want_logs) {
+        dump_logs(log, pid, false);
+    }
+    return detach_failed;
+}
+
+/*
+ * find_and_open_tombstone - find an available tombstone slot, if any, of the
+ * form tombstone_XX where XX is 00 to MAX_TOMBSTONES-1, inclusive. If no
+ * file is available, we reuse the least-recently-modified file.
+ *
+ * Returns the path of the tombstone file, allocated using malloc().  Caller must free() it.
+ */
+static char* find_and_open_tombstone(int* fd)
+{
+    unsigned long mtime = ULONG_MAX;
+    struct stat sb;
+
+    /*
+     * XXX: Our stat.st_mtime isn't time_t. If it changes, as it probably ought
+     * to, our logic breaks. This check will generate a warning if that happens.
+     */
+    typecheck(mtime, sb.st_mtime);
+
+    /*
+     * In a single wolf-like pass, find an available slot and, in case none
+     * exist, find and record the least-recently-modified file.
+     */
+    char path[128];
+    int oldest = 0;
+    for (int i = 0; i < MAX_TOMBSTONES; i++) {
+        snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", i);
+
+        if (!stat(path, &sb)) {
+            if (sb.st_mtime < mtime) {
+                oldest = i;
+                mtime = sb.st_mtime;
+            }
+            continue;
+        }
+        if (errno != ENOENT)
+            continue;
+
+        *fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0600);
+        if (*fd < 0)
+            continue;   /* raced ? */
+
+        fchown(*fd, AID_SYSTEM, AID_SYSTEM);
+        return strdup(path);
+    }
+
+    /* we didn't find an available file, so we clobber the oldest one */
+    snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", oldest);
+    *fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
+    if (*fd < 0) {
+        LOG("failed to open tombstone file '%s': %s\n", path, strerror(errno));
+        return NULL;
+    }
+    fchown(*fd, AID_SYSTEM, AID_SYSTEM);
+    return strdup(path);
+}
+
+char* engrave_tombstone(pid_t pid, pid_t tid, int signal,
+        bool dump_sibling_threads, bool quiet, bool* detach_failed,
+        int* total_sleep_time_usec) {
+    mkdir(TOMBSTONE_DIR, 0755);
+    chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM);
+
+    int fd;
+    char* path = find_and_open_tombstone(&fd);
+    if (!path) {
+        *detach_failed = false;
+        return NULL;
+    }
+
+    log_t log;
+    log.tfd = fd;
+    log.quiet = quiet;
+    *detach_failed = dump_crash(&log, pid, tid, signal, dump_sibling_threads,
+            total_sleep_time_usec);
+
+    close(fd);
+    return path;
+}
diff --git a/debuggerd/tombstone.h b/debuggerd/tombstone.h
new file mode 100644
index 0000000..edcd7b1
--- /dev/null
+++ b/debuggerd/tombstone.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DEBUGGERD_TOMBSTONE_H
+#define _DEBUGGERD_TOMBSTONE_H
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include <corkscrew/ptrace.h>
+
+/* Creates a tombstone file and writes the crash dump to it.
+ * Returns the path of the tombstone, which must be freed using free(). */
+char* engrave_tombstone(pid_t pid, pid_t tid, int signal,
+        bool dump_sibling_threads, bool quiet, bool* detach_failed, int* total_sleep_time_usec);
+
+#endif // _DEBUGGERD_TOMBSTONE_H
diff --git a/debuggerd/utility.c b/debuggerd/utility.c
index 2ccf947..aabaf74 100644
--- a/debuggerd/utility.c
+++ b/debuggerd/utility.c
@@ -15,295 +15,80 @@
 ** limitations under the License.
 */
 
-#include <signal.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdio.h>
 #include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
 #include <cutils/logd.h>
 #include <sys/ptrace.h>
-#include <errno.h>
-#include <corkscrew/demangle.h>
+#include <sys/wait.h>
 
 #include "utility.h"
 
-#define STACK_DEPTH 32
-#define STACK_WORDS 16
+const int sleep_time_usec = 50000;         /* 0.05 seconds */
+const int max_total_sleep_usec = 10000000; /* 10 seconds */
 
-void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...) {
+void _LOG(log_t* log, bool in_tombstone_only, const char *fmt, ...) {
     char buf[512];
 
     va_list ap;
     va_start(ap, fmt);
 
-    if (tfd >= 0) {
+    if (log && log->tfd >= 0) {
         int len;
         vsnprintf(buf, sizeof(buf), fmt, ap);
         len = strlen(buf);
-        if(tfd >= 0) write(tfd, buf, len);
+        write(log->tfd, buf, len);
     }
 
-    if (!in_tombstone_only)
+    if (!in_tombstone_only && (!log || !log->quiet)) {
         __android_log_vprint(ANDROID_LOG_INFO, "DEBUG", fmt, ap);
+    }
     va_end(ap);
 }
 
-bool signal_has_address(int sig) {
-    switch (sig) {
-        case SIGILL:
-        case SIGFPE:
-        case SIGSEGV:
-        case SIGBUS:
-            return true;
-        default:
-            return false;
-    }
-}
-
-static void dump_backtrace(const ptrace_context_t* context __attribute((unused)),
-        int tfd, pid_t tid __attribute((unused)), bool at_fault,
-        const backtrace_frame_t* backtrace, size_t frames) {
-    _LOG(tfd, !at_fault, "\nbacktrace:\n");
-
-    backtrace_symbol_t backtrace_symbols[STACK_DEPTH];
-    get_backtrace_symbols_ptrace(context, backtrace, frames, backtrace_symbols);
-    for (size_t i = 0; i < frames; i++) {
-        char line[MAX_BACKTRACE_LINE_LENGTH];
-        format_backtrace_line(i, &backtrace[i], &backtrace_symbols[i],
-                line, MAX_BACKTRACE_LINE_LENGTH);
-        _LOG(tfd, !at_fault, "    %s\n", line);
-    }
-    free_backtrace_symbols(backtrace_symbols, frames);
-}
-
-static void dump_stack_segment(const ptrace_context_t* context, int tfd, pid_t tid,
-        bool only_in_tombstone, uintptr_t* sp, size_t words, int label) {
-    for (size_t i = 0; i < words; i++) {
-        uint32_t stack_content;
-        if (!try_get_word_ptrace(tid, *sp, &stack_content)) {
-            break;
-        }
-
-        const map_info_t* mi;
-        const symbol_t* symbol;
-        find_symbol_ptrace(context, stack_content, &mi, &symbol);
-
-        if (symbol) {
-            char* demangled_name = demangle_symbol_name(symbol->name);
-            const char* symbol_name = demangled_name ? demangled_name : symbol->name;
-            uint32_t offset = stack_content - (mi->start + symbol->start);
-            if (!i && label >= 0) {
-                if (offset) {
-                    _LOG(tfd, only_in_tombstone, "    #%02d  %08x  %08x  %s (%s+%u)\n",
-                            label, *sp, stack_content, mi ? mi->name : "", symbol_name, offset);
-                } else {
-                    _LOG(tfd, only_in_tombstone, "    #%02d  %08x  %08x  %s (%s)\n",
-                            label, *sp, stack_content, mi ? mi->name : "", symbol_name);
-                }
+int wait_for_signal(pid_t tid, int* total_sleep_time_usec) {
+    for (;;) {
+        int status;
+        pid_t n = waitpid(tid, &status, __WALL | WNOHANG);
+        if (n < 0) {
+            if(errno == EAGAIN) continue;
+            LOG("waitpid failed: %s\n", strerror(errno));
+            return -1;
+        } else if (n > 0) {
+            XLOG("waitpid: n=%d status=%08x\n", n, status);
+            if (WIFSTOPPED(status)) {
+                return WSTOPSIG(status);
             } else {
-                if (offset) {
-                    _LOG(tfd, only_in_tombstone, "         %08x  %08x  %s (%s+%u)\n",
-                            *sp, stack_content, mi ? mi->name : "", symbol_name, offset);
-                } else {
-                    _LOG(tfd, only_in_tombstone, "         %08x  %08x  %s (%s)\n",
-                            *sp, stack_content, mi ? mi->name : "", symbol_name);
-                }
-            }
-            free(demangled_name);
-        } else {
-            if (!i && label >= 0) {
-                _LOG(tfd, only_in_tombstone, "    #%02d  %08x  %08x  %s\n",
-                        label, *sp, stack_content, mi ? mi->name : "");
-            } else {
-                _LOG(tfd, only_in_tombstone, "         %08x  %08x  %s\n",
-                        *sp, stack_content, mi ? mi->name : "");
+                LOG("unexpected waitpid response: n=%d, status=%08x\n", n, status);
+                return -1;
             }
         }
 
-        *sp += sizeof(uint32_t);
+        if (*total_sleep_time_usec > max_total_sleep_usec) {
+            LOG("timed out waiting for tid=%d to die\n", tid);
+            return -1;
+        }
+
+        /* not ready yet */
+        XLOG("not ready yet\n");
+        usleep(sleep_time_usec);
+        *total_sleep_time_usec += sleep_time_usec;
     }
 }
 
-static void dump_stack(const ptrace_context_t* context, int tfd, pid_t tid, bool at_fault,
-        const backtrace_frame_t* backtrace, size_t frames) {
-    bool have_first = false;
-    size_t first, last;
-    for (size_t i = 0; i < frames; i++) {
-        if (backtrace[i].stack_top) {
-            if (!have_first) {
-                have_first = true;
-                first = i;
-            }
-            last = i;
-        }
-    }
-    if (!have_first) {
-        return;
-    }
-
-    _LOG(tfd, !at_fault, "\nstack:\n");
-
-    // Dump a few words before the first frame.
-    bool only_in_tombstone = !at_fault;
-    uintptr_t sp = backtrace[first].stack_top - STACK_WORDS * sizeof(uint32_t);
-    dump_stack_segment(context, tfd, tid, only_in_tombstone, &sp, STACK_WORDS, -1);
-
-    // Dump a few words from all successive frames.
-    // Only log the first 3 frames, put the rest in the tombstone.
-    for (size_t i = first; i <= last; i++) {
-        const backtrace_frame_t* frame = &backtrace[i];
-        if (sp != frame->stack_top) {
-            _LOG(tfd, only_in_tombstone, "         ........  ........\n");
-            sp = frame->stack_top;
-        }
-        if (i - first == 3) {
-            only_in_tombstone = true;
-        }
-        if (i == last) {
-            dump_stack_segment(context, tfd, tid, only_in_tombstone, &sp, STACK_WORDS, i);
-            if (sp < frame->stack_top + frame->stack_size) {
-                _LOG(tfd, only_in_tombstone, "         ........  ........\n");
-            }
-        } else {
-            size_t words = frame->stack_size / sizeof(uint32_t);
-            if (words == 0) {
-                words = 1;
-            } else if (words > STACK_WORDS) {
-                words = STACK_WORDS;
-            }
-            dump_stack_segment(context, tfd, tid, only_in_tombstone, &sp, words, i);
-        }
-    }
-}
-
-void dump_backtrace_and_stack(const ptrace_context_t* context, int tfd, pid_t tid,
-        bool at_fault) {
-    backtrace_frame_t backtrace[STACK_DEPTH];
-    ssize_t frames = unwind_backtrace_ptrace(tid, context, backtrace, 0, STACK_DEPTH);
-    if (frames > 0) {
-        dump_backtrace(context, tfd, tid, at_fault, backtrace, frames);
-        dump_stack(context, tfd, tid, at_fault, backtrace, frames);
-    }
-}
-
-void dump_memory(int tfd, pid_t tid, uintptr_t addr, bool at_fault) {
-    char code_buffer[64];       /* actual 8+1+((8+1)*4) + 1 == 45 */
-    char ascii_buffer[32];      /* actual 16 + 1 == 17 */
-    uintptr_t p, end;
-
-    p = addr & ~3;
-    p -= 32;
-    if (p > addr) {
-        /* catch underflow */
-        p = 0;
-    }
-    end = p + 80;
-    /* catch overflow; 'end - p' has to be multiples of 16 */
-    while (end < p)
-        end -= 16;
-
-    /* Dump the code around PC as:
-     *  addr     contents                             ascii
-     *  00008d34 ef000000 e8bd0090 e1b00000 512fff1e  ............../Q
-     *  00008d44 ea00b1f9 e92d0090 e3a070fc ef000000  ......-..p......
-     */
-    while (p < end) {
-        char* asc_out = ascii_buffer;
-
-        sprintf(code_buffer, "%08x ", p);
-
-        int i;
-        for (i = 0; i < 4; i++) {
-            /*
-             * If we see (data == -1 && errno != 0), we know that the ptrace
-             * call failed, probably because we're dumping memory in an
-             * unmapped or inaccessible page.  I don't know if there's
-             * value in making that explicit in the output -- it likely
-             * just complicates parsing and clarifies nothing for the
-             * enlightened reader.
-             */
-            long data = ptrace(PTRACE_PEEKTEXT, tid, (void*)p, NULL);
-            sprintf(code_buffer + strlen(code_buffer), "%08lx ", data);
-
-            int j;
-            for (j = 0; j < 4; j++) {
-                /*
-                 * Our isprint() allows high-ASCII characters that display
-                 * differently (often badly) in different viewers, so we
-                 * just use a simpler test.
-                 */
-                char val = (data >> (j*8)) & 0xff;
-                if (val >= 0x20 && val < 0x7f) {
-                    *asc_out++ = val;
-                } else {
-                    *asc_out++ = '.';
-                }
-            }
-            p += 4;
-        }
-        *asc_out = '\0';
-        _LOG(tfd, !at_fault, "    %s %s\n", code_buffer, ascii_buffer);
-    }
-}
-
-void dump_nearby_maps(const ptrace_context_t* context, int tfd, pid_t tid) {
+void wait_for_stop(pid_t tid, int* total_sleep_time_usec) {
     siginfo_t si;
-    memset(&si, 0, sizeof(si));
-    if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si)) {
-        _LOG(tfd, false, "cannot get siginfo for %d: %s\n",
-                tid, strerror(errno));
-        return;
-    }
-    if (!signal_has_address(si.si_signo)) {
-        return;
-    }
-
-    uintptr_t addr = (uintptr_t) si.si_addr;
-    addr &= ~0xfff;     /* round to 4K page boundary */
-    if (addr == 0) {    /* null-pointer deref */
-        return;
-    }
-
-    _LOG(tfd, false, "\nmemory map around fault addr %08x:\n", (int)si.si_addr);
-
-    /*
-     * Search for a match, or for a hole where the match would be.  The list
-     * is backward from the file content, so it starts at high addresses.
-     */
-    bool found = false;
-    map_info_t* map = context->map_info_list;
-    map_info_t *next = NULL;
-    map_info_t *prev = NULL;
-    while (map != NULL) {
-        if (addr >= map->start && addr < map->end) {
-            found = true;
-            next = map->next;
-            break;
-        } else if (addr >= map->end) {
-            /* map would be between "prev" and this entry */
-            next = map;
-            map = NULL;
+    while (TEMP_FAILURE_RETRY(ptrace(PTRACE_GETSIGINFO, tid, 0, &si)) < 0 && errno == ESRCH) {
+        if (*total_sleep_time_usec > max_total_sleep_usec) {
+            LOG("timed out waiting for tid=%d to stop\n", tid);
             break;
         }
 
-        prev = map;
-        map = map->next;
-    }
-
-    /*
-     * Show "next" then "match" then "prev" so that the addresses appear in
-     * ascending order (like /proc/pid/maps).
-     */
-    if (next != NULL) {
-        _LOG(tfd, false, "    %08x-%08x %s\n", next->start, next->end, next->name);
-    } else {
-        _LOG(tfd, false, "    (no map below)\n");
-    }
-    if (map != NULL) {
-        _LOG(tfd, false, "    %08x-%08x %s\n", map->start, map->end, map->name);
-    } else {
-        _LOG(tfd, false, "    (no map for address)\n");
-    }
-    if (prev != NULL) {
-        _LOG(tfd, false, "    %08x-%08x %s\n", prev->start, prev->end, prev->name);
-    } else {
-        _LOG(tfd, false, "    (no map above)\n");
+        usleep(sleep_time_usec);
+        *total_sleep_time_usec += sleep_time_usec;
     }
 }
diff --git a/debuggerd/utility.h b/debuggerd/utility.h
index 39f91cb..136f46d 100644
--- a/debuggerd/utility.h
+++ b/debuggerd/utility.h
@@ -20,53 +20,35 @@
 
 #include <stddef.h>
 #include <stdbool.h>
-#include <sys/types.h>
-#include <corkscrew/backtrace.h>
+
+typedef struct {
+    /* tombstone file descriptor */
+    int tfd;
+    /* if true, does not log anything to the Android logcat */
+    bool quiet;
+} log_t;
 
 /* Log information onto the tombstone. */
-void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...)
+void _LOG(log_t* log, bool in_tombstone_only, const char *fmt, ...)
         __attribute__ ((format(printf, 3, 4)));
 
-#define LOG(fmt...) _LOG(-1, 0, fmt)
+#define LOG(fmt...) _LOG(NULL, 0, fmt)
 
 /* Set to 1 for normal debug traces */
 #if 0
-#define XLOG(fmt...) _LOG(-1, 0, fmt)
+#define XLOG(fmt...) _LOG(NULL, 0, fmt)
 #else
 #define XLOG(fmt...) do {} while(0)
 #endif
 
 /* Set to 1 for chatty debug traces. Includes all resolved dynamic symbols */
 #if 0
-#define XLOG2(fmt...) _LOG(-1, 0, fmt)
+#define XLOG2(fmt...) _LOG(NULL, 0, fmt)
 #else
 #define XLOG2(fmt...) do {} while(0)
 #endif
 
-/*
- * Returns true if the specified signal has an associated address.
- * (i.e. it sets siginfo_t.si_addr).
- */
-bool signal_has_address(int sig);
-
-/*
- * Dumps the backtrace and contents of the stack.
- */
-void dump_backtrace_and_stack(const ptrace_context_t* context, int tfd, pid_t tid, bool at_fault);
-
-/*
- * Dumps a few bytes of memory, starting a bit before and ending a bit
- * after the specified address.
- */
-void dump_memory(int tfd, pid_t tid, uintptr_t addr, bool at_fault);
-
-/*
- * If this isn't clearly a null pointer dereference, dump the
- * /proc/maps entries near the fault address.
- *
- * This only makes sense to do on the thread that crashed.
- */
-void dump_nearby_maps(const ptrace_context_t* context, int tfd, pid_t tid);
-
+int wait_for_signal(pid_t tid, int* total_sleep_time_usec);
+void wait_for_stop(pid_t tid, int* total_sleep_time_usec);
 
 #endif // _DEBUGGERD_UTILITY_H
diff --git a/debuggerd/x86/machine.c b/debuggerd/x86/machine.c
index 2729c7e..01da5fe 100644
--- a/debuggerd/x86/machine.c
+++ b/debuggerd/x86/machine.c
@@ -15,59 +15,44 @@
 ** limitations under the License.
 */
 
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
 #include <stdio.h>
 #include <errno.h>
-#include <signal.h>
-#include <pthread.h>
-#include <fcntl.h>
 #include <sys/types.h>
-#include <dirent.h>
-
 #include <sys/ptrace.h>
-#include <sys/wait.h>
-#include <sys/exec_elf.h>
-#include <sys/stat.h>
 
-#include <cutils/sockets.h>
-#include <cutils/properties.h>
-
-#include <corkscrew/backtrace.h>
 #include <corkscrew/ptrace.h>
 
-#include <linux/input.h>
+#include <linux/user.h>
 
-#include "../machine.h"
 #include "../utility.h"
+#include "../machine.h"
 
-static void dump_registers(const ptrace_context_t* context __attribute((unused)),
-        int tfd, pid_t tid, bool at_fault) {
+void dump_memory_and_code(const ptrace_context_t* context __attribute((unused)),
+        log_t* log, pid_t tid, bool at_fault) {
+}
+
+void dump_registers(const ptrace_context_t* context __attribute((unused)),
+        log_t* log, pid_t tid, bool at_fault) {
     struct pt_regs_x86 r;
     bool only_in_tombstone = !at_fault;
 
     if(ptrace(PTRACE_GETREGS, tid, 0, &r)) {
-        _LOG(tfd, only_in_tombstone, "cannot get registers: %s\n", strerror(errno));
+        _LOG(log, only_in_tombstone, "cannot get registers: %s\n", strerror(errno));
         return;
     }
     //if there is no stack, no print just like arm
     if(!r.ebp)
         return;
-    _LOG(tfd, only_in_tombstone, "    eax %08x  ebx %08x  ecx %08x  edx %08x\n",
+    _LOG(log, only_in_tombstone, "    eax %08x  ebx %08x  ecx %08x  edx %08x\n",
          r.eax, r.ebx, r.ecx, r.edx);
-    _LOG(tfd, only_in_tombstone, "    esi %08x  edi %08x\n",
+    _LOG(log, only_in_tombstone, "    esi %08x  edi %08x\n",
          r.esi, r.edi);
-    _LOG(tfd, only_in_tombstone, "    xcs %08x  xds %08x  xes %08x  xfs %08x  xss %08x\n",
+    _LOG(log, only_in_tombstone, "    xcs %08x  xds %08x  xes %08x  xfs %08x  xss %08x\n",
          r.xcs, r.xds, r.xes, r.xfs, r.xss);
-    _LOG(tfd, only_in_tombstone, "    eip %08x  ebp %08x  esp %08x  flags %08x\n",
+    _LOG(log, only_in_tombstone, "    eip %08x  ebp %08x  esp %08x  flags %08x\n",
          r.eip, r.ebp, r.esp, r.eflags);
 }
-
-void dump_thread(const ptrace_context_t* context, int tfd, pid_t tid, bool at_fault) {
-    dump_registers(context, tfd, tid, at_fault);
-
-    dump_backtrace_and_stack(context, tfd, tid, at_fault);
-
-    if (at_fault) {
-        dump_nearby_maps(context, tfd, tid);
-    }
-}
-
diff --git a/include/cutils/debugger.h b/include/cutils/debugger.h
new file mode 100644
index 0000000..5a8e796
--- /dev/null
+++ b/include/cutils/debugger.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_DEBUGGER_H
+#define __CUTILS_DEBUGGER_H
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DEBUGGER_SOCKET_NAME "android:debuggerd"
+
+typedef enum {
+    // dump a crash
+    DEBUGGER_ACTION_CRASH,
+    // dump a tombstone file
+    DEBUGGER_ACTION_DUMP_TOMBSTONE,
+    // dump a backtrace only back to the socket
+    DEBUGGER_ACTION_DUMP_BACKTRACE,
+} debugger_action_t;
+
+/* message sent over the socket */
+typedef struct {
+    debugger_action_t action;
+    pid_t tid;
+} debugger_msg_t;
+
+/* Dumps a process backtrace, registers, and stack to a tombstone file (requires root).
+ * Stores the tombstone path in the provided buffer.
+ * Returns 0 on success, -1 on error.
+ */
+int dump_tombstone(pid_t tid, char* pathbuf, size_t pathlen);
+
+/* Dumps a process backtrace only to the specified file (requires root).
+ * Returns 0 on success, -1 on error.
+ */
+int dump_backtrace_to_file(pid_t tid, int fd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_DEBUGGER_H */
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
index 0d49165..5c227b6 100644
--- a/libcutils/Android.mk
+++ b/libcutils/Android.mk
@@ -116,7 +116,15 @@
 
 include $(CLEAR_VARS)
 LOCAL_MODULE := libcutils
-LOCAL_SRC_FILES := $(commonSources) ashmem-dev.c mq.c android_reboot.c partition_utils.c uevent.c qtaguid.c klog.c
+LOCAL_SRC_FILES := $(commonSources) \
+        android_reboot.c \
+        ashmem-dev.c \
+        debugger.c \
+        klog.c \
+        mq.c \
+        partition_utils.c \
+        qtaguid.c \
+        uevent.c
 
 ifeq ($(TARGET_ARCH),arm)
 LOCAL_SRC_FILES += arch-arm/memset32.S
diff --git a/libcutils/debugger.c b/libcutils/debugger.c
new file mode 100644
index 0000000..9425006
--- /dev/null
+++ b/libcutils/debugger.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <cutils/debugger.h>
+#include <cutils/sockets.h>
+
+int dump_tombstone(pid_t tid, char* pathbuf, size_t pathlen) {
+    int s = socket_local_client(DEBUGGER_SOCKET_NAME,
+            ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
+    if (s < 0) {
+        return -1;
+    }
+
+    debugger_msg_t msg;
+    msg.tid = tid;
+    msg.action = DEBUGGER_ACTION_DUMP_TOMBSTONE;
+
+    int result = 0;
+    if (TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg))) != sizeof(msg)) {
+        result = -1;
+    } else {
+        char ack;
+        if (TEMP_FAILURE_RETRY(read(s, &ack, 1)) != 1) {
+            result = -1;
+        } else {
+            if (pathbuf && pathlen) {
+                ssize_t n = TEMP_FAILURE_RETRY(read(s, pathbuf, pathlen - 1));
+                if (n <= 0) {
+                    result = -1;
+                } else {
+                    pathbuf[n] = '\0';
+                }
+            }
+        }
+    }
+    TEMP_FAILURE_RETRY(close(s));
+    return result;
+}
+
+int dump_backtrace_to_file(pid_t tid, int fd) {
+    int s = socket_local_client(DEBUGGER_SOCKET_NAME,
+            ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
+    if (s < 0) {
+        return -1;
+    }
+
+    debugger_msg_t msg;
+    msg.tid = tid;
+    msg.action = DEBUGGER_ACTION_DUMP_BACKTRACE;
+
+    int result = 0;
+    if (TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg))) != sizeof(msg)) {
+        result = -1;
+    } else {
+        char ack;
+        if (TEMP_FAILURE_RETRY(read(s, &ack, 1)) != 1) {
+            result = -1;
+        } else {
+            char buffer[4096];
+            ssize_t n;
+            while ((n = TEMP_FAILURE_RETRY(read(s, buffer, sizeof(buffer)))) > 0) {
+                if (TEMP_FAILURE_RETRY(write(fd, buffer, n)) != n) {
+                    result = -1;
+                    break;
+                }
+            }
+        }
+    }
+    TEMP_FAILURE_RETRY(close(s));
+    return result;
+}
diff --git a/libcutils/sched_policy.c b/libcutils/sched_policy.c
index e03ce00..d20d217 100644
--- a/libcutils/sched_policy.c
+++ b/libcutils/sched_policy.c
@@ -63,8 +63,6 @@
 #if CAN_SET_SP_SYSTEM
 static int system_cgroup_fd = -1;
 #endif
-static int audio_app_cgroup_fd = -1;
-static int audio_sys_cgroup_fd = -1;
 
 /* Add tid to the scheduling group defined by the policy */
 static int add_tid_to_cgroup(int tid, SchedPolicy policy)
@@ -76,6 +74,8 @@
         fd = bg_cgroup_fd;
         break;
     case SP_FOREGROUND:
+    case SP_AUDIO_APP:
+    case SP_AUDIO_SYS:
         fd = fg_cgroup_fd;
         break;
 #if CAN_SET_SP_SYSTEM
@@ -83,12 +83,6 @@
         fd = system_cgroup_fd;
         break;
 #endif
-    case SP_AUDIO_APP:
-        fd = audio_app_cgroup_fd;
-        break;
-    case SP_AUDIO_SYS:
-        fd = audio_sys_cgroup_fd;
-        break;
     default:
         fd = -1;
         break;
@@ -137,30 +131,17 @@
         }
 #endif
 
-        filename = "/dev/cpuctl/foreground/tasks";
+        filename = "/dev/cpuctl/apps/tasks";
         fg_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC);
         if (fg_cgroup_fd < 0) {
             SLOGE("open of %s failed: %s\n", filename, strerror(errno));
         }
 
-        filename = "/dev/cpuctl/bg_non_interactive/tasks";
+        filename = "/dev/cpuctl/apps/bg_non_interactive/tasks";
         bg_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC);
         if (bg_cgroup_fd < 0) {
             SLOGE("open of %s failed: %s\n", filename, strerror(errno));
         }
-
-        filename = "/dev/cpuctl/audio_app/tasks";
-        audio_app_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC);
-        if (audio_app_cgroup_fd < 0) {
-            SLOGV("open of %s failed: %s\n", filename, strerror(errno));
-        }
-
-        filename = "/dev/cpuctl/audio_sys/tasks";
-        audio_sys_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC);
-        if (audio_sys_cgroup_fd < 0) {
-            SLOGV("open of %s failed: %s\n", filename, strerror(errno));
-        }
-
     } else {
         __sys_supports_schedgroups = 0;
     }
@@ -253,14 +234,10 @@
             return -1;
         if (grpBuf[0] == '\0') {
             *policy = SP_SYSTEM;
-        } else if (!strcmp(grpBuf, "bg_non_interactive")) {
+        } else if (!strcmp(grpBuf, "apps/bg_non_interactive")) {
             *policy = SP_BACKGROUND;
-        } else if (!strcmp(grpBuf, "foreground")) {
+        } else if (!strcmp(grpBuf, "apps")) {
             *policy = SP_FOREGROUND;
-        } else if (!strcmp(grpBuf, "audio_app")) {
-            *policy = SP_AUDIO_APP;
-        } else if (!strcmp(grpBuf, "audio_sys")) {
-            *policy = SP_AUDIO_SYS;
         } else {
             errno = ERANGE;
             return -1;
@@ -319,17 +296,13 @@
         SLOGD("vvv tid %d (%s)", tid, thread_name);
         break;
     case SP_FOREGROUND:
+    case SP_AUDIO_APP:
+    case SP_AUDIO_SYS:
         SLOGD("^^^ tid %d (%s)", tid, thread_name);
         break;
     case SP_SYSTEM:
         SLOGD("/// tid %d (%s)", tid, thread_name);
         break;
-    case SP_AUDIO_APP:
-        SLOGD("aaa tid %d (%s)", tid, thread_name);
-        break;
-    case SP_AUDIO_SYS:
-        SLOGD("sss tid %d (%s)", tid, thread_name);
-        break;
     default:
         SLOGD("??? tid %d (%s)", tid, thread_name);
         break;
diff --git a/rootdir/init.rc b/rootdir/init.rc
index ef1dc3d..bb7e4ea 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -94,34 +94,20 @@
     write /dev/cpuctl/cpu.rt_runtime_us 950000
     write /dev/cpuctl/cpu.rt_period_us 1000000
 
-    mkdir /dev/cpuctl/foreground
-    chown system system /dev/cpuctl/foreground/tasks
-    chmod 0666 /dev/cpuctl/foreground/tasks
-    write /dev/cpuctl/foreground/cpu.shares 1024
-    write /dev/cpuctl/foreground/cpu.rt_runtime_us 100000
-    write /dev/cpuctl/foreground/cpu.rt_period_us 1000000
+    mkdir /dev/cpuctl/apps
+    chown system system /dev/cpuctl/apps/tasks
+    chmod 0666 /dev/cpuctl/apps/tasks
+    write /dev/cpuctl/apps/cpu.shares 1024
+    write /dev/cpuctl/apps/cpu.rt_runtime_us 800000
+    write /dev/cpuctl/apps/cpu.rt_period_us 1000000
 
-    mkdir /dev/cpuctl/bg_non_interactive
-    chown system system /dev/cpuctl/bg_non_interactive/tasks
-    chmod 0666 /dev/cpuctl/bg_non_interactive/tasks
+    mkdir /dev/cpuctl/apps/bg_non_interactive
+    chown system system /dev/cpuctl/apps/bg_non_interactive/tasks
+    chmod 0666 /dev/cpuctl/apps/bg_non_interactive/tasks
     # 5.0 %
-    write /dev/cpuctl/bg_non_interactive/cpu.shares 52
-    write /dev/cpuctl/bg_non_interactive/cpu.rt_runtime_us 100000
-    write /dev/cpuctl/bg_non_interactive/cpu.rt_period_us 1000000
-
-    mkdir /dev/cpuctl/audio_app
-    chown system system /dev/cpuctl/audio_app/tasks
-    chmod 0660 /dev/cpuctl/audio_app/tasks
-    write /dev/cpuctl/audio_app/cpu.shares 10
-    write /dev/cpuctl/audio_app/cpu.rt_runtime_us 100000
-    write /dev/cpuctl/audio_app/cpu.rt_period_us 1000000
-
-    mkdir /dev/cpuctl/audio_sys
-    chown system system /dev/cpuctl/audio_sys/tasks
-    chmod 0660 /dev/cpuctl/audio_sys/tasks
-    write /dev/cpuctl/audio_sys/cpu.shares 10
-    write /dev/cpuctl/audio_sys/cpu.rt_runtime_us 100000
-    write /dev/cpuctl/audio_sys/cpu.rt_period_us 1000000
+    write /dev/cpuctl/apps/bg_non_interactive/cpu.shares 52
+    write /dev/cpuctl/apps/bg_non_interactive/cpu.rt_runtime_us 700000
+    write /dev/cpuctl/apps/bg_non_interactive/cpu.rt_period_us 1000000
 
 # Allow everybody to read the xt_qtaguid resource tracking misc dev.
 # This is needed by any process that uses socket tagging.