Merge commit '4102af05a86aae36d2560fd8b3f740a52399342c' into HEAD
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 74ec29d..0254bd2 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -51,3 +51,5 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/init.rc)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/init.rc)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/reboot)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop)
diff --git a/adb/usb_vendors.c b/adb/usb_vendors.c
index cd5885e..0c46a0c 100755
--- a/adb/usb_vendors.c
+++ b/adb/usb_vendors.c
@@ -90,6 +90,8 @@
 #define VENDOR_ID_INQ_MOBILE    0x2314
 // Intel's USB Vendor ID
 #define VENDOR_ID_INTEL         0x8087
+// Intermec's USB Vendor ID
+#define VENDOR_ID_INTERMEC      0x067e
 // IRiver's USB Vendor ID
 #define VENDOR_ID_IRIVER        0x2420
 // K-Touch's USB Vendor ID
@@ -138,6 +140,8 @@
 #define VENDOR_ID_PMC           0x04DA
 // Positivo's USB Vendor ID
 #define VENDOR_ID_POSITIVO      0x1662
+// Prestigio's USB Vendor ID
+#define VENDOR_ID_PRESTIGIO     0x29e4
 // Qisda's USB Vendor ID
 #define VENDOR_ID_QISDA         0x1D45
 // Qualcomm's USB Vendor ID
@@ -210,6 +214,7 @@
     VENDOR_ID_HUAWEI,
     VENDOR_ID_INQ_MOBILE,
     VENDOR_ID_INTEL,
+    VENDOR_ID_INTERMEC,
     VENDOR_ID_IRIVER,
     VENDOR_ID_KOBO,
     VENDOR_ID_K_TOUCH,
@@ -234,6 +239,7 @@
     VENDOR_ID_PHILIPS,
     VENDOR_ID_PMC,
     VENDOR_ID_POSITIVO,
+    VENDOR_ID_PRESTIGIO,
     VENDOR_ID_QISDA,
     VENDOR_ID_QUALCOMM,
     VENDOR_ID_QUANTA,
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk
index 77fcbe0..f6d8f0c 100644
--- a/debuggerd/Android.mk
+++ b/debuggerd/Android.mk
@@ -1,35 +1,93 @@
 LOCAL_PATH:= $(call my-dir)
 
-debuggerd_2nd_arch_var_prefix :=
-include $(LOCAL_PATH)/debuggerd.mk
-
-ifdef TARGET_2ND_ARCH
-debuggerd_2nd_arch_var_prefix := $(TARGET_2ND_ARCH_VAR_PREFIX)
-include $(LOCAL_PATH)/debuggerd.mk
-endif
-
-ifeq ($(ARCH_ARM_HAVE_VFP),true)
 include $(CLEAR_VARS)
 
-LOCAL_CFLAGS += -DWITH_VFP
+LOCAL_SRC_FILES:= \
+	backtrace.cpp \
+	debuggerd.cpp \
+	getevent.cpp \
+	tombstone.cpp \
+	utility.cpp \
+
+LOCAL_SRC_FILES_arm    := arm/machine.cpp
+LOCAL_SRC_FILES_arm64  := arm64/machine.cpp
+LOCAL_SRC_FILES_mips   := mips/machine.cpp
+LOCAL_SRC_FILES_x86    := x86/machine.cpp
+LOCAL_SRC_FILES_x86_64 := x86_64/machine.cpp
+
+LOCAL_CONLYFLAGS := -std=gnu99
+LOCAL_CPPFLAGS := -std=gnu++11
+LOCAL_CFLAGS := \
+	-Wall \
+	-Wno-array-bounds \
+	-Werror \
+	-Wno-unused-parameter \
+
+ifeq ($(ARCH_ARM_HAVE_VFP),true)
+LOCAL_CFLAGS_arm += -DWITH_VFP
+endif # ARCH_ARM_HAVE_VFP
 ifeq ($(ARCH_ARM_HAVE_VFP_D32),true)
-LOCAL_CFLAGS += -DWITH_VFP_D32
+LOCAL_CFLAGS_arm += -DWITH_VFP_D32
 endif # ARCH_ARM_HAVE_VFP_D32
 
-LOCAL_SRC_FILES := vfp-crasher.c arm/vfp.S
+LOCAL_SHARED_LIBRARIES := \
+	libbacktrace \
+	libc \
+	libcutils \
+	liblog \
+	libselinux \
+
+include external/stlport/libstlport.mk
+
+LOCAL_MODULE := debuggerd
+LOCAL_MODULE_STEM_32 := debuggerd
+LOCAL_MODULE_STEM_64 := debuggerd64
+LOCAL_MULTILIB := both
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := crasher.c
+LOCAL_SRC_FILES_arm    := arm/crashglue.S
+LOCAL_SRC_FILES_arm64  := arm64/crashglue.S
+LOCAL_SRC_FILES_mips   := mips/crashglue.S
+LOCAL_SRC_FILES_x86    := x86/crashglue.S
+LOCAL_SRC_FILES_x86_64 := x86_64/crashglue.S
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS += -fstack-protector-all -Wno-unused-parameter -Wno-free-nonheap-object
+#LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_SHARED_LIBRARIES := libcutils liblog libc
+
+LOCAL_MODULE := crasher
+LOCAL_MODULE_STEM_32 := crasher
+LOCAL_MODULE_STEM_64 := crasher64
+LOCAL_MULTILIB := both
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+
+ifeq ($(ARCH_ARM_HAVE_VFP),true)
+LOCAL_MODULE_TARGET_ARCH += arm
+LOCAL_SRC_FILES_arm := arm/vfp.S
+LOCAL_CFLAGS_arm += -DWITH_VFP
+ifeq ($(ARCH_ARM_HAVE_VFP_D32),true)
+LOCAL_CFLAGS_arm += -DWITH_VFP_D32
+endif # ARCH_ARM_HAVE_VFP_D32
+endif # ARCH_ARM_HAVE_VFP == true
+
+LOCAL_SRC_FILES_arm64 := arm64/vfp.S
+LOCAL_MODULE_TARGET_ARCH += arm64
+
+LOCAL_SRC_FILES := vfp-crasher.c
 LOCAL_MODULE := vfp-crasher
 LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
 LOCAL_MODULE_TAGS := optional
 LOCAL_SHARED_LIBRARIES := libcutils liblog libc
-LOCAL_MODULE_TARGET_ARCH := arm
-include $(BUILD_EXECUTABLE)
-endif # ARCH_ARM_HAVE_VFP == true
 
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := vfp-crasher.c arm64/vfp.S
-LOCAL_MODULE := vfp-crasher64
-LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
-LOCAL_MODULE_TAGS := optional
-LOCAL_SHARED_LIBRARIES := libcutils liblog libc
-LOCAL_MODULE_TARGET_ARCH := arm64
+LOCAL_MODULE_STEM_32 := vfp-crasher
+LOCAL_MODULE_STEM_64 := vfp-crasher64
+LOCAL_MULTILIB := both
+
 include $(BUILD_EXECUTABLE)
diff --git a/debuggerd/crasher.c b/debuggerd/crasher.c
index 5a2bc3c..3e3ab5a 100644
--- a/debuggerd/crasher.c
+++ b/debuggerd/crasher.c
@@ -1,20 +1,17 @@
-
-//#include <cutils/misc.h>
-
-#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <pthread.h>
+#include <sched.h>
+#include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sched.h>
-#include <errno.h>
-
-#include <signal.h>
 #include <sys/ptrace.h>
-#include <sys/wait.h>
 #include <sys/socket.h>
+#include <sys/wait.h>
+#include <unistd.h>
 
-#include <pthread.h>
-
+#include <cutils/log.h>
 #include <cutils/sockets.h>
 
 extern const char* __progname;
@@ -23,8 +20,8 @@
 void crashnostack(void);
 static int do_action(const char* arg);
 
-static void maybeabort() {
-    if(time(0) != 42) {
+static void maybe_abort() {
+    if (time(0) != 42) {
         abort();
     }
 }
@@ -119,35 +116,54 @@
 
     if (!strncmp(arg, "thread-", strlen("thread-"))) {
         return do_action_on_thread(arg + strlen("thread-"));
-    } else if (!strcmp(arg,"smash-stack")) {
+    } else if (!strcmp(arg, "smash-stack")) {
         return smash_stack(42);
-    } else if (!strcmp(arg,"stack-overflow")) {
+    } else if (!strcmp(arg, "stack-overflow")) {
         overflow_stack(NULL);
-    } else if (!strcmp(arg,"nostack")) {
+    } else if (!strcmp(arg, "nostack")) {
         crashnostack();
-    } else if (!strcmp(arg,"ctest")) {
+    } else if (!strcmp(arg, "ctest")) {
         return ctest();
-    } else if (!strcmp(arg,"exit")) {
+    } else if (!strcmp(arg, "exit")) {
         exit(1);
-    } else if (!strcmp(arg,"crash")) {
+    } else if (!strcmp(arg, "crash") || !strcmp(arg, "SIGSEGV")) {
         return crash(42);
-    } else if (!strcmp(arg,"abort")) {
-        maybeabort();
+    } else if (!strcmp(arg, "abort")) {
+        maybe_abort();
+    } else if (!strcmp(arg, "assert")) {
+        __assert("some_file.c", 123, "false");
+    } else if (!strcmp(arg, "assert2")) {
+      __assert2("some_file.c", 123, "some_function", "false");
+    } else if (!strcmp(arg, "LOG_ALWAYS_FATAL")) {
+        LOG_ALWAYS_FATAL("hello %s", "world");
+    } else if (!strcmp(arg, "LOG_ALWAYS_FATAL_IF")) {
+        LOG_ALWAYS_FATAL_IF(true, "hello %s", "world");
+    } else if (!strcmp(arg, "SIGPIPE")) {
+        int pipe_fds[2];
+        pipe(pipe_fds);
+        close(pipe_fds[0]);
+        write(pipe_fds[1], "oops", 4);
+        return EXIT_SUCCESS;
     } else if (!strcmp(arg, "heap-usage")) {
         abuse_heap();
     }
 
     fprintf(stderr, "%s OP\n", __progname);
     fprintf(stderr, "where OP is:\n");
-    fprintf(stderr, "  smash-stack     overwrite a stack-guard canary\n");
-    fprintf(stderr, "  stack-overflow  recurse until the stack overflows\n");
-    fprintf(stderr, "  heap-corruption cause a libc abort by corrupting the heap\n");
-    fprintf(stderr, "  heap-usage      cause a libc abort by abusing a heap function\n");
-    fprintf(stderr, "  nostack         crash with a NULL stack pointer\n");
-    fprintf(stderr, "  ctest           (obsoleted by thread-crash?)\n");
-    fprintf(stderr, "  exit            call exit(1)\n");
-    fprintf(stderr, "  crash           cause a SIGSEGV\n");
-    fprintf(stderr, "  abort           call abort()\n");
+    fprintf(stderr, "  smash-stack           overwrite a stack-guard canary\n");
+    fprintf(stderr, "  stack-overflow        recurse until the stack overflows\n");
+    fprintf(stderr, "  heap-corruption       cause a libc abort by corrupting the heap\n");
+    fprintf(stderr, "  heap-usage            cause a libc abort by abusing a heap function\n");
+    fprintf(stderr, "  nostack               crash with a NULL stack pointer\n");
+    fprintf(stderr, "  ctest                 (obsoleted by thread-crash?)\n");
+    fprintf(stderr, "  exit                  call exit(1)\n");
+    fprintf(stderr, "  abort                 call abort()\n");
+    fprintf(stderr, "  assert                call assert() without a function\n");
+    fprintf(stderr, "  assert2               call assert() with a function\n");
+    fprintf(stderr, "  LOG_ALWAYS_FATAL      call LOG_ALWAYS_FATAL\n");
+    fprintf(stderr, "  LOG_ALWAYS_FATAL_IF   call LOG_ALWAYS_FATAL\n");
+    fprintf(stderr, "  SIGPIPE               cause a SIGPIPE\n");
+    fprintf(stderr, "  SIGSEGV               cause a SIGSEGV (synonym: crash)\n");
     fprintf(stderr, "prefix any of the above with 'thread-' to not run\n");
     fprintf(stderr, "on the process' main thread.\n");
     return EXIT_SUCCESS;
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index a2b164e..76bd7a3 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -51,6 +51,7 @@
   pid_t pid, tid;
   uid_t uid, gid;
   uintptr_t abort_msg_address;
+  int32_t original_si_code;
 };
 
 static int write_string(const char* file, const char* string) {
@@ -218,6 +219,7 @@
   out_request->uid = cr.uid;
   out_request->gid = cr.gid;
   out_request->abort_msg_address = msg.abort_msg_address;
+  out_request->original_si_code = msg.original_si_code;
 
   if (msg.action == DEBUGGER_ACTION_CRASH) {
     // Ensure that the tid reported by the crashing process is valid.
@@ -302,9 +304,10 @@
             case SIGSTOP:
               if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
                 XLOG("stopped -- dumping to tombstone\n");
-                tombstone_path = engrave_tombstone(
-                    request.pid, request.tid, signal, request.abort_msg_address, true, true,
-                    &detach_failed, &total_sleep_time_usec);
+                tombstone_path = engrave_tombstone(request.pid, request.tid,
+                                                   signal, request.original_si_code,
+                                                   request.abort_msg_address, 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, -1, request.pid, request.tid, &detach_failed,
@@ -336,9 +339,10 @@
               kill(request.pid, SIGSTOP);
               // 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, request.abort_msg_address, !attach_gdb,
-                  false, &detach_failed, &total_sleep_time_usec);
+              tombstone_path = engrave_tombstone(request.pid, request.tid,
+                                                 signal, request.original_si_code,
+                                                 request.abort_msg_address, !attach_gdb, false,
+                                                 &detach_failed, &total_sleep_time_usec);
               break;
 
             default:
diff --git a/debuggerd/debuggerd.mk b/debuggerd/debuggerd.mk
deleted file mode 100644
index a3982c1..0000000
--- a/debuggerd/debuggerd.mk
+++ /dev/null
@@ -1,75 +0,0 @@
-# Copyright 2005 The Android Open Source Project
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-	backtrace.cpp \
-	debuggerd.cpp \
-	getevent.cpp \
-	tombstone.cpp \
-	utility.cpp \
-
-LOCAL_SRC_FILES_arm    := arm/machine.cpp
-LOCAL_SRC_FILES_arm64  := arm64/machine.cpp
-LOCAL_SRC_FILES_mips   := mips/machine.cpp
-LOCAL_SRC_FILES_x86    := x86/machine.cpp
-LOCAL_SRC_FILES_x86_64 := x86_64/machine.cpp
-
-LOCAL_CONLYFLAGS := -std=gnu99
-LOCAL_CPPFLAGS := -std=gnu++11
-LOCAL_CFLAGS := \
-	-Wall \
-	-Wno-array-bounds \
-	-Werror \
-	-Wno-unused-parameter \
-
-ifeq ($(ARCH_ARM_HAVE_VFP),true)
-LOCAL_CFLAGS_arm += -DWITH_VFP
-endif # ARCH_ARM_HAVE_VFP
-ifeq ($(ARCH_ARM_HAVE_VFP_D32),true)
-LOCAL_CFLAGS_arm += -DWITH_VFP_D32
-endif # ARCH_ARM_HAVE_VFP_D32
-
-LOCAL_SHARED_LIBRARIES := \
-	libbacktrace \
-	libc \
-	libcutils \
-	liblog \
-	libselinux \
-
-include external/stlport/libstlport.mk
-
-ifeq ($(TARGET_IS_64_BIT)|$(debuggerd_2nd_arch_var_prefix),true|)
-LOCAL_MODULE := debuggerd64
-LOCAL_NO_2ND_ARCH := true
-else
-LOCAL_MODULE := debuggerd
-LOCAL_32_BIT_ONLY := true
-endif
-
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := crasher.c
-LOCAL_SRC_FILES_arm    := arm/crashglue.S
-LOCAL_SRC_FILES_arm64  := arm64/crashglue.S
-LOCAL_SRC_FILES_mips   := mips/crashglue.S
-LOCAL_SRC_FILES_x86    := x86/crashglue.S
-LOCAL_SRC_FILES_x86_64 := x86_64/crashglue.S
-LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS += -fstack-protector-all -Wno-unused-parameter -Wno-free-nonheap-object
-#LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_SHARED_LIBRARIES := libcutils liblog libc
-
-LOCAL_2ND_ARCH_VAR_PREFIX := $(debuggerd_2nd_arch_var_prefix)
-
-ifeq ($(TARGET_IS_64_BIT)|$(debuggerd_2nd_arch_var_prefix),true|)
-LOCAL_MODULE := crasher64
-LOCAL_NO_2ND_ARCH := true
-else
-LOCAL_MODULE := crasher
-LOCAL_32_BIT_ONLY := true
-endif
-include $(BUILD_EXECUTABLE)
diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp
index 6a1b963..f95e572 100755
--- a/debuggerd/tombstone.cpp
+++ b/debuggerd/tombstone.cpp
@@ -55,7 +55,7 @@
 // Must match the path defined in NativeCrashListener.java
 #define NCRASH_SOCKET_PATH "/data/system/ndebugsocket"
 
-static bool signal_has_address(int sig) {
+static bool signal_has_si_addr(int sig) {
   switch (sig) {
     case SIGILL:
     case SIGFPE:
@@ -75,7 +75,7 @@
     case SIGFPE: return "SIGFPE";
     case SIGSEGV: return "SIGSEGV";
     case SIGPIPE: return "SIGPIPE";
-#ifdef SIGSTKFLT
+#if defined(SIGSTKFLT)
     case SIGSTKFLT: return "SIGSTKFLT";
 #endif
     case SIGSTOP: return "SIGSTOP";
@@ -97,13 +97,17 @@
         case ILL_COPROC: return "ILL_COPROC";
         case ILL_BADSTK: return "ILL_BADSTK";
       }
+      static_assert(NSIGILL == ILL_BADSTK, "missing ILL_* si_code");
       break;
     case SIGBUS:
       switch (code) {
         case BUS_ADRALN: return "BUS_ADRALN";
         case BUS_ADRERR: return "BUS_ADRERR";
         case BUS_OBJERR: return "BUS_OBJERR";
+        case BUS_MCEERR_AR: return "BUS_MCEERR_AR";
+        case BUS_MCEERR_AO: return "BUS_MCEERR_AO";
       }
+      static_assert(NSIGBUS == BUS_MCEERR_AO, "missing BUS_* si_code");
       break;
     case SIGFPE:
       switch (code) {
@@ -116,36 +120,36 @@
         case FPE_FLTINV: return "FPE_FLTINV";
         case FPE_FLTSUB: return "FPE_FLTSUB";
       }
+      static_assert(NSIGFPE == FPE_FLTSUB, "missing FPE_* si_code");
       break;
     case SIGSEGV:
       switch (code) {
         case SEGV_MAPERR: return "SEGV_MAPERR";
         case SEGV_ACCERR: return "SEGV_ACCERR";
       }
+      static_assert(NSIGSEGV == SEGV_ACCERR, "missing SEGV_* si_code");
       break;
     case SIGTRAP:
       switch (code) {
         case TRAP_BRKPT: return "TRAP_BRKPT";
         case TRAP_TRACE: return "TRAP_TRACE";
+        case TRAP_BRANCH: return "TRAP_BRANCH";
+        case TRAP_HWBKPT: return "TRAP_HWBKPT";
       }
+      static_assert(NSIGTRAP == TRAP_HWBKPT, "missing TRAP_* si_code");
       break;
   }
   // Then the other codes...
   switch (code) {
     case SI_USER: return "SI_USER";
-#if defined(SI_KERNEL)
     case SI_KERNEL: return "SI_KERNEL";
-#endif
     case SI_QUEUE: return "SI_QUEUE";
     case SI_TIMER: return "SI_TIMER";
     case SI_MESGQ: return "SI_MESGQ";
     case SI_ASYNCIO: return "SI_ASYNCIO";
-#if defined(SI_SIGIO)
     case SI_SIGIO: return "SI_SIGIO";
-#endif
-#if defined(SI_TKILL)
     case SI_TKILL: return "SI_TKILL";
-#endif
+    case SI_DETHREAD: return "SI_DETHREAD";
   }
   // Then give up...
   return "?";
@@ -167,20 +171,26 @@
   _LOG(log, SCOPE_AT_FAULT, "Build fingerprint: '%s'\n", fingerprint);
 }
 
-static void dump_fault_addr(log_t* log, pid_t tid, int sig) {
+static void dump_signal_info(log_t* log, pid_t tid, int signal, int si_code) {
   siginfo_t si;
-
   memset(&si, 0, sizeof(si));
-  if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si)){
+  if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) == -1) {
     _LOG(log, SCOPE_AT_FAULT, "cannot get siginfo: %s\n", strerror(errno));
-  } else if (signal_has_address(sig)) {
-    _LOG(log, SCOPE_AT_FAULT, "signal %d (%s), code %d (%s), fault addr %" PRIPTR "\n",
-         sig, get_signame(sig), si.si_code, get_sigcode(sig, si.si_code),
-         reinterpret_cast<uintptr_t>(si.si_addr));
-  } else {
-    _LOG(log, SCOPE_AT_FAULT, "signal %d (%s), code %d (%s), fault addr --------\n",
-         sig, get_signame(sig), si.si_code, get_sigcode(sig, si.si_code));
+    return;
   }
+
+  // bionic has to re-raise some signals, which overwrites the si_code with SI_TKILL.
+  si.si_code = si_code;
+
+  char addr_desc[32]; // ", fault addr 0x1234"
+  if (signal_has_si_addr(signal)) {
+    snprintf(addr_desc, sizeof(addr_desc), "%p", si.si_addr);
+  } else {
+    snprintf(addr_desc, sizeof(addr_desc), "--------");
+  }
+
+  _LOG(log, SCOPE_AT_FAULT, "signal %d (%s), code %d (%s), fault addr %s\n",
+       signal, get_signame(signal), si.si_code, get_sigcode(signal, si.si_code), addr_desc);
 }
 
 static void dump_thread_info(log_t* log, pid_t pid, pid_t tid, int scope_flags) {
@@ -345,7 +355,7 @@
     _LOG(log, scope_flags, "cannot get siginfo for %d: %s\n", tid, strerror(errno));
     return;
   }
-  if (!signal_has_address(si.si_signo)) {
+  if (!signal_has_si_addr(si.si_signo)) {
     return;
   }
 
@@ -584,8 +594,9 @@
 }
 
 // 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, uintptr_t abort_msg_address,
-                       bool dump_sibling_threads, int* total_sleep_time_usec) {
+static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, int si_code,
+                       uintptr_t abort_msg_address, 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");
@@ -607,7 +618,7 @@
   dump_revision_info(log);
   dump_thread_info(log, pid, tid, SCOPE_AT_FAULT);
   if (signal) {
-    dump_fault_addr(log, tid, signal);
+    dump_signal_info(log, tid, signal, si_code);
   }
 
   UniquePtr<BacktraceMap> map(BacktraceMap::Create(pid));
@@ -721,9 +732,9 @@
   return amfd;
 }
 
-char* engrave_tombstone(
-    pid_t pid, pid_t tid, int signal, uintptr_t abort_msg_address, bool dump_sibling_threads,
-    bool quiet, bool* detach_failed, int* total_sleep_time_usec) {
+char* engrave_tombstone(pid_t pid, pid_t tid, int signal, int original_si_code,
+                        uintptr_t abort_msg_address, bool dump_sibling_threads, bool quiet,
+                        bool* detach_failed, int* total_sleep_time_usec) {
   if ((mkdir(TOMBSTONE_DIR, 0755) == -1) && (errno != EEXIST)) {
       LOG("failed to create %s: %s\n", TOMBSTONE_DIR, strerror(errno));
   }
@@ -748,8 +759,8 @@
   log.tfd = fd;
   log.amfd = activity_manager_connect();
   log.quiet = quiet;
-  *detach_failed = dump_crash(
-      &log, pid, tid, signal, abort_msg_address, dump_sibling_threads, total_sleep_time_usec);
+  *detach_failed = dump_crash(&log, pid, tid, signal, original_si_code, abort_msg_address,
+                              dump_sibling_threads, total_sleep_time_usec);
 
   close(log.amfd);
   close(fd);
diff --git a/debuggerd/tombstone.h b/debuggerd/tombstone.h
index e9878bf..3574e84 100644
--- a/debuggerd/tombstone.h
+++ b/debuggerd/tombstone.h
@@ -23,7 +23,9 @@
 
 /* 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, uintptr_t abort_msg_address,
-        bool dump_sibling_threads, bool quiet, bool* detach_failed, int* total_sleep_time_usec);
+char* engrave_tombstone(pid_t pid, pid_t tid, int signal, int original_si_code,
+                        uintptr_t abort_msg_address,
+                        bool dump_sibling_threads, bool quiet,
+                        bool* detach_failed, int* total_sleep_time_usec);
 
 #endif // _DEBUGGERD_TOMBSTONE_H
diff --git a/debuggerd/utility.cpp b/debuggerd/utility.cpp
index 9b20914..d4c252f 100644
--- a/debuggerd/utility.cpp
+++ b/debuggerd/utility.cpp
@@ -24,6 +24,7 @@
 #include <sys/wait.h>
 
 #include <backtrace/Backtrace.h>
+#include <log/log.h>
 #include <log/logd.h>
 
 const int sleep_time_usec = 50000;         // 0.05 seconds
@@ -64,7 +65,7 @@
   }
 
   if (want_log_write) {
-    __android_log_write(ANDROID_LOG_INFO, "DEBUG", buf);
+    __android_log_buf_write(LOG_ID_CRASH, ANDROID_LOG_INFO, "DEBUG", buf);
     if (want_amfd_write) {
       int written = write_to_am(log->amfd, buf, len);
       if (written <= 0) {
diff --git a/fs_mgr/fs_mgr_fstab.c b/fs_mgr/fs_mgr_fstab.c
index 6145771..45bbfdc 100644
--- a/fs_mgr/fs_mgr_fstab.c
+++ b/fs_mgr/fs_mgr_fstab.c
@@ -337,7 +337,7 @@
 /* Add an entry to the fstab, and return 0 on success or -1 on error */
 int fs_mgr_add_entry(struct fstab *fstab,
                      const char *mount_point, const char *fs_type,
-                     const char *blk_device, long long length)
+                     const char *blk_device)
 {
     struct fstab_rec *new_fstab_recs;
     int n = fstab->num_entries;
diff --git a/fs_mgr/fs_mgr_main.c b/fs_mgr/fs_mgr_main.c
index 4bde4a1..e5a00d5 100644
--- a/fs_mgr/fs_mgr_main.c
+++ b/fs_mgr/fs_mgr_main.c
@@ -80,10 +80,10 @@
     int a_flag=0;
     int u_flag=0;
     int n_flag=0;
-    char *n_name;
-    char *n_blk_dev;
-    char *fstab_file;
-    struct fstab *fstab;
+    char *n_name=NULL;
+    char *n_blk_dev=NULL;
+    char *fstab_file=NULL;
+    struct fstab *fstab=NULL;
 
     klog_init();
     klog_set_level(6);
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 0f90c32..835cf64 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -57,7 +57,7 @@
                           char *real_blk_device, int size);
 int fs_mgr_add_entry(struct fstab *fstab,
                      const char *mount_point, const char *fs_type,
-                     const char *blk_device, long long length);
+                     const char *blk_device);
 struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path);
 int fs_mgr_is_voldmanaged(struct fstab_rec *fstab);
 int fs_mgr_is_nonremovable(struct fstab_rec *fstab);
diff --git a/healthd/healthd_board_default.cpp b/healthd/healthd_board_default.cpp
index b2bb516..ed4ddb4 100644
--- a/healthd/healthd_board_default.cpp
+++ b/healthd/healthd_board_default.cpp
@@ -16,13 +16,13 @@
 
 #include <healthd.h>
 
-void healthd_board_init(struct healthd_config *config)
+void healthd_board_init(struct healthd_config*)
 {
     // use defaults
 }
 
 
-int healthd_board_battery_update(struct android::BatteryProperties *props)
+int healthd_board_battery_update(struct android::BatteryProperties*)
 {
     // return 0 to log periodic polled battery status to kernel log
     return 0;
diff --git a/include/android/log.h b/include/android/log.h
index 0ea4c29..f5b1900 100644
--- a/include/android/log.h
+++ b/include/android/log.h
@@ -110,11 +110,11 @@
                          const char *fmt, va_list ap);
 
 /*
- * Log an assertion failure and SIGTRAP the process to have a chance
- * to inspect it, if a debugger is attached. This uses the FATAL priority.
+ * Log an assertion failure and abort the process to have a chance
+ * to inspect it if a debugger is attached. This uses the FATAL priority.
  */
 void __android_log_assert(const char *cond, const char *tag,
-			  const char *fmt, ...)    
+                          const char *fmt, ...)
 #if defined(__GNUC__)
     __attribute__ ((noreturn))
     __attribute__ ((format(printf, 3, 4)))
diff --git a/include/backtrace/BacktraceMap.h b/include/backtrace/BacktraceMap.h
index 06da2f4..13083bd 100644
--- a/include/backtrace/BacktraceMap.h
+++ b/include/backtrace/BacktraceMap.h
@@ -45,7 +45,7 @@
   virtual ~BacktraceMap();
 
   // Get the map data structure for the given address.
-  const backtrace_map_t* Find(uintptr_t addr);
+  virtual const backtrace_map_t* Find(uintptr_t addr);
 
   // The flags returned are the same flags as used by the mmap call.
   // The values are PROT_*.
diff --git a/include/cutils/debugger.h b/include/cutils/debugger.h
index af80e2c..ae6bfc4 100644
--- a/include/cutils/debugger.h
+++ b/include/cutils/debugger.h
@@ -42,6 +42,7 @@
     debugger_action_t action;
     pid_t tid;
     uintptr_t abort_msg_address;
+    int32_t original_si_code;
 } debugger_msg_t;
 
 /* Dumps a process backtrace, registers, and stack to a tombstone file (requires root).
diff --git a/include/log/log.h b/include/log/log.h
index d469f40..5b76c1a 100644
--- a/include/log/log.h
+++ b/include/log/log.h
@@ -550,6 +550,7 @@
     LOG_ID_RADIO = 1,
     LOG_ID_EVENTS = 2,
     LOG_ID_SYSTEM = 3,
+    LOG_ID_CRASH = 4,
 
     LOG_ID_MAX
 } log_id_t;
diff --git a/include/log/logger.h b/include/log/logger.h
index 3c6ea30..ed39c4f 100644
--- a/include/log/logger.h
+++ b/include/log/logger.h
@@ -142,9 +142,7 @@
 
 int android_logger_clear(struct logger *logger);
 long android_logger_get_log_size(struct logger *logger);
-#ifdef USERDEBUG_BUILD
 int android_logger_set_log_size(struct logger *logger, unsigned long size);
-#endif
 long android_logger_get_log_readable_size(struct logger *logger);
 int android_logger_get_log_version(struct logger *logger);
 
@@ -152,12 +150,10 @@
 
 ssize_t android_logger_get_statistics(struct logger_list *logger_list,
                                       char *buf, size_t len);
-#ifdef USERDEBUG_BUILD
 ssize_t android_logger_get_prune_list(struct logger_list *logger_list,
                                       char *buf, size_t len);
 int android_logger_set_prune_list(struct logger_list *logger_list,
                                   char *buf, size_t len);
-#endif
 
 struct logger_list *android_logger_list_alloc(int mode,
                                               unsigned int tail,
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index 9c26baf..d662107 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -251,6 +251,7 @@
 
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/*" },
     { 00755, AID_ROOT,      AID_ROOT,      0, "system/lib/valgrind/*" },
+    { 00755, AID_ROOT,      AID_ROOT,      0, "system/lib64/valgrind/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/xbin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/vendor/bin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "vendor/bin/*" },
diff --git a/include/system/audio.h b/include/system/audio.h
index aa7ac02..f36befb 100644
--- a/include/system/audio.h
+++ b/include/system/audio.h
@@ -471,12 +471,16 @@
 
 static inline bool audio_is_bluetooth_sco_device(audio_devices_t device)
 {
-    device &= ~AUDIO_DEVICE_BIT_IN;
-    if ((popcount(device) == 1) && (device & (AUDIO_DEVICE_OUT_ALL_SCO |
-                   AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET)))
-        return true;
-    else
-        return false;
+    if ((device & AUDIO_DEVICE_BIT_IN) == 0) {
+        if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_OUT_ALL_SCO) == 0))
+            return true;
+    } else {
+        device &= ~AUDIO_DEVICE_BIT_IN;
+        if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) == 0))
+            return true;
+    }
+
+    return false;
 }
 
 static inline bool audio_is_usb_device(audio_devices_t device)
diff --git a/include/sysutils/FrameworkListener.h b/include/sysutils/FrameworkListener.h
index f1a4b43..18049cd 100644
--- a/include/sysutils/FrameworkListener.h
+++ b/include/sysutils/FrameworkListener.h
@@ -36,6 +36,7 @@
 public:
     FrameworkListener(const char *socketName);
     FrameworkListener(const char *socketName, bool withSeq);
+    FrameworkListener(int sock);
     virtual ~FrameworkListener() {}
 
 protected:
diff --git a/include/utils/Unicode.h b/include/utils/Unicode.h
index c8c87c3..5b98de2 100644
--- a/include/utils/Unicode.h
+++ b/include/utils/Unicode.h
@@ -22,8 +22,11 @@
 
 extern "C" {
 
+// Definitions exist in C++11
+#if defined __cplusplus && __cplusplus < 201103L
 typedef uint32_t char32_t;
 typedef uint16_t char16_t;
+#endif
 
 // Standard string functions on char16_t strings.
 int strcmp16(const char16_t *, const char16_t *);
diff --git a/init/Android.mk b/init/Android.mk
index 740d10f..15a23be 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -17,6 +17,8 @@
 	ueventd_parser.c \
 	watchdogd.c
 
+LOCAL_CFLAGS    += -Wno-unused-parameter
+
 ifeq ($(strip $(INIT_BOOTCHART)),true)
 LOCAL_SRC_FILES += bootchart.c
 LOCAL_CFLAGS    += -DBOOTCHART=1
diff --git a/init/property_service.c b/init/property_service.c
index fe7cbb5..eb19f93 100644
--- a/init/property_service.c
+++ b/init/property_service.c
@@ -269,6 +269,7 @@
         return;
     }
     write(fd, value, strlen(value));
+    fsync(fd);
     close(fd);
 
     snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, name);
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk
index 2e56756..a7305da 100755
--- a/libbacktrace/Android.mk
+++ b/libbacktrace/Android.mk
@@ -46,10 +46,6 @@
 	libcutils \
 	libgccdemangle \
 
-# To enable using libunwind on each arch, add it to this list.
-libunwind_architectures := arm arm64 mips x86 x86_64
-
-ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),$(libunwind_architectures)))
 libbacktrace_src_files += \
 	UnwindCurrent.cpp \
 	UnwindMap.cpp \
@@ -68,24 +64,6 @@
 libbacktrace_static_libraries_host := \
 	libcutils \
 
-else
-libbacktrace_src_files += \
-	Corkscrew.cpp \
-
-libbacktrace_c_includes := \
-	system/core/libcorkscrew \
-
-libbacktrace_shared_libraries := \
-	libcorkscrew \
-
-libbacktrace_shared_libraries_target += \
-	libdl \
-
-libbacktrace_ldlibs_host := \
-	-ldl \
-
-endif
-
 module := libbacktrace
 module_tag := optional
 build_type := target
@@ -118,20 +96,13 @@
 	-fno-builtin \
 	-O0 \
 	-g \
-	-DGTEST_HAS_STD_STRING \
-
-ifneq ($(TARGET_ARCH),arm64)
-backtrace_test_cflags += -fstack-protector-all
-else
-  $(info TODO: $(LOCAL_PATH)/Android.mk -fstack-protector not yet available for the AArch64 toolchain)
-  common_cflags += -fno-stack-protector
-endif # arm64
 
 backtrace_test_cflags_target := \
-	-DGTEST_OS_LINUX_ANDROID \
+	-DENABLE_PSS_TESTS \
 
 backtrace_test_src_files := \
 	backtrace_test.cpp \
+	GetPss.cpp \
 	thread_utils.c \
 
 backtrace_test_ldlibs := \
diff --git a/libbacktrace/BacktraceImpl.cpp b/libbacktrace/BacktraceImpl.cpp
index 855810e..05007d9 100644
--- a/libbacktrace/BacktraceImpl.cpp
+++ b/libbacktrace/BacktraceImpl.cpp
@@ -27,6 +27,7 @@
 #include <backtrace/BacktraceMap.h>
 
 #include "BacktraceImpl.h"
+#include "BacktraceLog.h"
 #include "thread_utils.h"
 
 //-------------------------------------------------------------------------
diff --git a/libbacktrace/BacktraceImpl.h b/libbacktrace/BacktraceImpl.h
index 48dd11c..7b31c38 100755
--- a/libbacktrace/BacktraceImpl.h
+++ b/libbacktrace/BacktraceImpl.h
@@ -21,11 +21,6 @@
 #include <backtrace/BacktraceMap.h>
 
 #include <sys/types.h>
-#include <log/log.h>
-
-// Macro to log the function name along with the warning message.
-#define BACK_LOGW(format, ...) \
-  ALOGW("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__)
 
 class BacktraceImpl {
 public:
diff --git a/libbacktrace/BacktraceLog.h b/libbacktrace/BacktraceLog.h
new file mode 100755
index 0000000..1632ec2
--- /dev/null
+++ b/libbacktrace/BacktraceLog.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 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 _LIBBACKTRACE_BACKTRACE_LOG_H
+#define _LIBBACKTRACE_BACKTRACE_LOG_H
+
+#define LOG_TAG "libbacktrace"
+
+#include <log/log.h>
+
+// Macro to log the function name along with the warning message.
+#define BACK_LOGW(format, ...) \
+  ALOGW("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__)
+
+#endif // _LIBBACKTRACE_BACKTRACE_LOG_H
diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp
index 6eb290d..0056f4b 100644
--- a/libbacktrace/BacktraceMap.cpp
+++ b/libbacktrace/BacktraceMap.cpp
@@ -108,11 +108,11 @@
 
 #if defined(__APPLE__)
   // cmd is guaranteed to always be big enough to hold this string.
-  sprintf(cmd, "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid_);
+  snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid_);
   FILE* fp = popen(cmd, "r");
 #else
   // path is guaranteed to always be big enough to hold this string.
-  sprintf(path, "/proc/%d/maps", pid_);
+  snprintf(path, sizeof(path), "/proc/%d/maps", pid_);
   FILE* fp = fopen(path, "r");
 #endif
   if (fp == NULL) {
diff --git a/libbacktrace/BacktraceThread.cpp b/libbacktrace/BacktraceThread.cpp
index 5ffe516..4cda19e 100644
--- a/libbacktrace/BacktraceThread.cpp
+++ b/libbacktrace/BacktraceThread.cpp
@@ -23,6 +23,7 @@
 
 #include <cutils/atomic.h>
 
+#include "BacktraceLog.h"
 #include "BacktraceThread.h"
 #include "thread_utils.h"
 
diff --git a/libbacktrace/Corkscrew.cpp b/libbacktrace/Corkscrew.cpp
deleted file mode 100644
index efeee2e..0000000
--- a/libbacktrace/Corkscrew.cpp
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-#define LOG_TAG "libbacktrace"
-
-#include <backtrace/Backtrace.h>
-
-#include <string.h>
-
-#include <backtrace-arch.h>
-#include <corkscrew/backtrace.h>
-
-#ifndef __USE_GNU
-#define __USE_GNU
-#endif
-#include <dlfcn.h>
-
-#include "Corkscrew.h"
-
-//-------------------------------------------------------------------------
-// CorkscrewMap functions.
-//-------------------------------------------------------------------------
-CorkscrewMap::CorkscrewMap(pid_t pid) : BacktraceMap(pid), map_info_(NULL) {
-}
-
-CorkscrewMap::~CorkscrewMap() {
-  if (map_info_) {
-    free_map_info_list(map_info_);
-    map_info_ = NULL;
-  }
-}
-
-bool CorkscrewMap::Build() {
-  map_info_ = load_map_info_list(pid_);
-
-  // Use the information in map_info_ to construct the BacktraceMap data
-  // rather than reparsing /proc/self/maps.
-  map_info_t* cur_map = map_info_;
-  while (cur_map) {
-    backtrace_map_t map;
-    map.start = cur_map->start;
-    map.end = cur_map->end;
-    map.flags = 0;
-    if (cur_map->is_readable) {
-      map.flags |= PROT_READ;
-    }
-    if (cur_map->is_writable) {
-      map.flags |= PROT_WRITE;
-    }
-    if (cur_map->is_executable) {
-      map.flags |= PROT_EXEC;
-    }
-    map.name = cur_map->name;
-
-    // The maps are in descending order, but we want them in ascending order.
-    maps_.push_front(map);
-
-    cur_map = cur_map->next;
-  }
-  return map_info_ != NULL;
-}
-
-//-------------------------------------------------------------------------
-// CorkscrewCommon functions.
-//-------------------------------------------------------------------------
-bool CorkscrewCommon::GenerateFrameData(
-    backtrace_frame_t* cork_frames, ssize_t num_frames) {
-  if (num_frames < 0) {
-    BACK_LOGW("libcorkscrew unwind failed.");
-    return false;
-  }
-
-  std::vector<backtrace_frame_data_t>* frames = GetFrames();
-  frames->resize(num_frames);
-  size_t i = 0;
-  for (std::vector<backtrace_frame_data_t>::iterator it = frames->begin();
-       it != frames->end(); ++it, ++i) {
-    it->num = i;
-    it->pc = cork_frames[i].absolute_pc;
-    it->sp = cork_frames[i].stack_top;
-    it->stack_size = cork_frames[i].stack_size;
-    it->func_offset = 0;
-
-    it->map = FindMap(it->pc);
-    it->func_name = GetFunctionName(it->pc, &it->func_offset);
-  }
-  return true;
-}
-
-//-------------------------------------------------------------------------
-// CorkscrewCurrent functions.
-//-------------------------------------------------------------------------
-CorkscrewCurrent::CorkscrewCurrent() {
-}
-
-CorkscrewCurrent::~CorkscrewCurrent() {
-}
-
-bool CorkscrewCurrent::Unwind(size_t num_ignore_frames) {
-  backtrace_frame_t frames[MAX_BACKTRACE_FRAMES];
-  ssize_t num_frames = unwind_backtrace(frames, num_ignore_frames, MAX_BACKTRACE_FRAMES);
-
-  return GenerateFrameData(frames, num_frames);
-}
-
-std::string CorkscrewCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
-  *offset = 0;
-
-  Dl_info info;
-  const backtrace_map_t* map = FindMap(pc);
-  if (map) {
-    if (dladdr((const void*)pc, &info)) {
-      if (info.dli_sname) {
-        *offset = pc - map->start - (uintptr_t)info.dli_saddr + (uintptr_t)info.dli_fbase;
-        return info.dli_sname;
-      }
-    } else {
-      // dladdr(3) didn't find a symbol; maybe it's static? Look in the ELF file...
-      symbol_table_t* symbol_table = load_symbol_table(map->name.c_str());
-      if (symbol_table) {
-        // First check if we can find the symbol using a relative pc.
-        std::string name;
-        const symbol_t* elf_symbol = find_symbol(symbol_table, pc - map->start);
-        if (elf_symbol) {
-          name = elf_symbol->name;
-          *offset = pc - map->start - elf_symbol->start;
-        } else if ((elf_symbol = find_symbol(symbol_table, pc)) != NULL) {
-          // Found the symbol using the absolute pc.
-          name = elf_symbol->name;
-          *offset = pc - elf_symbol->start;
-        }
-        free_symbol_table(symbol_table);
-        return name;
-      }
-    }
-  }
-  return "";
-}
-
-//-------------------------------------------------------------------------
-// CorkscrewThread functions.
-//-------------------------------------------------------------------------
-CorkscrewThread::CorkscrewThread() {
-}
-
-CorkscrewThread::~CorkscrewThread() {
-}
-
-void CorkscrewThread::ThreadUnwind(
-    siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) {
-  backtrace_frame_t cork_frames[MAX_BACKTRACE_FRAMES];
-  CorkscrewMap* map = static_cast<CorkscrewMap*>(GetMap());
-  ssize_t num_frames = unwind_backtrace_signal_arch(
-      siginfo, sigcontext, map->GetMapInfo(), cork_frames,
-      num_ignore_frames, MAX_BACKTRACE_FRAMES);
-  if (num_frames > 0) {
-    std::vector<backtrace_frame_data_t>* frames = GetFrames();
-    frames->resize(num_frames);
-    size_t i = 0;
-    for (std::vector<backtrace_frame_data_t>::iterator it = frames->begin();
-         it != frames->end(); ++it, ++i) {
-      it->num = i;
-      it->pc = cork_frames[i].absolute_pc;
-      it->sp = cork_frames[i].stack_top;
-      it->stack_size = cork_frames[i].stack_size;
-      it->map = NULL;
-      it->func_offset = 0;
-    }
-  }
-}
-
-//-------------------------------------------------------------------------
-// CorkscrewPtrace functions.
-//-------------------------------------------------------------------------
-CorkscrewPtrace::CorkscrewPtrace() : ptrace_context_(NULL) {
-}
-
-CorkscrewPtrace::~CorkscrewPtrace() {
-  if (ptrace_context_) {
-    free_ptrace_context(ptrace_context_);
-    ptrace_context_ = NULL;
-  }
-}
-
-bool CorkscrewPtrace::Unwind(size_t num_ignore_frames) {
-  ptrace_context_ = load_ptrace_context(Tid());
-
-  backtrace_frame_t frames[MAX_BACKTRACE_FRAMES];
-  ssize_t num_frames = unwind_backtrace_ptrace(
-      Tid(), ptrace_context_, frames, num_ignore_frames, MAX_BACKTRACE_FRAMES);
-
-  return GenerateFrameData(frames, num_frames);
-}
-
-std::string CorkscrewPtrace::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
-  // Get information about a different process.
-  const map_info_t* map_info;
-  const symbol_t* symbol;
-  find_symbol_ptrace(ptrace_context_, pc, &map_info, &symbol);
-  char* symbol_name = NULL;
-  if (symbol) {
-    if (map_info) {
-      *offset = pc - map_info->start - symbol->start;
-    }
-    symbol_name = symbol->name;
-    return symbol_name;
-  }
-
-  return "";
-}
-
-//-------------------------------------------------------------------------
-// C++ object creation functions.
-//-------------------------------------------------------------------------
-Backtrace* CreateCurrentObj(BacktraceMap* map) {
-  return new BacktraceCurrent(new CorkscrewCurrent(), map);
-}
-
-Backtrace* CreatePtraceObj(pid_t pid, pid_t tid, BacktraceMap* map) {
-  return new BacktracePtrace(new CorkscrewPtrace(), pid, tid, map);
-}
-
-Backtrace* CreateThreadObj(pid_t tid, BacktraceMap* map) {
-  CorkscrewThread* thread_obj = new CorkscrewThread();
-  return new BacktraceThread(thread_obj, thread_obj, tid, map);
-}
-
-//-------------------------------------------------------------------------
-// BacktraceMap create function.
-//-------------------------------------------------------------------------
-BacktraceMap* BacktraceMap::Create(pid_t pid) {
-  BacktraceMap* map = new CorkscrewMap(pid);
-  if (!map->Build()) {
-    delete map;
-    return NULL;
-  }
-  return map;
-}
diff --git a/libbacktrace/Corkscrew.h b/libbacktrace/Corkscrew.h
deleted file mode 100644
index 1633398..0000000
--- a/libbacktrace/Corkscrew.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2013 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 _LIBBACKTRACE_CORKSCREW_H
-#define _LIBBACKTRACE_CORKSCREW_H
-
-#include <inttypes.h>
-
-#include <string>
-
-#include <backtrace/Backtrace.h>
-#include <backtrace/BacktraceMap.h>
-
-#include <corkscrew/backtrace.h>
-
-#include "BacktraceImpl.h"
-#include "BacktraceThread.h"
-
-class CorkscrewMap : public BacktraceMap {
-public:
-  CorkscrewMap(pid_t pid);
-  virtual ~CorkscrewMap();
-
-  virtual bool Build();
-
-  map_info_t* GetMapInfo() { return map_info_; }
-
-private:
-  map_info_t* map_info_;
-};
-
-class CorkscrewCommon : public BacktraceImpl {
-public:
-  bool GenerateFrameData(backtrace_frame_t* cork_frames, ssize_t num_frames);
-};
-
-class CorkscrewCurrent : public CorkscrewCommon {
-public:
-  CorkscrewCurrent();
-  virtual ~CorkscrewCurrent();
-
-  virtual bool Unwind(size_t num_ignore_threads);
-
-  virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
-};
-
-class CorkscrewThread : public CorkscrewCurrent, public BacktraceThreadInterface {
-public:
-  CorkscrewThread();
-  virtual ~CorkscrewThread();
-
-  virtual void ThreadUnwind(
-      siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames);
-};
-
-class CorkscrewPtrace : public CorkscrewCommon {
-public:
-  CorkscrewPtrace();
-  virtual ~CorkscrewPtrace();
-
-  virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
-
-  virtual bool Unwind(size_t num_ignore_threads);
-
-private:
-  ptrace_context_t* ptrace_context_;
-};
-
-#endif // _LIBBACKTRACE_CORKSCREW_H
diff --git a/libbacktrace/GetPss.cpp b/libbacktrace/GetPss.cpp
new file mode 100644
index 0000000..442383b
--- /dev/null
+++ b/libbacktrace/GetPss.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2014 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 <assert.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+// This is an extremely simplified version of libpagemap.
+
+#define _BITS(x, offset, bits) (((x) >> offset) & ((1LL << (bits)) - 1))
+
+#define PAGEMAP_PRESENT(x)     (_BITS(x, 63, 1))
+#define PAGEMAP_SWAPPED(x)     (_BITS(x, 62, 1))
+#define PAGEMAP_SHIFT(x)       (_BITS(x, 55, 6))
+#define PAGEMAP_PFN(x)         (_BITS(x, 0, 55))
+#define PAGEMAP_SWAP_OFFSET(x) (_BITS(x, 5, 50))
+#define PAGEMAP_SWAP_TYPE(x)   (_BITS(x, 0,  5))
+
+static bool ReadData(int fd, unsigned long place, uint64_t *data) {
+  if (lseek(fd, place * sizeof(uint64_t), SEEK_SET) < 0) {
+    return false;
+  }
+  if (read(fd, (void*)data, sizeof(uint64_t)) != (ssize_t)sizeof(uint64_t)) {
+    return false;
+  }
+  return true;
+}
+
+size_t GetPssBytes() {
+  FILE* maps = fopen("/proc/self/maps", "r");
+  assert(maps != NULL);
+
+  int pagecount_fd = open("/proc/kpagecount", O_RDONLY);
+  assert(pagecount_fd >= 0);
+
+  int pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
+  assert(pagemap_fd >= 0);
+
+  char line[4096];
+  size_t total_pss = 0;
+  int pagesize = getpagesize();
+  while (fgets(line, sizeof(line), maps)) {
+    uintptr_t start, end;
+    if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " ", &start, &end) != 2) {
+      total_pss = 0;
+      break;
+    }
+    for (size_t page = start/pagesize; page < end/pagesize; page++) {
+      uint64_t data;
+      if (ReadData(pagemap_fd, page, &data)) {
+        if (PAGEMAP_PRESENT(data) && !PAGEMAP_SWAPPED(data)) {
+          uint64_t count;
+          if (ReadData(pagecount_fd, PAGEMAP_PFN(data), &count)) {
+            total_pss += (count >= 1) ? pagesize / count : 0;
+          }
+        }
+      }
+    }
+  }
+
+  fclose(maps);
+
+  close(pagecount_fd);
+  close(pagemap_fd);
+
+  return total_pss;
+}
diff --git a/libbacktrace/GetPss.h b/libbacktrace/GetPss.h
new file mode 100644
index 0000000..787c33d
--- /dev/null
+++ b/libbacktrace/GetPss.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2014 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 _LIBBACKTRACE_GET_PSS_H
+#define _LIBBACKTRACE_GET_PSS_H
+
+size_t GetPssBytes();
+
+#endif // _LIBBACKTRACE_GET_PSS_H
diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp
index 81e69bb..67d372a 100755
--- a/libbacktrace/UnwindCurrent.cpp
+++ b/libbacktrace/UnwindCurrent.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "libbacktrace"
-
 #include <sys/ucontext.h>
 #include <sys/types.h>
 
@@ -25,6 +23,7 @@
 #define UNW_LOCAL_ONLY
 #include <libunwind.h>
 
+#include "BacktraceLog.h"
 #include "UnwindCurrent.h"
 #include "UnwindMap.h"
 
@@ -43,7 +42,7 @@
     BACK_LOGW("unw_getcontext failed %d", ret);
     return false;
   }
-  return UnwindFromContext(num_ignore_frames, true);
+  return UnwindFromContext(num_ignore_frames, false);
 }
 
 std::string UnwindCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
@@ -58,12 +57,14 @@
   return "";
 }
 
-bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool resolve) {
+bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool within_handler) {
   // The cursor structure is pretty large, do not put it on the stack.
   unw_cursor_t* cursor = new unw_cursor_t;
   int ret = unw_init_local(cursor, &context_);
   if (ret < 0) {
-    BACK_LOGW("unw_init_local failed %d", ret);
+    if (!within_handler) {
+      BACK_LOGW("unw_init_local failed %d", ret);
+    }
     delete cursor;
     return false;
   }
@@ -75,13 +76,17 @@
     unw_word_t pc;
     ret = unw_get_reg(cursor, UNW_REG_IP, &pc);
     if (ret < 0) {
-      BACK_LOGW("Failed to read IP %d", ret);
+      if (!within_handler) {
+        BACK_LOGW("Failed to read IP %d", ret);
+      }
       break;
     }
     unw_word_t sp;
     ret = unw_get_reg(cursor, UNW_REG_SP, &sp);
     if (ret < 0) {
-      BACK_LOGW("Failed to read SP %d", ret);
+      if (!within_handler) {
+        BACK_LOGW("Failed to read SP %d", ret);
+      }
       break;
     }
 
@@ -99,7 +104,7 @@
         prev->stack_size = frame->sp - prev->sp;
       }
 
-      if (resolve) {
+      if (!within_handler) {
         frame->func_name = GetFunctionName(frame->pc, &frame->func_offset);
         frame->map = FindMap(frame->pc);
       } else {
@@ -155,7 +160,7 @@
 void UnwindThread::ThreadUnwind(
     siginfo_t* /*siginfo*/, void* sigcontext, size_t num_ignore_frames) {
   ExtractContext(sigcontext);
-  UnwindFromContext(num_ignore_frames, false);
+  UnwindFromContext(num_ignore_frames, true);
 }
 
 //-------------------------------------------------------------------------
diff --git a/libbacktrace/UnwindCurrent.h b/libbacktrace/UnwindCurrent.h
index acce110..41080c7 100644
--- a/libbacktrace/UnwindCurrent.h
+++ b/libbacktrace/UnwindCurrent.h
@@ -34,7 +34,7 @@
 
   virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
 
-  bool UnwindFromContext(size_t num_ignore_frames, bool resolve);
+  bool UnwindFromContext(size_t num_ignore_frames, bool within_handler);
 
   void ExtractContext(void* sigcontext);
 
diff --git a/libbacktrace/UnwindMap.cpp b/libbacktrace/UnwindMap.cpp
index 8268db6..1615518 100644
--- a/libbacktrace/UnwindMap.cpp
+++ b/libbacktrace/UnwindMap.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "libbacktrace"
-
 #include <pthread.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -24,6 +22,7 @@
 
 #include <libunwind.h>
 
+#include "BacktraceLog.h"
 #include "UnwindMap.h"
 
 //-------------------------------------------------------------------------
@@ -32,57 +31,21 @@
 // only update the local address space once, and keep a reference count
 // of maps using the same map cursor.
 //-------------------------------------------------------------------------
-static pthread_mutex_t g_map_mutex = PTHREAD_MUTEX_INITIALIZER;
-static unw_map_cursor_t g_map_cursor;
-static int g_map_references = 0;
-
 UnwindMap::UnwindMap(pid_t pid) : BacktraceMap(pid) {
-  map_cursor_.map_list = NULL;
 }
 
 UnwindMap::~UnwindMap() {
-  if (pid_ == getpid()) {
-    pthread_mutex_lock(&g_map_mutex);
-    if (--g_map_references == 0) {
-      // Clear the local address space map.
-      unw_map_local_set(NULL);
-      unw_map_cursor_destroy(&map_cursor_);
-    }
-    pthread_mutex_unlock(&g_map_mutex);
-  } else {
-    unw_map_cursor_destroy(&map_cursor_);
-  }
+  unw_map_cursor_destroy(&map_cursor_);
+  unw_map_cursor_clear(&map_cursor_);
 }
 
-bool UnwindMap::Build() {
-  bool return_value = true;
-  if (pid_ == getpid()) {
-    pthread_mutex_lock(&g_map_mutex);
-    if (g_map_references == 0) {
-      return_value = (unw_map_cursor_create(&map_cursor_, pid_) == 0);
-      if (return_value) {
-        // Set the local address space map to our new map.
-        unw_map_local_set(&map_cursor_);
-        g_map_references = 1;
-        g_map_cursor = map_cursor_;
-      }
-    } else {
-      g_map_references++;
-      map_cursor_ = g_map_cursor;
-    }
-    pthread_mutex_unlock(&g_map_mutex);
-  } else {
-    return_value = (unw_map_cursor_create(&map_cursor_, pid_) == 0);
-  }
-
-  if (!return_value)
-    return false;
-
+bool UnwindMap::GenerateMap() {
   // Use the map_cursor information to construct the BacktraceMap data
   // rather than reparsing /proc/self/maps.
   unw_map_cursor_reset(&map_cursor_);
+
   unw_map_t unw_map;
-  while (unw_map_cursor_get(&map_cursor_, &unw_map)) {
+  while (unw_map_cursor_get_next(&map_cursor_, &unw_map)) {
     backtrace_map_t map;
 
     map.start = unw_map.start;
@@ -97,11 +60,82 @@
   return true;
 }
 
+bool UnwindMap::Build() {
+  return (unw_map_cursor_create(&map_cursor_, pid_) == 0) && GenerateMap();
+}
+
+UnwindMapLocal::UnwindMapLocal() : UnwindMap(getpid()), map_created_(false) {
+}
+
+UnwindMapLocal::~UnwindMapLocal() {
+  if (map_created_) {
+    unw_map_local_destroy();
+    unw_map_cursor_clear(&map_cursor_);
+  }
+}
+
+bool UnwindMapLocal::GenerateMap() {
+  // It's possible for the map to be regenerated while this loop is occurring.
+  // If that happens, get the map again, but only try at most three times
+  // before giving up.
+  for (int i = 0; i < 3; i++) {
+    maps_.clear();
+
+    unw_map_local_cursor_get(&map_cursor_);
+
+    unw_map_t unw_map;
+    int ret;
+    while ((ret = unw_map_local_cursor_get_next(&map_cursor_, &unw_map)) > 0) {
+      backtrace_map_t map;
+
+      map.start = unw_map.start;
+      map.end = unw_map.end;
+      map.flags = unw_map.flags;
+      map.name = unw_map.path;
+
+      free(unw_map.path);
+
+      // The maps are in descending order, but we want them in ascending order.
+      maps_.push_front(map);
+    }
+    // Check to see if the map changed while getting the data.
+    if (ret != -UNW_EINVAL) {
+      return true;
+    }
+  }
+
+  BACK_LOGW("Unable to generate the map.");
+  return false;
+}
+
+bool UnwindMapLocal::Build() {
+  return (map_created_ = (unw_map_local_create() == 0)) && GenerateMap();;
+}
+
+const backtrace_map_t* UnwindMapLocal::Find(uintptr_t addr) {
+  const backtrace_map_t* map = BacktraceMap::Find(addr);
+  if (!map) {
+    // Check to see if the underlying map changed and regenerate the map
+    // if it did.
+    if (unw_map_local_cursor_valid(&map_cursor_) < 0) {
+      if (GenerateMap()) {
+        map = BacktraceMap::Find(addr);
+      }
+    }
+  }
+  return map;
+}
+
 //-------------------------------------------------------------------------
 // BacktraceMap create function.
 //-------------------------------------------------------------------------
 BacktraceMap* BacktraceMap::Create(pid_t pid) {
-  BacktraceMap* map = new UnwindMap(pid);
+  BacktraceMap* map;
+  if (pid == getpid()) {
+    map = new UnwindMapLocal();
+  } else {
+    map = new UnwindMap(pid);
+  }
   if (!map->Build()) {
     delete map;
     return NULL;
diff --git a/libbacktrace/UnwindMap.h b/libbacktrace/UnwindMap.h
index 5a874e8..2fdb29f 100644
--- a/libbacktrace/UnwindMap.h
+++ b/libbacktrace/UnwindMap.h
@@ -32,8 +32,25 @@
 
   unw_map_cursor_t* GetMapCursor() { return &map_cursor_; }
 
-private:
+protected:
+  virtual bool GenerateMap();
+
   unw_map_cursor_t map_cursor_;
 };
 
+class UnwindMapLocal : public UnwindMap {
+public:
+  UnwindMapLocal();
+  virtual ~UnwindMapLocal();
+
+  virtual bool Build();
+
+  virtual const backtrace_map_t* Find(uintptr_t addr);
+
+protected:
+  virtual bool GenerateMap();
+
+  bool map_created_;
+};
+
 #endif // _LIBBACKTRACE_UNWIND_MAP_H
diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp
index 732dae8..5ca7e60 100644
--- a/libbacktrace/UnwindPtrace.cpp
+++ b/libbacktrace/UnwindPtrace.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "libbacktrace"
-
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
 
@@ -25,6 +23,7 @@
 #include <libunwind.h>
 #include <libunwind-ptrace.h>
 
+#include "BacktraceLog.h"
 #include "UnwindMap.h"
 #include "UnwindPtrace.h"
 
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 23eaf92..a5e141b 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -16,9 +16,10 @@
 
 #include <dirent.h>
 #include <errno.h>
+#include <inttypes.h>
 #include <pthread.h>
 #include <signal.h>
-#include <stdbool.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -35,6 +36,7 @@
 #include <cutils/atomic.h>
 #include <gtest/gtest.h>
 
+#include <algorithm>
 #include <vector>
 
 #include "thread_utils.h"
@@ -287,7 +289,7 @@
   pid_t pid;
   if ((pid = fork()) == 0) {
     ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
-    exit(1);
+    _exit(1);
   }
   VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyLevelDump);
 
@@ -300,7 +302,7 @@
   pid_t pid;
   if ((pid = fork()) == 0) {
     ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
-    exit(1);
+    _exit(1);
   }
 
   VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, true, ReadyLevelBacktrace, VerifyLevelDump);
@@ -314,7 +316,7 @@
   pid_t pid;
   if ((pid = fork()) == 0) {
     ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, NULL, NULL), 0);
-    exit(1);
+    _exit(1);
   }
   VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyMaxBacktrace, VerifyMaxDump);
 
@@ -339,7 +341,7 @@
   pid_t pid;
   if ((pid = fork()) == 0) {
     ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
-    exit(1);
+    _exit(1);
   }
   VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyProcessIgnoreFrames);
 
@@ -384,7 +386,7 @@
       ASSERT_TRUE(pthread_create(&thread, &attr, PtraceThreadLevelRun, NULL) == 0);
     }
     ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
-    exit(1);
+    _exit(1);
   }
 
   // Check to see that all of the threads are running before unwinding.
@@ -693,3 +695,136 @@
 #endif
             backtrace->FormatFrameData(&frame));
 }
+
+struct map_test_t {
+  uintptr_t start;
+  uintptr_t end;
+};
+
+bool map_sort(map_test_t i, map_test_t j) {
+  return i.start < j.start;
+}
+
+static void VerifyMap(pid_t pid) {
+  char buffer[4096];
+  snprintf(buffer, sizeof(buffer), "/proc/%d/maps", pid);
+
+  FILE* map_file = fopen(buffer, "r");
+  ASSERT_TRUE(map_file != NULL);
+  std::vector<map_test_t> test_maps;
+  while (fgets(buffer, sizeof(buffer), map_file)) {
+    map_test_t map;
+    ASSERT_EQ(2, sscanf(buffer, "%" SCNxPTR "-%" SCNxPTR " ", &map.start, &map.end));
+    test_maps.push_back(map);
+  }
+  fclose(map_file);
+  std::sort(test_maps.begin(), test_maps.end(), map_sort);
+
+  UniquePtr<BacktraceMap> map(BacktraceMap::Create(pid));
+
+  // Basic test that verifies that the map is in the expected order.
+  std::vector<map_test_t>::const_iterator test_it = test_maps.begin();
+  for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) {
+    ASSERT_TRUE(test_it != test_maps.end());
+    ASSERT_EQ(test_it->start, it->start);
+    ASSERT_EQ(test_it->end, it->end);
+    ++test_it;
+  }
+  ASSERT_TRUE(test_it == test_maps.end());
+}
+
+TEST(libbacktrace, verify_map_remote) {
+  pid_t pid;
+
+  if ((pid = fork()) == 0) {
+    while (true) {
+    }
+    _exit(0);
+  }
+  ASSERT_LT(0, pid);
+
+  ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
+
+  // Wait for the process to get to a stopping point.
+  WaitForStop(pid);
+
+  // The maps should match exactly since the forked process has been paused.
+  VerifyMap(pid);
+
+  ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+  kill(pid, SIGKILL);
+  ASSERT_EQ(waitpid(pid, NULL, 0), pid);
+}
+
+#if defined(ENABLE_PSS_TESTS)
+#include "GetPss.h"
+
+#define MAX_LEAK_BYTES 32*1024UL
+
+static void CheckForLeak(pid_t pid, pid_t tid) {
+  // Do a few runs to get the PSS stable.
+  for (size_t i = 0; i < 100; i++) {
+    Backtrace* backtrace = Backtrace::Create(pid, tid);
+    ASSERT_TRUE(backtrace != NULL);
+    ASSERT_TRUE(backtrace->Unwind(0));
+    delete backtrace;
+  }
+  size_t stable_pss = GetPssBytes();
+
+  // Loop enough that even a small leak should be detectable.
+  for (size_t i = 0; i < 4096; i++) {
+    Backtrace* backtrace = Backtrace::Create(pid, tid);
+    ASSERT_TRUE(backtrace != NULL);
+    ASSERT_TRUE(backtrace->Unwind(0));
+    delete backtrace;
+  }
+  size_t new_pss = GetPssBytes();
+  size_t abs_diff = (new_pss > stable_pss) ? new_pss - stable_pss : stable_pss - new_pss;
+  // As long as the new pss is within a certain amount, consider everything okay.
+  ASSERT_LE(abs_diff, MAX_LEAK_BYTES);
+}
+
+TEST(libbacktrace, check_for_leak_local) {
+  CheckForLeak(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD);
+}
+
+TEST(libbacktrace, check_for_leak_local_thread) {
+  thread_t thread_data = { 0, 0, 0 };
+  pthread_t thread;
+  ASSERT_TRUE(pthread_create(&thread, NULL, ThreadLevelRun, &thread_data) == 0);
+
+  // Wait up to 2 seconds for the tid to be set.
+  ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2));
+
+  CheckForLeak(BACKTRACE_CURRENT_PROCESS, thread_data.tid);
+
+  // Tell the thread to exit its infinite loop.
+  android_atomic_acquire_store(0, &thread_data.state);
+
+  ASSERT_TRUE(pthread_join(thread, NULL) == 0);
+}
+
+TEST(libbacktrace, check_for_leak_remote) {
+  pid_t pid;
+
+  if ((pid = fork()) == 0) {
+    while (true) {
+    }
+    _exit(0);
+  }
+  ASSERT_LT(0, pid);
+
+  ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
+
+  // Wait for the process to get to a stopping point.
+  WaitForStop(pid);
+
+  CheckForLeak(pid, BACKTRACE_CURRENT_THREAD);
+
+  ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+  kill(pid, SIGKILL);
+  ASSERT_EQ(waitpid(pid, NULL, 0), pid);
+}
+#endif
diff --git a/libcorkscrew/Android.mk b/libcorkscrew/Android.mk
deleted file mode 100644
index 8f3b68c..0000000
--- a/libcorkscrew/Android.mk
+++ /dev/null
@@ -1,100 +0,0 @@
-# Copyright (C) 2011 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-generic_src_files := \
-	backtrace.c \
-	backtrace-helper.c \
-	demangle.c \
-	map_info.c \
-	ptrace.c \
-	symbol_table.c
-
-arm_src_files := \
-	arch-arm/backtrace-arm.c \
-	arch-arm/ptrace-arm.c
-
-x86_src_files := \
-	arch-x86/backtrace-x86.c \
-	arch-x86/ptrace-x86.c
-
-ifneq ($(TARGET_IS_64_BIT),true)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(generic_src_files)
-
-ifeq ($(TARGET_ARCH),arm)
-LOCAL_SRC_FILES += $(arm_src_files)
-LOCAL_CFLAGS += -DCORKSCREW_HAVE_ARCH
-endif
-ifeq ($(TARGET_ARCH),x86)
-LOCAL_SRC_FILES += $(x86_src_files)
-LOCAL_CFLAGS += -DCORKSCREW_HAVE_ARCH
-endif
-ifeq ($(TARGET_ARCH),mips)
-LOCAL_SRC_FILES += \
-	arch-mips/backtrace-mips.c \
-	arch-mips/ptrace-mips.c
-LOCAL_CFLAGS += -DCORKSCREW_HAVE_ARCH
-endif
-
-LOCAL_SHARED_LIBRARIES += libdl libcutils liblog libgccdemangle
-
-LOCAL_CFLAGS += -std=gnu99 -Werror -Wno-unused-parameter
-LOCAL_MODULE := libcorkscrew
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_SHARED_LIBRARY)
-
-# Build test.
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := test.cpp
-LOCAL_CFLAGS += -Werror -fno-inline-small-functions
-LOCAL_SHARED_LIBRARIES := libcorkscrew
-LOCAL_MODULE := libcorkscrew_test
-LOCAL_MODULE_TAGS := optional
-include $(BUILD_EXECUTABLE)
-
-endif # TARGET_IS_64_BIT == false
-
-
-ifeq ($(HOST_OS)-$(HOST_ARCH),linux-x86)
-
-# Build libcorkscrew.
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES += $(generic_src_files) $(x86_src_files)
-LOCAL_CFLAGS += -DCORKSCREW_HAVE_ARCH
-LOCAL_STATIC_LIBRARIES += libcutils liblog
-LOCAL_LDLIBS += -ldl
-ifeq ($(HOST_OS),linux)
-  LOCAL_SHARED_LIBRARIES += libgccdemangle # TODO: is this even needed on Linux?
-  LOCAL_LDLIBS += -lrt
-endif
-LOCAL_CFLAGS += -std=gnu99 -Werror -Wno-unused-parameter
-LOCAL_MODULE := libcorkscrew
-LOCAL_MODULE_TAGS := optional
-include $(BUILD_HOST_SHARED_LIBRARY)
-
-# Build test.
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := test.cpp
-LOCAL_CFLAGS += -Werror
-LOCAL_SHARED_LIBRARIES := libcorkscrew
-LOCAL_MODULE := libcorkscrew_test
-LOCAL_MODULE_TAGS := optional
-include $(BUILD_HOST_EXECUTABLE)
-
-endif # $(HOST_OS)-$(HOST_ARCH) == linux-x86
diff --git a/libcorkscrew/MODULE_LICENSE_APACHE2 b/libcorkscrew/MODULE_LICENSE_APACHE2
deleted file mode 100644
index e69de29..0000000
--- a/libcorkscrew/MODULE_LICENSE_APACHE2
+++ /dev/null
diff --git a/libcorkscrew/NOTICE b/libcorkscrew/NOTICE
deleted file mode 100644
index becc120..0000000
--- a/libcorkscrew/NOTICE
+++ /dev/null
@@ -1,190 +0,0 @@
-
-   Copyright (c) 2011, 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.
-
-   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.
-
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
diff --git a/libcorkscrew/arch-arm/backtrace-arm.c b/libcorkscrew/arch-arm/backtrace-arm.c
deleted file mode 100644
index 751efbf..0000000
--- a/libcorkscrew/arch-arm/backtrace-arm.c
+++ /dev/null
@@ -1,589 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-/*
- * Backtracing functions for ARM.
- *
- * This implementation uses the exception unwinding tables provided by
- * the compiler to unwind call frames.  Refer to the ARM Exception Handling ABI
- * documentation (EHABI) for more details about what's going on here.
- *
- * An ELF binary may contain an EXIDX section that provides an index to
- * the exception handling table of each function, sorted by program
- * counter address.
- *
- * This implementation also supports unwinding other processes via ptrace().
- * In that case, the EXIDX section is found by reading the ELF section table
- * structures using ptrace().
- *
- * Because the tables are used for exception handling, it can happen that
- * a given function will not have an exception handling table.  In particular,
- * exceptions are assumed to only ever be thrown at call sites.  Therefore,
- * by definition leaf functions will not have exception handling tables.
- * This may make unwinding impossible in some cases although we can still get
- * some idea of the call stack by examining the PC and LR registers.
- *
- * As we are only interested in backtrace information, we do not need
- * to perform all of the work of unwinding such as restoring register
- * state and running cleanup functions.  Unwinding is performed virtually on
- * an abstract machine context consisting of just the ARM core registers.
- * Furthermore, we do not run generic "personality functions" because
- * we may not be in a position to execute arbitrary code, especially if
- * we are running in a signal handler or using ptrace()!
- */
-
-#define LOG_TAG "Corkscrew"
-//#define LOG_NDEBUG 0
-
-#include "../backtrace-arch.h"
-#include "../backtrace-helper.h"
-#include "../ptrace-arch.h"
-#include <corkscrew/ptrace.h>
-
-#include <stdlib.h>
-#include <signal.h>
-#include <stdbool.h>
-#include <limits.h>
-#include <errno.h>
-#include <sys/ptrace.h>
-#include <elf.h>
-#include <cutils/log.h>
-
-#include <ucontext.h>
-
-/* Unwind state. */
-typedef struct {
-    uint32_t gregs[16];
-} unwind_state_t;
-
-static const int R_SP = 13;
-static const int R_LR = 14;
-static const int R_PC = 15;
-
-/* Special EXIDX value that indicates that a frame cannot be unwound. */
-static const uint32_t EXIDX_CANTUNWIND = 1;
-
-/* Get the EXIDX section start and size for the module that contains a
- * given program counter address.
- *
- * When the executable is statically linked, the EXIDX section can be
- * accessed by querying the values of the __exidx_start and __exidx_end
- * symbols.
- *
- * When the executable is dynamically linked, the linker exports a function
- * called dl_unwind_find_exidx that obtains the EXIDX section for a given
- * absolute program counter address.
- *
- * Bionic exports a helpful function called __gnu_Unwind_Find_exidx that
- * handles both cases, so we use that here.
- */
-typedef long unsigned int* _Unwind_Ptr;
-extern _Unwind_Ptr __gnu_Unwind_Find_exidx(_Unwind_Ptr pc, int *pcount);
-
-static uintptr_t find_exidx(uintptr_t pc, size_t* out_exidx_size) {
-    int count;
-    uintptr_t start = (uintptr_t)__gnu_Unwind_Find_exidx((_Unwind_Ptr)pc, &count);
-    *out_exidx_size = count;
-    return start;
-}
-
-/* Transforms a 31-bit place-relative offset to an absolute address.
- * We assume the most significant bit is clear. */
-static uintptr_t prel_to_absolute(uintptr_t place, uint32_t prel_offset) {
-    return place + (((int32_t)(prel_offset << 1)) >> 1);
-}
-
-static uintptr_t get_exception_handler(const memory_t* memory,
-        const map_info_t* map_info_list, uintptr_t pc) {
-    if (!pc) {
-        ALOGV("get_exception_handler: pc is zero, no handler");
-        return 0;
-    }
-
-    uintptr_t exidx_start;
-    size_t exidx_size;
-    const map_info_t* mi;
-    if (memory->tid < 0) {
-        mi = NULL;
-        exidx_start = find_exidx(pc, &exidx_size);
-    } else {
-        mi = find_map_info(map_info_list, pc);
-        if (mi && mi->data) {
-            const map_info_data_t* data = (const map_info_data_t*)mi->data;
-            exidx_start = data->exidx_start;
-            exidx_size = data->exidx_size;
-        } else {
-            exidx_start = 0;
-            exidx_size = 0;
-        }
-    }
-
-    uintptr_t handler = 0;
-    int32_t handler_index = -1;
-    if (exidx_start) {
-        uint32_t low = 0;
-        uint32_t high = exidx_size;
-        while (low < high) {
-            uint32_t index = (low + high) / 2;
-            uintptr_t entry = exidx_start + index * 8;
-            uint32_t entry_prel_pc;
-            ALOGV("XXX low=%u, high=%u, index=%u", low, high, index);
-            if (!try_get_word(memory, entry, &entry_prel_pc)) {
-                break;
-            }
-            uintptr_t entry_pc = prel_to_absolute(entry, entry_prel_pc);
-            ALOGV("XXX entry_pc=0x%08x", entry_pc);
-            if (pc < entry_pc) {
-                high = index;
-                continue;
-            }
-            if (index + 1 < exidx_size) {
-                uintptr_t next_entry = entry + 8;
-                uint32_t next_entry_prel_pc;
-                if (!try_get_word(memory, next_entry, &next_entry_prel_pc)) {
-                    break;
-                }
-                uintptr_t next_entry_pc = prel_to_absolute(next_entry, next_entry_prel_pc);
-                ALOGV("XXX next_entry_pc=0x%08x", next_entry_pc);
-                if (pc >= next_entry_pc) {
-                    low = index + 1;
-                    continue;
-                }
-            }
-
-            uintptr_t entry_handler_ptr = entry + 4;
-            uint32_t entry_handler;
-            if (!try_get_word(memory, entry_handler_ptr, &entry_handler)) {
-                break;
-            }
-            if (entry_handler & (1L << 31)) {
-                handler = entry_handler_ptr; // in-place handler data
-            } else if (entry_handler != EXIDX_CANTUNWIND) {
-                handler = prel_to_absolute(entry_handler_ptr, entry_handler);
-            }
-            handler_index = index;
-            break;
-        }
-    }
-    if (mi) {
-        ALOGV("get_exception_handler: pc=0x%08x, module='%s', module_start=0x%08x, "
-                "exidx_start=0x%08x, exidx_size=%d, handler=0x%08x, handler_index=%d",
-                pc, mi->name, mi->start, exidx_start, exidx_size, handler, handler_index);
-    } else {
-        ALOGV("get_exception_handler: pc=0x%08x, "
-                "exidx_start=0x%08x, exidx_size=%d, handler=0x%08x, handler_index=%d",
-                pc, exidx_start, exidx_size, handler, handler_index);
-    }
-    return handler;
-}
-
-typedef struct {
-    uintptr_t ptr;
-    uint32_t word;
-} byte_stream_t;
-
-static bool try_next_byte(const memory_t* memory, byte_stream_t* stream, uint8_t* out_value) {
-    uint8_t result;
-    switch (stream->ptr & 3) {
-    case 0:
-        if (!try_get_word(memory, stream->ptr, &stream->word)) {
-            *out_value = 0;
-            return false;
-        }
-        *out_value = stream->word >> 24;
-        break;
-
-    case 1:
-        *out_value = stream->word >> 16;
-        break;
-
-    case 2:
-        *out_value = stream->word >> 8;
-        break;
-
-    default:
-        *out_value = stream->word;
-        break;
-    }
-
-    ALOGV("next_byte: ptr=0x%08x, value=0x%02x", stream->ptr, *out_value);
-    stream->ptr += 1;
-    return true;
-}
-
-static void set_reg(unwind_state_t* state, uint32_t reg, uint32_t value) {
-    ALOGV("set_reg: reg=%d, value=0x%08x", reg, value);
-    state->gregs[reg] = value;
-}
-
-static bool try_pop_registers(const memory_t* memory, unwind_state_t* state, uint32_t mask) {
-    uint32_t sp = state->gregs[R_SP];
-    bool sp_updated = false;
-    for (int i = 0; i < 16; i++) {
-        if (mask & (1 << i)) {
-            uint32_t value;
-            if (!try_get_word(memory, sp, &value)) {
-                return false;
-            }
-            if (i == R_SP) {
-                sp_updated = true;
-            }
-            set_reg(state, i, value);
-            sp += 4;
-        }
-    }
-    if (!sp_updated) {
-        set_reg(state, R_SP, sp);
-    }
-    return true;
-}
-
-/* Executes a built-in personality routine as defined in the EHABI.
- * Returns true if unwinding should continue.
- *
- * The data for the built-in personality routines consists of a sequence
- * of unwinding instructions, followed by a sequence of scope descriptors,
- * each of which has a length and offset encoded using 16-bit or 32-bit
- * values.
- *
- * We only care about the unwinding instructions.  They specify the
- * operations of an abstract machine whose purpose is to transform the
- * virtual register state (including the stack pointer) such that
- * the call frame is unwound and the PC register points to the call site.
- */
-static bool execute_personality_routine(const memory_t* memory,
-        unwind_state_t* state, byte_stream_t* stream, int pr_index) {
-    size_t size;
-    switch (pr_index) {
-    case 0: // Personality routine #0, short frame, descriptors have 16-bit scope.
-        size = 3;
-        break;
-    case 1: // Personality routine #1, long frame, descriptors have 16-bit scope.
-    case 2: { // Personality routine #2, long frame, descriptors have 32-bit scope.
-        uint8_t size_byte;
-        if (!try_next_byte(memory, stream, &size_byte)) {
-            return false;
-        }
-        size = (uint32_t)size_byte * sizeof(uint32_t) + 2;
-        break;
-    }
-    default: // Unknown personality routine.  Stop here.
-        return false;
-    }
-
-    bool pc_was_set = false;
-    while (size--) {
-        uint8_t op;
-        if (!try_next_byte(memory, stream, &op)) {
-            return false;
-        }
-        if ((op & 0xc0) == 0x00) {
-            // "vsp = vsp + (xxxxxx << 2) + 4"
-            set_reg(state, R_SP, state->gregs[R_SP] + ((op & 0x3f) << 2) + 4);
-        } else if ((op & 0xc0) == 0x40) {
-            // "vsp = vsp - (xxxxxx << 2) - 4"
-            set_reg(state, R_SP, state->gregs[R_SP] - ((op & 0x3f) << 2) - 4);
-        } else if ((op & 0xf0) == 0x80) {
-            uint8_t op2;
-            if (!(size--) || !try_next_byte(memory, stream, &op2)) {
-                return false;
-            }
-            uint32_t mask = (((uint32_t)op & 0x0f) << 12) | ((uint32_t)op2 << 4);
-            if (mask) {
-                // "Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}"
-                if (!try_pop_registers(memory, state, mask)) {
-                    return false;
-                }
-                if (mask & (1 << R_PC)) {
-                    pc_was_set = true;
-                }
-            } else {
-                // "Refuse to unwind"
-                return false;
-            }
-        } else if ((op & 0xf0) == 0x90) {
-            if (op != 0x9d && op != 0x9f) {
-                // "Set vsp = r[nnnn]"
-                set_reg(state, R_SP, state->gregs[op & 0x0f]);
-            } else {
-                // "Reserved as prefix for ARM register to register moves"
-                // "Reserved as prefix for Intel Wireless MMX register to register moves"
-                return false;
-            }
-        } else if ((op & 0xf8) == 0xa0) {
-            // "Pop r4-r[4+nnn]"
-            uint32_t mask = (0x0ff0 >> (7 - (op & 0x07))) & 0x0ff0;
-            if (!try_pop_registers(memory, state, mask)) {
-                return false;
-            }
-        } else if ((op & 0xf8) == 0xa8) {
-            // "Pop r4-r[4+nnn], r14"
-            uint32_t mask = ((0x0ff0 >> (7 - (op & 0x07))) & 0x0ff0) | 0x4000;
-            if (!try_pop_registers(memory, state, mask)) {
-                return false;
-            }
-        } else if (op == 0xb0) {
-            // "Finish"
-            break;
-        } else if (op == 0xb1) {
-            uint8_t op2;
-            if (!(size--) || !try_next_byte(memory, stream, &op2)) {
-                return false;
-            }
-            if (op2 != 0x00 && (op2 & 0xf0) == 0x00) {
-                // "Pop integer registers under mask {r3, r2, r1, r0}"
-                if (!try_pop_registers(memory, state, op2)) {
-                    return false;
-                }
-            } else {
-                // "Spare"
-                return false;
-            }
-        } else if (op == 0xb2) {
-            // "vsp = vsp + 0x204 + (uleb128 << 2)"
-            uint32_t value = 0;
-            uint32_t shift = 0;
-            uint8_t op2;
-            do {
-                if (!(size--) || !try_next_byte(memory, stream, &op2)) {
-                    return false;
-                }
-                value |= (op2 & 0x7f) << shift;
-                shift += 7;
-            } while (op2 & 0x80);
-            set_reg(state, R_SP, state->gregs[R_SP] + (value << 2) + 0x204);
-        } else if (op == 0xb3) {
-            // "Pop VFP double-precision registers D[ssss]-D[ssss+cccc] saved (as if) by FSTMFDX"
-            uint8_t op2;
-            if (!(size--) || !try_next_byte(memory, stream, &op2)) {
-                return false;
-            }
-            set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op2 & 0x0f) * 8 + 12);
-        } else if ((op & 0xf8) == 0xb8) {
-            // "Pop VFP double-precision registers D[8]-D[8+nnn] saved (as if) by FSTMFDX"
-            set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op & 0x07) * 8 + 12);
-        } else if ((op & 0xf8) == 0xc0) {
-            // "Intel Wireless MMX pop wR[10]-wR[10+nnn]"
-            set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op & 0x07) * 8 + 8);
-        } else if (op == 0xc6) {
-            // "Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc]"
-            uint8_t op2;
-            if (!(size--) || !try_next_byte(memory, stream, &op2)) {
-                return false;
-            }
-            set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op2 & 0x0f) * 8 + 8);
-        } else if (op == 0xc7) {
-            uint8_t op2;
-            if (!(size--) || !try_next_byte(memory, stream, &op2)) {
-                return false;
-            }
-            if (op2 != 0x00 && (op2 & 0xf0) == 0x00) {
-                // "Intel Wireless MMX pop wCGR registers under mask {wCGR3,2,1,0}"
-                set_reg(state, R_SP, state->gregs[R_SP] + __builtin_popcount(op2) * 4);
-            } else {
-                // "Spare"
-                return false;
-            }
-        } else if (op == 0xc8) {
-            // "Pop VFP double precision registers D[16+ssss]-D[16+ssss+cccc]
-            // saved (as if) by FSTMFD"
-            uint8_t op2;
-            if (!(size--) || !try_next_byte(memory, stream, &op2)) {
-                return false;
-            }
-            set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op2 & 0x0f) * 8 + 8);
-        } else if (op == 0xc9) {
-            // "Pop VFP double precision registers D[ssss]-D[ssss+cccc] saved (as if) by FSTMFDD"
-            uint8_t op2;
-            if (!(size--) || !try_next_byte(memory, stream, &op2)) {
-                return false;
-            }
-            set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op2 & 0x0f) * 8 + 8);
-        } else if ((op == 0xf8) == 0xd0) {
-            // "Pop VFP double-precision registers D[8]-D[8+nnn] saved (as if) by FSTMFDD"
-            set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op & 0x07) * 8 + 8);
-        } else {
-            // "Spare"
-            return false;
-        }
-    }
-    if (!pc_was_set) {
-        set_reg(state, R_PC, state->gregs[R_LR]);
-    }
-    return true;
-}
-
-static bool try_get_half_word(const memory_t* memory, uint32_t pc, uint16_t* out_value) {
-    uint32_t word;
-    if (try_get_word(memory, pc & ~2, &word)) {
-        *out_value = pc & 2 ? word >> 16 : word & 0xffff;
-        return true;
-    }
-    return false;
-}
-
-uintptr_t rewind_pc_arch(const memory_t* memory, uintptr_t pc) {
-    if (pc & 1) {
-        /* Thumb mode - need to check whether the bl(x) has long offset or not.
-         * Examples:
-         *
-         * arm blx in the middle of thumb:
-         * 187ae:       2300            movs    r3, #0
-         * 187b0:       f7fe ee1c       blx     173ec
-         * 187b4:       2c00            cmp     r4, #0
-         *
-         * arm bl in the middle of thumb:
-         * 187d8:       1c20            adds    r0, r4, #0
-         * 187da:       f136 fd15       bl      14f208
-         * 187de:       2800            cmp     r0, #0
-         *
-         * pure thumb:
-         * 18894:       189b            adds    r3, r3, r2
-         * 18896:       4798            blx     r3
-         * 18898:       b001            add     sp, #4
-         */
-        uint16_t prev1, prev2;
-        if (try_get_half_word(memory, pc - 5, &prev1)
-            && ((prev1 & 0xf000) == 0xf000)
-            && try_get_half_word(memory, pc - 3, &prev2)
-            && ((prev2 & 0xe000) == 0xe000)) {
-            pc -= 4; // long offset
-        } else {
-            pc -= 2;
-        }
-    } else {
-        /* ARM mode, all instructions are 32bit.  Yay! */
-        pc -= 4;
-    }
-    return pc;
-}
-
-static ssize_t unwind_backtrace_common(const memory_t* memory,
-        const map_info_t* map_info_list,
-        unwind_state_t* state, backtrace_frame_t* backtrace,
-        size_t ignore_depth, size_t max_depth) {
-    size_t ignored_frames = 0;
-    size_t returned_frames = 0;
-
-    for (size_t index = 0; returned_frames < max_depth; index++) {
-        uintptr_t pc = index ? rewind_pc_arch(memory, state->gregs[R_PC])
-                : state->gregs[R_PC];
-        backtrace_frame_t* frame = add_backtrace_entry(pc,
-                backtrace, ignore_depth, max_depth, &ignored_frames, &returned_frames);
-        if (frame) {
-            frame->stack_top = state->gregs[R_SP];
-        }
-
-        uintptr_t handler = get_exception_handler(memory, map_info_list, pc);
-        if (!handler) {
-            // If there is no handler for the PC and this is the first frame,
-            // then the program may have branched to an invalid address.
-            // Try starting from the LR instead, otherwise stop unwinding.
-            if (index == 0 && state->gregs[R_LR]
-                    && state->gregs[R_LR] != state->gregs[R_PC]) {
-                set_reg(state, R_PC, state->gregs[R_LR]);
-                continue;
-            } else {
-                break;
-            }
-        }
-
-        byte_stream_t stream;
-        stream.ptr = handler;
-        uint8_t pr;
-        if (!try_next_byte(memory, &stream, &pr)) {
-            break;
-        }
-        if ((pr & 0xf0) != 0x80) {
-            // The first word is a place-relative pointer to a generic personality
-            // routine function.  We don't support invoking such functions, so stop here.
-            break;
-        }
-
-        // The first byte indicates the personality routine to execute.
-        // Following bytes provide instructions to the personality routine.
-        if (!execute_personality_routine(memory, state, &stream, pr & 0x0f)) {
-            break;
-        }
-        if (frame && state->gregs[R_SP] > frame->stack_top) {
-            frame->stack_size = state->gregs[R_SP] - frame->stack_top;
-        }
-        if (!state->gregs[R_PC]) {
-            break;
-        }
-    }
-
-    // Ran out of frames that we could unwind using handlers.
-    // Add a final entry for the LR if it looks sane and call it good.
-    if (returned_frames < max_depth
-            && state->gregs[R_LR]
-            && state->gregs[R_LR] != state->gregs[R_PC]
-            && is_executable_map(map_info_list, state->gregs[R_LR])) {
-        // We don't know where the stack for this extra frame starts so we
-        // don't return any stack information for it.
-        add_backtrace_entry(rewind_pc_arch(memory, state->gregs[R_LR]),
-                backtrace, ignore_depth, max_depth, &ignored_frames, &returned_frames);
-    }
-    return returned_frames;
-}
-
-ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo, void* sigcontext,
-        const map_info_t* map_info_list,
-        backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) {
-    const ucontext_t* uc = (const ucontext_t*)sigcontext;
-
-    unwind_state_t state;
-
-    state.gregs[0] = uc->uc_mcontext.arm_r0;
-    state.gregs[1] = uc->uc_mcontext.arm_r1;
-    state.gregs[2] = uc->uc_mcontext.arm_r2;
-    state.gregs[3] = uc->uc_mcontext.arm_r3;
-    state.gregs[4] = uc->uc_mcontext.arm_r4;
-    state.gregs[5] = uc->uc_mcontext.arm_r5;
-    state.gregs[6] = uc->uc_mcontext.arm_r6;
-    state.gregs[7] = uc->uc_mcontext.arm_r7;
-    state.gregs[8] = uc->uc_mcontext.arm_r8;
-    state.gregs[9] = uc->uc_mcontext.arm_r9;
-    state.gregs[10] = uc->uc_mcontext.arm_r10;
-    state.gregs[11] = uc->uc_mcontext.arm_fp;
-    state.gregs[12] = uc->uc_mcontext.arm_ip;
-    state.gregs[13] = uc->uc_mcontext.arm_sp;
-    state.gregs[14] = uc->uc_mcontext.arm_lr;
-    state.gregs[15] = uc->uc_mcontext.arm_pc;
-
-    memory_t memory;
-    init_memory(&memory, map_info_list);
-    return unwind_backtrace_common(&memory, map_info_list, &state,
-            backtrace, ignore_depth, max_depth);
-}
-
-ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context,
-        backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) {
-    struct pt_regs regs;
-    if (ptrace(PTRACE_GETREGS, tid, 0, &regs)) {
-        return -1;
-    }
-
-    unwind_state_t state;
-    for (int i = 0; i < 16; i++) {
-        state.gregs[i] = regs.uregs[i];
-    }
-
-    memory_t memory;
-    init_memory_ptrace(&memory, tid);
-    return unwind_backtrace_common(&memory, context->map_info_list, &state,
-            backtrace, ignore_depth, max_depth);
-}
diff --git a/libcorkscrew/arch-arm/ptrace-arm.c b/libcorkscrew/arch-arm/ptrace-arm.c
deleted file mode 100644
index a50844e..0000000
--- a/libcorkscrew/arch-arm/ptrace-arm.c
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-#define LOG_TAG "Corkscrew"
-//#define LOG_NDEBUG 0
-
-#include "../ptrace-arch.h"
-
-#include <elf.h>
-#include <cutils/log.h>
-
-#ifndef PT_ARM_EXIDX
-#define PT_ARM_EXIDX 0x70000001
-#endif
-
-static void load_exidx_header(pid_t pid, map_info_t* mi,
-        uintptr_t* out_exidx_start, size_t* out_exidx_size) {
-    uint32_t elf_phoff;
-    uint32_t elf_phentsize_ehsize;
-    uint32_t elf_shentsize_phnum;
-    if (try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phoff), &elf_phoff)
-            && try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_ehsize),
-                    &elf_phentsize_ehsize)
-            && try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phnum),
-                    &elf_shentsize_phnum)) {
-        uint32_t elf_phentsize = elf_phentsize_ehsize >> 16;
-        uint32_t elf_phnum = elf_shentsize_phnum & 0xffff;
-        for (uint32_t i = 0; i < elf_phnum; i++) {
-            uintptr_t elf_phdr = mi->start + elf_phoff + i * elf_phentsize;
-            uint32_t elf_phdr_type;
-            if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_type), &elf_phdr_type)) {
-                break;
-            }
-            if (elf_phdr_type == PT_ARM_EXIDX) {
-                uint32_t elf_phdr_offset;
-                uint32_t elf_phdr_filesz;
-                if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_offset),
-                        &elf_phdr_offset)
-                        || !try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_filesz),
-                                &elf_phdr_filesz)) {
-                    break;
-                }
-                *out_exidx_start = mi->start + elf_phdr_offset;
-                *out_exidx_size = elf_phdr_filesz / 8;
-                ALOGV("Parsed EXIDX header info for %s: start=0x%08x, size=%d", mi->name,
-                        *out_exidx_start, *out_exidx_size);
-                return;
-            }
-        }
-    }
-    *out_exidx_start = 0;
-    *out_exidx_size = 0;
-}
-
-void load_ptrace_map_info_data_arch(pid_t pid, map_info_t* mi, map_info_data_t* data) {
-    load_exidx_header(pid, mi, &data->exidx_start, &data->exidx_size);
-}
-
-void free_ptrace_map_info_data_arch(map_info_t* mi, map_info_data_t* data) {
-}
diff --git a/libcorkscrew/arch-mips/backtrace-mips.c b/libcorkscrew/arch-mips/backtrace-mips.c
deleted file mode 100644
index 832fb86..0000000
--- a/libcorkscrew/arch-mips/backtrace-mips.c
+++ /dev/null
@@ -1,901 +0,0 @@
-/*
- * 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.
- */
-
-/*
- * Backtracing functions for mips
- */
-
-#define LOG_TAG "Corkscrew"
-//#define LOG_NDEBUG 0
-
-#include "../backtrace-arch.h"
-#include "../backtrace-helper.h"
-#include "../ptrace-arch.h"
-#include <corkscrew/ptrace.h>
-#include "dwarf.h"
-
-#include <stdlib.h>
-#include <signal.h>
-#include <stdbool.h>
-#include <limits.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/ptrace.h>
-#include <cutils/log.h>
-
-#include <sys/ucontext.h>
-
-/* For PTRACE_GETREGS */
-typedef struct {
-    uint64_t regs[32];
-    uint64_t lo;
-    uint64_t hi;
-    uint64_t epc;
-    uint64_t badvaddr;
-    uint64_t status;
-    uint64_t cause;
-} user_regs_struct;
-
-enum {
-    REG_ZERO = 0, REG_AT, REG_V0, REG_V1,
-    REG_A0, REG_A1, REG_A2, REG_A3,
-    REG_T0, REG_T1, REG_T2, REG_T3,
-    REG_T4, REG_T5, REG_T6, REG_T7,
-    REG_S0, REG_S1, REG_S2, REG_S3,
-    REG_S4, REG_S5, REG_S6, REG_S7,
-    REG_T8, REG_T9, REG_K0, REG_K1,
-    REG_GP, REG_SP, REG_S8, REG_RA,
-};
-
-
-/* Unwind state. */
-typedef struct {
-    uint32_t reg[DWARF_REGISTERS];
-} unwind_state_t;
-
-uintptr_t rewind_pc_arch(const memory_t* memory __attribute__((unused)), uintptr_t pc) {
-    if (pc == 0)
-        return pc;
-    if ((pc & 1) == 0)
-        return pc-8;            /* jal/bal/jalr + branch delay slot */
-    return pc;
-}
-
-/* Read byte through 4 byte cache. Usually we read byte by byte and updating cursor. */
-static bool try_get_byte(const memory_t* memory, uintptr_t ptr, uint8_t* out_value, uint32_t* cursor) {
-    static uintptr_t lastptr;
-    static uint32_t buf;
-
-    ptr += *cursor;
-
-    if (ptr < lastptr || lastptr + 3 < ptr) {
-        lastptr = (ptr >> 2) << 2;
-        if (!try_get_word(memory, lastptr, &buf)) {
-            return false;
-        }
-    }
-    *out_value = (uint8_t)((buf >> ((ptr & 3) * 8)) & 0xff);
-    ++*cursor;
-    return true;
-}
-
-/* Getting X bytes. 4 is maximum for now. */
-static bool try_get_xbytes(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint8_t bytes, uint32_t* cursor) {
-    uint32_t data = 0;
-    if (bytes > 4) {
-        ALOGE("can't read more than 4 bytes, trying to read %d", bytes);
-        return false;
-    }
-    for (int i = 0; i < bytes; i++) {
-        uint8_t buf;
-        if (!try_get_byte(memory, ptr, &buf, cursor)) {
-            return false;
-        }
-        data |= (uint32_t)buf << (i * 8);
-    }
-    *out_value = data;
-    return true;
-}
-
-/* Reads signed/unsigned LEB128 encoded data. From 1 to 4 bytes. */
-static bool try_get_leb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor, bool sign_extend) {
-    uint8_t buf = 0;
-    uint32_t val = 0;
-    uint8_t c = 0;
-    do {
-        if (!try_get_byte(memory, ptr, &buf, cursor)) {
-            return false;
-        }
-        val |= ((uint32_t)buf & 0x7f) << (c * 7);
-        c++;
-    } while (buf & 0x80 && (c * 7) <= 32);
-    if (c * 7 > 32) {
-        ALOGE("%s: data exceeds expected 4 bytes maximum", __FUNCTION__);
-        return false;
-    }
-    if (sign_extend) {
-        if (buf & 0x40) {
-            val |= ((uint32_t)-1 << (c * 7));
-        }
-    }
-    *out_value = val;
-    return true;
-}
-
-/* Reads signed LEB128 encoded data. From 1 to 4 bytes. */
-static bool try_get_sleb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor) {
-    return try_get_leb128(memory, ptr, out_value, cursor, true);
-}
-
-/* Reads unsigned LEB128 encoded data. From 1 to 4 bytes. */
-static bool try_get_uleb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor) {
-    return try_get_leb128(memory, ptr, out_value, cursor, false);
-}
-
-/* Getting data encoded by dwarf encodings. */
-static bool read_dwarf(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint8_t encoding, uint32_t* cursor) {
-    uint32_t data = 0;
-    bool issigned = true;
-    uintptr_t addr = ptr + *cursor;
-    /* Lower 4 bits is data type/size */
-    /* TODO: add more encodings if it becomes necessary */
-    switch (encoding & 0xf) {
-    case DW_EH_PE_absptr:
-        if (!try_get_xbytes(memory, ptr, &data, 4, cursor)) {
-            return false;
-        }
-        *out_value = data;
-        return true;
-    case DW_EH_PE_udata4:
-        issigned = false;
-    case DW_EH_PE_sdata4:
-        if (!try_get_xbytes(memory, ptr, &data, 4, cursor)) {
-            return false;
-        }
-        break;
-    default:
-        ALOGE("unrecognized dwarf lower part encoding: 0x%x", encoding);
-        return false;
-    }
-    /* Higher 4 bits is modifier */
-    /* TODO: add more encodings if it becomes necessary */
-    switch (encoding & 0xf0) {
-    case 0:
-        *out_value = data;
-        break;
-    case DW_EH_PE_pcrel:
-        if (issigned) {
-            *out_value = addr + (int32_t)data;
-        } else {
-            *out_value = addr + data;
-        }
-        break;
-        /* Assuming ptr is correct base to calculate datarel */
-    case DW_EH_PE_datarel:
-        if (issigned) {
-            *out_value = ptr + (int32_t)data;
-        } else {
-            *out_value = ptr + data;
-        }
-        break;
-    default:
-        ALOGE("unrecognized dwarf higher part encoding: 0x%x", encoding);
-        return false;
-    }
-    return true;
-}
-
-/* Having PC find corresponding FDE by reading .eh_frame_hdr section data. */
-static uintptr_t find_fde(const memory_t* memory,
-                          const map_info_t* map_info_list, uintptr_t pc) {
-    if (!pc) {
-        ALOGV("find_fde: pc is zero, no eh_frame");
-        return 0;
-    }
-    const map_info_t* mi = find_map_info(map_info_list, pc);
-    if (!mi) {
-        ALOGV("find_fde: no map info for pc:0x%x", pc);
-        return 0;
-    }
-    const map_info_data_t* midata = mi->data;
-    if (!midata) {
-        ALOGV("find_fde: no eh_frame_hdr for map: start=0x%x, end=0x%x", mi->start, mi->end);
-        return 0;
-    }
-
-    eh_frame_hdr_info_t eh_hdr_info;
-    memset(&eh_hdr_info, 0, sizeof(eh_frame_hdr_info_t));
-
-    /* Getting the first word of eh_frame_hdr:
-       1st byte is version;
-       2nd byte is encoding of pointer to eh_frames;
-       3rd byte is encoding of count of FDEs in lookup table;
-       4th byte is encoding of lookup table entries.
-    */
-    uintptr_t eh_frame_hdr = midata->eh_frame_hdr;
-    uint32_t c = 0;
-    if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.version, &c)) return 0;
-    if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.eh_frame_ptr_enc, &c)) return 0;
-    if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.fde_count_enc, &c)) return 0;
-    if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.fde_table_enc, &c)) return 0;
-
-    /* TODO: 3rd byte can be DW_EH_PE_omit, that means no lookup table available and we should
-       try to parse eh_frame instead. Not sure how often it may occur, skipping now.
-    */
-    if (eh_hdr_info.version != 1) {
-        ALOGV("find_fde: eh_frame_hdr version %d is not supported", eh_hdr_info.version);
-        return 0;
-    }
-    /* Getting the data:
-       2nd word is eh_frame pointer (normally not used, because lookup table has all we need);
-       3rd word is count of FDEs in the lookup table;
-       starting from 4 word there is FDE lookup table (pairs of PC and FDE pointer) sorted by PC;
-    */
-    if (!read_dwarf(memory, eh_frame_hdr, &eh_hdr_info.eh_frame_ptr, eh_hdr_info.eh_frame_ptr_enc, &c)) return 0;
-    if (!read_dwarf(memory, eh_frame_hdr, &eh_hdr_info.fde_count, eh_hdr_info.fde_count_enc, &c)) return 0;
-    ALOGV("find_fde: found %d FDEs", eh_hdr_info.fde_count);
-
-    int32_t low = 0;
-    int32_t high = eh_hdr_info.fde_count;
-    uintptr_t start = 0;
-    uintptr_t fde = 0;
-    /* eh_frame_hdr + c points to lookup table at this point. */
-    while (low <= high) {
-        uint32_t mid = (high + low)/2;
-        uint32_t entry = c + mid * 8;
-        if (!read_dwarf(memory, eh_frame_hdr, &start, eh_hdr_info.fde_table_enc, &entry)) return 0;
-        if (pc <= start) {
-            high = mid - 1;
-        } else {
-            low = mid + 1;
-        }
-    }
-    /* Value found is at high. */
-    if (high < 0) {
-        ALOGV("find_fde: pc %x is out of FDE bounds: %x", pc, start);
-        return 0;
-    }
-    c += high * 8;
-    if (!read_dwarf(memory, eh_frame_hdr, &start, eh_hdr_info.fde_table_enc, &c)) return 0;
-    if (!read_dwarf(memory, eh_frame_hdr, &fde, eh_hdr_info.fde_table_enc, &c)) return 0;
-    ALOGV("pc 0x%x, ENTRY %d: start=0x%x, fde=0x%x", pc, high, start, fde);
-    return fde;
-}
-
-/* Execute single dwarf instruction and update dwarf state accordingly. */
-static bool execute_dwarf(const memory_t* memory, uintptr_t ptr, cie_info_t* cie_info,
-                          dwarf_state_t* dstate, uint32_t* cursor,
-                          dwarf_state_t* stack, uint8_t* stack_ptr) {
-    uint8_t inst;
-    uint8_t op = 0;
-
-    if (!try_get_byte(memory, ptr, &inst, cursor)) {
-        return false;
-    }
-    ALOGV("DW_CFA inst: 0x%x", inst);
-
-    /* For some instructions upper 2 bits is opcode and lower 6 bits is operand. See dwarf-2.0 7.23. */
-    if (inst & 0xc0) {
-        op = inst & 0x3f;
-        inst &= 0xc0;
-    }
-
-    switch ((dwarf_CFA)inst) {
-        uint32_t reg = 0;
-        uint32_t offset = 0;
-    case DW_CFA_advance_loc:
-        dstate->loc += op * cie_info->code_align;
-        ALOGV("DW_CFA_advance_loc: %d to 0x%x", op, dstate->loc);
-        break;
-    case DW_CFA_offset:
-        if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false;
-        dstate->regs[op].rule = 'o';
-        dstate->regs[op].value = offset * cie_info->data_align;
-        ALOGV("DW_CFA_offset: r%d = o(%d)", op, dstate->regs[op].value);
-        break;
-    case DW_CFA_restore:
-        dstate->regs[op].rule = stack->regs[op].rule;
-        dstate->regs[op].value = stack->regs[op].value;
-        ALOGV("DW_CFA_restore: r%d = %c(%d)", op, dstate->regs[op].rule, dstate->regs[op].value);
-        break;
-    case DW_CFA_nop:
-        break;
-    case DW_CFA_set_loc: // probably we don't have it on mips.
-        if (!try_get_xbytes(memory, ptr, &offset, 4, cursor)) return false;
-        if (offset < dstate->loc) {
-            ALOGE("DW_CFA_set_loc: attempt to move location backward");
-            return false;
-        }
-        dstate->loc = offset * cie_info->code_align;
-        ALOGV("DW_CFA_set_loc: %d to 0x%x", offset * cie_info->code_align, dstate->loc);
-        break;
-    case DW_CFA_advance_loc1:
-        if (!try_get_byte(memory, ptr, (uint8_t*)&offset, cursor)) return false;
-        dstate->loc += (uint8_t)offset * cie_info->code_align;
-        ALOGV("DW_CFA_advance_loc1: %d to 0x%x", (uint8_t)offset * cie_info->code_align, dstate->loc);
-        break;
-    case DW_CFA_advance_loc2:
-        if (!try_get_xbytes(memory, ptr, &offset, 2, cursor)) return false;
-        dstate->loc += (uint16_t)offset * cie_info->code_align;
-        ALOGV("DW_CFA_advance_loc2: %d to 0x%x", (uint16_t)offset * cie_info->code_align, dstate->loc);
-        break;
-    case DW_CFA_advance_loc4:
-        if (!try_get_xbytes(memory, ptr, &offset, 4, cursor)) return false;
-        dstate->loc += offset * cie_info->code_align;
-        ALOGV("DW_CFA_advance_loc4: %d to 0x%x", offset * cie_info->code_align, dstate->loc);
-        break;
-    case DW_CFA_offset_extended: // probably we don't have it on mips.
-        if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
-        if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false;
-        if (reg >= DWARF_REGISTERS) {
-            ALOGE("DW_CFA_offset_extended: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS);
-            return false;
-        }
-        dstate->regs[reg].rule = 'o';
-        dstate->regs[reg].value = offset * cie_info->data_align;
-        ALOGV("DW_CFA_offset_extended: r%d = o(%d)", reg, dstate->regs[reg].value);
-        break;
-    case DW_CFA_restore_extended: // probably we don't have it on mips.
-        if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
-        if (reg >= DWARF_REGISTERS) {
-            ALOGE("DW_CFA_restore_extended: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS);
-            return false;
-        }
-        dstate->regs[reg].rule = stack->regs[reg].rule;
-        dstate->regs[reg].value = stack->regs[reg].value;
-        ALOGV("DW_CFA_restore: r%d = %c(%d)", reg, dstate->regs[reg].rule, dstate->regs[reg].value);
-        break;
-    case DW_CFA_undefined: // probably we don't have it on mips.
-        if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
-        if (reg >= DWARF_REGISTERS) {
-            ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS);
-            return false;
-        }
-        dstate->regs[reg].rule = 'u';
-        dstate->regs[reg].value = 0;
-        ALOGV("DW_CFA_undefined: r%d", reg);
-        break;
-    case DW_CFA_same_value: // probably we don't have it on mips.
-        if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
-        if (reg >= DWARF_REGISTERS) {
-            ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS);
-            return false;
-        }
-        dstate->regs[reg].rule = 's';
-        dstate->regs[reg].value = 0;
-        ALOGV("DW_CFA_same_value: r%d", reg);
-        break;
-    case DW_CFA_register: // probably we don't have it on mips.
-        if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
-        /* that's new register actually, not offset */
-        if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false;
-        if (reg >= DWARF_REGISTERS || offset >= DWARF_REGISTERS) {
-            ALOGE("DW_CFA_register: r%d or r%d exceeds supported number of registers (%d)", reg, offset, DWARF_REGISTERS);
-            return false;
-        }
-        dstate->regs[reg].rule = 'r';
-        dstate->regs[reg].value = offset;
-        ALOGV("DW_CFA_register: r%d = r(%d)", reg, dstate->regs[reg].value);
-        break;
-    case DW_CFA_remember_state:
-        if (*stack_ptr == DWARF_STATES_STACK) {
-            ALOGE("DW_CFA_remember_state: states stack overflow %d", *stack_ptr);
-            return false;
-        }
-        stack[(*stack_ptr)++] = *dstate;
-        ALOGV("DW_CFA_remember_state: stacktop moves to %d", *stack_ptr);
-        break;
-    case DW_CFA_restore_state:
-        /* We have CIE state saved at 0 position. It's not supposed to be taken
-           by DW_CFA_restore_state. */
-        if (*stack_ptr == 1) {
-            ALOGE("DW_CFA_restore_state: states stack is empty");
-            return false;
-        }
-        /* Don't touch location on restore. */
-        uintptr_t saveloc = dstate->loc;
-        *dstate = stack[--*stack_ptr];
-        dstate->loc = saveloc;
-        ALOGV("DW_CFA_restore_state: stacktop moves to %d", *stack_ptr);
-        break;
-    case DW_CFA_def_cfa:
-        if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
-        if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false;
-        dstate->cfa_reg = reg;
-        dstate->cfa_off = offset;
-        ALOGV("DW_CFA_def_cfa: %x(r%d)", offset, reg);
-        break;
-    case DW_CFA_def_cfa_register:
-        if (!try_get_uleb128(memory, ptr, &reg, cursor)) {
-            return false;
-        }
-        dstate->cfa_reg = reg;
-        ALOGV("DW_CFA_def_cfa_register: r%d", reg);
-        break;
-    case DW_CFA_def_cfa_offset:
-        if (!try_get_uleb128(memory, ptr, &offset, cursor)) {
-            return false;
-        }
-        dstate->cfa_off = offset;
-        ALOGV("DW_CFA_def_cfa_offset: %x", offset);
-        break;
-    default:
-        ALOGE("unrecognized DW_CFA_* instruction: 0x%x", inst);
-        return false;
-    }
-    return true;
-}
-
-/* Restoring particular register value based on dwarf state. */
-static bool get_old_register_value(const memory_t* memory, uint32_t cfa,
-                                   dwarf_state_t* dstate, uint8_t reg,
-                                   unwind_state_t* state, unwind_state_t* newstate) {
-    uint32_t addr;
-    switch (dstate->regs[reg].rule) {
-    case 0:
-        /* We don't have dstate updated for this register, so assuming value kept the same.
-           Normally we should look into state and return current value as the old one
-           but we don't have all registers in state to handle this properly */
-        ALOGV("get_old_register_value: value of r%d is the same", reg);
-        // for SP if it's not updated by dwarf rule we assume it's equal to CFA
-        // for PC if it's not updated by dwarf rule we assume it's equal to RA
-        if (reg == DWARF_SP) {
-            ALOGV("get_old_register_value: adjusting sp to CFA: 0x%x", cfa);
-            newstate->reg[reg] = cfa;
-        } else if (reg == DWARF_PC) {
-            ALOGV("get_old_register_value: adjusting PC to RA: 0x%x", newstate->reg[DWARF_RA]);
-            newstate->reg[reg] = newstate->reg[DWARF_RA];
-        } else {
-            newstate->reg[reg] = state->reg[reg];
-        }
-        break;
-    case 'o':
-        addr = cfa + (int32_t)dstate->regs[reg].value;
-        if (!try_get_word(memory, addr, &newstate->reg[reg])) {
-            ALOGE("get_old_register_value: can't read from 0x%x", addr);
-            return false;
-        }
-        ALOGV("get_old_register_value: r%d at 0x%x is 0x%x", reg, addr, newstate->reg[reg]);
-        break;
-    case 'r':
-        /* We don't have all registers in state so don't even try to look at 'r' */
-        ALOGE("get_old_register_value: register lookup not implemented yet");
-        break;
-    default:
-        ALOGE("get_old_register_value: unexpected rule:%c value:%d for register %d",
-              dstate->regs[reg].rule, (int32_t)dstate->regs[reg].value, reg);
-        return false;
-    }
-    return true;
-}
-
-/* Updaing state based on dwarf state. */
-static bool update_state(const memory_t* memory, unwind_state_t* state,
-                         dwarf_state_t* dstate) {
-    unwind_state_t newstate;
-    /* We can restore more registers here if we need them. Meanwile doing minimal work here. */
-    /* Getting CFA. */
-    uintptr_t cfa = 0;
-    if (dstate->cfa_reg == DWARF_SP) {
-        cfa = state->reg[DWARF_SP] + dstate->cfa_off;
-    } else if (dstate->cfa_reg == DWARF_FP) {
-        cfa = state->reg[DWARF_FP] + dstate->cfa_off;
-    } else {
-        ALOGE("update_state: unexpected CFA register: %d", dstate->cfa_reg);
-        return false;
-    }
-    ALOGV("update_state: new CFA: 0x%x", cfa);
-
-    /* Update registers. Order is important to allow RA to propagate to PC */
-    /* Getting FP. */
-    if (!get_old_register_value(memory, cfa, dstate, DWARF_FP, state, &newstate)) return false;
-    /* Getting SP. */
-    if (!get_old_register_value(memory, cfa, dstate, DWARF_SP, state, &newstate)) return false;
-    /* Getting RA. */
-    if (!get_old_register_value(memory, cfa, dstate, DWARF_RA, state, &newstate)) return false;
-    /* Getting PC. */
-    if (!get_old_register_value(memory, cfa, dstate, DWARF_PC, state, &newstate)) return false;
-
-    ALOGV("update_state: PC: 0x%x; restore PC: 0x%x", state->reg[DWARF_PC], newstate.reg[DWARF_PC]);
-    ALOGV("update_state: RA: 0x%x; restore RA: 0x%x", state->reg[DWARF_RA], newstate.reg[DWARF_RA]);
-    ALOGV("update_state: FP: 0x%x; restore FP: 0x%x", state->reg[DWARF_FP], newstate.reg[DWARF_FP]);
-    ALOGV("update_state: SP: 0x%x; restore SP: 0x%x", state->reg[DWARF_SP], newstate.reg[DWARF_SP]);
-
-    if (newstate.reg[DWARF_PC] == 0)
-        return false;
-
-    /* End backtrace if registers do not change */
-    if ((state->reg[DWARF_PC] == newstate.reg[DWARF_PC]) &&
-        (state->reg[DWARF_RA] == newstate.reg[DWARF_RA]) &&
-        (state->reg[DWARF_FP] == newstate.reg[DWARF_FP]) &&
-        (state->reg[DWARF_SP] == newstate.reg[DWARF_SP]))
-        return false;
-
-    *state = newstate;
-    return true;
-}
-
-/* Execute CIE and FDE instructions for FDE found with find_fde. */
-static bool execute_fde(const memory_t* memory,
-                        uintptr_t fde,
-                        unwind_state_t* state) {
-    uint32_t fde_length = 0;
-    uint32_t cie_length = 0;
-    uintptr_t cie = 0;
-    uintptr_t cie_offset = 0;
-    cie_info_t cie_i;
-    cie_info_t* cie_info = &cie_i;
-    fde_info_t fde_i;
-    fde_info_t* fde_info = &fde_i;
-    dwarf_state_t dwarf_state;
-    dwarf_state_t* dstate = &dwarf_state;
-    dwarf_state_t stack[DWARF_STATES_STACK];
-    uint8_t stack_ptr = 0;
-
-    memset(dstate, 0, sizeof(dwarf_state_t));
-    memset(cie_info, 0, sizeof(cie_info_t));
-    memset(fde_info, 0, sizeof(fde_info_t));
-
-    /* Read common CIE or FDE area:
-       1st word is length;
-       2nd word is ID: 0 for CIE, CIE pointer for FDE.
-    */
-    if (!try_get_word(memory, fde, &fde_length)) {
-        return false;
-    }
-    if ((int32_t)fde_length == -1) {
-        ALOGV("execute_fde: 64-bit dwarf detected, not implemented yet");
-        return false;
-    }
-    if (!try_get_word(memory, fde + 4, &cie_offset)) {
-        return false;
-    }
-    if (cie_offset == 0) {
-        /* This is CIE. We shouldn't be here normally. */
-        cie = fde;
-        cie_length = fde_length;
-    } else {
-        /* Find CIE. */
-        /* Positive cie_offset goes backward from current field. */
-        cie = fde + 4 - cie_offset;
-        if (!try_get_word(memory, cie, &cie_length)) {
-            return false;
-        }
-        if ((int32_t)cie_length == -1) {
-            ALOGV("execute_fde: 64-bit dwarf detected, not implemented yet");
-            return false;
-        }
-        if (!try_get_word(memory, cie + 4, &cie_offset)) {
-            return false;
-        }
-        if (cie_offset != 0) {
-            ALOGV("execute_fde: can't find CIE");
-            return false;
-        }
-    }
-    ALOGV("execute_fde: FDE length: %d", fde_length);
-    ALOGV("execute_fde: CIE pointer: %x", cie);
-    ALOGV("execute_fde: CIE length: %d", cie_length);
-
-    /* Read CIE:
-       Augmentation independent:
-       1st byte is version;
-       next x bytes is /0 terminated augmentation string;
-       next x bytes is unsigned LEB128 encoded code alignment factor;
-       next x bytes is signed LEB128 encoded data alignment factor;
-       next 1 (CIE version 1) or x (CIE version 3 unsigned LEB128) bytes is return register column;
-       Augmentation dependent:
-       if 'z' next x bytes is unsigned LEB128 encoded augmentation data size;
-       if 'L' next 1 byte is LSDA encoding;
-       if 'R' next 1 byte is FDE encoding;
-       if 'S' CIE represents signal handler stack frame;
-       if 'P' next 1 byte is personality encoding folowed by personality function pointer;
-       Next x bytes is CIE program.
-    */
-
-    uint32_t c = 8;
-    if (!try_get_byte(memory, cie, &cie_info->version, &c)) {
-        return false;
-    }
-    ALOGV("execute_fde: CIE version: %d", cie_info->version);
-    uint8_t ch;
-    do {
-        if (!try_get_byte(memory, cie, &ch, &c)) {
-            return false;
-        }
-        switch (ch) {
-        case '\0': break;
-        case 'z': cie_info->aug_z = 1; break;
-        case 'L': cie_info->aug_L = 1; break;
-        case 'R': cie_info->aug_R = 1; break;
-        case 'S': cie_info->aug_S = 1; break;
-        case 'P': cie_info->aug_P = 1; break;
-        default:
-            ALOGV("execute_fde: Unrecognized CIE augmentation char: '%c'", ch);
-            return false;
-            break;
-        }
-    } while (ch);
-    if (!try_get_uleb128(memory, cie, &cie_info->code_align, &c)) {
-        return false;
-    }
-    if (!try_get_sleb128(memory, cie, &cie_info->data_align, &c)) {
-        return false;
-    }
-    if (cie_info->version >= 3) {
-        if (!try_get_uleb128(memory, cie, &cie_info->reg, &c)) {
-            return false;
-        }
-    } else {
-        if (!try_get_byte(memory, cie, (uint8_t*)&cie_info->reg, &c)) {
-            return false;
-        }
-    }
-    ALOGV("execute_fde: CIE code alignment factor: %d", cie_info->code_align);
-    ALOGV("execute_fde: CIE data alignment factor: %d", cie_info->data_align);
-    if (cie_info->aug_z) {
-        if (!try_get_uleb128(memory, cie, &cie_info->aug_z, &c)) {
-            return false;
-        }
-    }
-    if (cie_info->aug_L) {
-        if (!try_get_byte(memory, cie, &cie_info->aug_L, &c)) {
-            return false;
-        }
-    } else {
-        /* Default encoding. */
-        cie_info->aug_L = DW_EH_PE_absptr;
-    }
-    if (cie_info->aug_R) {
-        if (!try_get_byte(memory, cie, &cie_info->aug_R, &c)) {
-            return false;
-        }
-    } else {
-        /* Default encoding. */
-        cie_info->aug_R = DW_EH_PE_absptr;
-    }
-    if (cie_info->aug_P) {
-        /* Get encoding of personality routine pointer. We don't use it now. */
-        if (!try_get_byte(memory, cie, (uint8_t*)&cie_info->aug_P, &c)) {
-            return false;
-        }
-        /* Get routine pointer. */
-        if (!read_dwarf(memory, cie, &cie_info->aug_P, (uint8_t)cie_info->aug_P, &c)) {
-            return false;
-        }
-    }
-    /* CIE program. */
-    /* Length field itself (4 bytes) is not included into length. */
-    stack[0] = *dstate;
-    stack_ptr = 1;
-    while (c < cie_length + 4) {
-        if (!execute_dwarf(memory, cie, cie_info, dstate, &c, stack, &stack_ptr)) {
-            return false;
-        }
-    }
-
-    /* We went directly to CIE. Normally it shouldn't occur. */
-    if (cie == fde) return true;
-
-    /* Go back to FDE. */
-    c = 8;
-    /* Read FDE:
-       Augmentation independent:
-       next x bytes (encoded as specified in CIE) is FDE starting address;
-       next x bytes (encoded as specified in CIE) is FDE number of instructions covered;
-       Augmentation dependent:
-       if 'z' next x bytes is unsigned LEB128 encoded augmentation data size;
-       if 'L' next x bytes is LSDA pointer (encoded as specified in CIE);
-       Next x bytes is FDE program.
-    */
-    if (!read_dwarf(memory, fde, &fde_info->start, (uint8_t)cie_info->aug_R, &c)) {
-        return false;
-    }
-    dstate->loc = fde_info->start;
-    ALOGV("execute_fde: FDE start: %x", dstate->loc);
-    if (!read_dwarf(memory, fde, &fde_info->length, 0, &c)) {
-        return false;
-    }
-    ALOGV("execute_fde: FDE length: %x", fde_info->length);
-    if (cie_info->aug_z) {
-        if (!try_get_uleb128(memory, fde, &fde_info->aug_z, &c)) {
-            return false;
-        }
-    }
-    if (cie_info->aug_L && cie_info->aug_L != DW_EH_PE_omit) {
-        if (!read_dwarf(memory, fde, &fde_info->aug_L, cie_info->aug_L, &c)) {
-            return false;
-        }
-    }
-    /* FDE program. */
-    /* Length field itself (4 bytes) is not included into length. */
-    /* Save CIE state as 0 element of stack. Used by DW_CFA_restore. */
-    stack[0] = *dstate;
-    stack_ptr = 1;
-    while (c < fde_length + 4 && state->reg[DWARF_PC] >= dstate->loc) {
-        if (!execute_dwarf(memory, fde, cie_info, dstate, &c, stack, &stack_ptr)) {
-            return false;
-        }
-        ALOGV("PC: %x, LOC: %x", state->reg[DWARF_PC], dstate->loc);
-    }
-
-    return update_state(memory, state, dstate);
-}
-
-static bool heuristic_state_update(const memory_t* memory, unwind_state_t* state)
-{
-    bool found_start = false;
-    int maxcheck = 1024;
-    int32_t stack_size = 0;
-    int32_t ra_offset = 0;
-    dwarf_state_t dwarf_state;
-    dwarf_state_t* dstate = &dwarf_state;
-
-    static struct {
-        uint32_t insn;
-        uint32_t mask;
-    } frame0sig[] = {
-        {0x3c1c0000, 0xffff0000}, /* lui     gp,xxxx */
-        {0x279c0000, 0xffff0000}, /* addiu   gp,gp,xxxx */
-        {0x039fe021, 0xffffffff}, /* addu    gp,gp,ra */
-    };
-    const int nframe0sig = sizeof(frame0sig)/sizeof(frame0sig[0]);
-    int f0 = nframe0sig;
-    memset(dstate, 0, sizeof(dwarf_state_t));
-
-    /* Search code backwards looking for function prologue */
-    for (uint32_t pc = state->reg[DWARF_PC]-4; maxcheck-- > 0 && !found_start; pc -= 4) {
-        uint32_t op;
-        int32_t immediate;
-
-        if (!try_get_word(memory, pc, &op))
-            return false;
-
-        // ALOGV("@0x%08x: 0x%08x\n", pc, op);
-
-        // Check for frame 0 signature
-        if ((op & frame0sig[f0].mask) == frame0sig[f0].insn) {
-            if (f0 == 0)
-                return false;
-            f0--;
-        }
-        else {
-            f0 = nframe0sig;
-        }
-
-        switch (op & 0xffff0000) {
-        case 0x27bd0000: // addiu sp, imm
-            // looking for stack being decremented
-            immediate = (((int32_t)op) << 16) >> 16;
-            if (immediate < 0) {
-                stack_size = -immediate;
-                ALOGV("@0x%08x: found stack adjustment=%d\n", pc, stack_size);
-            }
-            break;
-        case 0x039f0000: // e021
-
-        case 0xafbf0000: // sw ra, imm(sp)
-            ra_offset = (((int32_t)op) << 16) >> 16;
-            ALOGV("@0x%08x: found ra offset=%d\n", pc, ra_offset);
-            break;
-        case 0x3c1c0000: // lui gp
-            ALOGV("@0x%08x: found function boundary", pc);
-            found_start = true;
-            break;
-        default:
-            break;
-        }
-    }
-
-    dstate->cfa_reg = DWARF_SP;
-    dstate->cfa_off = stack_size;
-
-    if (ra_offset) {
-        dstate->regs[DWARF_RA].rule = 'o';
-        dstate->regs[DWARF_RA].value = -stack_size + ra_offset;
-    }
-
-    return update_state(memory, state, dstate);
-}
-
-static ssize_t unwind_backtrace_common(const memory_t* memory,
-        const map_info_t* map_info_list,
-        unwind_state_t* state, backtrace_frame_t* backtrace,
-        size_t ignore_depth, size_t max_depth) {
-
-    size_t ignored_frames = 0;
-    size_t returned_frames = 0;
-
-    ALOGV("Unwinding tid: %d", memory->tid);
-    ALOGV("PC: %x", state->reg[DWARF_PC]);
-    ALOGV("RA: %x", state->reg[DWARF_RA]);
-    ALOGV("FP: %x", state->reg[DWARF_FP]);
-    ALOGV("SP: %x", state->reg[DWARF_SP]);
-
-    for (size_t index = 0; returned_frames < max_depth; index++) {
-        uintptr_t fde = find_fde(memory, map_info_list, state->reg[DWARF_PC]);
-        backtrace_frame_t* frame = add_backtrace_entry(
-            index ? rewind_pc_arch(memory, state->reg[DWARF_PC]) : state->reg[DWARF_PC],
-            backtrace, ignore_depth, max_depth,
-            &ignored_frames, &returned_frames);
-        uint32_t stack_top = state->reg[DWARF_SP];
-
-        if (fde) {
-            /* Use FDE to update state */
-            if (!execute_fde(memory, fde, state))
-                break;
-        }
-        else {
-            /* FDE is not found, update state heuristically */
-            if (!heuristic_state_update(memory, state))
-                break;
-        }
-
-        if (frame) {
-            frame->stack_top = stack_top;
-            if (stack_top < state->reg[DWARF_SP]) {
-                frame->stack_size = state->reg[DWARF_SP] - stack_top;
-            }
-        }
-        ALOGV("Stack: 0x%x ... 0x%x - %d bytes", frame->stack_top, state->reg[DWARF_SP], frame->stack_size);
-    }
-    return returned_frames;
-}
-
-ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo __attribute__((unused)), void* sigcontext,
-        const map_info_t* map_info_list,
-        backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) {
-    const ucontext_t* uc = (const ucontext_t*)sigcontext;
-
-    unwind_state_t state;
-    state.reg[DWARF_PC] = uc->uc_mcontext.pc;
-    state.reg[DWARF_RA] = uc->uc_mcontext.gregs[REG_RA];
-    state.reg[DWARF_FP] = uc->uc_mcontext.gregs[REG_S8];
-    state.reg[DWARF_SP] = uc->uc_mcontext.gregs[REG_SP];
-
-    ALOGV("unwind_backtrace_signal_arch: "
-          "ignore_depth=%d max_depth=%d pc=0x%08x sp=0x%08x ra=0x%08x\n",
-          ignore_depth, max_depth, state.reg[DWARF_PC], state.reg[DWARF_SP], state.reg[DWARF_RA]);
-
-    memory_t memory;
-    init_memory(&memory, map_info_list);
-    return unwind_backtrace_common(&memory, map_info_list,
-                                   &state, backtrace, ignore_depth, max_depth);
-}
-
-ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context,
-        backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) {
-
-    user_regs_struct regs;
-    if (ptrace(PTRACE_GETREGS, tid, 0, &regs)) {
-        return -1;
-    }
-
-    unwind_state_t state;
-    state.reg[DWARF_PC] = regs.epc;
-    state.reg[DWARF_RA] = regs.regs[REG_RA];
-    state.reg[DWARF_FP] = regs.regs[REG_S8];
-    state.reg[DWARF_SP] = regs.regs[REG_SP];
-
-    ALOGV("unwind_backtrace_ptrace_arch: "
-          "ignore_depth=%d max_depth=%d pc=0x%08x sp=0x%08x ra=0x%08x\n",
-          ignore_depth, max_depth, state.reg[DWARF_PC], state.reg[DWARF_SP], state.reg[DWARF_RA]);
-
-    memory_t memory;
-    init_memory_ptrace(&memory, tid);
-    return unwind_backtrace_common(&memory, context->map_info_list,
-                                   &state, backtrace, ignore_depth, max_depth);
-}
diff --git a/libcorkscrew/arch-mips/dwarf.h b/libcorkscrew/arch-mips/dwarf.h
deleted file mode 100644
index 8504ea0..0000000
--- a/libcorkscrew/arch-mips/dwarf.h
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-/*
- * Dwarf2 data encoding flags.
- */
-
-#define DW_EH_PE_absptr         0x00
-#define DW_EH_PE_omit           0xff
-#define DW_EH_PE_uleb128        0x01
-#define DW_EH_PE_udata2         0x02
-#define DW_EH_PE_udata4         0x03
-#define DW_EH_PE_udata8         0x04
-#define DW_EH_PE_sleb128        0x09
-#define DW_EH_PE_sdata2         0x0A
-#define DW_EH_PE_sdata4         0x0B
-#define DW_EH_PE_sdata8         0x0C
-#define DW_EH_PE_signed         0x08
-#define DW_EH_PE_pcrel          0x10
-#define DW_EH_PE_textrel        0x20
-#define DW_EH_PE_datarel        0x30
-#define DW_EH_PE_funcrel        0x40
-#define DW_EH_PE_aligned        0x50
-#define DW_EH_PE_indirect       0x80
-
-/*
- * Dwarf2 call frame instructions.
- */
-
-typedef enum {
-    DW_CFA_advance_loc = 0x40,
-    DW_CFA_offset = 0x80,
-    DW_CFA_restore = 0xc0,
-    DW_CFA_nop = 0x00,
-    DW_CFA_set_loc = 0x01,
-    DW_CFA_advance_loc1 = 0x02,
-    DW_CFA_advance_loc2 = 0x03,
-    DW_CFA_advance_loc4 = 0x04,
-    DW_CFA_offset_extended = 0x05,
-    DW_CFA_restore_extended = 0x06,
-    DW_CFA_undefined = 0x07,
-    DW_CFA_same_value = 0x08,
-    DW_CFA_register = 0x09,
-    DW_CFA_remember_state = 0x0a,
-    DW_CFA_restore_state = 0x0b,
-    DW_CFA_def_cfa = 0x0c,
-    DW_CFA_def_cfa_register = 0x0d,
-    DW_CFA_def_cfa_offset = 0x0e
-} dwarf_CFA;
-
-/*
- * eh_frame_hdr information.
-*/
-
-typedef struct {
-      uint8_t version;
-      uint8_t eh_frame_ptr_enc;
-      uint8_t fde_count_enc;
-      uint8_t fde_table_enc;
-      uintptr_t eh_frame_ptr;
-      uint32_t fde_count;
-} eh_frame_hdr_info_t;
-
-/*
- * CIE information.
-*/
-
-typedef struct {
-      uint8_t version;
-      uint32_t code_align;
-      uint32_t data_align;
-      uint32_t reg;
-      uint32_t aug_z;
-      uint8_t aug_L;
-      uint8_t aug_R;
-      uint8_t aug_S;
-      uint32_t aug_P;
-} cie_info_t;
-
-/*
- * FDE information.
-*/
-
-typedef struct {
-      uint32_t start;
-      uint32_t length; // number of instructions covered by FDE
-      uint32_t aug_z;
-      uint32_t aug_L;
-} fde_info_t;
-
-/*
- * Dwarf state.
-*/
-
-/* Stack of states: required for DW_CFA_remember_state/DW_CFA_restore_state
-   30 should be enough */
-#define DWARF_STATES_STACK 30
-
-typedef struct {
-    char rule;         // rule: o - offset(value); r - register(value)
-    uint32_t value;    // value
-} reg_rule_t;
-
-/* Dwarf preserved number of registers for mips */
-typedef enum
-  {
-    UNW_MIPS_R0,
-    UNW_MIPS_R1,
-    UNW_MIPS_R2,
-    UNW_MIPS_R3,
-    UNW_MIPS_R4,
-    UNW_MIPS_R5,
-    UNW_MIPS_R6,
-    UNW_MIPS_R7,
-    UNW_MIPS_R8,
-    UNW_MIPS_R9,
-    UNW_MIPS_R10,
-    UNW_MIPS_R11,
-    UNW_MIPS_R12,
-    UNW_MIPS_R13,
-    UNW_MIPS_R14,
-    UNW_MIPS_R15,
-    UNW_MIPS_R16,
-    UNW_MIPS_R17,
-    UNW_MIPS_R18,
-    UNW_MIPS_R19,
-    UNW_MIPS_R20,
-    UNW_MIPS_R21,
-    UNW_MIPS_R22,
-    UNW_MIPS_R23,
-    UNW_MIPS_R24,
-    UNW_MIPS_R25,
-    UNW_MIPS_R26,
-    UNW_MIPS_R27,
-    UNW_MIPS_R28,
-    UNW_MIPS_R29,
-    UNW_MIPS_R30,
-    UNW_MIPS_R31,
-
-    UNW_MIPS_PC = 34,
-
-    /* FIXME: Other registers!  */
-
-    /* For MIPS, the CFA is the value of SP (r29) at the call site in the
-       previous frame.  */
-    UNW_MIPS_CFA,
-
-    UNW_TDEP_LASTREG,
-
-    UNW_TDEP_LAST_REG = UNW_MIPS_R31,
-
-    UNW_TDEP_IP = UNW_MIPS_R31,
-    UNW_TDEP_SP = UNW_MIPS_R29,
-    UNW_TDEP_EH = UNW_MIPS_R0   /* FIXME.  */
-
-  }
-mips_regnum_t;
-
-#define DWARF_REGISTERS UNW_TDEP_LASTREG
-
-typedef struct {
-    uintptr_t loc;     // location (ip)
-    uint8_t cfa_reg;   // index of register where CFA location stored
-    intptr_t cfa_off;  // offset
-    reg_rule_t regs[DWARF_REGISTERS]; // dwarf preserved registers for mips
-} dwarf_state_t;
-
-/* DWARF registers we are caring about. */
-
-
-#define DWARF_SP      UNW_MIPS_R29
-#define DWARF_RA      UNW_MIPS_R31
-#define DWARF_PC      UNW_MIPS_PC
-#define DWARF_FP      UNW_MIPS_CFA /* FIXME is this correct? */
diff --git a/libcorkscrew/arch-mips/ptrace-mips.c b/libcorkscrew/arch-mips/ptrace-mips.c
deleted file mode 100644
index ba3b60a..0000000
--- a/libcorkscrew/arch-mips/ptrace-mips.c
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-#define LOG_TAG "Corkscrew"
-//#define LOG_NDEBUG 0
-
-#include "../ptrace-arch.h"
-
-#include <stddef.h>
-#include <elf.h>
-#include <cutils/log.h>
-
-static void load_eh_frame_hdr(pid_t pid, map_info_t* mi, uintptr_t *eh_frame_hdr) {
-    uint32_t elf_phoff;
-    uint32_t elf_phentsize_ehsize;
-    uint32_t elf_shentsize_phnum;
-
-
-    try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phoff), &elf_phoff);
-    ALOGV("reading 0x%08x elf_phoff:%x",  mi->start + offsetof(Elf32_Ehdr, e_phoff), elf_phoff);
-    try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_ehsize), &elf_phentsize_ehsize);
-    ALOGV("reading 0x%08x elf_phentsize_ehsize:%x", mi->start + offsetof(Elf32_Ehdr, e_ehsize), elf_phentsize_ehsize);
-    try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phnum), &elf_shentsize_phnum);
-    ALOGV("reading 0x%08x elf_shentsize_phnum:%x", mi->start + offsetof(Elf32_Ehdr, e_phnum), elf_shentsize_phnum);
-
-
-
-    if (try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phoff), &elf_phoff)
-            && try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_ehsize),
-                    &elf_phentsize_ehsize)
-            && try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phnum),
-                    &elf_shentsize_phnum)) {
-        uint32_t elf_phentsize = elf_phentsize_ehsize >> 16;
-        uint32_t elf_phnum = elf_shentsize_phnum & 0xffff;
-        for (uint32_t i = 0; i < elf_phnum; i++) {
-            uintptr_t elf_phdr = mi->start + elf_phoff + i * elf_phentsize;
-            uint32_t elf_phdr_type;
-            if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_type), &elf_phdr_type)) {
-                break;
-            }
-            if (elf_phdr_type == PT_GNU_EH_FRAME) {
-                uint32_t elf_phdr_offset;
-                if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_offset),
-                        &elf_phdr_offset)) {
-                    break;
-                }
-                *eh_frame_hdr = mi->start + elf_phdr_offset;
-                ALOGV("Parsed .eh_frame_hdr info for %s: start=0x%08x", mi->name, *eh_frame_hdr);
-                return;
-            }
-        }
-    }
-    *eh_frame_hdr = 0;
-}
-
-void load_ptrace_map_info_data_arch(pid_t pid, map_info_t* mi, map_info_data_t* data) {
-    ALOGV("load_ptrace_map_info_data_arch");
-    load_eh_frame_hdr(pid, mi, &data->eh_frame_hdr);
-}
-
-void free_ptrace_map_info_data_arch(map_info_t* mi __attribute__((unused)),
-                                    map_info_data_t* data __attribute__((unused))) {
-    ALOGV("free_ptrace_map_info_data_arch");
-}
diff --git a/libcorkscrew/arch-x86/backtrace-x86.c b/libcorkscrew/arch-x86/backtrace-x86.c
deleted file mode 100755
index df486de..0000000
--- a/libcorkscrew/arch-x86/backtrace-x86.c
+++ /dev/null
@@ -1,823 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-/*
- * Backtracing functions for x86.
- */
-
-#define LOG_TAG "Corkscrew"
-//#define LOG_NDEBUG 0
-
-#include "../backtrace-arch.h"
-#include "../backtrace-helper.h"
-#include "../ptrace-arch.h"
-#include <corkscrew/ptrace.h>
-#include "dwarf.h"
-
-#include <stdlib.h>
-#include <signal.h>
-#include <stdbool.h>
-#include <limits.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/ptrace.h>
-#include <cutils/log.h>
-
-#if defined(__APPLE__)
-
-#define _XOPEN_SOURCE
-#include <ucontext.h>
-
-#else
-
-// glibc has its own renaming of the Linux kernel's structures.
-#define __USE_GNU // For REG_EBP, REG_ESP, and REG_EIP.
-#include <ucontext.h>
-
-#endif
-
-/* Unwind state. */
-typedef struct {
-    uint32_t reg[DWARF_REGISTERS];
-} unwind_state_t;
-
-typedef struct {
-    backtrace_frame_t* backtrace;
-    size_t ignore_depth;
-    size_t max_depth;
-    size_t ignored_frames;
-    size_t returned_frames;
-    memory_t memory;
-} backtrace_state_t;
-
-uintptr_t rewind_pc_arch(const memory_t* memory __attribute__((unused)), uintptr_t pc) {
-    /* TODO: x86 instructions are 1-16 bytes, to define exact size of previous instruction
-       we have to disassemble from the function entry point up to pc.
-       Returning pc-1 is probably enough for now, the only drawback is that
-       it points somewhere between the first byte of instruction we are looking for and
-       the first byte of the next instruction. */
-
-    return pc-1;
-    /* TODO: We should adjust that for the signal frames and return pc for them instead of pc-1.
-       To recognize signal frames we should read cie_info property. */
-}
-
-/* Read byte through 4 byte cache. Usually we read byte by byte and updating cursor. */
-static bool try_get_byte(const memory_t* memory, uintptr_t ptr, uint8_t* out_value, uint32_t* cursor) {
-    static uintptr_t lastptr;
-    static uint32_t buf;
-
-    ptr += *cursor;
-
-    if (ptr < lastptr || lastptr + 3 < ptr) {
-        lastptr = (ptr >> 2) << 2;
-        if (!try_get_word(memory, lastptr, &buf)) {
-            return false;
-        }
-    }
-    *out_value = (uint8_t)((buf >> ((ptr & 3) * 8)) & 0xff);
-    ++*cursor;
-    return true;
-}
-
-/* Getting X bytes. 4 is maximum for now. */
-static bool try_get_xbytes(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint8_t bytes, uint32_t* cursor) {
-    uint32_t data = 0;
-    if (bytes > 4) {
-        ALOGE("can't read more than 4 bytes, trying to read %d", bytes);
-        return false;
-    }
-    for (int i = 0; i < bytes; i++) {
-        uint8_t buf;
-        if (!try_get_byte(memory, ptr, &buf, cursor)) {
-            return false;
-        }
-        data |= (uint32_t)buf << (i * 8);
-    }
-    *out_value = data;
-    return true;
-}
-
-/* Reads signed/unsigned LEB128 encoded data. From 1 to 4 bytes. */
-static bool try_get_leb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor, bool sign_extend) {
-    uint8_t buf = 0;
-    uint32_t val = 0;
-    uint8_t c = 0;
-    do {
-       if (!try_get_byte(memory, ptr, &buf, cursor)) {
-           return false;
-       }
-       val |= ((uint32_t)buf & 0x7f) << (c * 7);
-       c++;
-    } while (buf & 0x80 && (c * 7) <= 32);
-    if (c * 7 > 32) {
-       ALOGE("%s: data exceeds expected 4 bytes maximum", __FUNCTION__);
-       return false;
-    }
-    if (sign_extend) {
-        if (buf & 0x40) {
-            val |= ((uint32_t)-1 << (c * 7));
-        }
-    }
-    *out_value = val;
-    return true;
-}
-
-/* Reads signed LEB128 encoded data. From 1 to 4 bytes. */
-static bool try_get_sleb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor) {
-  return try_get_leb128(memory, ptr, out_value, cursor, true);
-}
-
-/* Reads unsigned LEB128 encoded data. From 1 to 4 bytes. */
-static bool try_get_uleb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor) {
-  return try_get_leb128(memory, ptr, out_value, cursor, false);
-}
-
-/* Getting data encoded by dwarf encodings. */
-static bool read_dwarf(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint8_t encoding, uint32_t* cursor) {
-    uint32_t data = 0;
-    bool issigned = true;
-    uintptr_t addr = ptr + *cursor;
-    /* Lower 4 bits is data type/size */
-    /* TODO: add more encodings if it becomes necessary */
-    switch (encoding & 0xf) {
-        case DW_EH_PE_absptr:
-            if (!try_get_xbytes(memory, ptr, &data, 4, cursor)) {
-                return false;
-            }
-            *out_value = data;
-            return true;
-        case DW_EH_PE_udata4:
-            issigned = false;
-        case DW_EH_PE_sdata4:
-            if (!try_get_xbytes(memory, ptr, &data, 4, cursor)) {
-                return false;
-            }
-            break;
-        default:
-            ALOGE("unrecognized dwarf lower part encoding: 0x%x", encoding);
-            return false;
-    }
-    /* Higher 4 bits is modifier */
-    /* TODO: add more encodings if it becomes necessary */
-    switch (encoding & 0xf0) {
-        case 0:
-            *out_value = data;
-            break;
-        case DW_EH_PE_pcrel:
-            if (issigned) {
-                *out_value = addr + (int32_t)data;
-            } else {
-                *out_value = addr + data;
-            }
-            break;
-        /* Assuming ptr is correct base to calculate datarel */
-        case DW_EH_PE_datarel:
-            if (issigned) {
-                *out_value = ptr + (int32_t)data;
-            } else {
-                *out_value = ptr + data;
-            }
-            break;
-        default:
-            ALOGE("unrecognized dwarf higher part encoding: 0x%x", encoding);
-            return false;
-    }
-    return true;
-}
-
-/* Having PC find corresponding FDE by reading .eh_frame_hdr section data. */
-static uintptr_t find_fde(const memory_t* memory,
-                          const map_info_t* map_info_list, uintptr_t pc) {
-    if (!pc) {
-        ALOGV("find_fde: pc is zero, no eh_frame");
-        return 0;
-    }
-    const map_info_t* mi = find_map_info(map_info_list, pc);
-    if (!mi) {
-        ALOGV("find_fde: no map info for pc:0x%x", pc);
-        return 0;
-    }
-    const map_info_data_t* midata = mi->data;
-    if (!midata) {
-        ALOGV("find_fde: no eh_frame_hdr for map: start=0x%x, end=0x%x", mi->start, mi->end);
-        return 0;
-    }
-
-    eh_frame_hdr_info_t eh_hdr_info;
-    memset(&eh_hdr_info, 0, sizeof(eh_frame_hdr_info_t));
-
-    /* Getting the first word of eh_frame_hdr:
-        1st byte is version;
-        2nd byte is encoding of pointer to eh_frames;
-        3rd byte is encoding of count of FDEs in lookup table;
-        4th byte is encoding of lookup table entries.
-    */
-    uintptr_t eh_frame_hdr = midata->eh_frame_hdr;
-    uint32_t c = 0;
-    if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.version, &c)) return 0;
-    if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.eh_frame_ptr_enc, &c)) return 0;
-    if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.fde_count_enc, &c)) return 0;
-    if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.fde_table_enc, &c)) return 0;
-
-    /* TODO: 3rd byte can be DW_EH_PE_omit, that means no lookup table available and we should
-       try to parse eh_frame instead. Not sure how often it may occur, skipping now.
-    */
-    if (eh_hdr_info.version != 1) {
-        ALOGV("find_fde: eh_frame_hdr version %d is not supported", eh_hdr_info.version);
-        return 0;
-    }
-    /* Getting the data:
-        2nd word is eh_frame pointer (normally not used, because lookup table has all we need);
-        3rd word is count of FDEs in the lookup table;
-        starting from 4 word there is FDE lookup table (pairs of PC and FDE pointer) sorted by PC;
-    */
-    if (!read_dwarf(memory, eh_frame_hdr, &eh_hdr_info.eh_frame_ptr, eh_hdr_info.eh_frame_ptr_enc, &c)) return 0;
-    if (!read_dwarf(memory, eh_frame_hdr, &eh_hdr_info.fde_count, eh_hdr_info.fde_count_enc, &c)) return 0;
-    ALOGV("find_fde: found %d FDEs", eh_hdr_info.fde_count);
-
-    int32_t low = 0;
-    int32_t high = eh_hdr_info.fde_count;
-    uintptr_t start = 0;
-    uintptr_t fde = 0;
-    /* eh_frame_hdr + c points to lookup table at this point. */
-    while (low <= high) {
-        uint32_t mid = (high + low)/2;
-        uint32_t entry = c + mid * 8;
-        if (!read_dwarf(memory, eh_frame_hdr, &start, eh_hdr_info.fde_table_enc, &entry)) return 0;
-        if (pc <= start) {
-            high = mid - 1;
-        } else {
-            low = mid + 1;
-        }
-    }
-    /* Value found is at high. */
-    if (high < 0) {
-        ALOGV("find_fde: pc %x is out of FDE bounds: %x", pc, start);
-        return 0;
-    }
-    c += high * 8;
-    if (!read_dwarf(memory, eh_frame_hdr, &start, eh_hdr_info.fde_table_enc, &c)) return 0;
-    if (!read_dwarf(memory, eh_frame_hdr, &fde, eh_hdr_info.fde_table_enc, &c)) return 0;
-    ALOGV("pc 0x%x, ENTRY %d: start=0x%x, fde=0x%x", pc, high, start, fde);
-    return fde;
-}
-
-/* Execute single dwarf instruction and update dwarf state accordingly. */
-static bool execute_dwarf(const memory_t* memory, uintptr_t ptr, cie_info_t* cie_info,
-                          dwarf_state_t* dstate, uint32_t* cursor,
-                          dwarf_state_t* stack, uint8_t* stack_ptr) {
-    uint8_t inst;
-    uint8_t op = 0;
-
-    if (!try_get_byte(memory, ptr, &inst, cursor)) {
-        return false;
-    }
-    ALOGV("DW_CFA inst: 0x%x", inst);
-
-    /* For some instructions upper 2 bits is opcode and lower 6 bits is operand. See dwarf-2.0 7.23. */
-    if (inst & 0xc0) {
-        op = inst & 0x3f;
-        inst &= 0xc0;
-    }
-
-    switch ((dwarf_CFA)inst) {
-        uint32_t reg = 0;
-        uint32_t offset = 0;
-        case DW_CFA_advance_loc:
-            dstate->loc += op * cie_info->code_align;
-            ALOGV("DW_CFA_advance_loc: %d to 0x%x", op, dstate->loc);
-            break;
-        case DW_CFA_offset:
-            if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false;
-            dstate->regs[op].rule = 'o';
-            dstate->regs[op].value = offset * cie_info->data_align;
-            ALOGV("DW_CFA_offset: r%d = o(%d)", op, dstate->regs[op].value);
-            break;
-        case DW_CFA_restore:
-            dstate->regs[op].rule = stack->regs[op].rule;
-            dstate->regs[op].value = stack->regs[op].value;
-            ALOGV("DW_CFA_restore: r%d = %c(%d)", op, dstate->regs[op].rule, dstate->regs[op].value);
-            break;
-        case DW_CFA_nop:
-            break;
-        case DW_CFA_set_loc: // probably we don't have it on x86.
-            if (!try_get_xbytes(memory, ptr, &offset, 4, cursor)) return false;
-            if (offset < dstate->loc) {
-                ALOGE("DW_CFA_set_loc: attempt to move location backward");
-                return false;
-            }
-            dstate->loc = offset * cie_info->code_align;
-            ALOGV("DW_CFA_set_loc: %d to 0x%x", offset * cie_info->code_align, dstate->loc);
-            break;
-        case DW_CFA_advance_loc1:
-            if (!try_get_byte(memory, ptr, (uint8_t*)&offset, cursor)) return false;
-            dstate->loc += (uint8_t)offset * cie_info->code_align;
-            ALOGV("DW_CFA_advance_loc1: %d to 0x%x", (uint8_t)offset * cie_info->code_align, dstate->loc);
-            break;
-        case DW_CFA_advance_loc2:
-            if (!try_get_xbytes(memory, ptr, &offset, 2, cursor)) return false;
-            dstate->loc += (uint16_t)offset * cie_info->code_align;
-            ALOGV("DW_CFA_advance_loc2: %d to 0x%x", (uint16_t)offset * cie_info->code_align, dstate->loc);
-            break;
-        case DW_CFA_advance_loc4:
-            if (!try_get_xbytes(memory, ptr, &offset, 4, cursor)) return false;
-            dstate->loc += offset * cie_info->code_align;
-            ALOGV("DW_CFA_advance_loc4: %d to 0x%x", offset * cie_info->code_align, dstate->loc);
-            break;
-        case DW_CFA_offset_extended: // probably we don't have it on x86.
-            if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
-            if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false;
-            if (reg >= DWARF_REGISTERS) {
-                ALOGE("DW_CFA_offset_extended: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS);
-                return false;
-            }
-            dstate->regs[reg].rule = 'o';
-            dstate->regs[reg].value = offset * cie_info->data_align;
-            ALOGV("DW_CFA_offset_extended: r%d = o(%d)", reg, dstate->regs[reg].value);
-            break;
-        case DW_CFA_restore_extended: // probably we don't have it on x86.
-            if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
-            if (reg >= DWARF_REGISTERS) {
-                ALOGE("DW_CFA_restore_extended: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS);
-                return false;
-            }
-            dstate->regs[reg].rule = stack->regs[reg].rule;
-            dstate->regs[reg].value = stack->regs[reg].value;
-            ALOGV("DW_CFA_restore: r%d = %c(%d)", reg, dstate->regs[reg].rule, dstate->regs[reg].value);
-            break;
-        case DW_CFA_undefined: // probably we don't have it on x86.
-            if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
-            if (reg >= DWARF_REGISTERS) {
-                ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS);
-                return false;
-            }
-            dstate->regs[reg].rule = 'u';
-            dstate->regs[reg].value = 0;
-            ALOGV("DW_CFA_undefined: r%d", reg);
-            break;
-        case DW_CFA_same_value: // probably we don't have it on x86.
-            if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
-            if (reg >= DWARF_REGISTERS) {
-                ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS);
-                return false;
-            }
-            dstate->regs[reg].rule = 's';
-            dstate->regs[reg].value = 0;
-            ALOGV("DW_CFA_same_value: r%d", reg);
-            break;
-        case DW_CFA_register: // probably we don't have it on x86.
-            if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
-            /* that's new register actually, not offset */
-            if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false;
-            if (reg >= DWARF_REGISTERS || offset >= DWARF_REGISTERS) {
-                ALOGE("DW_CFA_register: r%d or r%d exceeds supported number of registers (%d)", reg, offset, DWARF_REGISTERS);
-                return false;
-            }
-            dstate->regs[reg].rule = 'r';
-            dstate->regs[reg].value = offset;
-            ALOGV("DW_CFA_register: r%d = r(%d)", reg, dstate->regs[reg].value);
-            break;
-        case DW_CFA_remember_state:
-            if (*stack_ptr == DWARF_STATES_STACK) {
-                ALOGE("DW_CFA_remember_state: states stack overflow %d", *stack_ptr);
-                return false;
-            }
-            stack[(*stack_ptr)++] = *dstate;
-            ALOGV("DW_CFA_remember_state: stacktop moves to %d", *stack_ptr);
-            break;
-        case DW_CFA_restore_state:
-            /* We have CIE state saved at 0 position. It's not supposed to be taken
-               by DW_CFA_restore_state. */
-            if (*stack_ptr == 1) {
-                ALOGE("DW_CFA_restore_state: states stack is empty");
-                return false;
-            }
-            /* Don't touch location on restore. */
-            uintptr_t saveloc = dstate->loc;
-            *dstate = stack[--*stack_ptr];
-            dstate->loc = saveloc;
-            ALOGV("DW_CFA_restore_state: stacktop moves to %d", *stack_ptr);
-            break;
-        case DW_CFA_def_cfa:
-            if (!try_get_uleb128(memory, ptr, &reg, cursor)) return false;
-            if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false;
-            dstate->cfa_reg = reg;
-            dstate->cfa_off = offset;
-            ALOGV("DW_CFA_def_cfa: %x(r%d)", offset, reg);
-            break;
-        case DW_CFA_def_cfa_register:
-            if (!try_get_uleb128(memory, ptr, &reg, cursor)) {
-                return false;
-            }
-            dstate->cfa_reg = reg;
-            ALOGV("DW_CFA_def_cfa_register: r%d", reg);
-            break;
-        case DW_CFA_def_cfa_offset:
-            if (!try_get_uleb128(memory, ptr, &offset, cursor)) {
-                return false;
-            }
-            dstate->cfa_off = offset;
-            ALOGV("DW_CFA_def_cfa_offset: %x", offset);
-            break;
-        default:
-            ALOGE("unrecognized DW_CFA_* instruction: 0x%x", inst);
-            return false;
-    }
-    return true;
-}
-
-/* Restoring particular register value based on dwarf state. */
-static bool get_old_register_value(const memory_t* memory, uint32_t cfa,
-                                   dwarf_state_t* dstate, uint8_t reg,
-                                   unwind_state_t* state, unwind_state_t* newstate) {
-    uint32_t addr;
-    switch (dstate->regs[reg].rule) {
-        case 0:
-            /* We don't have dstate updated for this register, so assuming value kept the same.
-               Normally we should look into state and return current value as the old one
-               but we don't have all registers in state to handle this properly */
-            ALOGV("get_old_register_value: value of r%d is the same", reg);
-            // for ESP if it's not updated by dwarf rule we assume it's equal to CFA
-            if (reg == DWARF_ESP) {
-                ALOGV("get_old_register_value: adjusting esp to CFA: 0x%x", cfa);
-                newstate->reg[reg] = cfa;
-            } else {
-                newstate->reg[reg] = state->reg[reg];
-            }
-            break;
-        case 'o':
-            addr = cfa + (int32_t)dstate->regs[reg].value;
-            if (!try_get_word(memory, addr, &newstate->reg[reg])) {
-                ALOGE("get_old_register_value: can't read from 0x%x", addr);
-                return false;
-            }
-            ALOGV("get_old_register_value: r%d at 0x%x is 0x%x", reg, addr, newstate->reg[reg]);
-            break;
-        case 'r':
-            /* We don't have all registers in state so don't even try to look at 'r' */
-            ALOGE("get_old_register_value: register lookup not implemented yet");
-            break;
-        default:
-            ALOGE("get_old_register_value: unexpected rule:%c value:%d for register %d",
-                   dstate->regs[reg].rule, (int32_t)dstate->regs[reg].value, reg);
-            return false;
-    }
-    return true;
-}
-
-/* Updaing state based on dwarf state. */
-static bool update_state(const memory_t* memory, unwind_state_t* state,
-                         dwarf_state_t* dstate) {
-    unwind_state_t newstate;
-    /* We can restore more registers here if we need them. Meanwile doing minimal work here. */
-    /* Getting CFA. */
-    uintptr_t cfa = 0;
-    if (dstate->cfa_reg == DWARF_ESP) {
-        cfa = state->reg[DWARF_ESP] + dstate->cfa_off;
-    } else if (dstate->cfa_reg == DWARF_EBP) {
-        cfa = state->reg[DWARF_EBP] + dstate->cfa_off;
-    } else {
-        ALOGE("update_state: unexpected CFA register: %d", dstate->cfa_reg);
-        return false;
-    }
-    ALOGV("update_state: new CFA: 0x%x", cfa);
-    /* Getting EIP. */
-    if (!get_old_register_value(memory, cfa, dstate, DWARF_EIP, state, &newstate)) return false;
-    /* Getting EBP. */
-    if (!get_old_register_value(memory, cfa, dstate, DWARF_EBP, state, &newstate)) return false;
-    /* Getting ESP. */
-    if (!get_old_register_value(memory, cfa, dstate, DWARF_ESP, state, &newstate)) return false;
-
-    ALOGV("update_state: IP:  0x%x; restore IP:  0x%x", state->reg[DWARF_EIP], newstate.reg[DWARF_EIP]);
-    ALOGV("update_state: EBP: 0x%x; restore EBP: 0x%x", state->reg[DWARF_EBP], newstate.reg[DWARF_EBP]);
-    ALOGV("update_state: ESP: 0x%x; restore ESP: 0x%x", state->reg[DWARF_ESP], newstate.reg[DWARF_ESP]);
-    *state = newstate;
-    return true;
-}
-
-/* Execute CIE and FDE instructions for FDE found with find_fde. */
-static bool execute_fde(const memory_t* memory,
-                        uintptr_t fde,
-                        unwind_state_t* state) {
-    uint32_t fde_length = 0;
-    uint32_t cie_length = 0;
-    uintptr_t cie = 0;
-    uintptr_t cie_offset = 0;
-    cie_info_t cie_i;
-    cie_info_t* cie_info = &cie_i;
-    fde_info_t fde_i;
-    fde_info_t* fde_info = &fde_i;
-    dwarf_state_t dwarf_state;
-    dwarf_state_t* dstate = &dwarf_state;
-    dwarf_state_t stack[DWARF_STATES_STACK];
-    uint8_t stack_ptr = 0;
-
-    memset(dstate, 0, sizeof(dwarf_state_t));
-    memset(cie_info, 0, sizeof(cie_info_t));
-    memset(fde_info, 0, sizeof(fde_info_t));
-
-    /* Read common CIE or FDE area:
-        1st word is length;
-        2nd word is ID: 0 for CIE, CIE pointer for FDE.
-    */
-    if (!try_get_word(memory, fde, &fde_length)) {
-        return false;
-    }
-    if ((int32_t)fde_length == -1) {
-        ALOGV("execute_fde: 64-bit dwarf detected, not implemented yet");
-        return false;
-    }
-    if (!try_get_word(memory, fde + 4, &cie_offset)) {
-        return false;
-    }
-    if (cie_offset == 0) {
-        /* This is CIE. We shouldn't be here normally. */
-        cie = fde;
-        cie_length = fde_length;
-    } else {
-        /* Find CIE. */
-        /* Positive cie_offset goes backward from current field. */
-        cie = fde + 4 - cie_offset;
-        if (!try_get_word(memory, cie, &cie_length)) {
-           return false;
-        }
-        if ((int32_t)cie_length == -1) {
-           ALOGV("execute_fde: 64-bit dwarf detected, not implemented yet");
-           return false;
-        }
-        if (!try_get_word(memory, cie + 4, &cie_offset)) {
-           return false;
-        }
-        if (cie_offset != 0) {
-           ALOGV("execute_fde: can't find CIE");
-           return false;
-        }
-    }
-    ALOGV("execute_fde: FDE length: %d", fde_length);
-    ALOGV("execute_fde: CIE pointer: %x", cie);
-    ALOGV("execute_fde: CIE length: %d", cie_length);
-
-    /* Read CIE:
-       Augmentation independent:
-        1st byte is version;
-        next x bytes is /0 terminated augmentation string;
-        next x bytes is unsigned LEB128 encoded code alignment factor;
-        next x bytes is signed LEB128 encoded data alignment factor;
-        next 1 (CIE version 1) or x (CIE version 3 unsigned LEB128) bytes is return register column;
-       Augmentation dependent:
-        if 'z' next x bytes is unsigned LEB128 encoded augmentation data size;
-        if 'L' next 1 byte is LSDA encoding;
-        if 'R' next 1 byte is FDE encoding;
-        if 'S' CIE represents signal handler stack frame;
-        if 'P' next 1 byte is personality encoding folowed by personality function pointer;
-       Next x bytes is CIE program.
-    */
-
-    uint32_t c = 8;
-    if (!try_get_byte(memory, cie, &cie_info->version, &c)) {
-       return false;
-    }
-    ALOGV("execute_fde: CIE version: %d", cie_info->version);
-    uint8_t ch;
-    do {
-        if (!try_get_byte(memory, cie, &ch, &c)) {
-           return false;
-        }
-        switch (ch) {
-           case '\0': break;
-           case 'z': cie_info->aug_z = 1; break;
-           case 'L': cie_info->aug_L = 1; break;
-           case 'R': cie_info->aug_R = 1; break;
-           case 'S': cie_info->aug_S = 1; break;
-           case 'P': cie_info->aug_P = 1; break;
-           default:
-              ALOGV("execute_fde: Unrecognized CIE augmentation char: '%c'", ch);
-              return false;
-              break;
-        }
-    } while (ch);
-    if (!try_get_uleb128(memory, cie, &cie_info->code_align, &c)) {
-        return false;
-    }
-    if (!try_get_sleb128(memory, cie, &cie_info->data_align, &c)) {
-        return false;
-    }
-    if (cie_info->version >= 3) {
-        if (!try_get_uleb128(memory, cie, &cie_info->reg, &c)) {
-            return false;
-        }
-    } else {
-        if (!try_get_byte(memory, cie, (uint8_t*)&cie_info->reg, &c)) {
-            return false;
-        }
-    }
-    ALOGV("execute_fde: CIE code alignment factor: %d", cie_info->code_align);
-    ALOGV("execute_fde: CIE data alignment factor: %d", cie_info->data_align);
-    if (cie_info->aug_z) {
-        if (!try_get_uleb128(memory, cie, &cie_info->aug_z, &c)) {
-            return false;
-        }
-    }
-    if (cie_info->aug_L) {
-        if (!try_get_byte(memory, cie, &cie_info->aug_L, &c)) {
-            return false;
-        }
-    } else {
-        /* Default encoding. */
-        cie_info->aug_L = DW_EH_PE_absptr;
-    }
-    if (cie_info->aug_R) {
-        if (!try_get_byte(memory, cie, &cie_info->aug_R, &c)) {
-            return false;
-        }
-    } else {
-        /* Default encoding. */
-        cie_info->aug_R = DW_EH_PE_absptr;
-    }
-    if (cie_info->aug_P) {
-        /* Get encoding of personality routine pointer. We don't use it now. */
-        if (!try_get_byte(memory, cie, (uint8_t*)&cie_info->aug_P, &c)) {
-            return false;
-        }
-        /* Get routine pointer. */
-        if (!read_dwarf(memory, cie, &cie_info->aug_P, (uint8_t)cie_info->aug_P, &c)) {
-            return false;
-        }
-    }
-    /* CIE program. */
-    /* Length field itself (4 bytes) is not included into length. */
-    stack[0] = *dstate;
-    stack_ptr = 1;
-    while (c < cie_length + 4) {
-        if (!execute_dwarf(memory, cie, cie_info, dstate, &c, stack, &stack_ptr)) {
-           return false;
-        }
-    }
-
-    /* We went directly to CIE. Normally it shouldn't occur. */
-    if (cie == fde) return true;
-
-    /* Go back to FDE. */
-    c = 8;
-    /* Read FDE:
-       Augmentation independent:
-        next x bytes (encoded as specified in CIE) is FDE starting address;
-        next x bytes (encoded as specified in CIE) is FDE number of instructions covered;
-       Augmentation dependent:
-        if 'z' next x bytes is unsigned LEB128 encoded augmentation data size;
-        if 'L' next x bytes is LSDA pointer (encoded as specified in CIE);
-       Next x bytes is FDE program.
-     */
-    if (!read_dwarf(memory, fde, &fde_info->start, (uint8_t)cie_info->aug_R, &c)) {
-        return false;
-    }
-    dstate->loc = fde_info->start;
-    ALOGV("execute_fde: FDE start: %x", dstate->loc);
-    if (!read_dwarf(memory, fde, &fde_info->length, 0, &c)) {
-        return false;
-    }
-    ALOGV("execute_fde: FDE length: %x", fde_info->length);
-    if (cie_info->aug_z) {
-        if (!try_get_uleb128(memory, fde, &fde_info->aug_z, &c)) {
-            return false;
-        }
-    }
-    if (cie_info->aug_L && cie_info->aug_L != DW_EH_PE_omit) {
-        if (!read_dwarf(memory, fde, &fde_info->aug_L, cie_info->aug_L, &c)) {
-            return false;
-        }
-    }
-    /* FDE program. */
-    /* Length field itself (4 bytes) is not included into length. */
-    /* Save CIE state as 0 element of stack. Used by DW_CFA_restore. */
-    stack[0] = *dstate;
-    stack_ptr = 1;
-    while (c < fde_length + 4 && state->reg[DWARF_EIP] >= dstate->loc) {
-        if (!execute_dwarf(memory, fde, cie_info, dstate, &c, stack, &stack_ptr)) {
-           return false;
-        }
-        ALOGV("IP: %x, LOC: %x", state->reg[DWARF_EIP], dstate->loc);
-    }
-
-    return update_state(memory, state, dstate);
-}
-
-static ssize_t unwind_backtrace_common(const memory_t* memory,
-        const map_info_t* map_info_list,
-        unwind_state_t* state, backtrace_frame_t* backtrace,
-        size_t ignore_depth, size_t max_depth) {
-
-    size_t ignored_frames = 0;
-    size_t returned_frames = 0;
-
-    ALOGV("Unwinding tid: %d", memory->tid);
-    ALOGV("IP: %x", state->reg[DWARF_EIP]);
-    ALOGV("BP: %x", state->reg[DWARF_EBP]);
-    ALOGV("SP: %x", state->reg[DWARF_ESP]);
-
-    for (size_t index = 0; returned_frames < max_depth; index++) {
-        uintptr_t fde = find_fde(memory, map_info_list, state->reg[DWARF_EIP]);
-        /* FDE is not found, it may happen if stack is corrupted or calling wrong adress.
-           Getting return address from stack.
-        */
-        if (!fde) {
-            uint32_t ip;
-            ALOGV("trying to restore registers from stack");
-            if (!try_get_word(memory, state->reg[DWARF_EBP] + 4, &ip) ||
-                ip == state->reg[DWARF_EIP]) {
-                ALOGV("can't get IP from stack");
-                break;
-            }
-            /* We've been able to get IP from stack so recording the frame before continue. */
-            backtrace_frame_t* frame = add_backtrace_entry(
-                    index ? rewind_pc_arch(memory, state->reg[DWARF_EIP]) : state->reg[DWARF_EIP],
-                    backtrace, ignore_depth, max_depth,
-                    &ignored_frames, &returned_frames);
-            state->reg[DWARF_EIP] = ip;
-            state->reg[DWARF_ESP] = state->reg[DWARF_EBP] + 8;
-            if (!try_get_word(memory, state->reg[DWARF_EBP], &state->reg[DWARF_EBP])) {
-                ALOGV("can't get EBP from stack");
-                break;
-            }
-            ALOGV("restore IP: %x", state->reg[DWARF_EIP]);
-            ALOGV("restore BP: %x", state->reg[DWARF_EBP]);
-            ALOGV("restore SP: %x", state->reg[DWARF_ESP]);
-            continue;
-        }
-        backtrace_frame_t* frame = add_backtrace_entry(
-                index ? rewind_pc_arch(memory, state->reg[DWARF_EIP]) : state->reg[DWARF_EIP],
-                backtrace, ignore_depth, max_depth,
-                &ignored_frames, &returned_frames);
-
-        uint32_t stack_top = state->reg[DWARF_ESP];
-
-        if (!execute_fde(memory, fde, state)) break;
-
-        if (frame) {
-            frame->stack_top = stack_top;
-            if (stack_top < state->reg[DWARF_ESP]) {
-                frame->stack_size = state->reg[DWARF_ESP] - stack_top;
-            }
-        }
-        ALOGV("Stack: 0x%x ... 0x%x - %d bytes", frame->stack_top, state->reg[DWARF_ESP], frame->stack_size);
-    }
-    return returned_frames;
-}
-
-ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo __attribute__((unused)), void* sigcontext,
-        const map_info_t* map_info_list,
-        backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) {
-    const ucontext_t* uc = (const ucontext_t*)sigcontext;
-
-    unwind_state_t state;
-#if defined(__APPLE__)
-    state.reg[DWARF_EBP] = uc->uc_mcontext->__ss.__ebp;
-    state.reg[DWARF_ESP] = uc->uc_mcontext->__ss.__esp;
-    state.reg[DWARF_EIP] = uc->uc_mcontext->__ss.__eip;
-#else
-    state.reg[DWARF_EBP] = uc->uc_mcontext.gregs[REG_EBP];
-    state.reg[DWARF_ESP] = uc->uc_mcontext.gregs[REG_ESP];
-    state.reg[DWARF_EIP] = uc->uc_mcontext.gregs[REG_EIP];
-#endif
-
-    memory_t memory;
-    init_memory(&memory, map_info_list);
-    return unwind_backtrace_common(&memory, map_info_list,
-            &state, backtrace, ignore_depth, max_depth);
-}
-
-ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context,
-        backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) {
-#if defined(__APPLE__)
-    return -1;
-#else
-    pt_regs_x86_t regs;
-    if (ptrace(PTRACE_GETREGS, tid, 0, &regs)) {
-        return -1;
-    }
-
-    unwind_state_t state;
-    state.reg[DWARF_EBP] = regs.ebp;
-    state.reg[DWARF_EIP] = regs.eip;
-    state.reg[DWARF_ESP] = regs.esp;
-
-    memory_t memory;
-    init_memory_ptrace(&memory, tid);
-    return unwind_backtrace_common(&memory, context->map_info_list,
-            &state, backtrace, ignore_depth, max_depth);
-#endif
-}
diff --git a/libcorkscrew/arch-x86/dwarf.h b/libcorkscrew/arch-x86/dwarf.h
deleted file mode 100755
index 962fc55..0000000
--- a/libcorkscrew/arch-x86/dwarf.h
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-/*
- * Dwarf2 data encoding flags.
- */
-
-#define DW_EH_PE_absptr         0x00
-#define DW_EH_PE_omit           0xff
-#define DW_EH_PE_uleb128        0x01
-#define DW_EH_PE_udata2         0x02
-#define DW_EH_PE_udata4         0x03
-#define DW_EH_PE_udata8         0x04
-#define DW_EH_PE_sleb128        0x09
-#define DW_EH_PE_sdata2         0x0A
-#define DW_EH_PE_sdata4         0x0B
-#define DW_EH_PE_sdata8         0x0C
-#define DW_EH_PE_signed         0x08
-#define DW_EH_PE_pcrel          0x10
-#define DW_EH_PE_textrel        0x20
-#define DW_EH_PE_datarel        0x30
-#define DW_EH_PE_funcrel        0x40
-#define DW_EH_PE_aligned        0x50
-#define DW_EH_PE_indirect       0x80
-
-/*
- * Dwarf2 call frame instructions.
- */
-
-typedef enum {
-    DW_CFA_advance_loc = 0x40,
-    DW_CFA_offset = 0x80,
-    DW_CFA_restore = 0xc0,
-    DW_CFA_nop = 0x00,
-    DW_CFA_set_loc = 0x01,
-    DW_CFA_advance_loc1 = 0x02,
-    DW_CFA_advance_loc2 = 0x03,
-    DW_CFA_advance_loc4 = 0x04,
-    DW_CFA_offset_extended = 0x05,
-    DW_CFA_restore_extended = 0x06,
-    DW_CFA_undefined = 0x07,
-    DW_CFA_same_value = 0x08,
-    DW_CFA_register = 0x09,
-    DW_CFA_remember_state = 0x0a,
-    DW_CFA_restore_state = 0x0b,
-    DW_CFA_def_cfa = 0x0c,
-    DW_CFA_def_cfa_register = 0x0d,
-    DW_CFA_def_cfa_offset = 0x0e
-} dwarf_CFA;
-
-/*
- * eh_frame_hdr information.
-*/
-
-typedef struct {
-      uint8_t version;
-      uint8_t eh_frame_ptr_enc;
-      uint8_t fde_count_enc;
-      uint8_t fde_table_enc;
-      uintptr_t eh_frame_ptr;
-      uint32_t fde_count;
-} eh_frame_hdr_info_t;
-
-/*
- * CIE information.
-*/
-
-typedef struct {
-      uint8_t version;
-      uint32_t code_align;
-      uint32_t data_align;
-      uint32_t reg;
-      uint32_t aug_z;
-      uint8_t aug_L;
-      uint8_t aug_R;
-      uint8_t aug_S;
-      uint32_t aug_P;
-} cie_info_t;
-
-/*
- * FDE information.
-*/
-
-typedef struct {
-      uint32_t start;
-      uint32_t length; // number of instructions covered by FDE
-      uint32_t aug_z;
-      uint32_t aug_L;
-} fde_info_t;
-
-/*
- * Dwarf state.
-*/
-
-/* Stack of states: required for DW_CFA_remember_state/DW_CFA_restore_state
-   30 should be enough */
-#define DWARF_STATES_STACK 30
-
-typedef struct {
-    char rule;         // rule: o - offset(value); r - register(value)
-    uint32_t value;    // value
-} reg_rule_t;
-
-/* Dwarf preserved number of registers for x86. */
-
-#define DWARF_REGISTERS 17
-
-typedef struct {
-    uintptr_t loc;     // location (ip)
-    uint8_t cfa_reg;   // index of register where CFA location stored
-    intptr_t cfa_off;  // offset
-    reg_rule_t regs[DWARF_REGISTERS]; // dwarf preserved registers for x86
-} dwarf_state_t;
-
-/* DWARF registers we are caring about. */
-
-#define DWARF_EAX     0
-#define DWARF_ECX     1
-#define DWARF_EDX     2
-#define DWARF_EBX     3
-#define DWARF_ESP     4
-#define DWARF_EBP     5
-#define DWARF_ESI     6
-#define DWARF_EDI     7
-#define DWARF_EIP     8
-
-
diff --git a/libcorkscrew/arch-x86/ptrace-x86.c b/libcorkscrew/arch-x86/ptrace-x86.c
deleted file mode 100755
index 9c49b93..0000000
--- a/libcorkscrew/arch-x86/ptrace-x86.c
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-#define LOG_TAG "Corkscrew"
-//#define LOG_NDEBUG 0
-
-#include "../ptrace-arch.h"
-
-#include <stddef.h>
-#include <elf.h>
-#include <cutils/log.h>
-
-static void load_eh_frame_hdr(pid_t pid, map_info_t* mi, uintptr_t *eh_frame_hdr) {
-    uint32_t elf_phoff;
-    uint32_t elf_phentsize_ehsize;
-    uint32_t elf_shentsize_phnum;
-    if (try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phoff), &elf_phoff)
-            && try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_ehsize),
-                    &elf_phentsize_ehsize)
-            && try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phnum),
-                    &elf_shentsize_phnum)) {
-        uint32_t elf_phentsize = elf_phentsize_ehsize >> 16;
-        uint32_t elf_phnum = elf_shentsize_phnum & 0xffff;
-        for (uint32_t i = 0; i < elf_phnum; i++) {
-            uintptr_t elf_phdr = mi->start + elf_phoff + i * elf_phentsize;
-            uint32_t elf_phdr_type;
-            if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_type), &elf_phdr_type)) {
-                break;
-            }
-            if (elf_phdr_type == PT_GNU_EH_FRAME) {
-                uint32_t elf_phdr_offset;
-                if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_offset),
-                        &elf_phdr_offset)) {
-                    break;
-                }
-                *eh_frame_hdr = mi->start + elf_phdr_offset;
-                ALOGV("Parsed .eh_frame_hdr info for %s: start=0x%08x", mi->name, *eh_frame_hdr);
-                return;
-            }
-        }
-    }
-    *eh_frame_hdr = 0;
-}
-
-void load_ptrace_map_info_data_arch(pid_t pid, map_info_t* mi, map_info_data_t* data) {
-    load_eh_frame_hdr(pid, mi, &data->eh_frame_hdr);
-}
-
-void free_ptrace_map_info_data_arch(map_info_t* mi __attribute__((unused)),
-                                    map_info_data_t* data __attribute__((unused))) {
-}
diff --git a/libcorkscrew/backtrace-arch.h b/libcorkscrew/backtrace-arch.h
deleted file mode 100644
index a46f80b..0000000
--- a/libcorkscrew/backtrace-arch.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-/* Architecture dependent functions. */
-
-#ifndef _CORKSCREW_BACKTRACE_ARCH_H
-#define _CORKSCREW_BACKTRACE_ARCH_H
-
-#include "ptrace-arch.h"
-#include <corkscrew/backtrace.h>
-
-#include <signal.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Rewind the program counter by one instruction. */
-uintptr_t rewind_pc_arch(const memory_t* memory, uintptr_t pc);
-
-ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo, void* sigcontext,
-        const map_info_t* map_info_list,
-        backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth);
-
-ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context,
-        backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // _CORKSCREW_BACKTRACE_ARCH_H
diff --git a/libcorkscrew/backtrace-helper.c b/libcorkscrew/backtrace-helper.c
deleted file mode 100644
index bf9d3f3..0000000
--- a/libcorkscrew/backtrace-helper.c
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-#define LOG_TAG "Corkscrew"
-//#define LOG_NDEBUG 0
-
-#include "backtrace-helper.h"
-
-#include <cutils/log.h>
-
-backtrace_frame_t* add_backtrace_entry(uintptr_t pc, backtrace_frame_t* backtrace,
-        size_t ignore_depth, size_t max_depth,
-        size_t* ignored_frames, size_t* returned_frames) {
-    if (*ignored_frames < ignore_depth) {
-        *ignored_frames += 1;
-        return NULL;
-    }
-    if (*returned_frames >= max_depth) {
-        return NULL;
-    }
-    backtrace_frame_t* frame = &backtrace[*returned_frames];
-    frame->absolute_pc = pc;
-    frame->stack_top = 0;
-    frame->stack_size = 0;
-    *returned_frames += 1;
-    return frame;
-}
diff --git a/libcorkscrew/backtrace-helper.h b/libcorkscrew/backtrace-helper.h
deleted file mode 100644
index 4d8a874..0000000
--- a/libcorkscrew/backtrace-helper.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-/* Backtrace helper functions. */
-
-#ifndef _CORKSCREW_BACKTRACE_HELPER_H
-#define _CORKSCREW_BACKTRACE_HELPER_H
-
-#include <corkscrew/backtrace.h>
-#include <sys/types.h>
-#include <stdbool.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * Add a program counter to a backtrace if it will fit.
- * Returns the newly added frame, or NULL if none.
- */
-backtrace_frame_t* add_backtrace_entry(uintptr_t pc,
-        backtrace_frame_t* backtrace,
-        size_t ignore_depth, size_t max_depth,
-        size_t* ignored_frames, size_t* returned_frames);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // _CORKSCREW_BACKTRACE_HELPER_H
diff --git a/libcorkscrew/backtrace.c b/libcorkscrew/backtrace.c
deleted file mode 100644
index f1dd61d..0000000
--- a/libcorkscrew/backtrace.c
+++ /dev/null
@@ -1,335 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-#define LOG_TAG "Corkscrew"
-//#define LOG_NDEBUG 0
-
-#include "backtrace-arch.h"
-#include "backtrace-helper.h"
-#include "ptrace-arch.h"
-#include <corkscrew/map_info.h>
-#include <corkscrew/symbol_table.h>
-#include <corkscrew/ptrace.h>
-#include <corkscrew/demangle.h>
-
-#include <unistd.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <string.h>
-#include <pthread.h>
-#include <unwind.h>
-#include <cutils/log.h>
-#include <cutils/atomic.h>
-
-#define __USE_GNU // For dladdr(3) in glibc.
-#include <dlfcn.h>
-
-#if defined(__BIONIC__)
-
-// Bionic implements and exports gettid but only implements tgkill.
-extern int tgkill(int tgid, int tid, int sig);
-
-#elif defined(__APPLE__)
-
-#include <sys/syscall.h>
-
-// Mac OS >= 10.6 has a system call equivalent to Linux's gettid().
-static pid_t gettid() {
-  return syscall(SYS_thread_selfid);
-}
-
-#else
-
-// glibc doesn't implement or export either gettid or tgkill.
-
-#include <unistd.h>
-#include <sys/syscall.h>
-
-static pid_t gettid() {
-  return syscall(__NR_gettid);
-}
-
-static int tgkill(int tgid, int tid, int sig) {
-  return syscall(__NR_tgkill, tgid, tid, sig);
-}
-
-#endif
-
-typedef struct {
-    backtrace_frame_t* backtrace;
-    size_t ignore_depth;
-    size_t max_depth;
-    size_t ignored_frames;
-    size_t returned_frames;
-    memory_t memory;
-} backtrace_state_t;
-
-static _Unwind_Reason_Code unwind_backtrace_callback(struct _Unwind_Context* context, void* arg) {
-    backtrace_state_t* state = (backtrace_state_t*)arg;
-    uintptr_t pc = _Unwind_GetIP(context);
-    if (pc) {
-        // TODO: Get information about the stack layout from the _Unwind_Context.
-        //       This will require a new architecture-specific function to query
-        //       the appropriate registers.  Current callers of unwind_backtrace
-        //       don't need this information, so we won't bother collecting it just yet.
-        add_backtrace_entry(rewind_pc_arch(&state->memory, pc), state->backtrace,
-                state->ignore_depth, state->max_depth,
-                &state->ignored_frames, &state->returned_frames);
-    }
-    return state->returned_frames < state->max_depth ? _URC_NO_REASON : _URC_END_OF_STACK;
-}
-
-ssize_t unwind_backtrace(backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) {
-    ALOGV("Unwinding current thread %d.", gettid());
-
-    map_info_t* milist = acquire_my_map_info_list();
-
-    backtrace_state_t state;
-    state.backtrace = backtrace;
-    state.ignore_depth = ignore_depth;
-    state.max_depth = max_depth;
-    state.ignored_frames = 0;
-    state.returned_frames = 0;
-    init_memory(&state.memory, milist);
-
-    _Unwind_Reason_Code rc = _Unwind_Backtrace(unwind_backtrace_callback, &state);
-
-    release_my_map_info_list(milist);
-
-    if (state.returned_frames) {
-        return state.returned_frames;
-    }
-    return rc == _URC_END_OF_STACK ? 0 : -1;
-}
-
-#ifdef CORKSCREW_HAVE_ARCH
-static const int32_t STATE_DUMPING = -1;
-static const int32_t STATE_DONE = -2;
-static const int32_t STATE_CANCEL = -3;
-
-static pthread_mutex_t g_unwind_signal_mutex = PTHREAD_MUTEX_INITIALIZER;
-static volatile struct {
-    int32_t tid_state;
-    const map_info_t* map_info_list;
-    backtrace_frame_t* backtrace;
-    size_t ignore_depth;
-    size_t max_depth;
-    size_t returned_frames;
-} g_unwind_signal_state;
-
-static void unwind_backtrace_thread_signal_handler(int n __attribute__((unused)), siginfo_t* siginfo, void* sigcontext) {
-    if (!android_atomic_acquire_cas(gettid(), STATE_DUMPING, &g_unwind_signal_state.tid_state)) {
-        g_unwind_signal_state.returned_frames = unwind_backtrace_signal_arch(
-                siginfo, sigcontext,
-                g_unwind_signal_state.map_info_list,
-                g_unwind_signal_state.backtrace,
-                g_unwind_signal_state.ignore_depth,
-                g_unwind_signal_state.max_depth);
-        android_atomic_release_store(STATE_DONE, &g_unwind_signal_state.tid_state);
-    } else {
-        ALOGV("Received spurious SIGURG on thread %d that was intended for thread %d.",
-                gettid(), android_atomic_acquire_load(&g_unwind_signal_state.tid_state));
-    }
-}
-#endif
-
-ssize_t unwind_backtrace_thread(pid_t tid, backtrace_frame_t* backtrace,
-        size_t ignore_depth, size_t max_depth) {
-    if (tid == gettid()) {
-        return unwind_backtrace(backtrace, ignore_depth + 1, max_depth);
-    }
-
-    ALOGV("Unwinding thread %d from thread %d.", tid, gettid());
-
-    // TODO: there's no tgkill(2) on Mac OS, so we'd either need the
-    // mach_port_t or the pthread_t rather than the tid.
-#if defined(CORKSCREW_HAVE_ARCH) && !defined(__APPLE__)
-    struct sigaction act;
-    struct sigaction oact;
-    memset(&act, 0, sizeof(act));
-    act.sa_sigaction = unwind_backtrace_thread_signal_handler;
-    act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
-    sigemptyset(&act.sa_mask);
-
-    pthread_mutex_lock(&g_unwind_signal_mutex);
-    map_info_t* milist = acquire_my_map_info_list();
-
-    ssize_t frames = -1;
-    if (!sigaction(SIGURG, &act, &oact)) {
-        g_unwind_signal_state.map_info_list = milist;
-        g_unwind_signal_state.backtrace = backtrace;
-        g_unwind_signal_state.ignore_depth = ignore_depth;
-        g_unwind_signal_state.max_depth = max_depth;
-        g_unwind_signal_state.returned_frames = 0;
-        android_atomic_release_store(tid, &g_unwind_signal_state.tid_state);
-
-        // Signal the specific thread that we want to dump.
-        int32_t tid_state = tid;
-        if (tgkill(getpid(), tid, SIGURG)) {
-            ALOGV("Failed to send SIGURG to thread %d.", tid);
-        } else {
-            // Wait for the other thread to start dumping the stack, or time out.
-            int wait_millis = 250;
-            for (;;) {
-                tid_state = android_atomic_acquire_load(&g_unwind_signal_state.tid_state);
-                if (tid_state != tid) {
-                    break;
-                }
-                if (wait_millis--) {
-                    ALOGV("Waiting for thread %d to start dumping the stack...", tid);
-                    usleep(1000);
-                } else {
-                    ALOGV("Timed out waiting for thread %d to start dumping the stack.", tid);
-                    break;
-                }
-            }
-        }
-
-        // Try to cancel the dump if it has not started yet.
-        if (tid_state == tid) {
-            if (!android_atomic_acquire_cas(tid, STATE_CANCEL, &g_unwind_signal_state.tid_state)) {
-                ALOGV("Canceled thread %d stack dump.", tid);
-                tid_state = STATE_CANCEL;
-            } else {
-                tid_state = android_atomic_acquire_load(&g_unwind_signal_state.tid_state);
-            }
-        }
-
-        // Wait indefinitely for the dump to finish or be canceled.
-        // We cannot apply a timeout here because the other thread is accessing state that
-        // is owned by this thread, such as milist.  It should not take very
-        // long to take the dump once started.
-        while (tid_state == STATE_DUMPING) {
-            ALOGV("Waiting for thread %d to finish dumping the stack...", tid);
-            usleep(1000);
-            tid_state = android_atomic_acquire_load(&g_unwind_signal_state.tid_state);
-        }
-
-        if (tid_state == STATE_DONE) {
-            frames = g_unwind_signal_state.returned_frames;
-        }
-
-        sigaction(SIGURG, &oact, NULL);
-    }
-
-    release_my_map_info_list(milist);
-    pthread_mutex_unlock(&g_unwind_signal_mutex);
-    return frames;
-#else
-    return -1;
-#endif
-}
-
-ssize_t unwind_backtrace_ptrace(pid_t tid, const ptrace_context_t* context,
-        backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) {
-#ifdef CORKSCREW_HAVE_ARCH
-    return unwind_backtrace_ptrace_arch(tid, context, backtrace, ignore_depth, max_depth);
-#else
-    return -1;
-#endif
-}
-
-static void init_backtrace_symbol(backtrace_symbol_t* symbol, uintptr_t pc) {
-    symbol->relative_pc = pc;
-    symbol->relative_symbol_addr = 0;
-    symbol->map_name = NULL;
-    symbol->symbol_name = NULL;
-    symbol->demangled_name = NULL;
-}
-
-void get_backtrace_symbols(const backtrace_frame_t* backtrace, size_t frames,
-        backtrace_symbol_t* backtrace_symbols) {
-    map_info_t* milist = acquire_my_map_info_list();
-    for (size_t i = 0; i < frames; i++) {
-        const backtrace_frame_t* frame = &backtrace[i];
-        backtrace_symbol_t* symbol = &backtrace_symbols[i];
-        init_backtrace_symbol(symbol, frame->absolute_pc);
-
-        const map_info_t* mi = find_map_info(milist, frame->absolute_pc);
-        if (mi) {
-            symbol->relative_pc = frame->absolute_pc - mi->start;
-            if (mi->name[0]) {
-                symbol->map_name = strdup(mi->name);
-            }
-            Dl_info info;
-            if (dladdr((const void*)frame->absolute_pc, &info) && info.dli_sname) {
-                symbol->relative_symbol_addr = (uintptr_t)info.dli_saddr
-                        - (uintptr_t)info.dli_fbase;
-                symbol->symbol_name = strdup(info.dli_sname);
-                symbol->demangled_name = demangle_symbol_name(symbol->symbol_name);
-            }
-        }
-    }
-    release_my_map_info_list(milist);
-}
-
-void get_backtrace_symbols_ptrace(const ptrace_context_t* context,
-        const backtrace_frame_t* backtrace, size_t frames,
-        backtrace_symbol_t* backtrace_symbols) {
-    for (size_t i = 0; i < frames; i++) {
-        const backtrace_frame_t* frame = &backtrace[i];
-        backtrace_symbol_t* symbol = &backtrace_symbols[i];
-        init_backtrace_symbol(symbol, frame->absolute_pc);
-
-        const map_info_t* mi;
-        const symbol_t* s;
-        find_symbol_ptrace(context, frame->absolute_pc, &mi, &s);
-        if (mi) {
-            symbol->relative_pc = frame->absolute_pc - mi->start;
-            if (mi->name[0]) {
-                symbol->map_name = strdup(mi->name);
-            }
-        }
-        if (s) {
-            symbol->relative_symbol_addr = s->start;
-            symbol->symbol_name = strdup(s->name);
-            symbol->demangled_name = demangle_symbol_name(symbol->symbol_name);
-        }
-    }
-}
-
-void free_backtrace_symbols(backtrace_symbol_t* backtrace_symbols, size_t frames) {
-    for (size_t i = 0; i < frames; i++) {
-        backtrace_symbol_t* symbol = &backtrace_symbols[i];
-        free(symbol->map_name);
-        free(symbol->symbol_name);
-        free(symbol->demangled_name);
-        init_backtrace_symbol(symbol, 0);
-    }
-}
-
-void format_backtrace_line(unsigned frameNumber, const backtrace_frame_t* frame __attribute__((unused)),
-        const backtrace_symbol_t* symbol, char* buffer, size_t bufferSize) {
-    const char* mapName = symbol->map_name ? symbol->map_name : "<unknown>";
-    const char* symbolName = symbol->demangled_name ? symbol->demangled_name : symbol->symbol_name;
-    int fieldWidth = (bufferSize - 80) / 2;
-    if (symbolName) {
-        uint32_t pc_offset = symbol->relative_pc - symbol->relative_symbol_addr;
-        if (pc_offset) {
-            snprintf(buffer, bufferSize, "#%02u  pc %08x  %.*s (%.*s+%u)",
-                    frameNumber, (unsigned int) symbol->relative_pc,
-                    fieldWidth, mapName, fieldWidth, symbolName, pc_offset);
-        } else {
-            snprintf(buffer, bufferSize, "#%02u  pc %08x  %.*s (%.*s)",
-                    frameNumber, (unsigned int) symbol->relative_pc,
-                    fieldWidth, mapName, fieldWidth, symbolName);
-        }
-    } else {
-        snprintf(buffer, bufferSize, "#%02u  pc %08x  %.*s",
-                frameNumber, (unsigned int) symbol->relative_pc,
-                fieldWidth, mapName);
-    }
-}
diff --git a/libcorkscrew/demangle.c b/libcorkscrew/demangle.c
deleted file mode 100644
index 30ab1b0..0000000
--- a/libcorkscrew/demangle.c
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-#define LOG_TAG "Corkscrew"
-//#define LOG_NDEBUG 0
-
-#include <corkscrew/demangle.h>
-
-#include <cutils/log.h>
-
-extern char *__cxa_demangle (const char *mangled, char *buf, size_t *len,
-                             int *status);
-
-char* demangle_symbol_name(const char* name) {
-#if defined(__APPLE__)
-    // Mac OS' __cxa_demangle demangles "f" as "float"; last tested on 10.7.
-    if (name != NULL && name[0] != '_') {
-        return NULL;
-    }
-#endif
-    // __cxa_demangle handles NULL by returning NULL
-    return __cxa_demangle(name, 0, 0, 0);
-}
diff --git a/libcorkscrew/map_info.c b/libcorkscrew/map_info.c
deleted file mode 100644
index 93dffbf..0000000
--- a/libcorkscrew/map_info.c
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-#define LOG_TAG "Corkscrew"
-//#define LOG_NDEBUG 0
-
-#include <corkscrew/map_info.h>
-
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
-#include <pthread.h>
-#include <unistd.h>
-#include <cutils/log.h>
-#include <sys/time.h>
-
-#if defined(__APPLE__)
-
-// Mac OS vmmap(1) output:
-// __TEXT                 0009f000-000a1000 [    8K     8K] r-x/rwx SM=COW  /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n
-// 012345678901234567890123456789012345678901234567890123456789
-// 0         1         2         3         4         5
-static map_info_t* parse_vmmap_line(const char* line) {
-    unsigned long int start;
-    unsigned long int end;
-    char permissions[4];
-    int name_pos;
-    if (sscanf(line, "%*21c %lx-%lx [%*13c] %3c/%*3c SM=%*3c  %n",
-               &start, &end, permissions, &name_pos) != 3) {
-        return NULL;
-    }
-
-    const char* name = line + name_pos;
-    size_t name_len = strlen(name);
-
-    map_info_t* mi = calloc(1, sizeof(map_info_t) + name_len);
-    if (mi != NULL) {
-        mi->start = start;
-        mi->end = end;
-        mi->is_readable = permissions[0] == 'r';
-        mi->is_writable = permissions[1] == 'w';
-        mi->is_executable = permissions[2] == 'x';
-        mi->data = NULL;
-        memcpy(mi->name, name, name_len);
-        mi->name[name_len - 1] = '\0';
-        ALOGV("Parsed map: start=0x%08x, end=0x%08x, "
-              "is_readable=%d, is_writable=%d is_executable=%d, name=%s",
-              mi->start, mi->end,
-              mi->is_readable, mi->is_writable, mi->is_executable, mi->name);
-    }
-    return mi;
-}
-
-map_info_t* load_map_info_list(pid_t pid) {
-    char cmd[1024];
-    snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid);
-    FILE* fp = popen(cmd, "r");
-    if (fp == NULL) {
-        return NULL;
-    }
-
-    char line[1024];
-    map_info_t* milist = NULL;
-    while (fgets(line, sizeof(line), fp) != NULL) {
-        map_info_t* mi = parse_vmmap_line(line);
-        if (mi != NULL) {
-            mi->next = milist;
-            milist = mi;
-        }
-    }
-    pclose(fp);
-    return milist;
-}
-
-#else
-
-// Linux /proc/<pid>/maps lines:
-// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419   /system/lib/libcomposer.so\n
-// 012345678901234567890123456789012345678901234567890123456789
-// 0         1         2         3         4         5
-static map_info_t* parse_maps_line(const char* line)
-{
-    unsigned long int start;
-    unsigned long int end;
-    char permissions[5];
-    int name_pos;
-    if (sscanf(line, "%lx-%lx %4s %*x %*x:%*x %*d%n", &start, &end,
-            permissions, &name_pos) != 3) {
-        return NULL;
-    }
-
-    while (isspace(line[name_pos])) {
-        name_pos += 1;
-    }
-    const char* name = line + name_pos;
-    size_t name_len = strlen(name);
-    if (name_len && name[name_len - 1] == '\n') {
-        name_len -= 1;
-    }
-
-    map_info_t* mi = calloc(1, sizeof(map_info_t) + name_len + 1);
-    if (mi) {
-        mi->start = start;
-        mi->end = end;
-        mi->is_readable = strlen(permissions) == 4 && permissions[0] == 'r';
-        mi->is_writable = strlen(permissions) == 4 && permissions[1] == 'w';
-        mi->is_executable = strlen(permissions) == 4 && permissions[2] == 'x';
-        mi->data = NULL;
-        memcpy(mi->name, name, name_len);
-        mi->name[name_len] = '\0';
-        ALOGV("Parsed map: start=0x%08x, end=0x%08x, "
-              "is_readable=%d, is_writable=%d, is_executable=%d, name=%s",
-              mi->start, mi->end,
-              mi->is_readable, mi->is_writable, mi->is_executable, mi->name);
-    }
-    return mi;
-}
-
-map_info_t* load_map_info_list(pid_t tid) {
-    char path[PATH_MAX];
-    char line[1024];
-    FILE* fp;
-    map_info_t* milist = NULL;
-
-    snprintf(path, PATH_MAX, "/proc/%d/maps", tid);
-    fp = fopen(path, "r");
-    if (fp) {
-        while(fgets(line, sizeof(line), fp)) {
-            map_info_t* mi = parse_maps_line(line);
-            if (mi) {
-                mi->next = milist;
-                milist = mi;
-            }
-        }
-        fclose(fp);
-    }
-    return milist;
-}
-
-#endif
-
-void free_map_info_list(map_info_t* milist) {
-    while (milist) {
-        map_info_t* next = milist->next;
-        free(milist);
-        milist = next;
-    }
-}
-
-const map_info_t* find_map_info(const map_info_t* milist, uintptr_t addr) {
-    const map_info_t* mi = milist;
-    while (mi && !(addr >= mi->start && addr < mi->end)) {
-        mi = mi->next;
-    }
-    return mi;
-}
-
-bool is_readable_map(const map_info_t* milist, uintptr_t addr) {
-    const map_info_t* mi = find_map_info(milist, addr);
-    return mi && mi->is_readable;
-}
-
-bool is_writable_map(const map_info_t* milist, uintptr_t addr) {
-    const map_info_t* mi = find_map_info(milist, addr);
-    return mi && mi->is_writable;
-}
-
-bool is_executable_map(const map_info_t* milist, uintptr_t addr) {
-    const map_info_t* mi = find_map_info(milist, addr);
-    return mi && mi->is_executable;
-}
-
-static pthread_mutex_t g_my_map_info_list_mutex = PTHREAD_MUTEX_INITIALIZER;
-static map_info_t* g_my_map_info_list = NULL;
-
-static const int64_t MAX_CACHE_AGE = 5 * 1000 * 1000000LL;
-
-typedef struct {
-    uint32_t refs;
-    int64_t timestamp;
-} my_map_info_data_t;
-
-static int64_t now_ns() {
-#if defined(HAVE_POSIX_CLOCKS)
-    struct timespec t;
-    t.tv_sec = t.tv_nsec = 0;
-    clock_gettime(CLOCK_MONOTONIC, &t);
-    return t.tv_sec * 1000000000LL + t.tv_nsec;
-#else
-    struct timeval t;
-    gettimeofday(&t, NULL);
-    return t.tv_sec * 1000000000LL + t.tv_usec * 1000LL;
-#endif
-}
-
-static void dec_ref(map_info_t* milist, my_map_info_data_t* data) {
-    if (!--data->refs) {
-        ALOGV("Freed my_map_info_list %p.", milist);
-        free(data);
-        free_map_info_list(milist);
-    }
-}
-
-map_info_t* acquire_my_map_info_list() {
-    pthread_mutex_lock(&g_my_map_info_list_mutex);
-
-    int64_t time = now_ns();
-    if (g_my_map_info_list != NULL) {
-        my_map_info_data_t* data = (my_map_info_data_t*)g_my_map_info_list->data;
-        int64_t age = time - data->timestamp;
-        if (age >= MAX_CACHE_AGE) {
-            ALOGV("Invalidated my_map_info_list %p, age=%lld.", g_my_map_info_list, age);
-            dec_ref(g_my_map_info_list, data);
-            g_my_map_info_list = NULL;
-        } else {
-            ALOGV("Reusing my_map_info_list %p, age=%lld.", g_my_map_info_list, age);
-        }
-    }
-
-    if (g_my_map_info_list == NULL) {
-        my_map_info_data_t* data = (my_map_info_data_t*)malloc(sizeof(my_map_info_data_t));
-        g_my_map_info_list = load_map_info_list(getpid());
-        if (g_my_map_info_list != NULL) {
-            ALOGV("Loaded my_map_info_list %p.", g_my_map_info_list);
-            g_my_map_info_list->data = data;
-            data->refs = 1;
-            data->timestamp = time;
-        } else {
-            free(data);
-        }
-    }
-
-    map_info_t* milist = g_my_map_info_list;
-    if (milist) {
-        my_map_info_data_t* data = (my_map_info_data_t*)g_my_map_info_list->data;
-        data->refs += 1;
-    }
-
-    pthread_mutex_unlock(&g_my_map_info_list_mutex);
-    return milist;
-}
-
-void release_my_map_info_list(map_info_t* milist) {
-    if (milist) {
-        pthread_mutex_lock(&g_my_map_info_list_mutex);
-
-        my_map_info_data_t* data = (my_map_info_data_t*)milist->data;
-        dec_ref(milist, data);
-
-        pthread_mutex_unlock(&g_my_map_info_list_mutex);
-    }
-}
-
-void flush_my_map_info_list() {
-    pthread_mutex_lock(&g_my_map_info_list_mutex);
-
-    if (g_my_map_info_list != NULL) {
-        my_map_info_data_t* data = (my_map_info_data_t*) g_my_map_info_list->data;
-        dec_ref(g_my_map_info_list, data);
-        g_my_map_info_list = NULL;
-    }
-
-    pthread_mutex_unlock(&g_my_map_info_list_mutex);
-}
diff --git a/libcorkscrew/ptrace-arch.h b/libcorkscrew/ptrace-arch.h
deleted file mode 100755
index 0bcff63..0000000
--- a/libcorkscrew/ptrace-arch.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-/* Architecture dependent functions. */
-
-#ifndef _CORKSCREW_PTRACE_ARCH_H
-#define _CORKSCREW_PTRACE_ARCH_H
-
-#include <corkscrew/ptrace.h>
-#include <corkscrew/map_info.h>
-#include <corkscrew/symbol_table.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Custom extra data we stuff into map_info_t structures as part
- * of our ptrace_context_t. */
-typedef struct {
-#ifdef __arm__
-    uintptr_t exidx_start;
-    size_t exidx_size;
-#elif __mips__
-    uintptr_t eh_frame_hdr;
-#elif __i386__
-    uintptr_t eh_frame_hdr;
-#endif
-    symbol_table_t* symbol_table;
-} map_info_data_t;
-
-void load_ptrace_map_info_data_arch(pid_t pid, map_info_t* mi, map_info_data_t* data);
-void free_ptrace_map_info_data_arch(map_info_t* mi, map_info_data_t* data);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // _CORKSCREW_PTRACE_ARCH_H
diff --git a/libcorkscrew/ptrace.c b/libcorkscrew/ptrace.c
deleted file mode 100644
index be58f7f..0000000
--- a/libcorkscrew/ptrace.c
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-#define LOG_TAG "Corkscrew"
-//#define LOG_NDEBUG 0
-
-#include "ptrace-arch.h"
-#include <corkscrew/ptrace.h>
-
-#include <errno.h>
-#include <stdlib.h>
-#include <sys/ptrace.h>
-#include <cutils/log.h>
-
-static const uint32_t ELF_MAGIC = 0x464C457f; // "ELF\0177"
-
-#ifndef PAGE_SIZE
-#define PAGE_SIZE 4096
-#endif
-
-#ifndef PAGE_MASK
-#define PAGE_MASK (~(PAGE_SIZE - 1))
-#endif
-
-void init_memory(memory_t* memory, const map_info_t* map_info_list) {
-    memory->tid = -1;
-    memory->map_info_list = map_info_list;
-}
-
-void init_memory_ptrace(memory_t* memory, pid_t tid) {
-    memory->tid = tid;
-    memory->map_info_list = NULL;
-}
-
-bool try_get_word(const memory_t* memory, uintptr_t ptr, uint32_t* out_value) {
-    ALOGV("try_get_word: reading word at %p", (void*) ptr);
-    if (ptr & 3) {
-        ALOGV("try_get_word: invalid pointer %p", (void*) ptr);
-        *out_value = 0xffffffffL;
-        return false;
-    }
-    if (memory->tid < 0) {
-        if (!is_readable_map(memory->map_info_list, ptr)) {
-            ALOGV("try_get_word: pointer %p not in a readable map", (void*) ptr);
-            *out_value = 0xffffffffL;
-            return false;
-        }
-        *out_value = *(uint32_t*)ptr;
-        return true;
-    } else {
-#if defined(__APPLE__)
-        ALOGV("no ptrace on Mac OS");
-        return false;
-#else
-        // ptrace() returns -1 and sets errno when the operation fails.
-        // To disambiguate -1 from a valid result, we clear errno beforehand.
-        errno = 0;
-        *out_value = ptrace(PTRACE_PEEKTEXT, memory->tid, (void*)ptr, NULL);
-        if (*out_value == 0xffffffffL && errno) {
-            ALOGV("try_get_word: invalid pointer 0x%08x reading from tid %d, "
-                    "ptrace() errno=%d", ptr, memory->tid, errno);
-            return false;
-        }
-        return true;
-#endif
-    }
-}
-
-bool try_get_word_ptrace(pid_t tid, uintptr_t ptr, uint32_t* out_value) {
-    memory_t memory;
-    init_memory_ptrace(&memory, tid);
-    return try_get_word(&memory, ptr, out_value);
-}
-
-static void load_ptrace_map_info_data(pid_t pid, map_info_t* mi) {
-    if (mi->is_executable && mi->is_readable) {
-        uint32_t elf_magic;
-        if (try_get_word_ptrace(pid, mi->start, &elf_magic) && elf_magic == ELF_MAGIC) {
-            map_info_data_t* data = (map_info_data_t*)calloc(1, sizeof(map_info_data_t));
-            if (data) {
-                mi->data = data;
-                if (mi->name[0]) {
-                    data->symbol_table = load_symbol_table(mi->name);
-                }
-#ifdef CORKSCREW_HAVE_ARCH
-                load_ptrace_map_info_data_arch(pid, mi, data);
-#endif
-            }
-        }
-    }
-}
-
-ptrace_context_t* load_ptrace_context(pid_t pid) {
-    ptrace_context_t* context =
-            (ptrace_context_t*)calloc(1, sizeof(ptrace_context_t));
-    if (context) {
-        context->map_info_list = load_map_info_list(pid);
-        for (map_info_t* mi = context->map_info_list; mi; mi = mi->next) {
-            load_ptrace_map_info_data(pid, mi);
-        }
-    }
-    return context;
-}
-
-static void free_ptrace_map_info_data(map_info_t* mi) {
-    map_info_data_t* data = (map_info_data_t*)mi->data;
-    if (data) {
-        if (data->symbol_table) {
-            free_symbol_table(data->symbol_table);
-        }
-#ifdef CORKSCREW_HAVE_ARCH
-        free_ptrace_map_info_data_arch(mi, data);
-#endif
-        free(data);
-        mi->data = NULL;
-    }
-}
-
-void free_ptrace_context(ptrace_context_t* context) {
-    for (map_info_t* mi = context->map_info_list; mi; mi = mi->next) {
-        free_ptrace_map_info_data(mi);
-    }
-    free_map_info_list(context->map_info_list);
-    free(context);
-}
-
-void find_symbol_ptrace(const ptrace_context_t* context,
-        uintptr_t addr, const map_info_t** out_map_info, const symbol_t** out_symbol) {
-    const map_info_t* mi = find_map_info(context->map_info_list, addr);
-    const symbol_t* symbol = NULL;
-    if (mi) {
-        const map_info_data_t* data = (const map_info_data_t*)mi->data;
-        if (data && data->symbol_table) {
-            symbol = find_symbol(data->symbol_table, addr - mi->start);
-        }
-    }
-    *out_map_info = mi;
-    *out_symbol = symbol;
-}
diff --git a/libcorkscrew/symbol_table.c b/libcorkscrew/symbol_table.c
deleted file mode 100644
index 982ccc8..0000000
--- a/libcorkscrew/symbol_table.c
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-#define LOG_TAG "Corkscrew"
-//#define LOG_NDEBUG 0
-
-#include <corkscrew/symbol_table.h>
-
-#include <stdbool.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <cutils/log.h>
-
-#if defined(__APPLE__)
-#else
-
-#include <elf.h>
-
-static bool is_elf(Elf32_Ehdr* e) {
-    return (e->e_ident[EI_MAG0] == ELFMAG0 &&
-            e->e_ident[EI_MAG1] == ELFMAG1 &&
-            e->e_ident[EI_MAG2] == ELFMAG2 &&
-            e->e_ident[EI_MAG3] == ELFMAG3);
-}
-
-#endif
-
-// Compare function for qsort
-static int qcompar(const void *a, const void *b) {
-    const symbol_t* asym = (const symbol_t*)a;
-    const symbol_t* bsym = (const symbol_t*)b;
-    if (asym->start > bsym->start) return 1;
-    if (asym->start < bsym->start) return -1;
-    return 0;
-}
-
-// Compare function for bsearch
-static int bcompar(const void *key, const void *element) {
-    uintptr_t addr = *(const uintptr_t*)key;
-    const symbol_t* symbol = (const symbol_t*)element;
-    if (addr < symbol->start) return -1;
-    if (addr >= symbol->end) return 1;
-    return 0;
-}
-
-symbol_table_t* load_symbol_table(const char *filename) {
-    symbol_table_t* table = NULL;
-#if !defined(__APPLE__)
-    ALOGV("Loading symbol table from '%s'.", filename);
-
-    int fd = open(filename, O_RDONLY);
-    if (fd < 0) {
-        goto out;
-    }
-
-    struct stat sb;
-    if (fstat(fd, &sb)) {
-        goto out_close;
-    }
-
-    size_t length = sb.st_size;
-    char* base = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0);
-    if (base == MAP_FAILED) {
-        goto out_close;
-    }
-
-    // Parse the file header
-    Elf32_Ehdr *hdr = (Elf32_Ehdr*)base;
-    if (!is_elf(hdr)) {
-        goto out_close;
-    }
-    Elf32_Shdr *shdr = (Elf32_Shdr*)(base + hdr->e_shoff);
-
-    // Search for the dynamic symbols section
-    int sym_idx = -1;
-    int dynsym_idx = -1;
-    for (Elf32_Half i = 0; i < hdr->e_shnum; i++) {
-        if (shdr[i].sh_type == SHT_SYMTAB) {
-            sym_idx = i;
-        }
-        if (shdr[i].sh_type == SHT_DYNSYM) {
-            dynsym_idx = i;
-        }
-    }
-    if (dynsym_idx == -1 && sym_idx == -1) {
-        goto out_unmap;
-    }
-
-    table = malloc(sizeof(symbol_table_t));
-    if(!table) {
-        goto out_unmap;
-    }
-    table->num_symbols = 0;
-
-    Elf32_Sym *dynsyms = NULL;
-    int dynnumsyms = 0;
-    char *dynstr = NULL;
-    if (dynsym_idx != -1) {
-        dynsyms = (Elf32_Sym*)(base + shdr[dynsym_idx].sh_offset);
-        dynnumsyms = shdr[dynsym_idx].sh_size / shdr[dynsym_idx].sh_entsize;
-        int dynstr_idx = shdr[dynsym_idx].sh_link;
-        dynstr = base + shdr[dynstr_idx].sh_offset;
-    }
-
-    Elf32_Sym *syms = NULL;
-    int numsyms = 0;
-    char *str = NULL;
-    if (sym_idx != -1) {
-        syms = (Elf32_Sym*)(base + shdr[sym_idx].sh_offset);
-        numsyms = shdr[sym_idx].sh_size / shdr[sym_idx].sh_entsize;
-        int str_idx = shdr[sym_idx].sh_link;
-        str = base + shdr[str_idx].sh_offset;
-    }
-
-    int dynsymbol_count = 0;
-    if (dynsym_idx != -1) {
-        // Iterate through the dynamic symbol table, and count how many symbols
-        // are actually defined
-        for (int i = 0; i < dynnumsyms; i++) {
-            if (dynsyms[i].st_shndx != SHN_UNDEF) {
-                dynsymbol_count++;
-            }
-        }
-    }
-
-    size_t symbol_count = 0;
-    if (sym_idx != -1) {
-        // Iterate through the symbol table, and count how many symbols
-        // are actually defined
-        for (int i = 0; i < numsyms; i++) {
-            if (syms[i].st_shndx != SHN_UNDEF
-                    && str[syms[i].st_name]
-                    && syms[i].st_value
-                    && syms[i].st_size) {
-                symbol_count++;
-            }
-        }
-    }
-
-    // Now, create an entry in our symbol table structure for each symbol...
-    table->num_symbols += symbol_count + dynsymbol_count;
-    table->symbols = malloc(table->num_symbols * sizeof(symbol_t));
-    if (!table->symbols) {
-        free(table);
-        table = NULL;
-        goto out_unmap;
-    }
-
-    size_t symbol_index = 0;
-    if (dynsym_idx != -1) {
-        // ...and populate them
-        for (int i = 0; i < dynnumsyms; i++) {
-            if (dynsyms[i].st_shndx != SHN_UNDEF) {
-                table->symbols[symbol_index].name = strdup(dynstr + dynsyms[i].st_name);
-                table->symbols[symbol_index].start = dynsyms[i].st_value;
-                table->symbols[symbol_index].end = dynsyms[i].st_value + dynsyms[i].st_size;
-                ALOGV("  [%d] '%s' 0x%08x-0x%08x (DYNAMIC)",
-                        symbol_index, table->symbols[symbol_index].name,
-                        table->symbols[symbol_index].start, table->symbols[symbol_index].end);
-                symbol_index += 1;
-            }
-        }
-    }
-
-    if (sym_idx != -1) {
-        // ...and populate them
-        for (int i = 0; i < numsyms; i++) {
-            if (syms[i].st_shndx != SHN_UNDEF
-                    && str[syms[i].st_name]
-                    && syms[i].st_value
-                    && syms[i].st_size) {
-                table->symbols[symbol_index].name = strdup(str + syms[i].st_name);
-                table->symbols[symbol_index].start = syms[i].st_value;
-                table->symbols[symbol_index].end = syms[i].st_value + syms[i].st_size;
-                ALOGV("  [%d] '%s' 0x%08x-0x%08x",
-                        symbol_index, table->symbols[symbol_index].name,
-                        table->symbols[symbol_index].start, table->symbols[symbol_index].end);
-                symbol_index += 1;
-            }
-        }
-    }
-
-    // Sort the symbol table entries, so they can be bsearched later
-    qsort(table->symbols, table->num_symbols, sizeof(symbol_t), qcompar);
-
-out_unmap:
-    munmap(base, length);
-
-out_close:
-    close(fd);
-#endif
-
-out:
-    return table;
-}
-
-void free_symbol_table(symbol_table_t* table) {
-    if (table) {
-        for (size_t i = 0; i < table->num_symbols; i++) {
-            free(table->symbols[i].name);
-        }
-        free(table->symbols);
-        free(table);
-    }
-}
-
-const symbol_t* find_symbol(const symbol_table_t* table, uintptr_t addr) {
-    if (!table) return NULL;
-    return (const symbol_t*)bsearch(&addr, table->symbols, table->num_symbols,
-            sizeof(symbol_t), bcompar);
-}
diff --git a/libcorkscrew/test.cpp b/libcorkscrew/test.cpp
deleted file mode 100644
index 22dfa7d..0000000
--- a/libcorkscrew/test.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-#include <corkscrew/backtrace.h>
-#include <corkscrew/symbol_table.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-int do_backtrace(float /* just to test demangling */) {
-  const size_t MAX_DEPTH = 32;
-  backtrace_frame_t* frames = (backtrace_frame_t*) malloc(sizeof(backtrace_frame_t) * MAX_DEPTH);
-  ssize_t frame_count = unwind_backtrace(frames, 0, MAX_DEPTH);
-  fprintf(stderr, "frame_count=%d\n", (int) frame_count);
-  if (frame_count <= 0) {
-    return frame_count;
-  }
-
-  backtrace_symbol_t* backtrace_symbols = (backtrace_symbol_t*) malloc(sizeof(backtrace_symbol_t) * frame_count);
-  get_backtrace_symbols(frames, frame_count, backtrace_symbols);
-
-  for (size_t i = 0; i < (size_t) frame_count; ++i) {
-    char line[MAX_BACKTRACE_LINE_LENGTH];
-    format_backtrace_line(i, &frames[i], &backtrace_symbols[i],
-                          line, MAX_BACKTRACE_LINE_LENGTH);
-    if (backtrace_symbols[i].symbol_name != NULL) {
-      // get_backtrace_symbols found the symbol's name with dladdr(3).
-      fprintf(stderr, "  %s\n", line);
-    } else {
-      // We don't have a symbol. Maybe this is a static symbol, and
-      // we can look it up?
-      symbol_table_t* symbols = NULL;
-      if (backtrace_symbols[i].map_name != NULL) {
-        symbols = load_symbol_table(backtrace_symbols[i].map_name);
-      }
-      const symbol_t* symbol = NULL;
-      if (symbols != NULL) {
-        symbol = find_symbol(symbols, frames[i].absolute_pc);
-      }
-      if (symbol != NULL) {
-        int offset = frames[i].absolute_pc - symbol->start;
-        fprintf(stderr, "  %s (%s%+d)\n", line, symbol->name, offset);
-      } else {
-        fprintf(stderr, "  %s (\?\?\?)\n", line);
-      }
-      free_symbol_table(symbols);
-    }
-  }
-
-  free_backtrace_symbols(backtrace_symbols, frame_count);
-  free(backtrace_symbols);
-  free(frames);
-  return frame_count;
-}
-
-struct C {
-  int g(int i);
-};
-
-__attribute__ ((noinline)) int C::g(int i) {
-  if (i == 0) {
-    return do_backtrace(0.1);
-  }
-  return g(i - 1);
-}
-
-extern "C" __attribute__ ((noinline)) int f() {
-  C c;
-  return c.g(5);
-}
-
-int main() {
-  flush_my_map_info_list();
-  f();
-
-  flush_my_map_info_list();
-  f();
-
-  return 0;
-}
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
index 93bccb0..635695a 100644
--- a/libcutils/Android.mk
+++ b/libcutils/Android.mk
@@ -91,6 +91,16 @@
 LOCAL_CFLAGS += $(hostSmpFlag) -m64
 include $(BUILD_HOST_STATIC_LIBRARY)
 
+# Tests for host
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := tst_str_parms
+LOCAL_CFLAGS += -DTEST_STR_PARMS
+LOCAL_SRC_FILES := str_parms.c hashmap.c memory.c
+LOCAL_STATIC_LIBRARIES := liblog
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_HOST_EXECUTABLE)
+
 
 # Shared and static library for target
 # ========================================================
diff --git a/libcutils/debugger.c b/libcutils/debugger.c
index 7d907fc..056de5d 100644
--- a/libcutils/debugger.c
+++ b/libcutils/debugger.c
@@ -15,6 +15,7 @@
  */
 
 #include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
 
 #include <cutils/debugger.h>
@@ -28,9 +29,9 @@
     }
 
     debugger_msg_t msg;
+    memset(&msg, 0, sizeof(msg));
     msg.tid = tid;
     msg.action = DEBUGGER_ACTION_DUMP_TOMBSTONE;
-    msg.abort_msg_address = 0;
 
     int result = 0;
     if (TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg))) != sizeof(msg)) {
@@ -62,9 +63,9 @@
     }
 
     debugger_msg_t msg;
+    memset(&msg, 0, sizeof(msg));
     msg.tid = tid;
     msg.action = DEBUGGER_ACTION_DUMP_BACKTRACE;
-    msg.abort_msg_address = 0;
 
     int result = 0;
     if (TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg))) != sizeof(msg)) {
diff --git a/libcutils/str_parms.c b/libcutils/str_parms.c
index 7cfbcb3..953b91d 100644
--- a/libcutils/str_parms.c
+++ b/libcutils/str_parms.c
@@ -194,23 +194,46 @@
 int str_parms_add_str(struct str_parms *str_parms, const char *key,
                       const char *value)
 {
-    void *old_val;
-    void *tmp_key;
-    void *tmp_val;
+    void *tmp_key = NULL;
+    void *tmp_val = NULL;
+    void *old_val = NULL;
+
+    // strdup and hashmapPut both set errno on failure.
+    // Set errno to 0 so we can recognize whether anything went wrong.
+    int saved_errno = errno;
+    errno = 0;
 
     tmp_key = strdup(key);
-    tmp_val = strdup(value);
-    old_val = hashmapPut(str_parms->map, tmp_key, tmp_val);
-
-    if (old_val) {
-        free(old_val);
-        free(tmp_key);
-    } else if (errno == ENOMEM) {
-        free(tmp_key);
-        free(tmp_val);
-        return -ENOMEM;
+    if (tmp_key == NULL) {
+        goto clean_up;
     }
-    return 0;
+
+    tmp_val = strdup(value);
+    if (tmp_val == NULL) {
+        goto clean_up;
+    }
+
+    old_val = hashmapPut(str_parms->map, tmp_key, tmp_val);
+    if (old_val == NULL) {
+        // Did hashmapPut fail?
+        if (errno == ENOMEM) {
+            goto clean_up;
+        }
+        // For new keys, hashmap takes ownership of tmp_key and tmp_val.
+        tmp_key = tmp_val = NULL;
+    } else {
+        // For existing keys, hashmap takes ownership of tmp_val.
+        // (It also gives up ownership of old_val.)
+        tmp_val = NULL;
+    }
+
+clean_up:
+    free(tmp_key);
+    free(tmp_val);
+    free(old_val);
+    int result = -errno;
+    errno = saved_errno;
+    return result;
 }
 
 int str_parms_add_int(struct str_parms *str_parms, const char *key, int value)
@@ -337,7 +360,6 @@
 {
     struct str_parms *str_parms;
     char *out_str;
-    int ret;
 
     str_parms = str_parms_create_str(str);
     str_parms_add_str(str_parms, "dude", "woah");
@@ -370,6 +392,15 @@
     test_str_parms_str("foo=bar;baz=bat;");
     test_str_parms_str("foo=bar;baz=bat;foo=bar");
 
+    // hashmapPut reports errors by setting errno to ENOMEM.
+    // Test that we're not confused by running in an environment where this is already true.
+    errno = ENOMEM;
+    test_str_parms_str("foo=bar;baz=");
+    if (errno != ENOMEM) {
+        abort();
+    }
+    test_str_parms_str("foo=bar;baz=");
+
     return 0;
 }
 #endif
diff --git a/liblog/Android.mk b/liblog/Android.mk
index a23de2d..5e01903 100644
--- a/liblog/Android.mk
+++ b/liblog/Android.mk
@@ -22,10 +22,6 @@
 liblog_sources := logd_write_kern.c
 endif
 
-ifneq ($(filter userdebug eng,$(TARGET_BUILD_VARIANT)),)
-liblog_cflags := -DUSERDEBUG_BUILD=1
-endif
-
 # some files must not be compiled when building against Mingw
 # they correspond to features not used by our host development tools
 # which are also hard or even impossible to port to native Win32
diff --git a/liblog/log_read.c b/liblog/log_read.c
index 2dd07e6..11fe848 100644
--- a/liblog/log_read.c
+++ b/liblog/log_read.c
@@ -17,6 +17,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
+#include <poll.h>
 #include <signal.h>
 #include <stddef.h>
 #define NOMINMAX /* for windows to suppress definition of min in stdlib.h */
@@ -196,7 +197,8 @@
     [LOG_ID_MAIN] = "main",
     [LOG_ID_RADIO] = "radio",
     [LOG_ID_EVENTS] = "events",
-    [LOG_ID_SYSTEM] = "system"
+    [LOG_ID_SYSTEM] = "system",
+    [LOG_ID_CRASH] = "crash",
 };
 
 const char *android_log_id_to_name(log_id_t log_id)
@@ -272,6 +274,8 @@
                             const char *msg, char *buf, size_t buf_size)
 {
     ssize_t ret;
+    size_t len;
+    char *cp;
     int errno_save = 0;
     int sock = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED,
                                    SOCK_STREAM);
@@ -283,12 +287,44 @@
         snprintf(buf, buf_size, msg, logger ? logger->id : (unsigned) -1);
     }
 
-    ret = write(sock, buf, strlen(buf) + 1);
+    len = strlen(buf) + 1;
+    ret = TEMP_FAILURE_RETRY(write(sock, buf, len));
     if (ret <= 0) {
         goto done;
     }
 
-    ret = read(sock, buf, buf_size);
+    len = buf_size;
+    cp = buf;
+    while ((ret = TEMP_FAILURE_RETRY(read(sock, cp, len))) > 0) {
+        struct pollfd p;
+
+        if (((size_t)ret == len) || (buf_size < PAGE_SIZE)) {
+            break;
+        }
+
+        len -= ret;
+        cp += ret;
+
+        memset(&p, 0, sizeof(p));
+        p.fd = sock;
+        p.events = POLLIN;
+
+        /* Give other side 20ms to refill pipe */
+        ret = TEMP_FAILURE_RETRY(poll(&p, 1, 20));
+
+        if (ret <= 0) {
+            break;
+        }
+
+        if (!(p.revents & POLLIN)) {
+            ret = 0;
+            break;
+        }
+    }
+
+    if (ret >= 0) {
+        ret += buf_size - len;
+    }
 
 done:
     if ((ret == -1) && errno) {
@@ -340,8 +376,6 @@
     return atol(buf);
 }
 
-#ifdef USERDEBUG_BUILD
-
 int android_logger_set_log_size(struct logger *logger, unsigned long size)
 {
     char buf[512];
@@ -352,8 +386,6 @@
     return check_log_success(buf, send_log_msg(NULL, NULL, buf, sizeof(buf)));
 }
 
-#endif /* USERDEBUG_BUILD */
-
 /*
  * returns the readable size of the log's ring buffer (that is, amount of the
  * log consumed)
@@ -408,8 +440,6 @@
     return send_log_msg(NULL, NULL, buf, len);
 }
 
-#ifdef USERDEBUG_BUILD
-
 ssize_t android_logger_get_prune_list(struct logger_list *logger_list UNUSED,
                                       char *buf, size_t len)
 {
@@ -432,8 +462,6 @@
     return check_log_success(buf, send_log_msg(NULL, NULL, buf, len));
 }
 
-#endif /* USERDEBUG_BUILD */
-
 struct logger_list *android_logger_list_alloc(int mode,
                                               unsigned int tail,
                                               pid_t pid)
diff --git a/liblog/log_read_kern.c b/liblog/log_read_kern.c
index 9cccb1d..021fe47 100644
--- a/liblog/log_read_kern.c
+++ b/liblog/log_read_kern.c
@@ -58,7 +58,8 @@
     [LOG_ID_MAIN] = "main",
     [LOG_ID_RADIO] = "radio",
     [LOG_ID_EVENTS] = "events",
-    [LOG_ID_SYSTEM] = "system"
+    [LOG_ID_SYSTEM] = "system",
+    [LOG_ID_CRASH] = "crash"
 };
 
 const char *android_log_id_to_name(log_id_t log_id)
@@ -232,16 +233,12 @@
     return logger_ioctl(logger, LOGGER_GET_LOG_BUF_SIZE, O_RDWR);
 }
 
-#ifdef USERDEBUG_BUILD
-
 int android_logger_set_log_size(struct logger *logger UNUSED,
                                 unsigned long size UNUSED)
 {
     return -ENOTSUP;
 }
 
-#endif /* USERDEBUG_BUILD */
-
 /*
  * returns the readable size of the log's ring buffer (that is, amount of the
  * log consumed)
@@ -272,8 +269,6 @@
     return -ENOTSUP;
 }
 
-#ifdef USERDEBUG_BUILD
-
 ssize_t android_logger_get_prune_list(struct logger_list *logger_list UNUSED,
                                       char *buf, size_t len)
 {
@@ -289,8 +284,6 @@
     return -ENOTSUP;
 }
 
-#endif /* USERDEBUG_BUILD */
-
 struct logger_list *android_logger_list_alloc(int mode,
                                               unsigned int tail,
                                               pid_t pid)
diff --git a/liblog/logd_write.c b/liblog/logd_write.c
index aa0ad39..bd36a65 100644
--- a/liblog/logd_write.c
+++ b/liblog/logd_write.c
@@ -54,7 +54,7 @@
 static int logd_fd = -1;
 #if FAKE_LOG_DEVICE
 #define WEAK __attribute__((weak))
-static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1 };
+static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1, -1 };
 #endif
 
 /*
@@ -77,32 +77,84 @@
     return (g_log_status == kLogAvailable);
 }
 
+/* give up, resources too limited */
 static int __write_to_log_null(log_id_t log_fd UNUSED, struct iovec *vec UNUSED,
                                size_t nr UNUSED)
 {
     return -1;
 }
 
+/* log_init_lock assumed */
+static int __write_to_log_initialize()
+{
+    int i, ret = 0;
+
+#if FAKE_LOG_DEVICE
+    for (i = 0; i < LOG_ID_MAX; i++) {
+        char buf[sizeof("/dev/log_system")];
+        snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i));
+        log_fds[i] = fakeLogOpen(buf, O_WRONLY);
+    }
+#else
+    if (logd_fd >= 0) {
+        i = logd_fd;
+        logd_fd = -1;
+        close(i);
+    }
+
+    i = socket(PF_UNIX, SOCK_DGRAM, 0);
+    if (i < 0) {
+        ret = -errno;
+        write_to_log = __write_to_log_null;
+    } else if (fcntl(i, F_SETFL, O_NONBLOCK) < 0) {
+        ret = -errno;
+        close(i);
+        i = -1;
+        write_to_log = __write_to_log_null;
+    } else {
+        struct sockaddr_un un;
+        memset(&un, 0, sizeof(struct sockaddr_un));
+        un.sun_family = AF_UNIX;
+        strcpy(un.sun_path, "/dev/socket/logdw");
+
+        if (connect(i, (struct sockaddr *)&un, sizeof(struct sockaddr_un)) < 0) {
+            ret = -errno;
+            close(i);
+            i = -1;
+        }
+    }
+    logd_fd = i;
+#endif
+
+    return ret;
+}
+
 static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr)
 {
-#if FAKE_LOG_DEVICE
     ssize_t ret;
+#if FAKE_LOG_DEVICE
     int log_fd;
 
     if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) {
         log_fd = log_fds[(int)log_id];
     } else {
-        return EBADF;
+        return -EBADF;
     }
     do {
         ret = fakeLogWritev(log_fd, vec, nr);
-    } while (ret < 0 && errno == EINTR);
-
-    return ret;
+        if (ret < 0) {
+            ret = -errno;
+        }
+    } while (ret == -EINTR);
 #else
-    if (logd_fd == -1) {
-        return -1;
-    }
+    static const unsigned header_length = 3;
+    struct iovec newVec[nr + header_length];
+    typeof_log_id_t log_id_buf;
+    uint16_t tid;
+    struct timespec ts;
+    log_time realtime_ts;
+    size_t i, payload_size;
+
     if (getuid() == AID_LOGD) {
         /*
          * ignore log messages we send to ourself.
@@ -111,6 +163,11 @@
          */
         return 0;
     }
+
+    if (logd_fd < 0) {
+        return -EBADF;
+    }
+
     /*
      *  struct {
      *      // what we provide
@@ -130,34 +187,70 @@
      *      };
      *  };
      */
-    static const unsigned header_length = 3;
-    struct iovec newVec[nr + header_length];
-    typeof_log_id_t log_id_buf = log_id;
-    uint16_t tid = gettid();
+
+    log_id_buf = log_id;
+    tid = gettid();
 
     newVec[0].iov_base   = (unsigned char *) &log_id_buf;
     newVec[0].iov_len    = sizeof_log_id_t;
     newVec[1].iov_base   = (unsigned char *) &tid;
     newVec[1].iov_len    = sizeof(tid);
 
-    struct timespec ts;
     clock_gettime(CLOCK_REALTIME, &ts);
-    log_time realtime_ts;
     realtime_ts.tv_sec = ts.tv_sec;
     realtime_ts.tv_nsec = ts.tv_nsec;
 
     newVec[2].iov_base   = (unsigned char *) &realtime_ts;
     newVec[2].iov_len    = sizeof(log_time);
 
-    size_t i;
-    for (i = header_length; i < nr + header_length; i++) {
-        newVec[i].iov_base = vec[i-header_length].iov_base;
-        newVec[i].iov_len  = vec[i-header_length].iov_len;
+    for (payload_size = 0, i = header_length; i < nr + header_length; i++) {
+        newVec[i].iov_base = vec[i - header_length].iov_base;
+        payload_size += newVec[i].iov_len = vec[i - header_length].iov_len;
+
+        if (payload_size > LOGGER_ENTRY_MAX_PAYLOAD) {
+            newVec[i].iov_len -= payload_size - LOGGER_ENTRY_MAX_PAYLOAD;
+            if (newVec[i].iov_len) {
+                ++i;
+            }
+            break;
+        }
     }
 
-    /* The write below could be lost, but will never block. */
-    return writev(logd_fd, newVec, nr + header_length);
+    /*
+     * The write below could be lost, but will never block.
+     *
+     * ENOTCONN occurs if logd dies.
+     * EAGAIN occurs if logd is overloaded.
+     */
+    ret = writev(logd_fd, newVec, i);
+    if (ret < 0) {
+        ret = -errno;
+        if (ret == -ENOTCONN) {
+#ifdef HAVE_PTHREADS
+            pthread_mutex_lock(&log_init_lock);
 #endif
+            ret = __write_to_log_initialize();
+#ifdef HAVE_PTHREADS
+            pthread_mutex_unlock(&log_init_lock);
+#endif
+
+            if (ret < 0) {
+                return ret;
+            }
+
+            ret = writev(logd_fd, newVec, nr + header_length);
+            if (ret < 0) {
+                ret = -errno;
+            }
+        }
+    }
+
+    if (ret > (ssize_t)(sizeof_log_id_t + sizeof(tid) + sizeof(log_time))) {
+        ret -= sizeof_log_id_t + sizeof(tid) + sizeof(log_time);
+    }
+#endif
+
+    return ret;
 }
 
 #if FAKE_LOG_DEVICE
@@ -165,7 +258,8 @@
     [LOG_ID_MAIN] = "main",
     [LOG_ID_RADIO] = "radio",
     [LOG_ID_EVENTS] = "events",
-    [LOG_ID_SYSTEM] = "system"
+    [LOG_ID_SYSTEM] = "system",
+    [LOG_ID_CRASH] = "crash"
 };
 
 const WEAK char *android_log_id_to_name(log_id_t log_id)
@@ -184,35 +278,17 @@
 #endif
 
     if (write_to_log == __write_to_log_init) {
-        write_to_log = __write_to_log_kernel;
+        int ret;
 
-#if FAKE_LOG_DEVICE
-        int i;
-        for (i = 0; i < LOG_ID_MAX; i++) {
-            char buf[sizeof("/dev/log_system")];
-            snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i));
-            log_fds[i] = fakeLogOpen(buf, O_WRONLY);
-        }
-#else
-        int sock = socket(PF_UNIX, SOCK_DGRAM, 0);
-        if (sock != -1) {
-            if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
-                /* NB: Loss of content */
-                close(sock);
-                sock = -1;
-            } else {
-                struct sockaddr_un un;
-                memset(&un, 0, sizeof(struct sockaddr_un));
-                un.sun_family = AF_UNIX;
-                strcpy(un.sun_path, "/dev/socket/logdw");
-
-                connect(sock, (struct sockaddr *)&un, sizeof(struct sockaddr_un));
-            }
-        } else {
-            write_to_log = __write_to_log_null;
-        }
-        logd_fd = sock;
+        ret = __write_to_log_initialize();
+        if (ret < 0) {
+#ifdef HAVE_PTHREADS
+            pthread_mutex_unlock(&log_init_lock);
 #endif
+            return ret;
+        }
+
+        write_to_log = __write_to_log_kernel;
     }
 
 #ifdef HAVE_PTHREADS
@@ -242,7 +318,7 @@
         !strcmp(tag, "PHONE") ||
         !strcmp(tag, "SMS")) {
             log_id = LOG_ID_RADIO;
-            // Inform third party apps/ril/radio.. to use Rlog or RLOG
+            /* Inform third party apps/ril/radio.. to use Rlog or RLOG */
             snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
             tag = tmp_tag;
     }
@@ -277,7 +353,7 @@
         !strcmp(tag, "PHONE") ||
         !strcmp(tag, "SMS"))) {
             bufID = LOG_ID_RADIO;
-            // Inform third party apps/ril/radio.. to use Rlog or RLOG
+            /* Inform third party apps/ril/radio.. to use Rlog or RLOG */
             snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
             tag = tmp_tag;
     }
@@ -346,9 +422,15 @@
             strcpy(buf, "Unspecified assertion failed");
     }
 
+#if __BIONIC__
+    // Ensure debuggerd gets to see what went wrong by keeping the C library in the loop.
+    extern __noreturn void __android_fatal(const char* tag, const char* format, ...) __printflike(2, 3);
+    __android_fatal(tag ? tag : "", "%s", buf);
+#else
     __android_log_write(ANDROID_LOG_FATAL, tag, buf);
-
     __builtin_trap(); /* trap so we have a chance to debug the situation */
+#endif
+    /* NOTREACHED */
 }
 
 int __android_log_bwrite(int32_t tag, const void *payload, size_t len)
diff --git a/liblog/logd_write_kern.c b/liblog/logd_write_kern.c
index 32a202b..8c707ad 100644
--- a/liblog/logd_write_kern.c
+++ b/liblog/logd_write_kern.c
@@ -93,14 +93,20 @@
     int log_fd;
 
     if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) {
+        if (log_id == LOG_ID_CRASH) {
+            log_id = LOG_ID_MAIN;
+        }
         log_fd = log_fds[(int)log_id];
     } else {
-        return EBADF;
+        return -EBADF;
     }
 
     do {
         ret = log_writev(log_fd, vec, nr);
-    } while (ret < 0 && errno == EINTR);
+        if (ret < 0) {
+            ret = -errno;
+        }
+    } while (ret == -EINTR);
 
     return ret;
 }
@@ -266,9 +272,15 @@
             strcpy(buf, "Unspecified assertion failed");
     }
 
+#if __BIONIC__
+    // Ensure debuggerd gets to see what went wrong by keeping the C library in the loop.
+    extern __noreturn void __android_fatal(const char* tag, const char* format, ...) __printflike(2, 3);
+    __android_fatal(tag ? tag : "", "%s", buf);
+#else
     __android_log_write(ANDROID_LOG_FATAL, tag, buf);
-
     __builtin_trap(); /* trap so we have a chance to debug the situation */
+#endif
+    /* NOTREACHED */
 }
 
 int __android_log_bwrite(int32_t tag, const void *payload, size_t len)
diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk
index db06cf7..d1d9115 100644
--- a/liblog/tests/Android.mk
+++ b/liblog/tests/Android.mk
@@ -32,7 +32,7 @@
 
 benchmark_src_files := \
     benchmark_main.cpp \
-    liblog_benchmark.cpp \
+    liblog_benchmark.cpp
 
 # Build benchmarks for the device. Run with:
 #   adb shell liblog-benchmarks
@@ -59,10 +59,22 @@
     -g \
     -Wall -Wextra \
     -Werror \
-    -fno-builtin \
+    -fno-builtin
 
 test_src_files := \
-    liblog_test.cpp \
+    liblog_test.cpp
+
+# to prevent breaking the build if bionic not relatively visible to us
+ifneq ($(wildcard $(LOCAL_PATH)/../../../../bionic/libc/bionic/libc_logging.cpp),)
+
+test_src_files += \
+    libc_test.cpp
+
+ifndef ($(TARGET_USES_LOGD),false)
+test_c_flags += -DTARGET_USES_LOGD
+endif
+
+endif
 
 # Build tests for the device (with .so). Run with:
 #   adb shell /data/nativetest/liblog-unit-tests/liblog-unit-tests
diff --git a/liblog/tests/libc_test.cpp b/liblog/tests/libc_test.cpp
new file mode 100644
index 0000000..0abc375
--- /dev/null
+++ b/liblog/tests/libc_test.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2014 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 <fcntl.h>
+#include <sys/cdefs.h>
+
+#include <gtest/gtest.h>
+
+// Should be in bionic test suite, *but* we are using liblog to confirm
+// end-to-end logging, so let the overly cute oedipus complex begin ...
+#include "../../../../bionic/libc/bionic/libc_logging.cpp" // not Standalone
+#define _ANDROID_LOG_H // Priorities redefined
+#define _LIBS_LOG_LOG_H // log ids redefined
+typedef unsigned char log_id_t; // log_id_t missing as a result
+#ifdef TARGET_USES_LOGD
+#define _LIBS_LOG_LOG_READ_H // log_time redefined
+#endif
+
+#include <log/log.h>
+#include <log/logger.h>
+#include <log/log_read.h>
+
+TEST(libc, __libc_android_log_event_int) {
+    struct logger_list *logger_list;
+
+    pid_t pid = getpid();
+
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        LOG_ID_EVENTS, O_RDONLY | O_NDELAY, 1000, pid)));
+
+    struct timespec ts;
+    clock_gettime(CLOCK_MONOTONIC, &ts);
+    int value = ts.tv_nsec;
+
+    __libc_android_log_event_int(0, value);
+    usleep(1000000);
+
+    int count = 0;
+
+    for (;;) {
+        log_msg log_msg;
+        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+            break;
+        }
+
+        ASSERT_EQ(log_msg.entry.pid, pid);
+
+        if ((log_msg.entry.len != (4 + 1 + 4))
+         || ((int)log_msg.id() != LOG_ID_EVENTS)) {
+            continue;
+        }
+
+        char *eventData = log_msg.msg();
+
+        int incoming = (eventData[0] & 0xFF) |
+                      ((eventData[1] & 0xFF) << 8) |
+                      ((eventData[2] & 0xFF) << 16) |
+                      ((eventData[3] & 0xFF) << 24);
+
+        if (incoming != 0) {
+            continue;
+        }
+
+        if (eventData[4] != EVENT_TYPE_INT) {
+            continue;
+        }
+
+        incoming = (eventData[4 + 1 + 0] & 0xFF) |
+                  ((eventData[4 + 1 + 1] & 0xFF) << 8) |
+                  ((eventData[4 + 1 + 2] & 0xFF) << 16) |
+                  ((eventData[4 + 1 + 3] & 0xFF) << 24);
+
+        if (incoming == value) {
+            ++count;
+        }
+    }
+
+    EXPECT_EQ(1, count);
+
+    android_logger_list_close(logger_list);
+}
+
+TEST(libc, __libc_fatal_no_abort) {
+    struct logger_list *logger_list;
+
+    pid_t pid = getpid();
+
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        (log_id_t)LOG_ID_MAIN, O_RDONLY | O_NDELAY, 1000, pid)));
+
+    char b[80];
+    struct timespec ts;
+    clock_gettime(CLOCK_MONOTONIC, &ts);
+
+    __libc_fatal_no_abort("%u.%09u", (unsigned)ts.tv_sec, (unsigned)ts.tv_nsec);
+    snprintf(b, sizeof(b),"%u.%09u", (unsigned)ts.tv_sec, (unsigned)ts.tv_nsec);
+    usleep(1000000);
+
+    int count = 0;
+
+    for (;;) {
+        log_msg log_msg;
+        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+            break;
+        }
+
+        ASSERT_EQ(log_msg.entry.pid, pid);
+
+        if ((int)log_msg.id() != LOG_ID_MAIN) {
+            continue;
+        }
+
+        char *data = log_msg.msg();
+
+        if ((*data == ANDROID_LOG_FATAL)
+                && !strcmp(data + 1, "libc")
+                && !strcmp(data + 1 + strlen(data + 1) + 1, b)) {
+            ++count;
+        }
+    }
+
+    EXPECT_EQ(1, count);
+
+    android_logger_list_close(logger_list);
+}
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index ffb7fd1..92b68ac 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -38,30 +38,30 @@
     _rc; })
 
 TEST(liblog, __android_log_buf_print) {
-    ASSERT_LT(0, __android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO,
+    EXPECT_LT(0, __android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO,
                                          "TEST__android_log_buf_print",
                                          "radio"));
     usleep(1000);
-    ASSERT_LT(0, __android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
+    EXPECT_LT(0, __android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
                                          "TEST__android_log_buf_print",
                                          "system"));
     usleep(1000);
-    ASSERT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
+    EXPECT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
                                          "TEST__android_log_buf_print",
                                          "main"));
     usleep(1000);
 }
 
 TEST(liblog, __android_log_buf_write) {
-    ASSERT_LT(0, __android_log_buf_write(LOG_ID_RADIO, ANDROID_LOG_INFO,
+    EXPECT_LT(0, __android_log_buf_write(LOG_ID_RADIO, ANDROID_LOG_INFO,
                                          "TEST__android_log_buf_write",
                                          "radio"));
     usleep(1000);
-    ASSERT_LT(0, __android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
+    EXPECT_LT(0, __android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
                                          "TEST__android_log_buf_write",
                                          "system"));
     usleep(1000);
-    ASSERT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO,
+    EXPECT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO,
                                          "TEST__android_log_buf_write",
                                          "main"));
     usleep(1000);
@@ -69,16 +69,16 @@
 
 TEST(liblog, __android_log_btwrite) {
     int intBuf = 0xDEADBEEF;
-    ASSERT_LT(0, __android_log_btwrite(0,
+    EXPECT_LT(0, __android_log_btwrite(0,
                                       EVENT_TYPE_INT,
                                       &intBuf, sizeof(intBuf)));
     long long longBuf = 0xDEADBEEFA55A5AA5;
-    ASSERT_LT(0, __android_log_btwrite(0,
+    EXPECT_LT(0, __android_log_btwrite(0,
                                       EVENT_TYPE_LONG,
                                       &longBuf, sizeof(longBuf)));
     usleep(1000);
     char Buf[] = "\20\0\0\0DeAdBeEfA55a5aA5";
-    ASSERT_LT(0, __android_log_btwrite(0,
+    EXPECT_LT(0, __android_log_btwrite(0,
                                       EVENT_TYPE_STRING,
                                       Buf, sizeof(Buf) - 1));
     usleep(1000);
@@ -120,7 +120,7 @@
 
     pid_t pid = getpid();
 
-    ASSERT_EQ(0, NULL == (logger_list = android_logger_list_open(
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
         LOG_ID_EVENTS, O_RDONLY | O_NDELAY, 1000, pid)));
 
     log_time ts(CLOCK_MONOTONIC);
@@ -155,7 +155,7 @@
         }
     }
 
-    ASSERT_EQ(1, count);
+    EXPECT_EQ(1, count);
 
     android_logger_list_close(logger_list);
 }
@@ -221,7 +221,7 @@
 
     v += pid & 0xFFFF;
 
-    ASSERT_EQ(0, NULL == (logger_list = android_logger_list_open(
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
         LOG_ID_EVENTS, O_RDONLY, 1000, pid)));
 
     int count = 0;
@@ -277,13 +277,13 @@
             ++signals;
             break;
         }
-    } while (!signaled || ({log_time t(CLOCK_MONOTONIC); t < signal_time;}));
+    } while (!signaled || (log_time(CLOCK_MONOTONIC) < signal_time));
     alarm(0);
     signal(SIGALRM, SIG_DFL);
 
-    ASSERT_LT(1, count);
+    EXPECT_LT(1, count);
 
-    ASSERT_EQ(1, signals);
+    EXPECT_EQ(1, signals);
 
     android_logger_list_close(logger_list);
 
@@ -295,9 +295,302 @@
     const unsigned long long one_percent_ticks = alarm_time;
     unsigned long long user_ticks = uticks_end - uticks_start;
     unsigned long long system_ticks = sticks_end - sticks_start;
-    ASSERT_GT(one_percent_ticks, user_ticks);
-    ASSERT_GT(one_percent_ticks, system_ticks);
-    ASSERT_GT(one_percent_ticks, user_ticks + system_ticks);
+    EXPECT_GT(one_percent_ticks, user_ticks);
+    EXPECT_GT(one_percent_ticks, system_ticks);
+    EXPECT_GT(one_percent_ticks, user_ticks + system_ticks);
+}
+
+static const char max_payload_tag[] = "TEST_max_payload_XXXX";
+static const char max_payload_buf[LOGGER_ENTRY_MAX_PAYLOAD
+    - sizeof(max_payload_tag) - 1] = "LEONATO\n\
+I learn in this letter that Don Peter of Arragon\n\
+comes this night to Messina\n\
+MESSENGER\n\
+He is very near by this: he was not three leagues off\n\
+when I left him\n\
+LEONATO\n\
+How many gentlemen have you lost in this action?\n\
+MESSENGER\n\
+But few of any sort, and none of name\n\
+LEONATO\n\
+A victory is twice itself when the achiever brings\n\
+home full numbers. I find here that Don Peter hath\n\
+bestowed much honour on a young Florentine called Claudio\n\
+MESSENGER\n\
+Much deserved on his part and equally remembered by\n\
+Don Pedro: he hath borne himself beyond the\n\
+promise of his age, doing, in the figure of a lamb,\n\
+the feats of a lion: he hath indeed better\n\
+bettered expectation than you must expect of me to\n\
+tell you how\n\
+LEONATO\n\
+He hath an uncle here in Messina will be very much\n\
+glad of it.\n\
+MESSENGER\n\
+I have already delivered him letters, and there\n\
+appears much joy in him; even so much that joy could\n\
+not show itself modest enough without a badge of\n\
+bitterness.\n\
+LEONATO\n\
+Did he break out into tears?\n\
+MESSENGER\n\
+In great measure.\n\
+LEONATO\n\
+A kind overflow of kindness: there are no faces\n\
+truer than those that are so washed. How much\n\
+better is it to weep at joy than to joy at weeping!\n\
+BEATRICE\n\
+I pray you, is Signior Mountanto returned from the\n\
+wars or no?\n\
+MESSENGER\n\
+I know none of that name, lady: there was none such\n\
+in the army of any sort.\n\
+LEONATO\n\
+What is he that you ask for, niece?\n\
+HERO\n\
+My cousin means Signior Benedick of Padua.\n\
+MESSENGER\n\
+O, he's returned; and as pleasant as ever he was.\n\
+BEATRICE\n\
+He set up his bills here in Messina and challenged\n\
+Cupid at the flight; and my uncle's fool, reading\n\
+the challenge, subscribed for Cupid, and challenged\n\
+him at the bird-bolt. I pray you, how many hath he\n\
+killed and eaten in these wars? But how many hath\n\
+he killed? for indeed I promised to eat all of his killing.\n\
+LEONATO\n\
+Faith, niece, you tax Signior Benedick too much;\n\
+but he'll be meet with you, I doubt it not.\n\
+MESSENGER\n\
+He hath done good service, lady, in these wars.\n\
+BEATRICE\n\
+You had musty victual, and he hath holp to eat it:\n\
+he is a very valiant trencherman; he hath an\n\
+excellent stomach.\n\
+MESSENGER\n\
+And a good soldier too, lady.\n\
+BEATRICE\n\
+And a good soldier to a lady: but what is he to a lord?\n\
+MESSENGER\n\
+A lord to a lord, a man to a man; stuffed with all\n\
+honourable virtues.\n\
+BEATRICE\n\
+It is so, indeed; he is no less than a stuffed man:\n\
+but for the stuffing,--well, we are all mortal.\n\
+LEONATO\n\
+You must not, sir, mistake my niece. There is a\n\
+kind of merry war betwixt Signior Benedick and her:\n\
+they never meet but there's a skirmish of wit\n\
+between them.\n\
+BEATRICE\n\
+Alas! he gets nothing by that. In our last\n\
+conflict four of his five wits went halting off, and\n\
+now is the whole man governed with one: so that if\n\
+he have wit enough to keep himself warm, let him\n\
+bear it for a difference between himself and his\n\
+horse; for it is all the wealth that he hath left,\n\
+to be known a reasonable creature. Who is his\n\
+companion now? He hath every month a new sworn brother.\n\
+MESSENGER\n\
+Is't possible?\n\
+BEATRICE\n\
+Very easily possible: he wears his faith but as\n\
+the fashion of his hat; it ever changes with the\n\
+next block.\n\
+MESSENGER\n\
+I see, lady, the gentleman is not in your books.\n\
+BEATRICE\n\
+No; an he were, I would burn my study. But, I pray\n\
+you, who is his companion? Is there no young\n\
+squarer now that will make a voyage with him to the devil?\n\
+MESSENGER\n\
+He is most in the company of the right noble Claudio.\n\
+BEATRICE\n\
+O Lord, he will hang upon him like a disease: he\n\
+is sooner caught than the pestilence, and the taker\n\
+runs presently mad. God help the noble Claudio! if\n\
+he have caught the Benedick, it will cost him a\n\
+thousand pound ere a' be cured.\n\
+MESSENGER\n\
+I will hold friends with you, lady.\n\
+BEATRICE\n\
+Do, good friend.\n\
+LEONATO\n\
+You will never run mad, niece.\n\
+BEATRICE\n\
+No, not till a hot January.\n\
+MESSENGER\n\
+Don Pedro is approached.\n\
+Enter DON PEDRO, DON JOHN, CLAUDIO, BENEDICK, and BALTHASAR\n\
+\n\
+DON PEDRO\n\
+Good Signior Leonato, you are come to meet your\n\
+trouble: the fashion of the world is to avoid\n\
+cost, and you encounter it\n\
+LEONATO\n\
+Never came trouble to my house in the likeness";
+
+TEST(liblog, max_payload) {
+    pid_t pid = getpid();
+    char tag[sizeof(max_payload_tag)];
+    memcpy(tag, max_payload_tag, sizeof(tag));
+    snprintf(tag + sizeof(tag) - 5, 5, "%04X", pid & 0xFFFF);
+
+    LOG_FAILURE_RETRY(__android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
+                                              tag, max_payload_buf));
+
+    struct logger_list *logger_list;
+
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        LOG_ID_SYSTEM, O_RDONLY, 100, 0)));
+
+    bool matches = false;
+    ssize_t max_len = 0;
+
+    for(;;) {
+        log_msg log_msg;
+        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+            break;
+        }
+
+        if ((log_msg.entry.pid != pid) || (log_msg.id() != LOG_ID_SYSTEM)) {
+            continue;
+        }
+
+        char *data = log_msg.msg() + 1;
+
+        if (strcmp(data, tag)) {
+            continue;
+        }
+
+        data += strlen(data) + 1;
+
+        const char *left = data;
+        const char *right = max_payload_buf;
+        while (*left && *right && (*left == *right)) {
+            ++left;
+            ++right;
+        }
+
+        if (max_len <= (left - data)) {
+            max_len = left - data + 1;
+        }
+
+        if (max_len > 512) {
+            matches = true;
+            break;
+        }
+    }
+
+    android_logger_list_close(logger_list);
+
+    EXPECT_EQ(true, matches);
+
+    EXPECT_LE(sizeof(max_payload_buf), static_cast<size_t>(max_len));
+}
+
+TEST(liblog, too_big_payload) {
+    pid_t pid = getpid();
+    static const char big_payload_tag[] = "TEST_big_payload_XXXX";
+    char tag[sizeof(big_payload_tag)];
+    memcpy(tag, big_payload_tag, sizeof(tag));
+    snprintf(tag + sizeof(tag) - 5, 5, "%04X", pid & 0xFFFF);
+
+    std::string longString(3266519, 'x');
+
+    ssize_t ret = LOG_FAILURE_RETRY(__android_log_buf_write(LOG_ID_SYSTEM,
+                                    ANDROID_LOG_INFO, tag, longString.c_str()));
+
+    struct logger_list *logger_list;
+
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        LOG_ID_SYSTEM, O_RDONLY | O_NDELAY, 100, 0)));
+
+    ssize_t max_len = 0;
+
+    for(;;) {
+        log_msg log_msg;
+        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+            break;
+        }
+
+        if ((log_msg.entry.pid != pid) || (log_msg.id() != LOG_ID_SYSTEM)) {
+            continue;
+        }
+
+        char *data = log_msg.msg() + 1;
+
+        if (strcmp(data, tag)) {
+            continue;
+        }
+
+        data += strlen(data) + 1;
+
+        const char *left = data;
+        const char *right = longString.c_str();
+        while (*left && *right && (*left == *right)) {
+            ++left;
+            ++right;
+        }
+
+        if (max_len <= (left - data)) {
+            max_len = left - data + 1;
+        }
+    }
+
+    android_logger_list_close(logger_list);
+
+    EXPECT_LE(LOGGER_ENTRY_MAX_PAYLOAD - sizeof(big_payload_tag),
+              static_cast<size_t>(max_len));
+
+    EXPECT_EQ(ret, max_len + sizeof(big_payload_tag));
+}
+
+TEST(liblog, dual_reader) {
+    struct logger_list *logger_list1;
+
+    // >25 messages due to liblog.__android_log_buf_print__concurrentXX above.
+    ASSERT_TRUE(NULL != (logger_list1 = android_logger_list_open(
+        LOG_ID_MAIN, O_RDONLY | O_NDELAY, 25, 0)));
+
+    struct logger_list *logger_list2;
+
+    if (NULL == (logger_list2 = android_logger_list_open(
+            LOG_ID_MAIN, O_RDONLY | O_NDELAY, 15, 0))) {
+        android_logger_list_close(logger_list1);
+        ASSERT_TRUE(NULL != logger_list2);
+    }
+
+    int count1 = 0;
+    bool done1 = false;
+    int count2 = 0;
+    bool done2 = false;
+
+    do {
+        log_msg log_msg;
+
+        if (!done1) {
+            if (android_logger_list_read(logger_list1, &log_msg) <= 0) {
+                done1 = true;
+            } else {
+                ++count1;
+            }
+        }
+
+        if (!done2) {
+            if (android_logger_list_read(logger_list2, &log_msg) <= 0) {
+                done2 = true;
+            } else {
+                ++count2;
+            }
+        }
+    } while ((!done1) || (!done2));
+
+    android_logger_list_close(logger_list1);
+    android_logger_list_close(logger_list2);
+
+    EXPECT_EQ(25, count1);
+    EXPECT_EQ(15, count2);
 }
 
 TEST(liblog, android_logger_get_) {
@@ -310,11 +603,11 @@
             continue;
         }
         struct logger * logger;
-        ASSERT_EQ(0, NULL == (logger = android_logger_open(logger_list, id)));
-        ASSERT_EQ(id, android_logger_get_id(logger));
-        ASSERT_LT(0, android_logger_get_log_size(logger));
-        ASSERT_LT(0, android_logger_get_log_readable_size(logger));
-        ASSERT_LT(0, android_logger_get_log_version(logger));
+        EXPECT_TRUE(NULL != (logger = android_logger_open(logger_list, id)));
+        EXPECT_EQ(id, android_logger_get_id(logger));
+        EXPECT_LT(0, android_logger_get_log_size(logger));
+        EXPECT_LT(0, android_logger_get_log_readable_size(logger));
+        EXPECT_LT(0, android_logger_get_log_version(logger));
     }
 
     android_logger_list_close(logger_list);
diff --git a/libpixelflinger/codeflinger/disassem.c b/libpixelflinger/codeflinger/disassem.c
index aeb8034..39dd614 100644
--- a/libpixelflinger/codeflinger/disassem.c
+++ b/libpixelflinger/codeflinger/disassem.c
@@ -301,19 +301,14 @@
 static void disassemble_printaddr(u_int address);
 
 u_int
-disasm(const disasm_interface_t *di, u_int loc, int altfmt)
+disasm(const disasm_interface_t *di, u_int loc, int __unused altfmt)
 {
 	const struct arm32_insn *i_ptr = &arm32_i[0];
-
-	u_int insn;
-	int matchp;
+	u_int insn = di->di_readword(loc);
+	int matchp = 0;
 	int branch;
 	char* f_ptr;
-	int fmt;
-
-	fmt = 0;
-	matchp = 0;
-	insn = di->di_readword(loc);
+	int fmt = 0;
 
 /*	di->di_printf("loc=%08x insn=%08x : ", loc, insn);*/
 
@@ -670,7 +665,7 @@
 }
 
 static void
-disasm_insn_ldcstc(const disasm_interface_t *di, u_int insn, u_int loc)
+disasm_insn_ldcstc(const disasm_interface_t *di, u_int insn, u_int __unused loc)
 {
 	if (((insn >> 8) & 0xf) == 1)
 		di->di_printf("f%d, ", (insn >> 12) & 0x07);
diff --git a/libpixelflinger/codeflinger/disassem.h b/libpixelflinger/codeflinger/disassem.h
index 02747cd..c7c60b6 100644
--- a/libpixelflinger/codeflinger/disassem.h
+++ b/libpixelflinger/codeflinger/disassem.h
@@ -49,8 +49,8 @@
 
 typedef struct {
 	u_int	(*di_readword)(u_int);
-	void	(*di_printaddr)(u_int);	
-	void	(*di_printf)(const char *, ...);
+	void	(*di_printaddr)(u_int);
+	int	(*di_printf)(const char *, ...);
 } disasm_interface_t;
 
 /* Prototypes for callable functions */
diff --git a/libsparse/include/sparse/sparse.h b/libsparse/include/sparse/sparse.h
index 17d085c..8b757d2 100644
--- a/libsparse/include/sparse/sparse.h
+++ b/libsparse/include/sparse/sparse.h
@@ -20,6 +20,10 @@
 #include <stdbool.h>
 #include <stdint.h>
 
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
 struct sparse_file;
 
 /**
@@ -273,4 +277,8 @@
  */
 extern void (*sparse_print_verbose)(const char *fmt, ...);
 
+#ifdef	__cplusplus
+}
+#endif
+
 #endif
diff --git a/libsuspend/autosuspend_earlysuspend.c b/libsuspend/autosuspend_earlysuspend.c
index 1df8c6a..2bece4c 100644
--- a/libsuspend/autosuspend_earlysuspend.c
+++ b/libsuspend/autosuspend_earlysuspend.c
@@ -75,13 +75,8 @@
     return err < 0 ? err : 0;
 }
 
-static void *earlysuspend_thread_func(void *arg)
+static void *earlysuspend_thread_func(void __unused *arg)
 {
-    char buf[80];
-    char wakeup_count[20];
-    int wakeup_count_len;
-    int ret;
-
     while (1) {
         if (wait_for_fb_sleep()) {
             ALOGE("Failed reading wait_for_fb_sleep, exiting earlysuspend thread\n");
diff --git a/libsysutils/EventLogTags.logtags b/libsysutils/EventLogTags.logtags
index 7aa5cad..713f8cd 100644
--- a/libsysutils/EventLogTags.logtags
+++ b/libsysutils/EventLogTags.logtags
@@ -1,5 +1,5 @@
 # See system/core/logcat/event.logtags for a description of the format of this file.
 
 # FrameworkListener dispatchCommand overflow
-78001 dispatchCommand_overflow
-65537 netlink_failure (uid|1)
+78001 exp_det_dispatchCommand_overflow
+65537 exp_det_netlink_failure (uid|1)
diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp
index a5ffda2..01ed54e 100644
--- a/libsysutils/src/FrameworkListener.cpp
+++ b/libsysutils/src/FrameworkListener.cpp
@@ -39,6 +39,11 @@
     init(socketName, false);
 }
 
+FrameworkListener::FrameworkListener(int sock) :
+                            SocketListener(sock, true) {
+    init(NULL, false);
+}
+
 void FrameworkListener::init(const char *socketName UNUSED, bool withSeq) {
     mCommands = new FrameworkCommandCollection();
     errorRate = 0;
diff --git a/libutils/BlobCache.cpp b/libutils/BlobCache.cpp
index 0fb1d8e..660917b 100644
--- a/libutils/BlobCache.cpp
+++ b/libutils/BlobCache.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "BlobCache"
 //#define LOG_NDEBUG 0
 
+#include <inttypes.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -54,18 +55,18 @@
 void BlobCache::set(const void* key, size_t keySize, const void* value,
         size_t valueSize) {
     if (mMaxKeySize < keySize) {
-        ALOGV("set: not caching because the key is too large: %d (limit: %d)",
+        ALOGV("set: not caching because the key is too large: %zu (limit: %zu)",
                 keySize, mMaxKeySize);
         return;
     }
     if (mMaxValueSize < valueSize) {
-        ALOGV("set: not caching because the value is too large: %d (limit: %d)",
+        ALOGV("set: not caching because the value is too large: %zu (limit: %zu)",
                 valueSize, mMaxValueSize);
         return;
     }
     if (mMaxTotalSize < keySize + valueSize) {
         ALOGV("set: not caching because the combined key/value size is too "
-                "large: %d (limit: %d)", keySize + valueSize, mMaxTotalSize);
+                "large: %zu (limit: %zu)", keySize + valueSize, mMaxTotalSize);
         return;
     }
     if (keySize == 0) {
@@ -94,15 +95,15 @@
                     continue;
                 } else {
                     ALOGV("set: not caching new key/value pair because the "
-                            "total cache size limit would be exceeded: %d "
-                            "(limit: %d)",
+                            "total cache size limit would be exceeded: %zu "
+                            "(limit: %zu)",
                             keySize + valueSize, mMaxTotalSize);
                     break;
                 }
             }
             mCacheEntries.add(CacheEntry(keyBlob, valueBlob));
             mTotalSize = newTotalSize;
-            ALOGV("set: created new cache entry with %d byte key and %d byte value",
+            ALOGV("set: created new cache entry with %zu byte key and %zu byte value",
                     keySize, valueSize);
         } else {
             // Update the existing cache entry.
@@ -116,14 +117,14 @@
                     continue;
                 } else {
                     ALOGV("set: not caching new value because the total cache "
-                            "size limit would be exceeded: %d (limit: %d)",
+                            "size limit would be exceeded: %zu (limit: %zu)",
                             keySize + valueSize, mMaxTotalSize);
                     break;
                 }
             }
             mCacheEntries.editItemAt(index).setValue(valueBlob);
             mTotalSize = newTotalSize;
-            ALOGV("set: updated existing cache entry with %d byte key and %d byte "
+            ALOGV("set: updated existing cache entry with %zu byte key and %zu byte "
                     "value", keySize, valueSize);
         }
         break;
@@ -133,7 +134,7 @@
 size_t BlobCache::get(const void* key, size_t keySize, void* value,
         size_t valueSize) {
     if (mMaxKeySize < keySize) {
-        ALOGV("get: not searching because the key is too large: %d (limit %d)",
+        ALOGV("get: not searching because the key is too large: %zu (limit %zu)",
                 keySize, mMaxKeySize);
         return 0;
     }
@@ -141,7 +142,7 @@
     CacheEntry dummyEntry(dummyKey, NULL);
     ssize_t index = mCacheEntries.indexOf(dummyEntry);
     if (index < 0) {
-        ALOGV("get: no cache entry found for key of size %d", keySize);
+        ALOGV("get: no cache entry found for key of size %zu", keySize);
         return 0;
     }
 
@@ -150,10 +151,10 @@
     sp<Blob> valueBlob(mCacheEntries[index].getValue());
     size_t valueBlobSize = valueBlob->getSize();
     if (valueBlobSize <= valueSize) {
-        ALOGV("get: copying %d bytes to caller's buffer", valueBlobSize);
+        ALOGV("get: copying %zu bytes to caller's buffer", valueBlobSize);
         memcpy(value, valueBlob->getData(), valueBlobSize);
     } else {
-        ALOGV("get: caller's buffer is too small for value: %d (needs %d)",
+        ALOGV("get: caller's buffer is too small for value: %zu (needs %zu)",
                 valueSize, valueBlobSize);
     }
     return valueBlobSize;
@@ -229,7 +230,7 @@
     }
     const Header* header = reinterpret_cast<const Header*>(buffer);
     if (header->mMagicNumber != blobCacheMagic) {
-        ALOGE("unflatten: bad magic number: %d", header->mMagicNumber);
+        ALOGE("unflatten: bad magic number: %" PRIu32, header->mMagicNumber);
         return BAD_VALUE;
     }
     if (header->mBlobCacheVersion != blobCacheVersion ||
diff --git a/libutils/FileMap.cpp b/libutils/FileMap.cpp
index 9ce370e..933e7aa 100644
--- a/libutils/FileMap.cpp
+++ b/libutils/FileMap.cpp
@@ -23,6 +23,7 @@
 #include <utils/FileMap.h>
 #include <utils/Log.h>
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 
@@ -39,37 +40,32 @@
 
 /*static*/ long FileMap::mPageSize = -1;
 
-
-/*
- * Constructor.  Create an empty object.
- */
+// Constructor.  Create an empty object.
 FileMap::FileMap(void)
     : mRefCount(1), mFileName(NULL), mBasePtr(NULL), mBaseLength(0),
       mDataPtr(NULL), mDataLength(0)
 {
 }
 
-/*
- * Destructor.
- */
+// Destructor.
 FileMap::~FileMap(void)
 {
     assert(mRefCount == 0);
 
-    //printf("+++ removing FileMap %p %u\n", mDataPtr, mDataLength);
+    //printf("+++ removing FileMap %p %zu\n", mDataPtr, mDataLength);
 
     mRefCount = -100;       // help catch double-free
     if (mFileName != NULL) {
         free(mFileName);
     }
-#ifdef HAVE_POSIX_FILEMAP    
+#ifdef HAVE_POSIX_FILEMAP
     if (mBasePtr && munmap(mBasePtr, mBaseLength) != 0) {
-        ALOGD("munmap(%p, %d) failed\n", mBasePtr, (int) mBaseLength);
+        ALOGD("munmap(%p, %zu) failed\n", mBasePtr, mBaseLength);
     }
 #endif
 #ifdef HAVE_WIN32_FILEMAP
     if (mBasePtr && UnmapViewOfFile(mBasePtr) == 0) {
-        ALOGD("UnmapViewOfFile(%p) failed, error = %ld\n", mBasePtr,
+        ALOGD("UnmapViewOfFile(%p) failed, error = %" PRId32 "\n", mBasePtr,
               GetLastError() );
     }
     if (mFileMapping != INVALID_HANDLE_VALUE) {
@@ -80,14 +76,12 @@
 }
 
 
-/*
- * Create a new mapping on an open file.
- *
- * Closing the file descriptor does not unmap the pages, so we don't
- * claim ownership of the fd.
- *
- * Returns "false" on failure.
- */
+// Create a new mapping on an open file.
+//
+// Closing the file descriptor does not unmap the pages, so we don't
+// claim ownership of the fd.
+//
+// Returns "false" on failure.
 bool FileMap::create(const char* origFileName, int fd, off64_t offset, size_t length,
         bool readOnly)
 {
@@ -98,32 +92,32 @@
 
     if (mPageSize == -1) {
         SYSTEM_INFO  si;
-        
+
         GetSystemInfo( &si );
         mPageSize = si.dwAllocationGranularity;
     }
 
     DWORD  protect = readOnly ? PAGE_READONLY : PAGE_READWRITE;
-    
+
     mFileHandle  = (HANDLE) _get_osfhandle(fd);
     mFileMapping = CreateFileMapping( mFileHandle, NULL, protect, 0, 0, NULL);
     if (mFileMapping == NULL) {
-        ALOGE("CreateFileMapping(%p, %lx) failed with error %ld\n",
+        ALOGE("CreateFileMapping(%p, %" PRIx32 ") failed with error %" PRId32 "\n",
               mFileHandle, protect, GetLastError() );
         return false;
     }
-    
+
     adjust    = offset % mPageSize;
     adjOffset = offset - adjust;
     adjLength = length + adjust;
-    
-    mBasePtr = MapViewOfFile( mFileMapping, 
+
+    mBasePtr = MapViewOfFile( mFileMapping,
                               readOnly ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS,
                               0,
                               (DWORD)(adjOffset),
                               adjLength );
     if (mBasePtr == NULL) {
-        ALOGE("MapViewOfFile(%ld, %ld) failed with error %ld\n",
+        ALOGE("MapViewOfFile(%" PRId64 ", %zu) failed with error %" PRId32 "\n",
               adjOffset, adjLength, GetLastError() );
         CloseHandle(mFileMapping);
         mFileMapping = INVALID_HANDLE_VALUE;
@@ -142,7 +136,7 @@
     assert(offset >= 0);
     assert(length > 0);
 
-    /* init on first use */
+    // init on first use
     if (mPageSize == -1) {
 #if NOT_USING_KLIBC
         mPageSize = sysconf(_SC_PAGESIZE);
@@ -151,7 +145,7 @@
             return false;
         }
 #else
-        /* this holds for Linux, Darwin, Cygwin, and doesn't pain the ARM */
+        // this holds for Linux, Darwin, Cygwin, and doesn't pain the ARM
         mPageSize = 4096;
 #endif
     }
@@ -168,19 +162,19 @@
 
     ptr = mmap(NULL, adjLength, prot, flags, fd, adjOffset);
     if (ptr == MAP_FAILED) {
-    	// Cygwin does not seem to like file mapping files from an offset.
-    	// So if we fail, try again with offset zero
-    	if (adjOffset > 0) {
-    		adjust = offset;
-    		goto try_again;
-    	}
-    
-        ALOGE("mmap(%ld,%ld) failed: %s\n",
-            (long) adjOffset, (long) adjLength, strerror(errno));
+        // Cygwin does not seem to like file mapping files from an offset.
+        // So if we fail, try again with offset zero
+        if (adjOffset > 0) {
+            adjust = offset;
+            goto try_again;
+        }
+
+        ALOGE("mmap(%" PRId64 ",%zu) failed: %s\n",
+            adjOffset, adjLength, strerror(errno));
         return false;
     }
     mBasePtr = ptr;
-#endif /* HAVE_POSIX_FILEMAP */
+#endif // HAVE_POSIX_FILEMAP
 
     mFileName = origFileName != NULL ? strdup(origFileName) : NULL;
     mBaseLength = adjLength;
@@ -190,15 +184,13 @@
 
     assert(mBasePtr != NULL);
 
-    ALOGV("MAP: base %p/%d data %p/%d\n",
-        mBasePtr, (int) mBaseLength, mDataPtr, (int) mDataLength);
+    ALOGV("MAP: base %p/%zu data %p/%zu\n",
+        mBasePtr, mBaseLength, mDataPtr, mDataLength);
 
     return true;
 }
 
-/*
- * Provide guidance to the system.
- */
+// Provide guidance to the system.
 int FileMap::advise(MapAdvice advice)
 {
 #if HAVE_MADVISE
@@ -220,6 +212,6 @@
         ALOGW("madvise(%d) failed: %s\n", sysAdvice, strerror(errno));
     return cc;
 #else
-	return -1;
+    return -1;
 #endif // HAVE_MADVISE
 }
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index f398a82..385c226 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -109,7 +109,7 @@
                 char inc = refs->ref >= 0 ? '+' : '-';
                 ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
 #if DEBUG_REFS_CALLSTACK_ENABLED
-                refs->stack.dump(LOG_TAG);
+                refs->stack.log(LOG_TAG);
 #endif
                 refs = refs->next;
             }
@@ -123,7 +123,7 @@
                 char inc = refs->ref >= 0 ? '+' : '-';
                 ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
 #if DEBUG_REFS_CALLSTACK_ENABLED
-                refs->stack.dump(LOG_TAG);
+                refs->stack.log(LOG_TAG);
 #endif
                 refs = refs->next;
             }
diff --git a/libutils/Timers.cpp b/libutils/Timers.cpp
index 5293cd2..a431e92 100644
--- a/libutils/Timers.cpp
+++ b/libutils/Timers.cpp
@@ -34,7 +34,7 @@
 
 nsecs_t systemTime(int clock)
 {
-#if defined(HAVE_POSIX_CLOCKS)
+#if defined(HAVE_ANDROID_OS)
     static const clockid_t clocks[] = {
             CLOCK_REALTIME,
             CLOCK_MONOTONIC,
@@ -47,7 +47,9 @@
     clock_gettime(clocks[clock], &t);
     return nsecs_t(t.tv_sec)*1000000000LL + t.tv_nsec;
 #else
-    // we don't support the clocks here.
+    // Clock support varies widely across hosts. Mac OS doesn't support
+    // posix clocks, older glibcs don't support CLOCK_BOOTTIME and Windows
+    // is windows.
     struct timeval t;
     t.tv_sec = t.tv_usec = 0;
     gettimeofday(&t, NULL);
diff --git a/libutils/tests/BasicHashtable_test.cpp b/libutils/tests/BasicHashtable_test.cpp
index 7dcf750..a61b1e1 100644
--- a/libutils/tests/BasicHashtable_test.cpp
+++ b/libutils/tests/BasicHashtable_test.cpp
@@ -397,7 +397,7 @@
         const SimpleEntry& entry = h.entryAt(index);
         ASSERT_GE(entry.key, 0);
         ASSERT_LT(entry.key, N);
-        ASSERT_EQ(false, set[entry.key]);
+        ASSERT_FALSE(set[entry.key]);
         ASSERT_EQ(entry.key * 10, entry.value);
 
         set[entry.key] = true;
diff --git a/libutils/tests/LruCache_test.cpp b/libutils/tests/LruCache_test.cpp
index e573952..bcbea32 100644
--- a/libutils/tests/LruCache_test.cpp
+++ b/libutils/tests/LruCache_test.cpp
@@ -184,7 +184,7 @@
 
     for (size_t i = 0; i < kNumKeys; i++) {
         strings[i] = (char *)malloc(16);
-        sprintf(strings[i], "%d", i);
+        sprintf(strings[i], "%zu", i);
     }
 
     srandom(12345);
diff --git a/logcat/Android.mk b/logcat/Android.mk
index dd15cb3..b5e27eb 100644
--- a/logcat/Android.mk
+++ b/logcat/Android.mk
@@ -3,10 +3,6 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-ifneq ($(filter userdebug eng,$(TARGET_BUILD_VARIANT)),)
-LOCAL_CFLAGS += -DUSERDEBUG_BUILD=1
-endif
-
 LOCAL_SRC_FILES:= logcat.cpp event.logtags
 
 LOCAL_SHARED_LIBRARIES := liblog
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 5a80efe..995a42e 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -227,22 +227,20 @@
                     "  -T <count>      print only the most recent <count> lines (does not imply -d)\n"
                     "  -g              get the size of the log's ring buffer and exit\n"
                     "  -b <buffer>     Request alternate ring buffer, 'main', 'system', 'radio',\n"
-                    "                  'events' or 'all'. Multiple -b parameters are allowed and\n"
-                    "                  results are interleaved. The default is -b main -b system.\n"
+                    "                  'events', 'crash' or 'all'. Multiple -b parameters are\n"
+                    "                  allowed and results are interleaved. The default is\n"
+                    "                  -b main -b system -b crash.\n"
                     "  -B              output the log in binary.\n"
-                    "  -S              output statistics.\n");
-
-#ifdef USERDEBUG_BUILD
-
-    fprintf(stderr, "--------------------- eng & userdebug builds only ---------------------------\n"
-                    "  -G <count>      set size of log's ring buffer and exit\n"
-                    "  -p              output prune white and ~black list\n"
-                    "  -P '<list> ...' set prune white and ~black list; UID, /PID or !(worst UID)\n"
-                    "                  default is ~!, prune worst UID.\n"
-                    "-----------------------------------------------------------------------------\n"
-    );
-
-#endif
+                    "  -S              output statistics.\n"
+                    "  -G <size>       set size of log ring buffer, may suffix with K or M.\n"
+                    "  -p              print prune white and ~black list. Service is specified as\n"
+                    "                  UID, UID/PID or /PID. Weighed for quicker pruning if prefix\n"
+                    "                  with ~, otherwise weighed for longevity if unadorned. All\n"
+                    "                  other pruning activity is oldest first. Special case ~!\n"
+                    "                  represents an automatic quicker pruning for the noisiest\n"
+                    "                  UID as determined by the current statistics.\n"
+                    "  -P '<list> ...' set prune white and ~black list, using same format as\n"
+                    "                  printed above. Must be quoted.\n");
 
     fprintf(stderr,"\nfilterspecs are a series of \n"
                    "  <tag>[:priority]\n\n"
@@ -291,11 +289,9 @@
     int hasSetLogFormat = 0;
     int clearLog = 0;
     int getLogSize = 0;
-#ifdef USERDEBUG_BUILD
     unsigned long setLogSize = 0;
     int getPruneList = 0;
     char *setPruneList = NULL;
-#endif
     int printStatistics = 0;
     int mode = O_RDONLY;
     const char *forceFilters = NULL;
@@ -323,13 +319,7 @@
     for (;;) {
         int ret;
 
-        ret = getopt(argc, argv,
-#ifdef USERDEBUG_BUILD
-            "cdt:T:gG:sQf:r::n:v:b:BSpP:"
-#else
-            "cdt:T:gsQf:r::n:v:b:BS"
-#endif
-        );
+        ret = getopt(argc, argv, "cdt:T:gG:sQf:r::n:v:b:BSpP:");
 
         if (ret < 0) {
             break;
@@ -386,8 +376,6 @@
                 getLogSize = 1;
             break;
 
-#ifdef USERDEBUG_BUILD
-
             case 'G': {
                 // would use atol if not for the multiplier
                 char *cp = optarg;
@@ -433,8 +421,6 @@
                 setPruneList = optarg;
             break;
 
-#endif
-
             case 'b': {
                 if (strcmp(optarg, "all") == 0) {
                     while (devices) {
@@ -462,10 +448,17 @@
                     if (android_name_to_log_id("events") == LOG_ID_EVENTS) {
                         dev->next = new log_device_t("events", true, 'e');
                         if (dev->next) {
+                            dev = dev->next;
                             android::g_devCount++;
                             needBinary = true;
                         }
                     }
+                    if (android_name_to_log_id("crash") == LOG_ID_CRASH) {
+                        dev->next = new log_device_t("crash", false, 'c');
+                        if (dev->next) {
+                            android::g_devCount++;
+                        }
+                    }
                     break;
                 }
 
@@ -622,6 +615,14 @@
             devices->next = new log_device_t("system", false, 's');
             android::g_devCount++;
         }
+        if (android_name_to_log_id("crash") == LOG_ID_CRASH) {
+            if (devices->next) {
+                devices->next->next = new log_device_t("crash", false, 'c');
+            } else {
+                devices->next = new log_device_t("crash", false, 'c');
+            }
+            android::g_devCount++;
+        }
     }
 
     if (android::g_logRotateSizeKBytes != 0
@@ -704,15 +705,11 @@
             }
         }
 
-#ifdef USERDEBUG_BUILD
-
         if (setLogSize && android_logger_set_log_size(dev->logger, setLogSize)) {
             perror("failed to set the log size");
             exit(EXIT_FAILURE);
         }
 
-#endif
-
         if (getLogSize) {
             long size, readable;
 
@@ -737,8 +734,6 @@
         dev = dev->next;
     }
 
-#ifdef USERDEBUG_BUILD
-
     if (setPruneList) {
         size_t len = strlen(setPruneList) + 32; // margin to allow rc
         char *buf = (char *) malloc(len);
@@ -753,30 +748,18 @@
         }
     }
 
-#endif
-
-    if (
-#ifdef USERDEBUG_BUILD
-        printStatistics || getPruneList
-#else
-        printStatistics
-#endif
-    ) {
+    if (printStatistics || getPruneList) {
         size_t len = 8192;
         char *buf;
 
         for(int retry = 32;
                 (retry >= 0) && ((buf = new char [len]));
                 delete [] buf, --retry) {
-#ifdef USERDEBUG_BUILD
             if (getPruneList) {
                 android_logger_get_prune_list(logger_list, buf, len);
             } else {
                 android_logger_get_statistics(logger_list, buf, len);
             }
-#else
-            android_logger_get_statistics(logger_list, buf, len);
-#endif
             buf[len-1] = '\0';
             size_t ret = atol(buf) + 1;
             if (ret < 4) {
@@ -824,11 +807,9 @@
     if (getLogSize) {
         exit(0);
     }
-#ifdef USERDEBUG_BUILD
     if (setLogSize || setPruneList) {
         exit(0);
     }
-#endif
     if (clearLog) {
         exit(0);
     }
diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk
index 733af31..d42b3d0 100644
--- a/logcat/tests/Android.mk
+++ b/logcat/tests/Android.mk
@@ -30,10 +30,6 @@
     -Werror \
     -fno-builtin
 
-ifneq ($(filter userdebug eng,$(TARGET_BUILD_VARIANT)),)
-test_c_flags += -DUSERDEBUG_BUILD=1
-endif
-
 test_src_files := \
     logcat_test.cpp \
 
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 958948b..b07cc8b 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
+#include <ctype.h>
 #include <signal.h>
 #include <stdio.h>
+#include <string.h>
+
 #include <gtest/gtest.h>
 #include <log/log.h>
 #include <log/logger.h>
@@ -41,7 +44,7 @@
 TEST(logcat, sorted_order) {
     FILE *fp;
 
-    ASSERT_EQ(0, NULL == (fp = popen(
+    ASSERT_TRUE(NULL != (fp = popen(
       "logcat -v time -b radio -b events -b system -b main -d 2>/dev/null",
       "r")));
 
@@ -92,34 +95,49 @@
         }
     } last(NULL);
 
+    char *last_buffer = NULL;
     char buffer[5120];
 
     int count = 0;
+    int next_lt_last = 0;
 
     while (fgets(buffer, sizeof(buffer), fp)) {
         if (!strncmp(begin, buffer, sizeof(begin) - 1)) {
             continue;
         }
         if (!last.valid()) {
+            free(last_buffer);
+            last_buffer = strdup(buffer);
             last.init(buffer);
         }
         timestamp next(buffer);
-        ASSERT_EQ(0, next < last);
+        if (next < last) {
+            if (last_buffer) {
+                fprintf(stderr, "<%s", last_buffer);
+            }
+            fprintf(stderr, ">%s", buffer);
+            ++next_lt_last;
+        }
         if (next.valid()) {
+            free(last_buffer);
+            last_buffer = strdup(buffer);
             last.init(buffer);
         }
         ++count;
     }
+    free(last_buffer);
 
     pclose(fp);
 
-    ASSERT_LT(100, count);
+    EXPECT_EQ(0, next_lt_last);
+
+    EXPECT_LT(100, count);
 }
 
 TEST(logcat, buckets) {
     FILE *fp;
 
-    ASSERT_EQ(0, NULL == (fp = popen(
+    ASSERT_TRUE(NULL != (fp = popen(
       "logcat -b radio -b events -b system -b main -d 2>/dev/null",
       "r")));
 
@@ -141,15 +159,15 @@
 
     pclose(fp);
 
-    ASSERT_EQ(15, ids);
+    EXPECT_EQ(15, ids);
 
-    ASSERT_EQ(4, count);
+    EXPECT_EQ(4, count);
 }
 
 TEST(logcat, tail_3) {
     FILE *fp;
 
-    ASSERT_EQ(0, NULL == (fp = popen(
+    ASSERT_TRUE(NULL != (fp = popen(
       "logcat -v long -b radio -b events -b system -b main -t 3 2>/dev/null",
       "r")));
 
@@ -173,7 +191,7 @@
 TEST(logcat, tail_10) {
     FILE *fp;
 
-    ASSERT_EQ(0, NULL == (fp = popen(
+    ASSERT_TRUE(NULL != (fp = popen(
       "logcat -v long -b radio -b events -b system -b main -t 10 2>/dev/null",
       "r")));
 
@@ -197,7 +215,7 @@
 TEST(logcat, tail_100) {
     FILE *fp;
 
-    ASSERT_EQ(0, NULL == (fp = popen(
+    ASSERT_TRUE(NULL != (fp = popen(
       "logcat -v long -b radio -b events -b system -b main -t 100 2>/dev/null",
       "r")));
 
@@ -221,7 +239,7 @@
 TEST(logcat, tail_1000) {
     FILE *fp;
 
-    ASSERT_EQ(0, NULL == (fp = popen(
+    ASSERT_TRUE(NULL != (fp = popen(
       "logcat -v long -b radio -b events -b system -b main -t 1000 2>/dev/null",
       "r")));
 
@@ -242,6 +260,72 @@
     ASSERT_EQ(1000, count);
 }
 
+TEST(logcat, tail_time) {
+    FILE *fp;
+
+    ASSERT_TRUE(NULL != (fp = popen("logcat -v long -b all -t 10 2>&1", "r")));
+
+    char buffer[5120];
+    char *last_timestamp = NULL;
+    char *first_timestamp = NULL;
+    int count = 0;
+    const unsigned int time_length = 18;
+    const unsigned int time_offset = 2;
+
+    while (fgets(buffer, sizeof(buffer), fp)) {
+        if ((buffer[0] == '[') && (buffer[1] == ' ')
+         && isdigit(buffer[time_offset]) && isdigit(buffer[time_offset + 1])
+         && (buffer[time_offset + 2] == '-')) {
+            ++count;
+            buffer[time_length + time_offset] = '\0';
+            if (!first_timestamp) {
+                first_timestamp = strdup(buffer + time_offset);
+            }
+            free(last_timestamp);
+            last_timestamp = strdup(buffer + time_offset);
+        }
+    }
+    pclose(fp);
+
+    EXPECT_EQ(10, count);
+    EXPECT_TRUE(last_timestamp != NULL);
+    EXPECT_TRUE(first_timestamp != NULL);
+
+    snprintf(buffer, sizeof(buffer), "logcat -v long -b all -t '%s' 2>&1",
+             first_timestamp);
+    ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
+
+    int second_count = 0;
+    int last_timestamp_count = -1;
+
+    while (fgets(buffer, sizeof(buffer), fp)) {
+        if ((buffer[0] == '[') && (buffer[1] == ' ')
+         && isdigit(buffer[time_offset]) && isdigit(buffer[time_offset + 1])
+         && (buffer[time_offset + 2] == '-')) {
+            ++second_count;
+            buffer[time_length + time_offset] = '\0';
+            if (first_timestamp) {
+                // we can get a transitory *extremely* rare failure if hidden
+                // underneath the time is *exactly* XX-XX XX:XX:XX.XXX000000
+                EXPECT_STREQ(buffer + time_offset, first_timestamp);
+                free(first_timestamp);
+                first_timestamp = NULL;
+            }
+            if (!strcmp(buffer + time_offset, last_timestamp)) {
+                last_timestamp_count = second_count;
+            }
+        }
+    }
+    pclose(fp);
+
+    free(last_timestamp);
+    last_timestamp = NULL;
+
+    EXPECT_TRUE(first_timestamp == NULL);
+    EXPECT_LE(count, second_count);
+    EXPECT_LE(count, last_timestamp_count);
+}
+
 TEST(logcat, End_to_End) {
     pid_t pid = getpid();
 
@@ -250,7 +334,7 @@
     ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
 
     FILE *fp;
-    ASSERT_EQ(0, NULL == (fp = popen(
+    ASSERT_TRUE(NULL != (fp = popen(
       "logcat -b events -t 100 2>/dev/null",
       "r")));
 
@@ -281,7 +365,7 @@
 TEST(logcat, get_) {
     FILE *fp;
 
-    ASSERT_EQ(0, NULL == (fp = popen(
+    ASSERT_TRUE(NULL != (fp = popen(
       "logcat -b radio -b events -b system -b main -g 2>/dev/null",
       "r")));
 
@@ -329,7 +413,7 @@
 
     v &= 0xFFFFFFFFFFFAFFFFULL;
 
-    ASSERT_EQ(0, NULL == (fp = popen(
+    ASSERT_TRUE(NULL != (fp = popen(
       "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
       " logcat -b events 2>&1",
       "r")));
@@ -372,9 +456,9 @@
 
     pclose(fp);
 
-    ASSERT_LE(2, count);
+    EXPECT_LE(2, count);
 
-    ASSERT_EQ(1, signals);
+    EXPECT_EQ(1, signals);
 }
 
 static void caught_blocking_tail(int signum)
@@ -398,7 +482,7 @@
 
     v &= 0xFFFAFFFFFFFFFFFFULL;
 
-    ASSERT_EQ(0, NULL == (fp = popen(
+    ASSERT_TRUE(NULL != (fp = popen(
       "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
       " logcat -b events -T 5 2>&1",
       "r")));
@@ -443,9 +527,9 @@
 
     pclose(fp);
 
-    ASSERT_LE(2, count);
+    EXPECT_LE(2, count);
 
-    ASSERT_EQ(1, signals);
+    EXPECT_EQ(1, signals);
 }
 
 static void caught_blocking_clear(int signum)
@@ -467,7 +551,7 @@
 
     // This test is racey; an event occurs between clear and dump.
     // We accept that we will get a false positive, but never a false negative.
-    ASSERT_EQ(0, NULL == (fp = popen(
+    ASSERT_TRUE(NULL != (fp = popen(
       "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
       " logcat -b events -c 2>&1 ;"
       " logcat -b events 2>&1",
@@ -520,12 +604,11 @@
 
     pclose(fp);
 
-    ASSERT_LE(1, count);
+    EXPECT_LE(1, count);
 
-    ASSERT_EQ(1, signals);
+    EXPECT_EQ(1, signals);
 }
 
-#ifdef USERDEBUG_BUILD
 static bool get_white_black(char **list) {
     FILE *fp;
 
@@ -601,33 +684,23 @@
     static const char adjustment[] = "~! 300/20 300/25 2000 ~1000/5 ~1000/30";
     ASSERT_EQ(true, set_white_black(adjustment));
     ASSERT_EQ(true, get_white_black(&adjust));
-    if (strcmp(adjustment, adjust)) {
-        fprintf(stderr, "ERROR: '%s' != '%s'\n", adjustment, adjust);
-    }
-    ASSERT_STREQ(adjustment, adjust);
+    EXPECT_STREQ(adjustment, adjust);
     free(adjust);
     adjust = NULL;
 
     static const char adjustment2[] = "300/20 300/21 2000 ~1000";
     ASSERT_EQ(true, set_white_black(adjustment2));
     ASSERT_EQ(true, get_white_black(&adjust));
-    if (strcmp(adjustment2, adjust)) {
-        fprintf(stderr, "ERROR: '%s' != '%s'\n", adjustment2, adjust);
-    }
-    ASSERT_STREQ(adjustment2, adjust);
+    EXPECT_STREQ(adjustment2, adjust);
     free(adjust);
     adjust = NULL;
 
     ASSERT_EQ(true, set_white_black(list));
     ASSERT_EQ(true, get_white_black(&adjust));
-    if (strcmp(list, adjust)) {
-        fprintf(stderr, "ERROR: '%s' != '%s'\n", list, adjust);
-    }
-    ASSERT_STREQ(list, adjust);
+    EXPECT_STREQ(list, adjust);
     free(adjust);
     adjust = NULL;
 
     free(list);
     list = NULL;
 }
-#endif // USERDEBUG_BUILD
diff --git a/logd/Android.mk b/logd/Android.mk
index b0bc746..0235478 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -4,10 +4,6 @@
 
 LOCAL_MODULE:= logd
 
-ifneq ($(filter userdebug eng,$(TARGET_BUILD_VARIANT)),)
-LOCAL_CFLAGS += -DUSERDEBUG_BUILD=1
-endif
-
 LOCAL_SRC_FILES := \
     main.cpp \
     LogCommand.cpp \
@@ -19,7 +15,9 @@
     LogBufferElement.cpp \
     LogTimes.cpp \
     LogStatistics.cpp \
-    LogWhiteBlackList.cpp
+    LogWhiteBlackList.cpp \
+    libaudit.c \
+    LogAudit.cpp
 
 LOCAL_SHARED_LIBRARIES := \
     libsysutils \
@@ -30,3 +28,5 @@
 LOCAL_MODULE_TAGS := optional
 
 include $(BUILD_EXECUTABLE)
+
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 12b10ca..1f3fd0e 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -21,9 +21,11 @@
 #include <netinet/in.h>
 #include <string.h>
 #include <stdlib.h>
+#include <sys/prctl.h>
 #include <sys/socket.h>
 #include <sys/types.h>
 
+#include <cutils/sockets.h>
 #include <private/android_filesystem_config.h>
 #include <sysutils/SocketClient.h>
 
@@ -32,20 +34,16 @@
 
 CommandListener::CommandListener(LogBuffer *buf, LogReader * /*reader*/,
                                  LogListener * /*swl*/)
-        : FrameworkListener("logd")
+        : FrameworkListener(getLogSocket())
         , mBuf(*buf) {
     // registerCmd(new ShutdownCmd(buf, writer, swl));
     registerCmd(new ClearCmd(buf));
     registerCmd(new GetBufSizeCmd(buf));
-#ifdef USERDEBUG_BUILD
     registerCmd(new SetBufSizeCmd(buf));
-#endif
     registerCmd(new GetBufSizeUsedCmd(buf));
     registerCmd(new GetStatisticsCmd(buf));
-#ifdef USERDEBUG_BUILD
     registerCmd(new SetPruneListCmd(buf));
     registerCmd(new GetPruneListCmd(buf));
-#endif
 }
 
 CommandListener::ShutdownCmd::ShutdownCmd(LogBuffer *buf, LogReader *reader,
@@ -69,8 +67,13 @@
         , mBuf(*buf)
 { }
 
+static void setname() {
+    prctl(PR_SET_NAME, "logd.control");
+}
+
 int CommandListener::ClearCmd::runCommand(SocketClient *cli,
                                          int argc, char **argv) {
+    setname();
     if (!clientHasLogCredentials(cli)) {
         cli->sendMsg("Permission Denied");
         return 0;
@@ -99,6 +102,7 @@
 
 int CommandListener::GetBufSizeCmd::runCommand(SocketClient *cli,
                                          int argc, char **argv) {
+    setname();
     if (argc < 2) {
         cli->sendMsg("Missing Argument");
         return 0;
@@ -117,8 +121,6 @@
     return 0;
 }
 
-#ifdef USERDEBUG_BUILD
-
 CommandListener::SetBufSizeCmd::SetBufSizeCmd(LogBuffer *buf)
         : LogCommand("setLogSize")
         , mBuf(*buf)
@@ -126,6 +128,7 @@
 
 int CommandListener::SetBufSizeCmd::runCommand(SocketClient *cli,
                                          int argc, char **argv) {
+    setname();
     if (!clientHasLogCredentials(cli)) {
         cli->sendMsg("Permission Denied");
         return 0;
@@ -152,8 +155,6 @@
     return 0;
 }
 
-#endif // USERDEBUG_BUILD
-
 CommandListener::GetBufSizeUsedCmd::GetBufSizeUsedCmd(LogBuffer *buf)
         : LogCommand("getLogSizeUsed")
         , mBuf(*buf)
@@ -161,6 +162,7 @@
 
 int CommandListener::GetBufSizeUsedCmd::runCommand(SocketClient *cli,
                                          int argc, char **argv) {
+    setname();
     if (argc < 2) {
         cli->sendMsg("Missing Argument");
         return 0;
@@ -204,6 +206,7 @@
 
 int CommandListener::GetStatisticsCmd::runCommand(SocketClient *cli,
                                          int argc, char **argv) {
+    setname();
     uid_t uid = cli->getUid();
     gid_t gid = cli->getGid();
     if (clientHasLogCredentials(cli)) {
@@ -236,8 +239,6 @@
     return 0;
 }
 
-#ifdef USERDEBUG_BUILD
-
 CommandListener::GetPruneListCmd::GetPruneListCmd(LogBuffer *buf)
         : LogCommand("getPruneList")
         , mBuf(*buf)
@@ -245,6 +246,7 @@
 
 int CommandListener::GetPruneListCmd::runCommand(SocketClient *cli,
                                          int /*argc*/, char ** /*argv*/) {
+    setname();
     char *buf = NULL;
     mBuf.formatPrune(&buf);
     if (!buf) {
@@ -264,6 +266,7 @@
 
 int CommandListener::SetPruneListCmd::runCommand(SocketClient *cli,
                                          int argc, char **argv) {
+    setname();
     if (!clientHasLogCredentials(cli)) {
         cli->sendMsg("Permission Denied");
         return 0;
@@ -294,4 +297,15 @@
     return 0;
 }
 
-#endif // USERDEBUG_BUILD
+int CommandListener::getLogSocket() {
+    static const char socketName[] = "logd";
+    int sock = android_get_control_socket(socketName);
+
+    if (sock < 0) {
+        sock = socket_local_server(socketName,
+                                   ANDROID_SOCKET_NAMESPACE_RESERVED,
+                                   SOCK_STREAM);
+    }
+
+    return sock;
+}
diff --git a/logd/CommandListener.h b/logd/CommandListener.h
index de1dcb9..cd1c306 100644
--- a/logd/CommandListener.h
+++ b/logd/CommandListener.h
@@ -31,6 +31,8 @@
     virtual ~CommandListener() {}
 
 private:
+    static int getLogSocket();
+
     class ShutdownCmd : public LogCommand {
         LogBuffer &mBuf;
         LogReader &mReader;
@@ -53,15 +55,11 @@
 
     LogBufferCmd(Clear)
     LogBufferCmd(GetBufSize)
-#ifdef USERDEBUG_BUILD
     LogBufferCmd(SetBufSize)
-#endif
     LogBufferCmd(GetBufSizeUsed)
     LogBufferCmd(GetStatistics)
-#ifdef USERDEBUG_BUILD
     LogBufferCmd(GetPruneList)
     LogBufferCmd(SetPruneList)
-#endif
 };
 
 #endif
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
new file mode 100644
index 0000000..add0f0e
--- /dev/null
+++ b/logd/LogAudit.cpp
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2014 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 <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <sys/klog.h>
+#include <sys/prctl.h>
+#include <sys/uio.h>
+
+#include "libaudit.h"
+#include "LogAudit.h"
+
+LogAudit::LogAudit(LogBuffer *buf, LogReader *reader, int fdDmsg)
+        : SocketListener(getLogSocket(), false)
+        , logbuf(buf)
+        , reader(reader)
+        , fdDmesg(-1) {
+    logDmesg();
+    fdDmesg = fdDmsg;
+}
+
+bool LogAudit::onDataAvailable(SocketClient *cli) {
+    prctl(PR_SET_NAME, "logd.auditd");
+
+    struct audit_message rep;
+
+    rep.nlh.nlmsg_type = 0;
+    rep.nlh.nlmsg_len = 0;
+    rep.data[0] = '\0';
+
+    if (audit_get_reply(cli->getSocket(), &rep, GET_REPLY_BLOCKING, 0) < 0) {
+        SLOGE("Failed on audit_get_reply with error: %s", strerror(errno));
+        return false;
+    }
+
+    logPrint("type=%d %.*s", rep.nlh.nlmsg_type, rep.nlh.nlmsg_len, rep.data);
+
+    return true;
+}
+
+#define AUDIT_LOG_ID   LOG_ID_MAIN
+#define AUDIT_LOG_PRIO ANDROID_LOG_WARN
+
+int LogAudit::logPrint(const char *fmt, ...) {
+    if (fmt == NULL) {
+        return -EINVAL;
+    }
+
+    va_list args;
+
+    char *str = NULL;
+    va_start(args, fmt);
+    int rc = vasprintf(&str, fmt, args);
+    va_end(args);
+
+    if (rc < 0) {
+        return rc;
+    }
+
+    if (fdDmesg >= 0) {
+        struct iovec iov[2];
+
+        iov[0].iov_base = str;
+        iov[0].iov_len = strlen(str);
+        iov[1].iov_base = const_cast<char *>("\n");
+        iov[1].iov_len = 1;
+
+        writev(fdDmesg, iov, sizeof(iov) / sizeof(iov[0]));
+    }
+
+    pid_t pid = getpid();
+    pid_t tid = gettid();
+    uid_t uid = getuid();
+    log_time now;
+
+    static const char audit_str[] = " audit(";
+    char *timeptr = strstr(str, audit_str);
+    char *cp;
+    if (timeptr
+            && ((cp = now.strptime(timeptr + sizeof(audit_str) - 1, "%s.%q")))
+            && (*cp == ':')) {
+        memcpy(timeptr + sizeof(audit_str) - 1, "0.0", 3);
+        strcpy(timeptr + sizeof(audit_str) - 1 + 3, cp);
+    } else {
+        now.strptime("", ""); // side effect of setting CLOCK_REALTIME
+    }
+
+    static const char pid_str[] = " pid=";
+    char *pidptr = strstr(str, pid_str);
+    if (pidptr && isdigit(pidptr[sizeof(pid_str) - 1])) {
+        cp = pidptr + sizeof(pid_str) - 1;
+        pid = 0;
+        while (isdigit(*cp)) {
+            pid = (pid * 10) + (*cp - '0');
+            ++cp;
+        }
+        tid = pid;
+        uid = logbuf->pidToUid(pid);
+        strcpy(pidptr, cp);
+    }
+
+    static const char comm_str[] = " comm=\"";
+    char *comm = strstr(str, comm_str);
+    if (comm) {
+        cp = comm;
+        comm += sizeof(comm_str) - 1;
+        char *ecomm = strchr(comm, '"');
+        if (ecomm) {
+            *ecomm = '\0';
+        }
+        comm = strdup(comm);
+        if (ecomm) {
+            strcpy(cp, ecomm + 1);
+        }
+    } else if (pid == getpid()) {
+        pid = tid;
+        comm = strdup("auditd");
+    } else if (!(comm = logbuf->pidToName(pid))) {
+        comm = strdup("unknown");
+    }
+
+    size_t l = strlen(comm) + 1;
+    size_t n = l + strlen(str) + 2;
+
+    char *newstr = reinterpret_cast<char *>(malloc(n));
+    if (!newstr) {
+        free(comm);
+        free(str);
+        return -ENOMEM;
+    }
+
+    *newstr = AUDIT_LOG_PRIO;
+    strcpy(newstr + 1, comm);
+    free(comm);
+    strcpy(newstr + 1 + l, str);
+    free(str);
+
+    logbuf->log(AUDIT_LOG_ID, now, uid, pid, tid, newstr,
+                (n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX);
+    reader->notifyNewLog();
+
+    free(newstr);
+
+    return rc;
+}
+
+void LogAudit::logDmesg() {
+    int len = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
+    if (len <= 0) {
+        return;
+    }
+
+    len++;
+    char buf[len];
+
+    int rc = klogctl(KLOG_READ_ALL, buf, len);
+
+    buf[len - 1] = '\0';
+
+    for(char *tok = buf; (rc >= 0) && ((tok = strtok(tok, "\r\n"))); tok = NULL) {
+        char *audit = strstr(tok, " audit(");
+        if (!audit) {
+            continue;
+        }
+
+        *audit++ = '\0';
+
+        char *type = strstr(tok, "type=");
+        if (type) {
+            rc = logPrint("%s %s", type, audit);
+        } else {
+            rc = logPrint("%s", audit);
+        }
+    }
+}
+
+int LogAudit::getLogSocket() {
+    int fd = audit_open();
+    if (fd < 0) {
+        return fd;
+    }
+    if (audit_set_pid(fd, getpid(), WAIT_YES) < 0) {
+        audit_close(fd);
+        fd = -1;
+    }
+    return fd;
+}
diff --git a/logd/LogAudit.h b/logd/LogAudit.h
new file mode 100644
index 0000000..111030a
--- /dev/null
+++ b/logd/LogAudit.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014 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 _LOGD_LOG_AUDIT_H__
+#define _LOGD_LOG_AUDIT_H__
+
+#include <sysutils/SocketListener.h>
+#include "LogReader.h"
+
+class LogAudit : public SocketListener {
+    LogBuffer *logbuf;
+    LogReader *reader;
+    int fdDmesg;
+
+public:
+    LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg);
+
+protected:
+    virtual bool onDataAvailable(SocketClient *cli);
+
+private:
+    static int getLogSocket();
+    void logDmesg();
+    int logPrint(const char *fmt, ...)
+        __attribute__ ((__format__ (__printf__, 2, 3)));
+};
+
+#endif
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 1c5cef0..38a237c 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -28,20 +28,16 @@
 
 // Default
 #define LOG_BUFFER_SIZE (256 * 1024) // Tuned on a per-platform basis here?
-#ifdef USERDEBUG_BUILD
 #define log_buffer_size(id) mMaxSize[id]
-#else
-#define log_buffer_size(id) LOG_BUFFER_SIZE
-#endif
 
 LogBuffer::LogBuffer(LastLogTimes *times)
         : mTimes(*times) {
     pthread_mutex_init(&mLogElementsLock, NULL);
-#ifdef USERDEBUG_BUILD
+    dgram_qlen_statistics = false;
+
     log_id_for_each(i) {
         mMaxSize[i] = LOG_BUFFER_SIZE;
     }
-#endif
 }
 
 void LogBuffer::log(log_id_t log_id, log_time realtime,
@@ -61,6 +57,23 @@
     LogBufferElementCollection::iterator last = it;
     while (--it != mLogElements.begin()) {
         if ((*it)->getRealTime() <= realtime) {
+            // halves the peak performance, use with caution
+            if (dgram_qlen_statistics) {
+                LogBufferElementCollection::iterator ib = it;
+                unsigned short buckets, num = 1;
+                for (unsigned short i = 0; (buckets = stats.dgram_qlen(i)); ++i) {
+                    buckets -= num;
+                    num += buckets;
+                    while (buckets && (--ib != mLogElements.begin())) {
+                        --buckets;
+                    }
+                    if (buckets) {
+                        break;
+                    }
+                    stats.recordDiff(
+                        elem->getRealTime() - (*ib)->getRealTime(), i);
+                }
+            }
             break;
         }
         last = it;
@@ -152,22 +165,16 @@
         size_t worst_sizes = 0;
         size_t second_worst_sizes = 0;
 
-#ifdef USERDEBUG_BUILD
-        if (mPrune.worstUidEnabled())
-#endif
-        {
+        if ((id != LOG_ID_CRASH) && mPrune.worstUidEnabled()) {
             LidStatistics &l = stats.id(id);
-            UidStatisticsCollection::iterator iu;
-            for (iu = l.begin(); iu != l.end(); ++iu) {
-                UidStatistics *u = (*iu);
-                size_t sizes = u->sizes();
-                if (worst_sizes < sizes) {
-                    second_worst_sizes = worst_sizes;
-                    worst_sizes = sizes;
-                    worst = u->getUid();
-                }
-                if ((second_worst_sizes < sizes) && (sizes < worst_sizes)) {
-                    second_worst_sizes = sizes;
+            l.sort();
+            UidStatisticsCollection::iterator iu = l.begin();
+            if (iu != l.end()) {
+                UidStatistics *u = *iu;
+                worst = u->getUid();
+                worst_sizes = u->sizes();
+                if (++iu != l.end()) {
+                    second_worst_sizes = (*iu)->sizes();
                 }
             }
         }
@@ -198,9 +205,7 @@
                     break;
                 }
                 worst_sizes -= len;
-            }
-#ifdef USERDEBUG_BUILD
-            else if (mPrune.naughty(e)) { // BlackListed
+            } else if (mPrune.naughty(e)) { // BlackListed
                 it = mLogElements.erase(it);
                 stats.subtract(e->getMsgLen(), id, uid, e->getPid());
                 delete e;
@@ -208,34 +213,23 @@
                 if (pruneRows == 0) {
                     break;
                 }
-            }
-#endif
-            else {
+            } else {
                 ++it;
             }
         }
 
-        if (!kick
-#ifdef USERDEBUG_BUILD
-                || !mPrune.worstUidEnabled()
-#endif
-        ) {
+        if (!kick || !mPrune.worstUidEnabled()) {
             break; // the following loop will ask bad clients to skip/drop
         }
     }
 
-#ifdef USERDEBUG_BUILD
     bool whitelist = false;
-#endif
     it = mLogElements.begin();
     while((pruneRows > 0) && (it != mLogElements.end())) {
         LogBufferElement *e = *it;
         if (e->getLogId() == id) {
             if (oldest && (oldest->mStart <= e->getMonotonicTime())) {
-#ifdef USERDEBUG_BUILD
-                if (!whitelist)
-#endif
-                {
+                if (!whitelist) {
                     if (stats.sizes(id) > (2 * log_buffer_size(id))) {
                         // kick a misbehaving log reader client off the island
                         oldest->release_Locked();
@@ -245,13 +239,13 @@
                 }
                 break;
             }
-#ifdef USERDEBUG_BUILD
+
             if (mPrune.nice(e)) { // WhiteListed
                 whitelist = true;
                 it++;
                 continue;
             }
-#endif
+
             it = mLogElements.erase(it);
             stats.subtract(e->getMsgLen(), id, e->getUid(), e->getPid());
             delete e;
@@ -261,7 +255,6 @@
         }
     }
 
-#ifdef USERDEBUG_BUILD
     if (whitelist && (pruneRows > 0)) {
         it = mLogElements.begin();
         while((it != mLogElements.end()) && (pruneRows > 0)) {
@@ -285,7 +278,6 @@
             }
         }
     }
-#endif
 
     LogTimeEntry::unlock();
 }
@@ -305,8 +297,6 @@
     return retval;
 }
 
-#ifdef USERDEBUG_BUILD
-
 // set the total space allocated to "id"
 int LogBuffer::setSize(log_id_t id, unsigned long size) {
     // Reasonable limits ...
@@ -327,15 +317,6 @@
     return retval;
 }
 
-#else // ! USERDEBUG_BUILD
-
-// get the total space allocated to "id"
-unsigned long LogBuffer::getSize(log_id_t /*id*/) {
-    return log_buffer_size(id);
-}
-
-#endif
-
 log_time LogBuffer::flushTo(
         SocketClient *reader, const log_time start, bool privileged,
         bool (*filter)(const LogBufferElement *element, void *arg), void *arg) {
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index cbbb2ce..b8a54b9 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -36,11 +36,11 @@
 
     LogStatistics stats;
 
-#ifdef USERDEBUG_BUILD
+    bool dgram_qlen_statistics;
+
     PruneList mPrune;
 
     unsigned long mMaxSize[LOG_ID_MAX];
-#endif
 
 public:
     LastLogTimes &mTimes;
@@ -57,18 +57,23 @@
 
     void clear(log_id_t id);
     unsigned long getSize(log_id_t id);
-#ifdef USERDEBUG_BUILD
     int setSize(log_id_t id, unsigned long size);
-#endif
     unsigned long getSizeUsed(log_id_t id);
     // *strp uses malloc, use free to release.
     void formatStatistics(char **strp, uid_t uid, unsigned int logMask);
 
-#ifdef USERDEBUG_BUILD
+    void enableDgramQlenStatistics() {
+        stats.enableDgramQlenStatistics();
+        dgram_qlen_statistics = true;
+    }
+
     int initPrune(char *cp) { return mPrune.init(cp); }
     // *strp uses malloc, use free to release.
     void formatPrune(char **strp) { mPrune.format(strp); }
-#endif
+
+    // helper
+    char *pidToName(pid_t pid) { return stats.pidToName(pid); }
+    uid_t pidToUid(pid_t pid) { return stats.pidToUid(pid); }
 
 private:
     void maybePrune(log_id_t id);
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index b835b4f..6ff4d3a 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <limits.h>
+#include <sys/prctl.h>
 #include <sys/socket.h>
 #include <sys/types.h>
 #include <sys/un.h>
@@ -31,7 +33,9 @@
 {  }
 
 bool LogListener::onDataAvailable(SocketClient *cli) {
-    char buffer[sizeof_log_id_t + sizeof(log_time) + sizeof(char)
+    prctl(PR_SET_NAME, "logd.writer");
+
+    char buffer[sizeof_log_id_t + sizeof(uint16_t) + sizeof(log_time)
         + LOGGER_ENTRY_MAX_PAYLOAD];
     struct iovec iov = { buffer, sizeof(buffer) };
     memset(buffer, 0, sizeof(buffer));
@@ -97,17 +101,24 @@
 
     // NB: hdr.msg_flags & MSG_TRUNC is not tested, silently passing a
     // truncated message to the logs.
-    unsigned short len = n; // cap to internal maximum
-    if (len == n) {
-        logbuf->log(log_id, realtime, cred->uid, cred->pid, tid, msg, len);
-        reader->notifyNewLog();
-    }
+
+    logbuf->log(log_id, realtime, cred->uid, cred->pid, tid, msg,
+        (n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX);
+    reader->notifyNewLog();
 
     return true;
 }
 
 int LogListener::getLogSocket() {
-    int sock = android_get_control_socket("logdw");
+    static const char socketName[] = "logdw";
+    int sock = android_get_control_socket(socketName);
+
+    if (sock < 0) {
+        sock = socket_local_server(socketName,
+                                   ANDROID_SOCKET_NAMESPACE_RESERVED,
+                                   SOCK_DGRAM);
+    }
+
     int on = 1;
     if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
         return -1;
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index 60a3507..8458c19 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -16,14 +16,16 @@
 
 #include <ctype.h>
 #include <poll.h>
+#include <sys/prctl.h>
 #include <sys/socket.h>
+
 #include <cutils/sockets.h>
 
 #include "LogReader.h"
 #include "FlushCommand.h"
 
 LogReader::LogReader(LogBuffer *logbuf)
-        : SocketListener("logdr", true)
+        : SocketListener(getLogSocket(), true)
         , mLogbuf(*logbuf)
 { }
 
@@ -35,6 +37,8 @@
 }
 
 bool LogReader::onDataAvailable(SocketClient *cli) {
+    prctl(PR_SET_NAME, "logd.reader");
+
     char buffer[255];
 
     int len = read(cli->getSocket(), buffer, sizeof(buffer) - 1);
@@ -167,3 +171,16 @@
     }
     LogTimeEntry::unlock();
 }
+
+int LogReader::getLogSocket() {
+    static const char socketName[] = "logdr";
+    int sock = android_get_control_socket(socketName);
+
+    if (sock < 0) {
+        sock = socket_local_server(socketName,
+                                   ANDROID_SOCKET_NAMESPACE_RESERVED,
+                                   SOCK_SEQPACKET);
+    }
+
+    return sock;
+}
diff --git a/logd/LogReader.h b/logd/LogReader.h
index b267c75..91559a3 100644
--- a/logd/LogReader.h
+++ b/logd/LogReader.h
@@ -34,6 +34,8 @@
     virtual bool onDataAvailable(SocketClient *cli);
 
 private:
+    static int getLogSocket();
+
     void doSocketDelete(SocketClient *cli);
 
 };
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index 49ee50d..f44f567 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <fcntl.h>
 #include <stdarg.h>
 #include <time.h>
 
@@ -23,12 +24,34 @@
 
 #include "LogStatistics.h"
 
-PidStatistics::PidStatistics(pid_t pid)
+PidStatistics::PidStatistics(pid_t pid, char *name)
         : pid(pid)
         , mSizesTotal(0)
         , mElementsTotal(0)
         , mSizes(0)
-        , mElements(0) { }
+        , mElements(0)
+        , name(name)
+{ }
+
+#ifdef DO_NOT_ERROR_IF_PIDSTATISTICS_USES_A_COPY_CONSTRUCTOR
+PidStatistics::PidStatistics(const PidStatistics &copy)
+        : pid(copy->pid)
+        , name(copy->name ? strdup(copy->name) : NULL)
+        , mSizesTotal(copy->mSizesTotal)
+        , mElementsTotal(copy->mElementsTotal)
+        , mSizes(copy->mSizes)
+        , mElements(copy->mElements)
+{ }
+#endif
+
+PidStatistics::~PidStatistics() {
+    free(name);
+}
+
+void PidStatistics::setName(char *new_name) {
+    free(name);
+    name = new_name;
+}
 
 void PidStatistics::add(unsigned short size) {
     mSizesTotal += size;
@@ -40,7 +63,7 @@
 bool PidStatistics::subtract(unsigned short size) {
     mSizes -= size;
     --mElements;
-    return mElements == 0 && kill(pid, 0);
+    return (mElements == 0) && kill(pid, 0) && (errno != EPERM);
 }
 
 void PidStatistics::addTotal(size_t size, size_t element) {
@@ -50,8 +73,32 @@
     }
 }
 
+// must call free to release return value
+char *PidStatistics::pidToName(pid_t pid) {
+    char *retval = NULL;
+    if (pid != PidStatistics::gone) {
+        char buffer[512];
+        snprintf(buffer, sizeof(buffer), "/proc/%u/cmdline", pid);
+        int fd = open(buffer, O_RDONLY);
+        if (fd >= 0) {
+            ssize_t ret = read(fd, buffer, sizeof(buffer));
+            if (ret > 0) {
+                buffer[sizeof(buffer)-1] = '\0';
+                // frameworks intermediate state
+                if (strcmp(buffer, "<pre-initialized>")) {
+                    retval = strdup(buffer);
+                }
+            }
+            close(fd);
+        }
+    }
+    return retval;
+}
+
 UidStatistics::UidStatistics(uid_t uid)
-        : uid(uid) {
+        : uid(uid)
+        , mSizes(0)
+        , mElements(0) {
     Pids.clear();
 }
 
@@ -64,6 +111,9 @@
 }
 
 void UidStatistics::add(unsigned short size, pid_t pid) {
+    mSizes += size;
+    ++mElements;
+
     PidStatistics *p;
     PidStatisticsCollection::iterator last;
     PidStatisticsCollection::iterator it;
@@ -71,19 +121,12 @@
         p = *it;
         if (pid == p->getPid()) {
             p->add(size);
-            // poor-man sort, bubble upwards if bigger than last
-            if ((last != it) && ((*last)->sizesTotal() < p->sizesTotal())) {
-                Pids.erase(it);
-                Pids.insert(last, p);
-            }
             return;
         }
     }
-    // poor-man sort, insert if bigger than last or last is the gone entry.
-    bool insert = (last != it)
-        && ((p->getPid() == p->gone)
-            || ((*last)->sizesTotal() < (size_t) size));
-    p = new PidStatistics(pid);
+    // insert if the gone entry.
+    bool insert = (last != it) && (p->getPid() == p->gone);
+    p = new PidStatistics(pid, pidToName(pid));
     if (insert) {
         Pids.insert(last, p);
     } else {
@@ -93,6 +136,9 @@
 }
 
 void UidStatistics::subtract(unsigned short size, pid_t pid) {
+    mSizes -= size;
+    --mElements;
+
     PidStatisticsCollection::iterator it;
     for (it = begin(); it != end(); ++it) {
         PidStatistics *p = *it;
@@ -121,28 +167,57 @@
     }
 }
 
+void UidStatistics::sort() {
+    for (bool pass = true; pass;) {
+        pass = false;
+        PidStatisticsCollection::iterator it = begin();
+        if (it != end()) {
+            PidStatisticsCollection::iterator lt = it;
+            PidStatistics *l = (*lt);
+            while (++it != end()) {
+                PidStatistics *n = (*it);
+                if ((n->getPid() != n->gone) && (n->sizes() > l->sizes())) {
+                    pass = true;
+                    Pids.erase(it);
+                    Pids.insert(lt, n);
+                    it = lt;
+                    n = l;
+                }
+                lt = it;
+                l = n;
+            }
+        }
+    }
+}
+
 size_t UidStatistics::sizes(pid_t pid) {
-    size_t sizes = 0;
+    if (pid == pid_all) {
+        return sizes();
+    }
+
     PidStatisticsCollection::iterator it;
     for (it = begin(); it != end(); ++it) {
         PidStatistics *p = *it;
-        if ((pid == pid_all) || (pid == p->getPid())) {
-            sizes += p->sizes();
+        if (pid == p->getPid()) {
+            return p->sizes();
         }
     }
-    return sizes;
+    return 0;
 }
 
 size_t UidStatistics::elements(pid_t pid) {
-    size_t elements = 0;
+    if (pid == pid_all) {
+        return elements();
+    }
+
     PidStatisticsCollection::iterator it;
     for (it = begin(); it != end(); ++it) {
         PidStatistics *p = *it;
-        if ((pid == pid_all) || (pid == p->getPid())) {
-            elements += p->elements();
+        if (pid == p->getPid()) {
+            return p->elements();
         }
     }
-    return elements;
+    return 0;
 }
 
 size_t UidStatistics::sizesTotal(pid_t pid) {
@@ -221,6 +296,29 @@
     }
 }
 
+void LidStatistics::sort() {
+    for (bool pass = true; pass;) {
+        pass = false;
+        UidStatisticsCollection::iterator it = begin();
+        if (it != end()) {
+            UidStatisticsCollection::iterator lt = it;
+            UidStatistics *l = (*lt);
+            while (++it != end()) {
+                UidStatistics *n = (*it);
+                if (n->sizes() > l->sizes()) {
+                    pass = true;
+                    Uids.erase(it);
+                    Uids.insert(lt, n);
+                    it = lt;
+                    n = l;
+                }
+                lt = it;
+                l = n;
+            }
+        }
+    }
+}
+
 size_t LidStatistics::sizes(uid_t uid, pid_t pid) {
     size_t sizes = 0;
     UidStatisticsCollection::iterator it;
@@ -275,6 +373,66 @@
         mSizes[i] = 0;
         mElements[i] = 0;
     }
+
+    dgram_qlen_statistics = false;
+    for(unsigned short bucket = 0; dgram_qlen(bucket); ++bucket) {
+        mMinimum[bucket].tv_sec = (uint32_t)-1;
+        mMinimum[bucket].tv_nsec = 999999999UL;
+    }
+}
+
+//   Each bucket below represents a dgram_qlen of log messages. By
+//   finding the minimum period of time from start to finish
+//   of each dgram_qlen, we can get a performance expectation for
+//   the user space logger. The net result is that the period
+//   of time divided by the dgram_qlen will give us the average time
+//   between log messages; at the point where the average time
+//   is greater than the throughput capability of the logger
+//   we will not longer require the benefits of the FIFO formed
+//   by max_dgram_qlen. We will also expect to see a very visible
+//   knee in the average time between log messages at this point,
+//   so we do not necessarily have to compare the rate against the
+//   measured performance (BM_log_maximum_retry) of the logger.
+//
+//   for example (reformatted):
+//
+//       Minimum time between log events per dgram_qlen:
+//       1   2   3   5   10  20  30  50  100  200 300 400 500 600
+//       5u2 12u 13u 15u 16u 27u 30u 36u 407u 3m1 3m3 3m9 3m9 5m5
+//
+//   demonstrates a clear knee rising at 100, so this means that for this
+//   case max_dgram_qlen = 100 would be more than sufficient to handle the
+//   worst that the system could stuff into the logger. The
+//   BM_log_maximum_retry performance (derated by the log collection) on the
+//   same system was 33.2us so we would almost be fine with max_dgram_qlen = 50.
+//   BM_log_maxumum_retry with statistics off is roughly 20us, so
+//   max_dgram_qlen = 20 would work. We will be more than willing to have
+//   a large engineering margin so the rule of thumb that lead us to 100 is
+//   fine.
+//
+// bucket dgram_qlen are tuned for /proc/sys/net/unix/max_dgram_qlen = 300
+const unsigned short LogStatistics::mBuckets[] = {
+    1, 2, 3, 5, 10, 20, 30, 50, 100, 200, 300, 400, 500, 600
+};
+
+unsigned short LogStatistics::dgram_qlen(unsigned short bucket) {
+    if (bucket >= sizeof(mBuckets) / sizeof(mBuckets[0])) {
+        return 0;
+    }
+    return mBuckets[bucket];
+}
+
+unsigned long long LogStatistics::minimum(unsigned short bucket) {
+    if (mMinimum[bucket].tv_sec == LONG_MAX) {
+        return 0;
+    }
+    return mMinimum[bucket].nsec();
+}
+
+void LogStatistics::recordDiff(log_time diff, unsigned short bucket) {
+    if ((diff.tv_sec || diff.tv_nsec) && (mMinimum[bucket] > diff)) {
+        mMinimum[bucket] = diff;
+    }
 }
 
 void LogStatistics::add(unsigned short size,
@@ -337,11 +495,11 @@
 
 void LogStatistics::format(char **buf,
                            uid_t uid, unsigned int logMask, log_time oldest) {
-    const unsigned short spaces_current = 13;
-    const unsigned short spaces_total = 19;
+    static const unsigned short spaces_current = 13;
+    static const unsigned short spaces_total = 19;
 
     if (*buf) {
-        free(buf);
+        free(*buf);
         *buf = NULL;
     }
 
@@ -350,10 +508,22 @@
     short spaces = 2;
 
     log_id_for_each(i) {
-        if (logMask & (1 << i)) {
-            oldLength = string.length();
-            string.appendFormat("%*s%s", spaces, "", android_log_id_to_name(i));
-            spaces += spaces_total + oldLength - string.length();
+        if (!logMask & (1 << i)) {
+            continue;
+        }
+        oldLength = string.length();
+        if (spaces < 0) {
+            spaces = 0;
+        }
+        string.appendFormat("%*s%s", spaces, "", android_log_id_to_name(i));
+        spaces += spaces_total + oldLength - string.length();
+
+        LidStatistics &l = id(i);
+        l.sort();
+
+        UidStatisticsCollection::iterator iu;
+        for (iu = l.begin(); iu != l.end(); ++iu) {
+            (*iu)->sort();
         }
     }
 
@@ -369,6 +539,9 @@
             continue;
         }
         oldLength = string.length();
+        if (spaces < 0) {
+            spaces = 0;
+        }
         string.appendFormat("%*s%zu/%zu", spaces, "",
                             sizesTotal(i), elementsTotal(i));
         spaces += spaces_total + oldLength - string.length();
@@ -388,18 +561,171 @@
         size_t els = elements(i);
         if (els) {
             oldLength = string.length();
+            if (spaces < 0) {
+                spaces = 0;
+            }
             string.appendFormat("%*s%zu/%zu", spaces, "", sizes(i), els);
             spaces -= string.length() - oldLength;
         }
         spaces += spaces_total;
     }
 
+    // Construct list of worst spammers by Pid
+    static const unsigned char num_spammers = 10;
+    bool header = false;
+
+    log_id_for_each(i) {
+        if (!(logMask & (1 << i))) {
+            continue;
+        }
+
+        PidStatisticsCollection pids;
+        pids.clear();
+
+        LidStatistics &l = id(i);
+        UidStatisticsCollection::iterator iu;
+        for (iu = l.begin(); iu != l.end(); ++iu) {
+            UidStatistics &u = *(*iu);
+            PidStatisticsCollection::iterator ip;
+            for (ip = u.begin(); ip != u.end(); ++ip) {
+                PidStatistics *p = (*ip);
+                if (p->getPid() == p->gone) {
+                    break;
+                }
+
+                size_t mySizes = p->sizes();
+
+                PidStatisticsCollection::iterator q;
+                unsigned char num = 0;
+                for (q = pids.begin(); q != pids.end(); ++q) {
+                    if (mySizes > (*q)->sizes()) {
+                        pids.insert(q, p);
+                        break;
+                    }
+                    // do we need to traverse deeper in the list?
+                    if (++num > num_spammers) {
+                        break;
+                    }
+                }
+                if (q == pids.end()) {
+                   pids.push_back(p);
+                }
+            }
+        }
+
+        size_t threshold = sizes(i);
+        if (threshold < 65536) {
+            threshold = 65536;
+        }
+        threshold /= 100;
+
+        PidStatisticsCollection::iterator pt = pids.begin();
+
+        for(int line = 0;
+                (pt != pids.end()) && (line < num_spammers);
+                ++line, pt = pids.erase(pt)) {
+            PidStatistics *p = *pt;
+
+            size_t sizes = p->sizes();
+            if (sizes < threshold) {
+                break;
+            }
+
+            char *name = p->getName();
+            pid_t pid = p->getPid();
+            if (!name || !*name) {
+                name = pidToName(pid);
+                if (name) {
+                    if (*name) {
+                        p->setName(name);
+                    } else {
+                        free(name);
+                        name = NULL;
+                    }
+                }
+            }
+
+            if (!header) {
+                string.appendFormat("\n\nChattiest clients:\n"
+                                    "log id %-*s PID[?] name",
+                                    spaces_total, "size/total");
+                header = true;
+            }
+
+            size_t sizesTotal = p->sizesTotal();
+
+            android::String8 sz("");
+            sz.appendFormat((sizes != sizesTotal) ? "%zu/%zu" : "%zu",
+                            sizes, sizesTotal);
+
+            android::String8 pd("");
+            pd.appendFormat("%u%c", pid,
+                            (kill(pid, 0) && (errno != EPERM)) ? '?' : ' ');
+
+            string.appendFormat("\n%-7s%-*s %-7s%s",
+                                line ? "" : android_log_id_to_name(i),
+                                spaces_total, sz.string(), pd.string(),
+                                name ? name : "");
+        }
+
+        pids.clear();
+    }
+
+    if (dgram_qlen_statistics) {
+        const unsigned short spaces_time = 6;
+        const unsigned long long max_seconds = 100000;
+        spaces = 0;
+        string.append("\n\nMinimum time between log events per dgram_qlen:\n");
+        for(unsigned short i = 0; dgram_qlen(i); ++i) {
+            oldLength = string.length();
+            if (spaces < 0) {
+                spaces = 0;
+            }
+            string.appendFormat("%*s%u", spaces, "", dgram_qlen(i));
+            spaces += spaces_time + oldLength - string.length();
+        }
+        string.append("\n");
+        spaces = 0;
+        unsigned short n;
+        for(unsigned short i = 0; (n = dgram_qlen(i)); ++i) {
+            unsigned long long duration = minimum(i);
+            if (duration) {
+                duration /= n;
+                if (duration >= (NS_PER_SEC * max_seconds)) {
+                    duration = NS_PER_SEC * (max_seconds - 1);
+                }
+                oldLength = string.length();
+                if (spaces < 0) {
+                    spaces = 0;
+                }
+                string.appendFormat("%*s", spaces, "");
+                if (duration >= (NS_PER_SEC * 10)) {
+                    string.appendFormat("%llu",
+                        (duration + (NS_PER_SEC / 2))
+                            / NS_PER_SEC);
+                } else if (duration >= (NS_PER_SEC / (1000 / 10))) {
+                    string.appendFormat("%llum",
+                        (duration + (NS_PER_SEC / 2 / 1000))
+                            / (NS_PER_SEC / 1000));
+                } else if (duration >= (NS_PER_SEC / (1000000 / 10))) {
+                    string.appendFormat("%lluu",
+                         (duration + (NS_PER_SEC / 2 / 1000000))
+                             / (NS_PER_SEC / 1000000));
+                } else {
+                    string.appendFormat("%llun", duration);
+                }
+                spaces -= string.length() - oldLength;
+            }
+            spaces += spaces_time;
+        }
+    }
+
     log_id_for_each(i) {
         if (!(logMask & (1 << i))) {
             continue;
         }
 
-        bool header = false;
+        header = false;
         bool first = true;
 
         UidStatisticsCollection::iterator ut;
@@ -433,7 +759,7 @@
 
             if (!oneline) {
                 first = true;
-            } else if (!first && spaces) {
+            } else if (!first && (spaces > 0)) {
                 string.appendFormat("%*s", spaces, "");
             }
             spaces = 0;
@@ -447,7 +773,7 @@
                                                  : "%d/%d")
                                              : "%d",
                                          u, p);
-            string.appendFormat((first) ? "\n%-12s" : "%-12s",
+            string.appendFormat(first ? "\n%-12s" : "%-12s",
                                 intermediate.string());
             intermediate.clear();
 
@@ -458,10 +784,16 @@
 
             size_t els = up->elements();
             if (els == elsTotal) {
+                if (spaces < 0) {
+                    spaces = 0;
+                }
                 string.appendFormat("%*s=", spaces, "");
                 spaces = -1;
             } else if (els) {
                 oldLength = string.length();
+                if (spaces < 0) {
+                    spaces = 0;
+                }
                 string.appendFormat("%*s%zu/%zu", spaces, "", up->sizes(), els);
                 spaces -= string.length() - oldLength;
             }
@@ -490,7 +822,7 @@
                     continue;
                 }
                 els = pp->elements();
-                bool gone = kill(p, 0);
+                bool gone = kill(p, 0) && (errno != EPERM);
                 if (gone && (els == 0)) {
                     // ToDo: garbage collection: move this statistical bucket
                     //       from its current UID/PID to UID/? (races and
@@ -502,13 +834,13 @@
                     continue;
                 }
 
-                if (!first && spaces) {
+                if (!first && (spaces > 0)) {
                     string.appendFormat("%*s", spaces, "");
                 }
                 spaces = 0;
 
-                intermediate = string.format((gone) ? "%d/%d?" : "%d/%d", u, p);
-                string.appendFormat((first) ? "\n%-12s" : "%-12s",
+                intermediate = string.format(gone ? "%d/%d?" : "%d/%d", u, p);
+                string.appendFormat(first ? "\n%-12s" : "%-12s",
                                     intermediate.string());
                 intermediate.clear();
 
@@ -517,10 +849,16 @@
                 spaces += spaces_total + oldLength - string.length();
 
                 if (els == elsTotal) {
+                    if (spaces < 0) {
+                        spaces = 0;
+                    }
                     string.appendFormat("%*s=", spaces, "");
                     spaces = -1;
                 } else if (els) {
                     oldLength = string.length();
+                    if (spaces < 0) {
+                        spaces = 0;
+                    }
                     string.appendFormat("%*s%zu/%zu", spaces, "",
                                         pp->sizes(), els);
                     spaces -= string.length() - oldLength;
@@ -531,12 +869,12 @@
             }
 
             if (gone_els) {
-                if (!first && spaces) {
+                if (!first && (spaces > 0)) {
                     string.appendFormat("%*s", spaces, "");
                 }
 
                 intermediate = string.format("%d/?", u);
-                string.appendFormat((first) ? "\n%-12s" : "%-12s",
+                string.appendFormat(first ? "\n%-12s" : "%-12s",
                                     intermediate.string());
                 intermediate.clear();
 
@@ -553,3 +891,20 @@
 
     *buf = strdup(string.string());
 }
+
+uid_t LogStatistics::pidToUid(pid_t pid) {
+    log_id_for_each(i) {
+        LidStatistics &l = id(i);
+        UidStatisticsCollection::iterator iu;
+        for (iu = l.begin(); iu != l.end(); ++iu) {
+            UidStatistics &u = *(*iu);
+            PidStatisticsCollection::iterator ip;
+            for (ip = u.begin(); ip != u.end(); ++ip) {
+                if ((*ip)->getPid() == pid) {
+                    return u.getUid();
+                }
+            }
+        }
+    }
+    return getuid(); // associate this with the logger
+}
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index d44afa2..cd6ef7b 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -36,12 +36,18 @@
     size_t mSizes;
     size_t mElements;
 
+    char *name;
+
 public:
     static const pid_t gone = (pid_t) -1;
 
-    PidStatistics(pid_t pid);
+    PidStatistics(pid_t pid, char *name = NULL);
+    PidStatistics(const PidStatistics &copy);
+    ~PidStatistics();
 
     pid_t getPid() const { return pid; }
+    char *getName() const { return name; }
+    void setName(char *name);
 
     void add(unsigned short size);
     bool subtract(unsigned short size); // returns true if stats and PID gone
@@ -52,6 +58,9 @@
 
     size_t sizesTotal() const { return mSizesTotal; }
     size_t elementsTotal() const { return mElementsTotal; }
+
+    // helper
+    static char *pidToName(pid_t pid);
 };
 
 typedef android::List<PidStatistics *> PidStatisticsCollection;
@@ -61,6 +70,9 @@
 
     PidStatisticsCollection Pids;
 
+    size_t mSizes;
+    size_t mElements;
+
 public:
     UidStatistics(uid_t uid);
     ~UidStatistics();
@@ -72,14 +84,23 @@
 
     void add(unsigned short size, pid_t pid);
     void subtract(unsigned short size, pid_t pid);
+    void sort();
 
     static const pid_t pid_all = (pid_t) -1;
 
-    size_t sizes(pid_t pid = pid_all);
-    size_t elements(pid_t pid = pid_all);
+    // fast track current value
+    size_t sizes() const { return mSizes; };
+    size_t elements() const { return mElements; };
+
+    // statistical track
+    size_t sizes(pid_t pid);
+    size_t elements(pid_t pid);
 
     size_t sizesTotal(pid_t pid = pid_all);
     size_t elementsTotal(pid_t pid = pid_all);
+
+    // helper
+    static char *pidToName(pid_t pid) { return PidStatistics::pidToName(pid); }
 };
 
 typedef android::List<UidStatistics *> UidStatisticsCollection;
@@ -96,6 +117,7 @@
 
     void add(unsigned short size, uid_t uid, pid_t pid);
     void subtract(unsigned short size, uid_t uid, pid_t pid);
+    void sort();
 
     static const pid_t pid_all = (pid_t) -1;
     static const uid_t uid_all = (uid_t) -1;
@@ -114,6 +136,11 @@
     size_t mSizes[LOG_ID_MAX];
     size_t mElements[LOG_ID_MAX];
 
+    bool dgram_qlen_statistics;
+
+    static const unsigned short mBuckets[14];
+    log_time mMinimum[sizeof(mBuckets) / sizeof(mBuckets[0])];
+
 public:
     const log_time start;
 
@@ -121,8 +148,14 @@
 
     LidStatistics &id(log_id_t log_id) { return LogIds[log_id]; }
 
+    void enableDgramQlenStatistics() { dgram_qlen_statistics = true; }
+    static unsigned short dgram_qlen(unsigned short bucket);
+    unsigned long long minimum(unsigned short bucket);
+    void recordDiff(log_time diff, unsigned short bucket);
+
     void add(unsigned short size, log_id_t log_id, uid_t uid, pid_t pid);
     void subtract(unsigned short size, log_id_t log_id, uid_t uid, pid_t pid);
+    void sort();
 
     // fast track current value by id only
     size_t sizes(log_id_t id) const { return mSizes[id]; }
@@ -147,6 +180,10 @@
 
     // *strp = malloc, balance with free
     void format(char **strp, uid_t uid, unsigned int logMask, log_time oldest);
+
+    // helper
+    static char *pidToName(pid_t pid) { return PidStatistics::pidToName(pid); }
+    uid_t pidToUid(pid_t pid);
 };
 
 #endif // _LOGD_LOG_STATISTICS_H__
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index 8cb015c..1a9a548 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <sys/prctl.h>
+
 #include "FlushCommand.h"
 #include "LogBuffer.h"
 #include "LogTimes.h"
@@ -46,14 +48,25 @@
 { }
 
 void LogTimeEntry::startReader_Locked(void) {
+    pthread_attr_t attr;
+
     threadRunning = true;
-    if (pthread_create(&mThread, NULL, LogTimeEntry::threadStart, this)) {
-        threadRunning = false;
-        if (mClient) {
-            mClient->decRef();
+
+    if (!pthread_attr_init(&attr)) {
+        if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
+            if (!pthread_create(&mThread, &attr,
+                                LogTimeEntry::threadStart, this)) {
+                pthread_attr_destroy(&attr);
+                return;
+            }
         }
-        decRef_Locked();
+        pthread_attr_destroy(&attr);
     }
+    threadRunning = false;
+    if (mClient) {
+        mClient->decRef();
+    }
+    decRef_Locked();
 }
 
 void LogTimeEntry::threadStop(void *obj) {
@@ -96,6 +109,8 @@
 }
 
 void *LogTimeEntry::threadStart(void *obj) {
+    prctl(PR_SET_NAME, "logd.reader.per");
+
     LogTimeEntry *me = reinterpret_cast<LogTimeEntry *>(obj);
 
     pthread_cleanup_push(threadStop, obj);
diff --git a/logd/LogWhiteBlackList.cpp b/logd/LogWhiteBlackList.cpp
index 5f8173f..c6c7b23 100644
--- a/logd/LogWhiteBlackList.cpp
+++ b/logd/LogWhiteBlackList.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#ifdef USERDEBUG_BUILD
-
 #include <ctype.h>
 
 #include <utils/String8.h>
@@ -239,5 +237,3 @@
     }
     return false;
 }
-
-#endif // USERDEBUG_BUILD
diff --git a/logd/README.auditd b/logd/README.auditd
new file mode 100644
index 0000000..3f614a3
--- /dev/null
+++ b/logd/README.auditd
@@ -0,0 +1,17 @@
+Auditd Daemon
+
+The audit daemon is a simplified version of its desktop
+counterpart designed to gather the audit logs from the
+audit kernel subsystem. The audit subsystem of the kernel
+includes Linux Security Modules (LSM) messages as well.
+
+To enable the audit subsystem, you must add this to your
+kernel config:
+CONFIG_AUDIT=y
+
+To enable a LSM, you must consult that LSM's documentation, the
+example below is for SELinux:
+CONFIG_SECURITY_SELINUX=y
+
+This does not include possible dependencies that may need to be
+satisfied for that particular LSM.
diff --git a/logd/README.property b/logd/README.property
new file mode 100644
index 0000000..5d92d09
--- /dev/null
+++ b/logd/README.property
@@ -0,0 +1,12 @@
+The properties that logd responds to are:
+
+name                       type default  description
+logd.auditd                 bool  true   Enable selinux audit daemon
+logd.auditd.dmesg           bool  true   selinux audit messages duplicated and
+                                         sent on to dmesg log
+logd.statistics.dgram_qlen  bool  false  Record dgram_qlen statistics. This
+                                         represents a performance impact and
+                                         is used to determine the platform's
+                                         minimum domain socket network FIFO
+                                         size (see source for details) based
+                                         on typical load (logcat -S to view)
diff --git a/logd/libaudit.c b/logd/libaudit.c
new file mode 100644
index 0000000..ca88d1b
--- /dev/null
+++ b/logd/libaudit.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright 2012, Samsung Telecommunications of America
+ * Copyright (C) 2014 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.
+ *
+ * Written by William Roberts <w.roberts@sta.samsung.com>
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#define LOG_TAG "libaudit"
+#include <log/log.h>
+
+#include "libaudit.h"
+
+/**
+ * Waits for an ack from the kernel
+ * @param fd
+ *  The netlink socket fd
+ * @param seq
+ *  The current sequence number were acking on
+ * @return
+ *  This function returns 0 on success, else -errno.
+ */
+static int get_ack(int fd, int16_t seq)
+{
+    int rc;
+    struct audit_message rep;
+
+    /* Sanity check, this is an internal interface this shouldn't happen */
+    if (fd < 0) {
+        return -EINVAL;
+    }
+
+    rc = audit_get_reply(fd, &rep, GET_REPLY_BLOCKING, MSG_PEEK);
+    if (rc < 0) {
+        return rc;
+    }
+
+    if (rep.nlh.nlmsg_type == NLMSG_ERROR) {
+        audit_get_reply(fd, &rep, GET_REPLY_BLOCKING, 0);
+        rc = ((struct nlmsgerr *)rep.data)->error;
+        if (rc) {
+            return -rc;
+        }
+    }
+
+    if ((int16_t)rep.nlh.nlmsg_seq != seq) {
+        SLOGW("Expected sequence number between user space and kernel space is out of skew, "
+          "expected %u got %u", seq, rep.nlh.nlmsg_seq);
+    }
+
+    return 0;
+}
+
+/**
+ *
+ * @param fd
+ *  The netlink socket fd
+ * @param type
+ *  The type of netlink message
+ * @param data
+ *  The data to send
+ * @param size
+ *  The length of the data in bytes
+ * @return
+ *  This function returns a positive sequence number on success, else -errno.
+ */
+static int audit_send(int fd, int type, const void *data, size_t size)
+{
+    int rc;
+    static int16_t sequence = 0;
+    struct audit_message req;
+    struct sockaddr_nl addr;
+
+    memset(&req, 0, sizeof(req));
+    memset(&addr, 0, sizeof(addr));
+
+    /* We always send netlink messaged */
+    addr.nl_family = AF_NETLINK;
+
+    /* Set up the netlink headers */
+    req.nlh.nlmsg_type = type;
+    req.nlh.nlmsg_len = NLMSG_SPACE(size);
+    req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+
+    /*
+     * Check for a valid fd, even though sendto would catch this, its easier
+     * to always blindly increment the sequence number
+     */
+    if (fd < 0) {
+        return -EBADF;
+    }
+
+    /* Ensure the message is not too big */
+    if (NLMSG_SPACE(size) > MAX_AUDIT_MESSAGE_LENGTH) {
+        SLOGE("netlink message is too large");
+        return -EINVAL;
+    }
+
+    /* Only memcpy in the data if it was specified */
+    if (size && data) {
+        memcpy(NLMSG_DATA(&req.nlh), data, size);
+    }
+
+    /*
+     * Only increment the sequence number on a guarantee
+     * you will send it to the kernel.
+     *
+     * Also, the sequence is defined as a u32 in the kernel
+     * struct. Using an int here might not work on 32/64 bit splits. A
+     * signed 64 bit value can overflow a u32..but a u32
+     * might not fit in the response, so we need to use s32.
+     * Which is still kind of hackish since int could be 16 bits
+     * in size. The only safe type to use here is a signed 16
+     * bit value.
+     */
+    req.nlh.nlmsg_seq = ++sequence;
+
+    /* While failing and its due to interrupts */
+
+    rc = TEMP_FAILURE_RETRY(sendto(fd, &req, req.nlh.nlmsg_len, 0,
+                                   (struct sockaddr*) &addr, sizeof(addr)));
+
+    /* Not all the bytes were sent */
+    if (rc < 0) {
+        rc = -errno;
+        SLOGE("Error sending data over the netlink socket: %s", strerror(-errno));
+        goto out;
+    } else if ((uint32_t) rc != req.nlh.nlmsg_len) {
+        rc = -EPROTO;
+        goto out;
+    }
+
+    /* We sent all the bytes, get the ack */
+    rc = get_ack(fd, sequence);
+
+    /* If the ack failed, return the error, else return the sequence number */
+    rc = (rc == 0) ? (int) sequence : rc;
+
+out:
+    /* Don't let sequence roll to negative */
+    if (sequence < 0) {
+        SLOGW("Auditd to Kernel sequence number has rolled over");
+        sequence = 0;
+    }
+
+    return rc;
+}
+
+int audit_set_pid(int fd, uint32_t pid, rep_wait_t wmode)
+{
+    int rc;
+    struct audit_message rep;
+    struct audit_status status;
+
+    memset(&status, 0, sizeof(status));
+
+    /*
+     * In order to set the auditd PID we send an audit message over the netlink
+     * socket with the pid field of the status struct set to our current pid,
+     * and the the mask set to AUDIT_STATUS_PID
+     */
+    status.pid = pid;
+    status.mask = AUDIT_STATUS_PID;
+
+    /* Let the kernel know this pid will be registering for audit events */
+    rc = audit_send(fd, AUDIT_SET, &status, sizeof(status));
+    if (rc < 0) {
+        SLOGE("Could net set pid for audit events, error: %s", strerror(-rc));
+        return rc;
+    }
+
+    /*
+     * In a request where we need to wait for a response, wait for the message
+     * and discard it. This message confirms and sync's us with the kernel.
+     * This daemon is now registered as the audit logger. Only wait if the
+     * wmode is != WAIT_NO
+     */
+    if (wmode != WAIT_NO) {
+        /* TODO
+         * If the daemon dies and restarts the message didn't come back,
+         * so I went to non-blocking and it seemed to fix the bug.
+         * Need to investigate further.
+         */
+        audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0);
+    }
+
+    return 0;
+}
+
+int audit_open()
+{
+    return socket(PF_NETLINK, SOCK_RAW, NETLINK_AUDIT);
+}
+
+int audit_get_reply(int fd, struct audit_message *rep, reply_t block, int peek)
+{
+    ssize_t len;
+    int flags;
+    int rc = 0;
+
+    struct sockaddr_nl nladdr;
+    socklen_t nladdrlen = sizeof(nladdr);
+
+    if (fd < 0) {
+        return -EBADF;
+    }
+
+    /* Set up the flags for recv from */
+    flags = (block == GET_REPLY_NONBLOCKING) ? MSG_DONTWAIT : 0;
+    flags |= peek;
+
+    /*
+     * Get the data from the netlink socket but on error we need to be carefull,
+     * the interface shows that EINTR can never be returned, other errors,
+     * however, can be returned.
+     */
+    len = TEMP_FAILURE_RETRY(recvfrom(fd, rep, sizeof(*rep), flags,
+                                      (struct sockaddr*) &nladdr, &nladdrlen));
+
+    /*
+     * EAGAIN should be re-tried until success or another error manifests.
+     */
+    if (len < 0) {
+        rc = -errno;
+        if (block == GET_REPLY_NONBLOCKING && rc == -EAGAIN) {
+            /* If request is non blocking and errno is EAGAIN, just return 0 */
+            return 0;
+        }
+        SLOGE("Error receiving from netlink socket, error: %s", strerror(-rc));
+        return rc;
+    }
+
+    if (nladdrlen != sizeof(nladdr)) {
+        SLOGE("Protocol fault, error: %s", strerror(EPROTO));
+        return -EPROTO;
+    }
+
+    /* Make sure the netlink message was not spoof'd */
+    if (nladdr.nl_pid) {
+        SLOGE("Invalid netlink pid received, expected 0 got: %d", nladdr.nl_pid);
+        return -EINVAL;
+    }
+
+    /* Check if the reply from the kernel was ok */
+    if (!NLMSG_OK(&rep->nlh, (size_t)len)) {
+        rc = (len == sizeof(*rep)) ? -EFBIG : -EBADE;
+        SLOGE("Bad kernel response %s", strerror(-rc));
+    }
+
+    return rc;
+}
+
+void audit_close(int fd)
+{
+    int rc = close(fd);
+    if (rc < 0) {
+        SLOGE("Attempting to close invalid fd %d, error: %s", fd, strerror(errno));
+    }
+    return;
+}
diff --git a/logd/libaudit.h b/logd/libaudit.h
new file mode 100644
index 0000000..cb114f9
--- /dev/null
+++ b/logd/libaudit.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2012, Samsung Telecommunications of America
+ * Copyright (C) 2014 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.
+ *
+ * Written by William Roberts <w.roberts@sta.samsung.com>
+ */
+
+#ifndef _LIBAUDIT_H_
+#define _LIBAUDIT_H_
+
+#include <stdint.h>
+#include <sys/cdefs.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <linux/netlink.h>
+#include <linux/audit.h>
+
+__BEGIN_DECLS
+
+#define MAX_AUDIT_MESSAGE_LENGTH    8970
+
+typedef enum {
+    GET_REPLY_BLOCKING=0,
+    GET_REPLY_NONBLOCKING
+} reply_t;
+
+typedef enum {
+    WAIT_NO,
+    WAIT_YES
+} rep_wait_t;
+
+/* type == AUDIT_SIGNAL_INFO */
+struct audit_sig_info {
+    uid_t uid;
+    pid_t pid;
+    char ctx[0];
+};
+
+struct audit_message {
+    struct nlmsghdr nlh;
+    char   data[MAX_AUDIT_MESSAGE_LENGTH];
+};
+
+/**
+ * Opens a connection to the Audit netlink socket
+ * @return
+ *  A valid fd on success or < 0 on error with errno set.
+ *  Returns the same errors as man 2 socket.
+ */
+extern int  audit_open(void);
+
+/**
+ * Closes the fd returned from audit_open()
+ * @param fd
+ *  The fd to close
+ */
+extern void audit_close(int fd);
+
+/**
+ *
+ * @param fd
+ *  The fd returned by a call to audit_open()
+ * @param rep
+ *  The response struct to store the response in.
+ * @param block
+ *  Whether or not to block on IO
+ * @param peek
+ *  Whether or not we are to remove the message from
+ *  the queue when we do a read on the netlink socket.
+ * @return
+ *  This function returns 0 on success, else -errno.
+ */
+extern int  audit_get_reply(int fd, struct audit_message *rep, reply_t block,
+               int peek);
+
+/**
+ * Sets a pid to recieve audit netlink events from the kernel
+ * @param fd
+ *  The fd returned by a call to audit_open()
+ * @param pid
+ *  The pid whom to set as the reciever of audit messages
+ * @param wmode
+ *  Whether or not to block on the underlying socket io calls.
+ * @return
+ *  This function returns 0 on success, -errno on error.
+ */
+extern int  audit_set_pid(int fd, uint32_t pid, rep_wait_t wmode);
+
+__END_DECLS
+
+#endif
diff --git a/logd/main.cpp b/logd/main.cpp
index 6216b95..ece5a3a 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -24,13 +24,46 @@
 #include <sys/capability.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <unistd.h>
 
 #include <linux/prctl.h>
 
+#include <cutils/properties.h>
+
 #include "private/android_filesystem_config.h"
 #include "CommandListener.h"
 #include "LogBuffer.h"
 #include "LogListener.h"
+#include "LogAudit.h"
+
+//
+//  The service is designed to be run by init, it does not respond well
+// to starting up manually. When starting up manually the sockets will
+// fail to open typically for one of the following reasons:
+//     EADDRINUSE if logger is running.
+//     EACCESS if started without precautions (below)
+//
+// Here is a cookbook procedure for starting up logd manually assuming
+// init is out of the way, pedantically all permissions and selinux
+// security is put back in place:
+//
+//    setenforce 0
+//    rm /dev/socket/logd*
+//    chmod 777 /dev/socket
+//        # here is where you would attach the debugger or valgrind for example
+//    runcon u:r:logd:s0 /system/bin/logd </dev/null >/dev/null 2>&1 &
+//    sleep 1
+//    chmod 755 /dev/socket
+//    chown logd.logd /dev/socket/logd*
+//    restorecon /dev/socket/logd*
+//    setenforce 1
+//
+// If minimalism prevails, typical for debugging and security is not a concern:
+//
+//    setenforce 0
+//    chmod 777 /dev/socket
+//    logd
+//
 
 static int drop_privs() {
     struct sched_param param;
@@ -60,7 +93,10 @@
     capheader.pid = 0;
 
     capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG);
-    capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective = CAP_TO_MASK(CAP_SYSLOG);
+    capdata[CAP_TO_INDEX(CAP_AUDIT_CONTROL)].permitted |= CAP_TO_MASK(CAP_AUDIT_CONTROL);
+
+    capdata[0].effective = capdata[0].permitted;
+    capdata[1].effective = capdata[1].permitted;
     capdata[0].inheritable = 0;
     capdata[1].inheritable = 0;
 
@@ -71,12 +107,34 @@
     return 0;
 }
 
+// Property helper
+static bool property_get_bool(const char *key, bool def) {
+    char property[PROPERTY_VALUE_MAX];
+    property_get(key, property, "");
+
+    if (!strcasecmp(property, "true")) {
+        return true;
+    }
+    if (!strcasecmp(property, "false")) {
+        return false;
+    }
+
+    return def;
+}
+
 // Foreground waits for exit of the three main persistent threads that
 // are started here.  The three threads are created to manage UNIX
 // domain client sockets for writing, reading and controlling the user
 // space logger.  Additional transitory per-client threads are created
 // for each reader once they register.
 int main() {
+    bool auditd = property_get_bool("logd.auditd", true);
+
+    int fdDmesg = -1;
+    if (auditd && property_get_bool("logd.auditd.dmesg", true)) {
+        fdDmesg = open("/dev/kmsg", O_WRONLY);
+    }
+
     if (drop_privs() != 0) {
         return -1;
     }
@@ -92,6 +150,10 @@
 
     LogBuffer *logBuf = new LogBuffer(times);
 
+    if (property_get_bool("logd.statistics.dgram_qlen", false)) {
+        logBuf->enableDgramQlenStatistics();
+    }
+
     // LogReader listens on /dev/socket/logdr. When a client
     // connects, log entries in the LogBuffer are written to the client.
 
@@ -118,6 +180,19 @@
         exit(1);
     }
 
+    // LogAudit listens on NETLINK_AUDIT socket for selinux
+    // initiated log messages. New log entries are added to LogBuffer
+    // and LogReader is notified to send updates to connected clients.
+
+    if (auditd) {
+        // failure is an option ... messages are in dmesg (required by standard)
+        LogAudit *al = new LogAudit(logBuf, reader, fdDmesg);
+        if (al->startListener()) {
+            delete al;
+            close(fdDmesg);
+        }
+    }
+
     pause();
     exit(0);
 }
diff --git a/logd/tests/Android.mk b/logd/tests/Android.mk
new file mode 100644
index 0000000..123e317
--- /dev/null
+++ b/logd/tests/Android.mk
@@ -0,0 +1,53 @@
+#
+# Copyright (C) 2014 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+# -----------------------------------------------------------------------------
+# Benchmarks. (see ../../liblog/tests)
+# -----------------------------------------------------------------------------
+
+test_module_prefix := logd-
+test_tags := tests
+
+# -----------------------------------------------------------------------------
+# Unit tests.
+# -----------------------------------------------------------------------------
+
+test_c_flags := \
+    -fstack-protector-all \
+    -g \
+    -Wall -Wextra \
+    -Werror \
+    -fno-builtin \
+
+ifeq ($(TARGET_USES_LOGD),true)
+test_c_flags += -DTARGET_USES_LOGD=1
+endif
+
+test_src_files := \
+    logd_test.cpp
+
+# Build tests for the logger. Run with:
+#   adb shell /data/nativetest/logd-unit-tests/logd-unit-tests
+include $(CLEAR_VARS)
+LOCAL_MODULE := $(test_module_prefix)unit-tests
+LOCAL_MODULE_TAGS := $(test_tags)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+LOCAL_CFLAGS += $(test_c_flags)
+LOCAL_SHARED_LIBRARIES := libcutils
+LOCAL_SRC_FILES := $(test_src_files)
+include $(BUILD_NATIVE_TEST)
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
new file mode 100644
index 0000000..731aff6
--- /dev/null
+++ b/logd/tests/logd_test.cpp
@@ -0,0 +1,679 @@
+/*
+ * Copyright (C) 2014 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 <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <gtest/gtest.h>
+
+#include "cutils/sockets.h"
+#include "log/log.h"
+#include "log/logger.h"
+
+#define __unused __attribute__((__unused__))
+
+/*
+ * returns statistics
+ */
+static void my_android_logger_get_statistics(char *buf, size_t len)
+{
+    snprintf(buf, len, "getStatistics 0 1 2 3 4");
+    int sock = socket_local_client("logd",
+                                   ANDROID_SOCKET_NAMESPACE_RESERVED,
+                                   SOCK_STREAM);
+    if (sock >= 0) {
+        if (write(sock, buf, strlen(buf) + 1) > 0) {
+            ssize_t ret;
+            while ((ret = read(sock, buf, len)) > 0) {
+                if ((size_t)ret == len) {
+                    break;
+                }
+                len -= ret;
+                buf += ret;
+
+                struct pollfd p = {
+                    .fd = sock,
+                    .events = POLLIN,
+                    .revents = 0
+                };
+
+                ret = poll(&p, 1, 20);
+                if ((ret <= 0) || !(p.revents & POLLIN)) {
+                    break;
+                }
+            }
+        }
+        close(sock);
+    }
+}
+
+static void alloc_statistics(char **buffer, size_t *length)
+{
+    size_t len = 8192;
+    char *buf;
+
+    for(int retry = 32; (retry >= 0); delete [] buf, --retry) {
+        buf = new char [len];
+        my_android_logger_get_statistics(buf, len);
+
+        buf[len-1] = '\0';
+        size_t ret = atol(buf) + 1;
+        if (ret < 4) {
+            delete [] buf;
+            buf = NULL;
+            break;
+        }
+        bool check = ret <= len;
+        len = ret;
+        if (check) {
+            break;
+        }
+        len += len / 8; // allow for some slop
+    }
+    *buffer = buf;
+    *length = len;
+}
+
+static char *find_benchmark_spam(char *cp)
+{
+    // liblog_benchmarks has been run designed to SPAM.  The signature of
+    // a noisiest UID statistics is one of the following:
+    //
+    // main: UID/PID Total size/num   Now          UID/PID[?]  Total
+    // 0           7500306/304207     71608/3183   0/4225?     7454388/303656
+    //    <wrap>                                                     93432/1012
+    // -or-
+    // 0/gone      7454388/303656     93432/1012
+    //
+    // basically if we see a *large* number of 0/????? entries
+    unsigned long value;
+    do {
+        char *benchmark = strstr(cp, " 0/");
+        char *benchmark_newline = strstr(cp, "\n0/");
+        if (!benchmark) {
+            benchmark = benchmark_newline;
+        }
+        if (benchmark_newline && (benchmark > benchmark_newline)) {
+            benchmark = benchmark_newline;
+        }
+        cp = benchmark;
+        if (!cp) {
+            break;
+        }
+        cp += 3;
+        while (isdigit(*cp) || (*cp == 'g') || (*cp == 'o') || (*cp == 'n')) {
+            ++cp;
+        }
+        value = 0;
+        // ###? or gone
+        if ((*cp == '?') || (*cp == 'e')) {
+            while (*++cp == ' ');
+            while (isdigit(*cp)) {
+                value = value * 10ULL + *cp - '0';
+                ++cp;
+            }
+            if (*cp != '/') {
+                value = 0;
+                continue;
+            }
+            while (isdigit(*++cp));
+            while (*cp == ' ') ++cp;
+            if (!isdigit(*cp)) {
+                value = 0;
+            }
+        }
+    } while ((value < 900000ULL) && *cp);
+    return cp;
+}
+
+TEST(logd, statistics) {
+    size_t len;
+    char *buf;
+
+    alloc_statistics(&buf, &len);
+
+#ifdef TARGET_USES_LOGD
+    ASSERT_TRUE(NULL != buf);
+#else
+    if (!buf) {
+        return;
+    }
+#endif
+
+    // remove trailing FF
+    char *cp = buf + len - 1;
+    *cp = '\0';
+    bool truncated = *--cp != '\f';
+    if (!truncated) {
+        *cp = '\0';
+    }
+
+    // squash out the byte count
+    cp = buf;
+    if (!truncated) {
+        while (isdigit(*cp) || (*cp == '\n')) {
+            ++cp;
+        }
+    }
+
+    fprintf(stderr, "%s", cp);
+
+    EXPECT_LT((size_t)64, strlen(cp));
+
+    EXPECT_EQ(0, truncated);
+
+#ifdef TARGET_USES_LOGD
+    char *main_logs = strstr(cp, "\nmain:");
+    EXPECT_TRUE(NULL != main_logs);
+
+    char *radio_logs = strstr(cp, "\nradio:");
+    EXPECT_TRUE(NULL != radio_logs);
+
+    char *system_logs = strstr(cp, "\nsystem:");
+    EXPECT_TRUE(NULL != system_logs);
+
+    char *events_logs = strstr(cp, "\nevents:");
+    EXPECT_TRUE(NULL != events_logs);
+#endif
+
+    // Parse timing stats
+
+    cp = strstr(cp, "Minimum time between log events per dgram_qlen:");
+
+    char *log_events_per_span = cp;
+
+    if (cp) {
+        while (*cp && (*cp != '\n')) {
+            ++cp;
+        }
+        if (*cp == '\n') {
+            ++cp;
+        }
+
+        char *list_of_spans = cp;
+        EXPECT_NE('\0', *list_of_spans);
+
+        unsigned short number_of_buckets = 0;
+        unsigned short *dgram_qlen = NULL;
+        unsigned short bucket = 0;
+        while (*cp && (*cp != '\n')) {
+            bucket = 0;
+            while (isdigit(*cp)) {
+                bucket = bucket * 10 + *cp - '0';
+                ++cp;
+            }
+            while (*cp == ' ') {
+                ++cp;
+            }
+            if (!bucket) {
+                break;
+            }
+            unsigned short *new_dgram_qlen = new unsigned short[number_of_buckets + 1];
+            EXPECT_TRUE(new_dgram_qlen != NULL);
+            if (dgram_qlen) {
+                memcpy(new_dgram_qlen, dgram_qlen, sizeof(*dgram_qlen) * number_of_buckets);
+                delete [] dgram_qlen;
+            }
+
+            dgram_qlen = new_dgram_qlen;
+            dgram_qlen[number_of_buckets++] = bucket;
+        }
+
+        char *end_of_spans = cp;
+        EXPECT_NE('\0', *end_of_spans);
+
+        EXPECT_LT(5, number_of_buckets);
+
+        unsigned long long *times = new unsigned long long [number_of_buckets];
+        ASSERT_TRUE(times != NULL);
+
+        memset(times, 0, sizeof(*times) * number_of_buckets);
+
+        while (*cp == '\n') {
+            ++cp;
+        }
+
+        unsigned short number_of_values = 0;
+        unsigned long long value;
+        while (*cp && (*cp != '\n')) {
+            EXPECT_GE(number_of_buckets, number_of_values);
+
+            value = 0;
+            while (isdigit(*cp)) {
+                value = value * 10ULL + *cp - '0';
+                ++cp;
+            }
+
+            switch(*cp) {
+            case ' ':
+            case '\n':
+                value *= 1000ULL;
+                /* FALLTHRU */
+            case 'm':
+                value *= 1000ULL;
+                /* FALLTHRU */
+            case 'u':
+                value *= 1000ULL;
+                /* FALLTHRU */
+            case 'n':
+            default:
+                break;
+            }
+            while (*++cp == ' ');
+
+            if (!value) {
+                break;
+            }
+
+            times[number_of_values] = value;
+            ++number_of_values;
+        }
+
+#ifdef TARGET_USES_LOGD
+        EXPECT_EQ(number_of_values, number_of_buckets);
+#endif
+
+        FILE *fp;
+        ASSERT_TRUE(NULL != (fp = fopen("/proc/sys/net/unix/max_dgram_qlen", "r")));
+
+        unsigned max_dgram_qlen = 0;
+        fscanf(fp, "%u", &max_dgram_qlen);
+
+        fclose(fp);
+
+        // Find launch point
+        unsigned short launch = 0;
+        unsigned long long total = 0;
+        do {
+            total += times[launch];
+        } while (((++launch < number_of_buckets)
+                && ((total / launch) >= (times[launch] / 8ULL)))
+            || (launch == 1)); // too soon
+
+        bool failure = number_of_buckets <= launch;
+        if (!failure) {
+            unsigned short l = launch;
+            if (l >= number_of_buckets) {
+                l = number_of_buckets - 1;
+            }
+            failure = max_dgram_qlen < dgram_qlen[l];
+        }
+
+        // We can get failure if at any time liblog_benchmarks has been run
+        // because designed to overload /proc/sys/net/unix/max_dgram_qlen even
+        // at excessive values like 20000. It does so to measure the raw processing
+        // performance of logd.
+        if (failure) {
+            cp = find_benchmark_spam(cp);
+        }
+
+        if (cp) {
+            // Fake a failure, but without the failure code
+            if (number_of_buckets <= launch) {
+                printf ("Expected: number_of_buckets > launch, actual: %u vs %u\n",
+                        number_of_buckets, launch);
+            }
+            if (launch >= number_of_buckets) {
+                launch = number_of_buckets - 1;
+            }
+            if (max_dgram_qlen < dgram_qlen[launch]) {
+                printf ("Expected: max_dgram_qlen >= dgram_qlen[%d],"
+                            " actual: %u vs %u\n",
+                        launch, max_dgram_qlen, dgram_qlen[launch]);
+            }
+        } else
+#ifndef TARGET_USES_LOGD
+        if (total)
+#endif
+        {
+            EXPECT_GT(number_of_buckets, launch);
+            if (launch >= number_of_buckets) {
+                launch = number_of_buckets - 1;
+            }
+            EXPECT_GE(max_dgram_qlen, dgram_qlen[launch]);
+        }
+
+        delete [] dgram_qlen;
+        delete [] times;
+    }
+    delete [] buf;
+}
+
+static void caught_signal(int signum __unused) { }
+
+static void dump_log_msg(const char *prefix,
+                         log_msg *msg, unsigned int version, int lid) {
+    switch(msg->entry.hdr_size) {
+    case 0:
+        version = 1;
+        break;
+
+    case sizeof(msg->entry_v2):
+        if (version == 0) {
+            version = 2;
+        }
+        break;
+    }
+
+    fprintf(stderr, "%s: v%u[%u] ", prefix, version, msg->len());
+    if (version != 1) {
+        fprintf(stderr, "hdr_size=%u ", msg->entry.hdr_size);
+    }
+    fprintf(stderr, "pid=%u tid=%u %u.%09u ",
+            msg->entry.pid, msg->entry.tid, msg->entry.sec, msg->entry.nsec);
+    switch(version) {
+    case 1:
+         break;
+    case 2:
+        fprintf(stderr, "euid=%u ", msg->entry_v2.euid);
+        break;
+    case 3:
+    default:
+        lid = msg->entry.lid;
+        break;
+    }
+
+    switch(lid) {
+    case 0:
+        fprintf(stderr, "lid=main ");
+        break;
+    case 1:
+        fprintf(stderr, "lid=radio ");
+        break;
+    case 2:
+        fprintf(stderr, "lid=events ");
+        break;
+    case 3:
+        fprintf(stderr, "lid=system ");
+        break;
+    default:
+        if (lid >= 0) {
+            fprintf(stderr, "lid=%d ", lid);
+        }
+    }
+
+    unsigned int len = msg->entry.len;
+    fprintf(stderr, "msg[%u]={", len);
+    unsigned char *cp = reinterpret_cast<unsigned char *>(msg->msg());
+    while(len) {
+        unsigned char *p = cp;
+        while (*p && (((' ' <= *p) && (*p < 0x7F)) || (*p == '\n'))) {
+            ++p;
+        }
+        if (((p - cp) > 3) && !*p && ((unsigned int)(p - cp) < len)) {
+            fprintf(stderr, "\"");
+            while (*cp) {
+                fprintf(stderr, (*cp != '\n') ? "%c" : "\\n", *cp);
+                ++cp;
+                --len;
+            }
+            fprintf(stderr, "\"");
+        } else {
+            fprintf(stderr, "%02x", *cp);
+        }
+        ++cp;
+        if (--len) {
+            fprintf(stderr, ", ");
+        }
+    }
+    fprintf(stderr, "}\n");
+}
+
+TEST(logd, both) {
+    log_msg msg;
+
+    // check if we can read any logs from logd
+    bool user_logger_available = false;
+    bool user_logger_content = false;
+
+    int fd = socket_local_client("logdr",
+                                 ANDROID_SOCKET_NAMESPACE_RESERVED,
+                                 SOCK_SEQPACKET);
+    if (fd >= 0) {
+        struct sigaction ignore, old_sigaction;
+        memset(&ignore, 0, sizeof(ignore));
+        ignore.sa_handler = caught_signal;
+        sigemptyset(&ignore.sa_mask);
+        sigaction(SIGALRM, &ignore, &old_sigaction);
+        unsigned int old_alarm = alarm(10);
+
+        static const char ask[] = "dumpAndClose lids=0,1,2,3";
+        user_logger_available = write(fd, ask, sizeof(ask)) == sizeof(ask);
+
+        user_logger_content = recv(fd, msg.buf, sizeof(msg), 0) > 0;
+
+        if (user_logger_content) {
+            dump_log_msg("user", &msg, 3, -1);
+        }
+
+        alarm(0);
+        sigaction(SIGALRM, &old_sigaction, NULL);
+
+        close(fd);
+    }
+
+    // check if we can read any logs from kernel logger
+    bool kernel_logger_available = false;
+    bool kernel_logger_content = false;
+
+    static const char *loggers[] = {
+        "/dev/log/main",   "/dev/log_main",
+        "/dev/log/radio",  "/dev/log_radio",
+        "/dev/log/events", "/dev/log_events",
+        "/dev/log/system", "/dev/log_system",
+    };
+
+    for (unsigned int i = 0; i < (sizeof(loggers) / sizeof(loggers[0])); ++i) {
+        fd = open(loggers[i], O_RDONLY);
+        if (fd < 0) {
+            continue;
+        }
+        kernel_logger_available = true;
+        fcntl(fd, F_SETFL, O_RDONLY | O_NONBLOCK);
+        int result = TEMP_FAILURE_RETRY(read(fd, msg.buf, sizeof(msg)));
+        if (result > 0) {
+            kernel_logger_content = true;
+            dump_log_msg("kernel", &msg, 0, i / 2);
+        }
+        close(fd);
+    }
+
+    static const char yes[] = "\xE2\x9C\x93";
+    static const char no[] = "\xE2\x9c\x98";
+    fprintf(stderr,
+            "LOGGER  Available  Content\n"
+            "user    %-13s%s\n"
+            "kernel  %-13s%s\n"
+            " status %-11s%s\n",
+            (user_logger_available)   ? yes : no,
+            (user_logger_content)     ? yes : no,
+            (kernel_logger_available) ? yes : no,
+            (kernel_logger_content)   ? yes : no,
+            (user_logger_available && kernel_logger_available) ? "WARNING" : "ok",
+            (user_logger_content && kernel_logger_content) ? "ERROR" : "ok");
+
+    if (user_logger_available && kernel_logger_available) {
+        printf("WARNING: kernel & user logger; both consuming resources!!!\n");
+    }
+
+    EXPECT_EQ(0, user_logger_content && kernel_logger_content);
+    EXPECT_EQ(0, !user_logger_content && !kernel_logger_content);
+}
+
+// BAD ROBOT
+//   Benchmark threshold are generally considered bad form unless there is
+//   is some human love applied to the continued maintenance and whether the
+//   thresholds are tuned on a per-target basis. Here we check if the values
+//   are more than double what is expected. Doubling will not prevent failure
+//   on busy or low-end systems that could have a tendency to stretch values.
+//
+//   The primary goal of this test is to simulate a spammy app (benchmark
+//   being the worst) and check to make sure the logger can deal with it
+//   appropriately by checking all the statistics are in an expected range.
+//
+TEST(logd, benchmark) {
+    size_t len;
+    char *buf;
+
+    alloc_statistics(&buf, &len);
+    bool benchmark_already_run = buf && find_benchmark_spam(buf);
+    delete [] buf;
+
+    if (benchmark_already_run) {
+        fprintf(stderr, "WARNING: spam already present and too much history\n"
+                        "         false OK for prune by worst UID check\n");
+    }
+
+    FILE *fp;
+
+    // Introduce some extreme spam for the worst UID filter
+    ASSERT_TRUE(NULL != (fp = popen(
+        "/data/nativetest/liblog-benchmarks/liblog-benchmarks",
+        "r")));
+
+    char buffer[5120];
+
+    static const char *benchmarks[] = {
+        "BM_log_maximum_retry ",
+        "BM_log_maximum ",
+        "BM_clock_overhead ",
+        "BM_log_overhead ",
+        "BM_log_latency ",
+        "BM_log_delay "
+    };
+    static const unsigned int log_maximum_retry = 0;
+    static const unsigned int log_maximum = 1;
+    static const unsigned int clock_overhead = 2;
+    static const unsigned int log_overhead = 3;
+    static const unsigned int log_latency = 4;
+    static const unsigned int log_delay = 5;
+
+    unsigned long ns[sizeof(benchmarks) / sizeof(benchmarks[0])];
+
+    memset(ns, 0, sizeof(ns));
+
+    while (fgets(buffer, sizeof(buffer), fp)) {
+        for (unsigned i = 0; i < sizeof(ns) / sizeof(ns[0]); ++i) {
+            if (strncmp(benchmarks[i], buffer, strlen(benchmarks[i]))) {
+                continue;
+            }
+            sscanf(buffer, "%*s %lu %lu", &ns[i], &ns[i]);
+            fprintf(stderr, "%-22s%8lu\n", benchmarks[i], ns[i]);
+        }
+    }
+    int ret = pclose(fp);
+
+    if (!WIFEXITED(ret) || (WEXITSTATUS(ret) == 127)) {
+        fprintf(stderr,
+                "WARNING: "
+                "/data/nativetest/liblog-benchmarks/liblog-benchmarks missing\n"
+                "         can not perform test\n");
+        return;
+    }
+
+#ifdef TARGET_USES_LOGD
+    EXPECT_GE(100000UL, ns[log_maximum_retry]); // 42777 user
+#else
+    EXPECT_GE(10000UL, ns[log_maximum_retry]); // 5636 kernel
+#endif
+
+#ifdef TARGET_USES_LOGD
+    EXPECT_GE(25000UL, ns[log_maximum]); // 14055 user
+#else
+    EXPECT_GE(10000UL, ns[log_maximum]); // 5637 kernel
+#endif
+
+    EXPECT_GE(4000UL, ns[clock_overhead]); // 2008
+
+#ifdef TARGET_USES_LOGD
+    EXPECT_GE(250000UL, ns[log_overhead]); // 113219 user
+#else
+    EXPECT_GE(100000UL, ns[log_overhead]); // 50945 kernel
+#endif
+
+#ifdef TARGET_USES_LOGD
+    EXPECT_GE(7500UL, ns[log_latency]); // 3718 user space
+#else
+    EXPECT_GE(500000UL, ns[log_latency]); // 254200 kernel
+#endif
+
+#ifdef TARGET_USES_LOGD
+    EXPECT_GE(20000000UL, ns[log_delay]); // 9542541 user
+#else
+    EXPECT_GE(55000UL, ns[log_delay]); // 27341 kernel
+#endif
+
+    for (unsigned i = 0; i < sizeof(ns) / sizeof(ns[0]); ++i) {
+        EXPECT_NE(0UL, ns[i]);
+    }
+
+    alloc_statistics(&buf, &len);
+
+#ifdef TARGET_USES_LOGD
+    bool collected_statistics = !!buf;
+    EXPECT_EQ(true, collected_statistics);
+#else
+    if (!buf) {
+        return;
+    }
+#endif
+
+    ASSERT_TRUE(NULL != buf);
+
+    char *benchmark_statistics_found = find_benchmark_spam(buf);
+    ASSERT_TRUE(benchmark_statistics_found != NULL);
+
+    // Check how effective the SPAM filter is, parse out Now size.
+    //             Total               Now
+    // 0/4225?     7454388/303656      31488/755
+    //                                 ^-- benchmark_statistics_found
+
+    unsigned long nowSize = atol(benchmark_statistics_found);
+
+    delete [] buf;
+
+    ASSERT_NE(0UL, nowSize);
+
+    int sock = socket_local_client("logd",
+                                   ANDROID_SOCKET_NAMESPACE_RESERVED,
+                                   SOCK_STREAM);
+    static const unsigned long expected_absolute_minimum_log_size = 65536UL;
+    unsigned long totalSize = expected_absolute_minimum_log_size;
+    if (sock >= 0) {
+        static const char getSize[] = {
+            'g', 'e', 't', 'L', 'o', 'g', 'S', 'i', 'z', 'e', ' ',
+            LOG_ID_MAIN + '0', '\0'
+        };
+        if (write(sock, getSize, sizeof(getSize)) > 0) {
+            char buffer[80];
+            memset(buffer, 0, sizeof(buffer));
+            read(sock, buffer, sizeof(buffer));
+            totalSize = atol(buffer);
+            if (totalSize < expected_absolute_minimum_log_size) {
+                totalSize = expected_absolute_minimum_log_size;
+            }
+        }
+        close(sock);
+    }
+    // logd allows excursions to 110% of total size
+    totalSize = (totalSize * 11 ) / 10;
+
+    // 50% threshold for SPAM filter (<20% typical, lots of engineering margin)
+    ASSERT_GT(totalSize, nowSize * 2);
+}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 01bef01..81d173a 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -7,6 +7,7 @@
 import /init.environ.rc
 import /init.usb.rc
 import /init.${ro.hardware}.rc
+import /init.${ro.zygote}.rc
 import /init.trace.rc
 
 on early-init
@@ -181,6 +182,9 @@
     chown system log /proc/last_kmsg
     chmod 0440 /proc/last_kmsg
 
+    # make the selinux kernel policy world-readable
+    chmod 0444 /sys/fs/selinux/policy
+
     # create the lost+found directories, so as to enforce our permissions
     mkdir /cache/lost+found 0770 root root
 
@@ -373,13 +377,13 @@
     setprop net.tcp.buffersize.wifi     524288,1048576,2097152,262144,524288,1048576
     setprop net.tcp.buffersize.ethernet 524288,1048576,3145728,524288,1048576,2097152
     setprop net.tcp.buffersize.lte      524288,1048576,2097152,262144,524288,1048576
-    setprop net.tcp.buffersize.umts     4094,87380,110208,4096,16384,110208
-    setprop net.tcp.buffersize.hspa     4094,87380,262144,4096,16384,262144
-    setprop net.tcp.buffersize.hsupa    4094,87380,262144,4096,16384,262144
-    setprop net.tcp.buffersize.hsdpa    4094,87380,262144,4096,16384,262144
-    setprop net.tcp.buffersize.hspap    4094,87380,1220608,4096,16384,1220608
-    setprop net.tcp.buffersize.edge     4093,26280,35040,4096,16384,35040
-    setprop net.tcp.buffersize.gprs     4092,8760,11680,4096,8760,11680
+    setprop net.tcp.buffersize.umts     58254,349525,1048576,58254,349525,1048576
+    setprop net.tcp.buffersize.hspa     40778,244668,734003,16777,100663,301990
+    setprop net.tcp.buffersize.hsupa    40778,244668,734003,16777,100663,301990
+    setprop net.tcp.buffersize.hsdpa    61167,367002,1101005,8738,52429,262114
+    setprop net.tcp.buffersize.hspap    122334,734003,2202010,32040,192239,576717
+    setprop net.tcp.buffersize.edge     4093,26280,70800,4096,16384,70800
+    setprop net.tcp.buffersize.gprs     4092,8760,48000,4096,8760,48000
     setprop net.tcp.buffersize.evdo     4094,87380,262144,4096,16384,262144
 
 # Define default initial receive window size in segments.
@@ -514,14 +518,6 @@
     group graphics drmrpc
     onrestart restart zygote
 
-service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
-    class main
-    socket zygote stream 660 root system
-    onrestart write /sys/android_power/request_state wake
-    onrestart write /sys/power/state on
-    onrestart restart media
-    onrestart restart netd
-
 service drm /system/bin/drmserver
     class main
     user drm
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
new file mode 100644
index 0000000..75961e6
--- /dev/null
+++ b/rootdir/init.zygote32.rc
@@ -0,0 +1,8 @@
+service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
+    class main
+    socket zygote stream 660 root system
+    onrestart write /sys/android_power/request_state wake
+    onrestart write /sys/power/state on
+    onrestart restart media
+    onrestart restart netd
+
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
new file mode 100644
index 0000000..3d60a31
--- /dev/null
+++ b/rootdir/init.zygote32_64.rc
@@ -0,0 +1,12 @@
+service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
+    class main
+    socket zygote stream 660 root system
+    onrestart write /sys/android_power/request_state wake
+    onrestart write /sys/power/state on
+    onrestart restart media
+    onrestart restart netd
+
+service zygote_secondary /system/bin/app_process64 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
+    class main
+    socket zygote_secondary stream 660 root system
+    onrestart restart zygote
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
new file mode 100644
index 0000000..afb6d63
--- /dev/null
+++ b/rootdir/init.zygote64.rc
@@ -0,0 +1,8 @@
+service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
+    class main
+    socket zygote stream 660 root system
+    onrestart write /sys/android_power/request_state wake
+    onrestart write /sys/power/state on
+    onrestart restart media
+    onrestart restart netd
+
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 9bf17e2..b8fe716 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -35,7 +35,7 @@
 /dev/uhid                 0660   system     net_bt_stack
 /dev/uinput               0660   system     net_bt_stack
 /dev/alarm                0664   system     radio
-/dev/rtc0                 0664   system     radio
+/dev/rtc0                 0640   system     system
 /dev/tty0                 0660   root       system
 /dev/graphics/*           0660   root       graphics
 /dev/msm_hw3dm            0660   system     graphics
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index 3deb3e7..5383b83 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -95,6 +95,7 @@
 LOCAL_C_INCLUDES := bionic/libc/bionic
 
 LOCAL_CFLAGS += \
+    -std=gnu99 \
     -Wno-unused-parameter \
     -include bsd-compatibility.h \
 
diff --git a/toolbox/date.c b/toolbox/date.c
index d6c9052..aa3b72e 100644
--- a/toolbox/date.c
+++ b/toolbox/date.c
@@ -1,10 +1,13 @@
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
 #include <string.h>
-#include <errno.h>
 #include <time.h>
+#include <unistd.h>
+
 #include <linux/android_alarm.h>
 #include <linux/rtc.h>
 #include <sys/ioctl.h>
@@ -108,6 +111,33 @@
         settime_rtc_tm(&tm);
 }
 
+static char *parse_time(const char *str, struct timeval *ts) {
+  char *s;
+  long fs = 0; /* fractional seconds */
+
+  ts->tv_sec = strtoumax(str, &s, 10);
+
+  if (*s == '.') {
+    s++;
+    int count = 0;
+
+    /* read up to 6 digits (microseconds) */
+    while (*s && isdigit(*s)) {
+      if (++count < 7) {
+        fs = fs*10 + (*s - '0');
+      }
+      s++;
+    }
+
+    for (; count < 6; count++) {
+      fs *= 10;
+    }
+  }
+
+  ts->tv_usec = fs;
+  return s;
+}
+
 int date_main(int argc, char *argv[])
 {
 	int c;
@@ -181,7 +211,7 @@
         //strptime(argv[optind], NULL, &tm);
         //tv.tv_sec = mktime(&tm);
         //tv.tv_usec = 0;
-        strtotimeval(argv[optind], &tv);
+        parse_time(argv[optind], &tv);
         printf("time %s -> %lu.%lu\n", argv[optind], tv.tv_sec, tv.tv_usec);
         res = settime_alarm_timeval(&tv);
         if (res < 0)
diff --git a/toolbox/syren.c b/toolbox/syren.c
index 06e329e..47c2460 100644
--- a/toolbox/syren.c
+++ b/toolbox/syren.c
@@ -123,7 +123,11 @@
 
 	r = find_reg(argv[2]);
 	if (r == NULL) {
-		strcpy(name, argv[2]);
+		if(strlen(argv[2]) >= sizeof(name)){
+			fprintf(stderr, "REGNAME too long\n");
+			return 0;
+		}
+		strlcpy(name, argv[2], sizeof(name));
 		char *addr_str = strchr(argv[2], ':');
 		if (addr_str == NULL)
 			return usage();
@@ -131,7 +135,7 @@
 		sio.page = strtoul(argv[2], 0, 0);
 		sio.addr = strtoul(addr_str, 0, 0);
 	} else {
-		strcpy(name, r->name);
+		strlcpy(name, r->name, sizeof(name));
 		sio.page = r->page;
 		sio.addr = r->addr;
 	}
diff --git a/toolbox/top.c b/toolbox/top.c
index 7642522..7382f1f 100644
--- a/toolbox/top.c
+++ b/toolbox/top.c
@@ -9,7 +9,7 @@
  *    notice, this list of conditions and the following disclaimer.
  *  * Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the 
+ *    the documentation and/or other materials provided with the
  *    distribution.
  *  * Neither the name of Google, Inc. nor the names of its contributors
  *    may be used to endorse or promote products derived from this
@@ -22,7 +22,7 @@
  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
@@ -108,16 +108,20 @@
 static int numcmp(long long a, long long b);
 static void usage(char *cmd);
 
-int top_main(int argc, char *argv[]) {
-    int i;
+static void exit_top(int signal) {
+  exit(EXIT_FAILURE);
+}
 
+int top_main(int argc, char *argv[]) {
     num_used_procs = num_free_procs = 0;
 
+    signal(SIGPIPE, exit_top);
+
     max_procs = 0;
     delay = 3;
     iterations = -1;
     proc_cmp = &proc_cpu_cmp;
-    for (i = 1; i < argc; i++) {
+    for (int i = 1; i < argc; i++) {
         if (!strcmp(argv[i], "-m")) {
             if (i + 1 >= argc) {
                 fprintf(stderr, "Option -m expects an argument.\n");
@@ -249,9 +253,9 @@
             continue;
 
         pid = atoi(pid_dir->d_name);
-        
+
         struct proc_info cur_proc;
-        
+
         if (!threads) {
             proc = alloc_proc();
 
@@ -275,7 +279,7 @@
 
             sprintf(filename, "/proc/%d/status", pid);
             read_status(filename, &cur_proc);
-            
+
             proc = NULL;
         }
 
@@ -310,7 +314,7 @@
         }
 
         closedir(task_dir);
-        
+
         if (!threads)
             add_proc(proc_num++, proc);
     }
@@ -339,7 +343,7 @@
     *open_paren = *close_paren = '\0';
     strncpy(proc->tname, open_paren + 1, THREAD_NAME_LEN);
     proc->tname[THREAD_NAME_LEN-1] = 0;
-    
+
     /* Scan rest of string. */
     sscanf(close_paren + 1, " %c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d "
                  "%lu %lu %*d %*d %*d %*d %*d %*d %*d %lu %ld "
@@ -452,7 +456,7 @@
             new_cpu.sirqtime - old_cpu.sirqtime,
             total_delta_time);
     printf("\n");
-    if (!threads) 
+    if (!threads)
         printf("%5s %2s %4s %1s %5s %7s %7s %3s %-8s %s\n", "PID", "PR", "CPU%", "S", "#THR", "VSS", "RSS", "PCY", "UID", "Name");
     else
         printf("%5s %5s %2s %4s %1s %7s %7s %3s %-8s %-15s %s\n", "PID", "TID", "PR", "CPU%", "S", "VSS", "RSS", "PCY", "UID", "Thread", "Proc");
@@ -476,7 +480,7 @@
             snprintf(group_buf, 20, "%d", proc->gid);
             group_str = group_buf;
         }
-        if (!threads) 
+        if (!threads)
             printf("%5d %2d %3ld%% %c %5d %6ldK %6ldK %3s %-8.8s %s\n", proc->pid, proc->prs, proc->delta_time * 100 / total_delta_time, proc->state, proc->num_threads,
                 proc->vss / 1024, proc->rss * getpagesize() / 1024, proc->policy, user_str, proc->name[0] != 0 ? proc->name : proc->tname);
         else