Merge "logd: whitelist should not preserve expire messages"
diff --git a/adb/Android.mk b/adb/Android.mk
index f3a4822..f030041 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -14,8 +14,9 @@
 adb_version := $(shell git -C $(LOCAL_PATH) rev-parse --short=12 HEAD 2>/dev/null)-android
 
 ADB_COMMON_CFLAGS := \
-    -Wall -Werror \
+    -Wall -Wextra -Werror \
     -Wno-unused-parameter \
+    -Wno-missing-field-initializers \
     -DADB_REVISION='"$(adb_version)"' \
 
 # libadb
@@ -45,7 +46,6 @@
 
 LIBADB_CFLAGS := \
     $(ADB_COMMON_CFLAGS) \
-    -Wno-missing-field-initializers \
     -fvisibility=hidden \
 
 LIBADB_darwin_SRC_FILES := \
@@ -77,6 +77,10 @@
 
 LOCAL_SHARED_LIBRARIES := libbase
 
+# Even though we're building a static library (and thus there's no link step for
+# this to take effect), this adds the includes to our path.
+LOCAL_STATIC_LIBRARIES := libbase
+
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -91,8 +95,8 @@
 LOCAL_SHARED_LIBRARIES := libbase
 
 # Even though we're building a static library (and thus there's no link step for
-# this to take effect), this adds the SSL includes to our path.
-LOCAL_STATIC_LIBRARIES := libcrypto_static
+# this to take effect), this adds the includes to our path.
+LOCAL_STATIC_LIBRARIES := libcrypto_static libbase
 
 ifeq ($(HOST_OS),windows)
     LOCAL_C_INCLUDES += development/host/windows/usb/api/
@@ -254,10 +258,10 @@
     libbase \
     libfs_mgr \
     liblog \
-    libcutils \
-    libc \
     libmincrypt \
     libselinux \
     libext4_utils_static \
+    libcutils \
+    libbase \
 
 include $(BUILD_EXECUTABLE)
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 3d1b7b4..9c1ead5 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -31,7 +31,10 @@
 #include <time.h>
 
 #include <string>
+#include <vector>
+#include <unordered_map>
 
+#include <base/logging.h>
 #include <base/stringprintf.h>
 #include <base/strings.h>
 
@@ -48,16 +51,25 @@
 #include <sys/mount.h>
 #endif
 
-ADB_MUTEX_DEFINE( D_lock );
+ADB_MUTEX_DEFINE(D_lock);
 
 int HOST = 0;
 
 #if !ADB_HOST
-const char *adb_device_banner = "device";
+const char* adb_device_banner = "device";
+static android::base::LogdLogger gLogdLogger;
 #endif
 
-void fatal(const char *fmt, ...)
-{
+void AdbLogger(android::base::LogId id, android::base::LogSeverity severity,
+               const char* tag, const char* file, unsigned int line,
+               const char* message) {
+    android::base::StderrLogger(id, severity, tag, file, line, message);
+#if !ADB_HOST
+    gLogdLogger(id, severity, tag, file, line, message);
+#endif
+}
+
+void fatal(const char *fmt, ...) {
     va_list ap;
     va_start(ap, fmt);
     fprintf(stderr, "error: ");
@@ -67,8 +79,7 @@
     exit(-1);
 }
 
-void fatal_errno(const char *fmt, ...)
-{
+void fatal_errno(const char* fmt, ...) {
     va_list ap;
     va_start(ap, fmt);
     fprintf(stderr, "error: %s: ", strerror(errno));
@@ -79,7 +90,7 @@
 }
 
 #if !ADB_HOST
-void start_device_log(void) {
+static std::string get_log_file_name() {
     struct tm now;
     time_t t;
     tzset();
@@ -89,13 +100,18 @@
     char timestamp[PATH_MAX];
     strftime(timestamp, sizeof(timestamp), "%Y-%m-%d-%H-%M-%S", &now);
 
-    std::string path = android::base::StringPrintf("/data/adb/adb-%s-%d", timestamp, getpid());
-    int fd = unix_open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0640);
+    return android::base::StringPrintf("/data/adb/adb-%s-%d", timestamp,
+                                       getpid());
+}
+
+void start_device_log(void) {
+    int fd = unix_open(get_log_file_name().c_str(),
+                       O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0640);
     if (fd == -1) {
         return;
     }
 
-    // redirect stdout and stderr to the log file
+    // Redirect stdout and stderr to the log file.
     dup2(fd, STDOUT_FILENO);
     dup2(fd, STDERR_FILENO);
     fprintf(stderr, "--- adb starting (pid %d) ---\n", getpid());
@@ -130,71 +146,58 @@
 #endif
 }
 
-// Split the comma/space/colum/semi-column separated list of tags from the trace
-// setting and build the trace mask from it. note that '1' and 'all' are special
-// cases to enable all tracing.
+// Split the space separated list of tags from the trace setting and build the
+// trace mask from it. note that '1' and 'all' are special cases to enable all
+// tracing.
 //
 // adb's trace setting comes from the ADB_TRACE environment variable, whereas
 // adbd's comes from the system property persist.adb.trace_mask.
-void adb_trace_init() {
+static void setup_trace_mask() {
     const std::string trace_setting = get_trace_setting();
 
-    static const struct {
-        const char*  tag;
-        int           flag;
-    } tags[] = {
-        { "1", 0 },
-        { "all", 0 },
-        { "adb", TRACE_ADB },
-        { "sockets", TRACE_SOCKETS },
-        { "packets", TRACE_PACKETS },
-        { "rwx", TRACE_RWX },
-        { "usb", TRACE_USB },
-        { "sync", TRACE_SYNC },
-        { "sysdeps", TRACE_SYSDEPS },
-        { "transport", TRACE_TRANSPORT },
-        { "jdwp", TRACE_JDWP },
-        { "services", TRACE_SERVICES },
-        { "auth", TRACE_AUTH },
-        { NULL, 0 }
-    };
+    std::unordered_map<std::string, int> trace_flags = {
+        {"1", 0},
+        {"all", 0},
+        {"adb", TRACE_ADB},
+        {"sockets", TRACE_SOCKETS},
+        {"packets", TRACE_PACKETS},
+        {"rwx", TRACE_RWX},
+        {"usb", TRACE_USB},
+        {"sync", TRACE_SYNC},
+        {"sysdeps", TRACE_SYSDEPS},
+        {"transport", TRACE_TRANSPORT},
+        {"jdwp", TRACE_JDWP},
+        {"services", TRACE_SERVICES},
+        {"auth", TRACE_AUTH}};
 
-    if (trace_setting.empty()) {
-        return;
-    }
-
-    // Use a comma/colon/semi-colon/space separated list
-    const char* p = trace_setting.c_str();
-    while (*p) {
-        int  len, tagn;
-
-        const char* q = strpbrk(p, " ,:;");
-        if (q == NULL) {
-            q = p + strlen(p);
+    std::vector<std::string> elements = android::base::Split(trace_setting, " ");
+    for (const auto& elem : elements) {
+        const auto& flag = trace_flags.find(elem);
+        if (flag == trace_flags.end()) {
+            D("Unknown trace flag: %s", flag->first.c_str());
+            continue;
         }
-        len = q - p;
 
-        for (tagn = 0; tags[tagn].tag != NULL; tagn++) {
-            int  taglen = strlen(tags[tagn].tag);
-
-            if (len == taglen && !memcmp(tags[tagn].tag, p, len)) {
-                int  flag = tags[tagn].flag;
-                if (flag == 0) {
-                    adb_trace_mask = ~0;
-                    return;
-                }
-                adb_trace_mask |= (1 << flag);
-                break;
-            }
+        if (flag->second == 0) {
+            // 0 is used for the special values "1" and "all" that enable all
+            // tracing.
+            adb_trace_mask = ~0;
+            return;
+        } else {
+            adb_trace_mask |= 1 << flag->second;
         }
-        p = q;
-        if (*p)
-            p++;
     }
+}
 
+void adb_trace_init(char** argv) {
 #if !ADB_HOST
-    start_device_log();
+    if (isatty(STDOUT_FILENO) == 0) {
+        start_device_log();
+    }
 #endif
+
+    setup_trace_mask();
+    android::base::InitLogging(argv, AdbLogger);
 }
 
 apacket* get_apacket(void)
@@ -718,56 +721,50 @@
         return 1;
     }
 
-    if (!strncmp(service, "forward:",8) ||
-        !strncmp(service, "killforward:",12)) {
-        char *local, *remote;
-        atransport *transport;
-
-        int createForward = strncmp(service, "kill", 4);
-        int no_rebind = 0;
-
-        local = strchr(service, ':') + 1;
-
-        // Handle forward:norebind:<local>... here
-        if (createForward && !strncmp(local, "norebind:", 9)) {
-            no_rebind = 1;
-            local = strchr(local, ':') + 1;
+    if (!strncmp(service, "forward:", 8) || !strncmp(service, "killforward:", 12)) {
+        // killforward:local
+        // forward:(norebind:)?local;remote
+        bool kill_forward = false;
+        bool no_rebind = false;
+        if (android::base::StartsWith(service, "killforward:")) {
+            kill_forward = true;
+            service += 12;
+            if (android::base::StartsWith(service, "norebind:")) {
+                no_rebind = true;
+                service += 9;
+            }
+        } else {
+            service += 8;
         }
 
-        remote = strchr(local,';');
+        std::vector<std::string> pieces = android::base::Split(service, ";");
 
-        if (createForward) {
-            // Check forward: parameter format: '<local>;<remote>'
-            if(remote == 0) {
-                SendFail(reply_fd, "malformed forward spec");
-                return 1;
-            }
-
-            *remote++ = 0;
-            if((local[0] == 0) || (remote[0] == 0) || (remote[0] == '*')) {
-                SendFail(reply_fd, "malformed forward spec");
+        if (kill_forward) {
+            // Check killforward: parameter format: '<local>'
+            if (pieces.size() != 1 || pieces[0].empty()) {
+                SendFail(reply_fd, android::base::StringPrintf("bad killforward: %s", service));
                 return 1;
             }
         } else {
-            // Check killforward: parameter format: '<local>'
-            if (local[0] == 0) {
-                SendFail(reply_fd, "malformed forward spec");
+            // Check forward: parameter format: '<local>;<remote>'
+            if (pieces.size() != 2 || pieces[0].empty() || pieces[1].empty() || pieces[1][0] == '*') {
+                SendFail(reply_fd, android::base::StringPrintf("bad forward: %s", service));
                 return 1;
             }
         }
 
         std::string error_msg;
-        transport = acquire_one_transport(kCsAny, type, serial, &error_msg);
+        atransport* transport = acquire_one_transport(kCsAny, type, serial, &error_msg);
         if (!transport) {
             SendFail(reply_fd, error_msg);
             return 1;
         }
 
         InstallStatus r;
-        if (createForward) {
-            r = install_listener(local, remote, transport, no_rebind);
+        if (kill_forward) {
+            r = remove_listener(pieces[0].c_str(), transport);
         } else {
-            r = remove_listener(local, transport);
+            r = install_listener(pieces[0], pieces[1].c_str(), transport, no_rebind);
         }
         if (r == INSTALL_STATUS_OK) {
 #if ADB_HOST
@@ -780,7 +777,7 @@
 
         std::string message;
         switch (r) {
-          case INSTALL_STATUS_OK: message = " "; break;
+          case INSTALL_STATUS_OK: message = "success (!)"; break;
           case INSTALL_STATUS_INTERNAL_ERROR: message = "internal error"; break;
           case INSTALL_STATUS_CANNOT_BIND:
             message = android::base::StringPrintf("cannot bind to socket: %s", strerror(errno));
@@ -788,7 +785,9 @@
           case INSTALL_STATUS_CANNOT_REBIND:
             message = android::base::StringPrintf("cannot rebind existing socket: %s", strerror(errno));
             break;
-          case INSTALL_STATUS_LISTENER_NOT_FOUND: message = "listener not found"; break;
+          case INSTALL_STATUS_LISTENER_NOT_FOUND:
+            message = android::base::StringPrintf("listener '%s' not found", service);
+            break;
         }
         SendFail(reply_fd, message);
         return 1;
diff --git a/adb/adb.h b/adb/adb.h
index e8960e3..1be83d7 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -20,6 +20,8 @@
 #include <limits.h>
 #include <sys/types.h>
 
+#include <base/macros.h>
+
 #include "adb_trace.h"
 #include "fdevent.h"
 
@@ -43,7 +45,7 @@
 // Increment this when we want to force users to start a new adb server.
 #define ADB_SERVER_VERSION 32
 
-struct atransport;
+class atransport;
 struct usb_handle;
 
 struct amessage {
@@ -152,20 +154,19 @@
 };
 
 
-/* a transport object models the connection to a remote device or emulator
-** there is one transport per connected device/emulator. a "local transport"
-** connects through TCP (for the emulator), while a "usb transport" through
-** USB (for real devices)
-**
-** note that kTransportHost doesn't really correspond to a real transport
-** object, it's a special value used to indicate that a client wants to
-** connect to a service implemented within the ADB server itself.
-*/
+// A transport object models the connection to a remote device or emulator there
+// is one transport per connected device/emulator. A "local transport" connects
+// through TCP (for the emulator), while a "usb transport" through USB (for real
+// devices).
+//
+// Note that kTransportHost doesn't really correspond to a real transport
+// object, it's a special value used to indicate that a client wants to connect
+// to a service implemented within the ADB server itself.
 enum TransportType {
-        kTransportUsb,
-        kTransportLocal,
-        kTransportAny,
-        kTransportHost,
+    kTransportUsb,
+    kTransportLocal,
+    kTransportAny,
+    kTransportHost,
 };
 
 #define TOKEN_SIZE 20
@@ -182,47 +183,59 @@
     kCsUnauthorized,
 };
 
-struct atransport
-{
-    atransport *next;
-    atransport *prev;
+class atransport {
+public:
+    // TODO(danalbert): We expose waaaaaaay too much stuff because this was
+    // historically just a struct, but making the whole thing a more idiomatic
+    // class in one go is a very large change. Given how bad our testing is,
+    // it's better to do this piece by piece.
 
-    int (*read_from_remote)(apacket *p, atransport *t);
-    int (*write_to_remote)(apacket *p, atransport *t);
-    void (*close)(atransport *t);
-    void (*kick)(atransport *t);
+    atransport() {
+        auth_fde = {};
+        transport_fde = {};
+    }
 
-    int fd;
-    int transport_socket;
+    virtual ~atransport() {}
+
+    int (*read_from_remote)(apacket* p, atransport* t) = nullptr;
+    int (*write_to_remote)(apacket* p, atransport* t) = nullptr;
+    void (*close)(atransport* t) = nullptr;
+    void (*kick)(atransport* t) = nullptr;
+
+    int fd = -1;
+    int transport_socket = -1;
     fdevent transport_fde;
-    int ref_count;
-    unsigned sync_token;
-    int connection_state;
-    int online;
-    TransportType type;
+    int ref_count = 0;
+    uint32_t sync_token = 0;
+    ConnectionState connection_state = kCsOffline;
+    bool online = false;
+    TransportType type = kTransportAny;
 
-        /* usb handle or socket fd as needed */
-    usb_handle *usb;
-    int sfd;
+    // USB handle or socket fd as needed.
+    usb_handle* usb = nullptr;
+    int sfd = -1;
 
-        /* used to identify transports for clients */
-    char *serial;
-    char *product;
-    char *model;
-    char *device;
-    char *devpath;
-    int adb_port; // Use for emulators (local transport)
+    // Used to identify transports for clients.
+    char* serial = nullptr;
+    char* product = nullptr;
+    char* model = nullptr;
+    char* device = nullptr;
+    char* devpath = nullptr;
+    int adb_port = -1;  // Use for emulators (local transport)
+    bool kicked = false;
 
-        /* a list of adisconnect callbacks called when the transport is kicked */
-    int          kicked;
-    adisconnect  disconnects;
+    // A list of adisconnect callbacks called when the transport is kicked.
+    adisconnect disconnects = {};
 
-    void *key;
-    unsigned char token[TOKEN_SIZE];
+    void* key = nullptr;
+    unsigned char token[TOKEN_SIZE] = {};
     fdevent auth_fde;
-    unsigned failed_auth_attempts;
+    size_t failed_auth_attempts = 0;
 
     const char* connection_state_name() const;
+
+private:
+    DISALLOW_COPY_AND_ASSIGN(atransport);
 };
 
 
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index c73d737..ef9a586 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -256,19 +256,21 @@
 }
 
 
-int adb_command(const std::string& service, std::string* error) {
-    int fd = adb_connect(service, error);
+bool adb_command(const std::string& service) {
+    std::string error;
+    int fd = adb_connect(service, &error);
     if (fd < 0) {
-        fprintf(stderr, "error: %s\n", error->c_str());
-        return -1;
+        fprintf(stderr, "error: %s\n", error.c_str());
+        return false;
     }
 
-    if (!adb_status(fd, error)) {
+    if (!adb_status(fd, &error)) {
+        fprintf(stderr, "error: %s\n", error.c_str());
         adb_close(fd);
-        return -1;
+        return false;
     }
 
-    return 0;
+    return true;
 }
 
 bool adb_query(const std::string& service, std::string* result, std::string* error) {
diff --git a/adb/adb_client.h b/adb/adb_client.h
index 9895c49..5de0638 100644
--- a/adb/adb_client.h
+++ b/adb/adb_client.h
@@ -26,9 +26,9 @@
 int adb_connect(const std::string& service, std::string* error);
 int _adb_connect(const std::string& service, std::string* error);
 
-// Connect to adb, connect to the named service, return 0 if the connection
-// succeeded AND the service returned OKAY.
-int adb_command(const std::string& service, std::string* error);
+// Connect to adb, connect to the named service, returns true if the connection
+// succeeded AND the service returned OKAY. Outputs any returned error otherwise.
+bool adb_command(const std::string& service);
 
 // Connects to the named adb service and fills 'result' with the response.
 // Returns true on success; returns false and fills 'error' on failure.
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index cf193ab..a335eec 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -26,13 +26,12 @@
 
 int gListenAll = 0; /* Not static because it is used in commandline.c. */
 
-alistener listener_list = {
+static alistener listener_list = {
     .next = &listener_list,
     .prev = &listener_list,
 };
 
-void ss_listener_event_func(int _fd, unsigned ev, void *_l)
-{
+static void ss_listener_event_func(int _fd, unsigned ev, void *_l) {
     asocket *s;
 
     if(ev & FDE_READ) {
@@ -56,7 +55,7 @@
     }
 }
 
-void listener_event_func(int _fd, unsigned ev, void* _l)
+static void listener_event_func(int _fd, unsigned ev, void* _l)
 {
     alistener* listener = reinterpret_cast<alistener*>(_l);
     asocket *s;
@@ -106,38 +105,27 @@
     free(l);
 }
 
-void listener_disconnect(void* listener, atransport*  t)
-{
+static void listener_disconnect(void* listener, atransport* t) {
     free_listener(reinterpret_cast<alistener*>(listener));
 }
 
-int local_name_to_fd(const char *name)
-{
-    int port;
-
-    if(!strncmp("tcp:", name, 4)){
-        int  ret;
-        port = atoi(name + 4);
-
+static int local_name_to_fd(const char* name) {
+    if (!strncmp("tcp:", name, 4)) {
+        int port = atoi(name + 4);
         if (gListenAll > 0) {
-            ret = socket_inaddr_any_server(port, SOCK_STREAM);
+            return socket_inaddr_any_server(port, SOCK_STREAM);
         } else {
-            ret = socket_loopback_server(port, SOCK_STREAM);
+            return socket_loopback_server(port, SOCK_STREAM);
         }
-
-        return ret;
     }
 #ifndef HAVE_WIN32_IPC  /* no Unix-domain sockets on Win32 */
-    // It's non-sensical to support the "reserved" space on the adb host side
-    if(!strncmp(name, "local:", 6)) {
-        return socket_local_server(name + 6,
-                ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
-    } else if(!strncmp(name, "localabstract:", 14)) {
-        return socket_local_server(name + 14,
-                ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
-    } else if(!strncmp(name, "localfilesystem:", 16)) {
-        return socket_local_server(name + 16,
-                ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM);
+    // It's nonsensical to support the "reserved" space on the adb host side
+    if (!strncmp(name, "local:", 6)) {
+        return socket_local_server(name + 6, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
+    } else if (!strncmp(name, "localabstract:", 14)) {
+        return socket_local_server(name + 14, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
+    } else if (!strncmp(name, "localfilesystem:", 16)) {
+        return socket_local_server(name + 16, ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM);
     }
 
 #endif
diff --git a/adb/adb_listeners.h b/adb/adb_listeners.h
index 9a7ded1..67deb21 100644
--- a/adb/adb_listeners.h
+++ b/adb/adb_listeners.h
@@ -30,12 +30,6 @@
   INSTALL_STATUS_LISTENER_NOT_FOUND = -4,
 };
 
-extern alistener listener_list;
-
-void listener_disconnect(void*  _l, atransport*  t);
-void listener_event_func(int _fd, unsigned ev, void *_l);
-void ss_listener_event_func(int _fd, unsigned ev, void *_l);
-
 InstallStatus install_listener(const std::string& local_name,
                                const char* connect_to,
                                atransport* transport,
diff --git a/adb/adb_trace.h b/adb/adb_trace.h
index 63d4151..dbc7ec8 100644
--- a/adb/adb_trace.h
+++ b/adb/adb_trace.h
@@ -57,9 +57,9 @@
 #define DQ(...) ((void)0)
 #endif  /* !ADB_HOST */
 
-extern int     adb_trace_mask;
-extern unsigned char    adb_trace_output_count;
-void    adb_trace_init(void);
+extern int adb_trace_mask;
+extern unsigned char adb_trace_output_count;
+void adb_trace_init(char**);
 
 #  define ADB_TRACING  ((adb_trace_mask & (1 << TRACE_TAG)) != 0)
 
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index f48182d..0cd6670 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -176,9 +176,7 @@
 
 int main(int argc, char** argv) {
     adb_sysdeps_init();
-
-    android::base::InitLogging(argv);
-    adb_trace_init();
+    adb_trace_init(argv);
     D("Handling commandline()\n");
     return adb_commandline(argc - 1, const_cast<const char**>(argv + 1));
 }
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index d7bee91..7fbca31 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -680,14 +680,7 @@
     }
 
     std::string cmd = format_host_command(service, t, serial);
-    std::string error;
-    if (adb_command(cmd, &error)) {
-        D("failure: %s *\n", error.c_str());
-        fprintf(stderr,"error: %s\n", error.c_str());
-        return false;
-    }
-
-    return true;
+    return adb_command(cmd);
 }
 
 static int send_shell_command(TransportType transport_type, const char* serial,
@@ -1249,90 +1242,50 @@
         if (argc != 1) return usage();
         return send_shell_command(transport_type, serial, "shell:bugreport");
     }
-    /* adb_command() wrapper commands */
     else if (!strcmp(argv[0], "forward") || !strcmp(argv[0], "reverse")) {
-        std::string cmd;
-        char host_prefix[64];
-        char reverse = (char) !strcmp(argv[0], "reverse");
-        char remove = 0;
-        char remove_all = 0;
-        char list = 0;
-        char no_rebind = 0;
-
-        // Parse options here.
-        while (argc > 1 && argv[1][0] == '-') {
-            if (!strcmp(argv[1], "--list"))
-                list = 1;
-            else if (!strcmp(argv[1], "--remove"))
-                remove = 1;
-            else if (!strcmp(argv[1], "--remove-all"))
-                remove_all = 1;
-            else if (!strcmp(argv[1], "--no-rebind"))
-                no_rebind = 1;
-            else {
-                return usage();
-            }
-            argc--;
-            argv++;
-        }
-
-        // Ensure we can only use one option at a time.
-        if (list + remove + remove_all + no_rebind > 1) {
-            return usage();
-        }
+        bool reverse = !strcmp(argv[0], "reverse");
+        ++argv;
+        --argc;
+        if (argc < 1) return usage();
 
         // Determine the <host-prefix> for this command.
+        std::string host_prefix;
         if (reverse) {
-            snprintf(host_prefix, sizeof host_prefix, "reverse");
+            host_prefix = "reverse";
         } else {
             if (serial) {
-                snprintf(host_prefix, sizeof host_prefix, "host-serial:%s",
-                        serial);
+                host_prefix = android::base::StringPrintf("host-serial:%s", serial);
             } else if (transport_type == kTransportUsb) {
-                snprintf(host_prefix, sizeof host_prefix, "host-usb");
+                host_prefix = "host-usb";
             } else if (transport_type == kTransportLocal) {
-                snprintf(host_prefix, sizeof host_prefix, "host-local");
+                host_prefix = "host-local";
             } else {
-                snprintf(host_prefix, sizeof host_prefix, "host");
+                host_prefix = "host";
             }
         }
 
-        // Implement forward --list
-        if (list) {
-            if (argc != 1) {
-                return usage();
-            }
-
-            std::string query = android::base::StringPrintf("%s:list-forward", host_prefix);
-            return adb_query_command(query);
-        }
-
-        // Implement forward --remove-all
-        else if (remove_all) {
+        std::string cmd;
+        if (strcmp(argv[0], "--list") == 0) {
             if (argc != 1) return usage();
-            cmd = android::base::StringPrintf("%s:killforward-all", host_prefix);
-        }
-
-        // Implement forward --remove <local>
-        else if (remove) {
+            return adb_query_command(host_prefix + ":list-forward");
+        } else if (strcmp(argv[0], "--remove-all") == 0) {
+            if (argc != 1) return usage();
+            cmd = host_prefix + ":killforward-all";
+        } else if (strcmp(argv[0], "--remove") == 0) {
+            // forward --remove <local>
             if (argc != 2) return usage();
-            cmd = android::base::StringPrintf("%s:killforward:%s", host_prefix, argv[1]);
-        }
-        // Or implement one of:
-        //    forward <local> <remote>
-        //    forward --no-rebind <local> <remote>
-        else {
+            cmd = host_prefix + ":killforward:" + argv[1];
+        } else if (strcmp(argv[0], "--no-rebind") == 0) {
+            // forward --no-rebind <local> <remote>
             if (argc != 3) return usage();
-            const char* command = no_rebind ? "forward:norebind" : "forward";
-            cmd = android::base::StringPrintf("%s:%s:%s;%s", host_prefix, command, argv[1], argv[2]);
+            cmd = host_prefix + ":forward:norebind:" + argv[1] + ";" + argv[2];
+        } else {
+            // forward <local> <remote>
+            if (argc != 2) return usage();
+            cmd = host_prefix + ":forward:" + argv[0] + ";" + argv[1];
         }
 
-        std::string error;
-        if (adb_command(cmd, &error)) {
-            fprintf(stderr, "error: %s\n", error.c_str());
-            return 1;
-        }
-        return 0;
+        return adb_command(cmd) ? 0 : 1;
     }
     /* do_sync_*() commands */
     else if (!strcmp(argv[0], "ls")) {
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 99ff539..c0612cd 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -230,8 +230,6 @@
 }
 
 int main(int argc, char** argv) {
-    android::base::InitLogging(argv);
-
     while (true) {
         static struct option opts[] = {
             {"root_seclabel", required_argument, nullptr, 's'},
@@ -265,7 +263,7 @@
 
     close_stdin();
 
-    adb_trace_init();
+    adb_trace_init(argv);
 
     /* If adbd runs inside the emulator this will enable adb tracing via
      * adb-debug qemud service in the emulator. */
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 818ed97..379c702 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -26,6 +26,8 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <list>
+
 #include <base/stringprintf.h>
 
 #include "adb.h"
@@ -33,15 +35,8 @@
 
 static void transport_unref(atransport *t);
 
-static atransport transport_list = {
-    .next = &transport_list,
-    .prev = &transport_list,
-};
-
-static atransport pending_list = {
-    .next = &pending_list,
-    .prev = &pending_list,
-};
+static std::list<atransport*> transport_list;
+static std::list<atransport*> pending_list;
 
 ADB_MUTEX_DEFINE( transport_lock );
 
@@ -553,8 +548,7 @@
         adb_close(t->fd);
 
         adb_mutex_lock(&transport_lock);
-        t->next->prev = t->prev;
-        t->prev->next = t->next;
+        transport_list.remove(t);
         adb_mutex_unlock(&transport_lock);
 
         run_transport_disconnects(t);
@@ -570,8 +564,7 @@
         if (t->devpath)
             free(t->devpath);
 
-        memset(t,0xee,sizeof(atransport));
-        free(t);
+        delete t;
 
         update_transports();
         return;
@@ -582,7 +575,7 @@
         /* initial references are the two threads */
         t->ref_count = 2;
 
-        if(adb_socketpair(s)) {
+        if (adb_socketpair(s)) {
             fatal_errno("cannot open transport socketpair");
         }
 
@@ -608,14 +601,8 @@
     }
 
     adb_mutex_lock(&transport_lock);
-    /* remove from pending list */
-    t->next->prev = t->prev;
-    t->prev->next = t->next;
-    /* put us on the master device list */
-    t->next = &transport_list;
-    t->prev = transport_list.prev;
-    t->next->prev = t;
-    t->prev->next = t;
+    pending_list.remove(t);
+    transport_list.push_front(t);
     adb_mutex_unlock(&transport_lock);
 
     t->disconnects.next = t->disconnects.prev = &t->disconnects;
@@ -740,7 +727,6 @@
 
 atransport* acquire_one_transport(ConnectionState state, TransportType type,
                                   const char* serial, std::string* error_out) {
-    atransport *t;
     atransport *result = NULL;
     int ambiguous = 0;
 
@@ -748,7 +734,7 @@
     if (error_out) *error_out = android::base::StringPrintf("device '%s' not found", serial);
 
     adb_mutex_lock(&transport_lock);
-    for (t = transport_list.next; t != &transport_list; t = t->next) {
+    for (auto t : transport_list) {
         if (t->connection_state == kCsNoPerm) {
             if (error_out) *error_out = "insufficient permissions for device";
             continue;
@@ -866,7 +852,8 @@
     }
 }
 
-static void append_transport(atransport* t, std::string* result, bool long_listing) {
+static void append_transport(const atransport* t, std::string* result,
+                             bool long_listing) {
     const char* serial = t->serial;
     if (!serial || !serial[0]) {
         serial = "(no serial number)";
@@ -890,7 +877,7 @@
 std::string list_transports(bool long_listing) {
     std::string result;
     adb_mutex_lock(&transport_lock);
-    for (atransport* t = transport_list.next; t != &transport_list; t = t->next) {
+    for (const auto t : transport_list) {
         append_transport(t, &result, long_listing);
     }
     adb_mutex_unlock(&transport_lock);
@@ -898,11 +885,10 @@
 }
 
 /* hack for osx */
-void close_usb_devices()
-{
+void close_usb_devices() {
     adb_mutex_lock(&transport_lock);
-    for (atransport* t = transport_list.next; t != &transport_list; t = t->next) {
-        if ( !t->kicked ) {
+    for (auto t : transport_list) {
+        if (!t->kicked) {
             t->kicked = 1;
             t->kick(t);
         }
@@ -911,47 +897,39 @@
 }
 #endif // ADB_HOST
 
-int register_socket_transport(int s, const char *serial, int port, int local)
-{
-    atransport *t = reinterpret_cast<atransport*>(calloc(1, sizeof(atransport)));
-    if (t == nullptr) {
-        return -1;
-    }
-
-    atransport *n;
-    char buff[32];
+int register_socket_transport(int s, const char *serial, int port, int local) {
+    atransport* t = new atransport();
 
     if (!serial) {
-        snprintf(buff, sizeof buff, "T-%p", t);
-        serial = buff;
+        char buf[32];
+        snprintf(buf, sizeof(buf), "T-%p", t);
+        serial = buf;
     }
+
     D("transport: %s init'ing for socket %d, on port %d\n", serial, s, port);
     if (init_socket_transport(t, s, port, local) < 0) {
-        free(t);
+        delete t;
         return -1;
     }
 
     adb_mutex_lock(&transport_lock);
-    for (n = pending_list.next; n != &pending_list; n = n->next) {
-        if (n->serial && !strcmp(serial, n->serial)) {
+    for (auto transport : pending_list) {
+        if (transport->serial && strcmp(serial, transport->serial) == 0) {
             adb_mutex_unlock(&transport_lock);
-            free(t);
+            delete t;
             return -1;
         }
     }
 
-    for (n = transport_list.next; n != &transport_list; n = n->next) {
-        if (n->serial && !strcmp(serial, n->serial)) {
+    for (auto transport : transport_list) {
+        if (transport->serial && strcmp(serial, transport->serial) == 0) {
             adb_mutex_unlock(&transport_lock);
-            free(t);
+            delete t;
             return -1;
         }
     }
 
-    t->next = &pending_list;
-    t->prev = pending_list.prev;
-    t->next->prev = t;
-    t->prev->next = t;
+    pending_list.push_front(t);
     t->serial = strdup(serial);
     adb_mutex_unlock(&transport_lock);
 
@@ -960,95 +938,83 @@
 }
 
 #if ADB_HOST
-atransport *find_transport(const char *serial)
-{
-    atransport *t;
+atransport *find_transport(const char *serial) {
+    atransport* result = nullptr;
 
     adb_mutex_lock(&transport_lock);
-    for(t = transport_list.next; t != &transport_list; t = t->next) {
-        if (t->serial && !strcmp(serial, t->serial)) {
+    for (auto t : transport_list) {
+        if (t->serial && strcmp(serial, t->serial) == 0) {
+            result = t;
             break;
         }
-     }
+    }
     adb_mutex_unlock(&transport_lock);
 
-    if (t != &transport_list)
-        return t;
-    else
-        return 0;
+    return result;
 }
 
 void unregister_transport(atransport *t)
 {
     adb_mutex_lock(&transport_lock);
-    t->next->prev = t->prev;
-    t->prev->next = t->next;
+    transport_list.remove(t);
     adb_mutex_unlock(&transport_lock);
 
     kick_transport(t);
     transport_unref(t);
 }
 
-// unregisters all non-emulator TCP transports
-void unregister_all_tcp_transports()
-{
-    atransport *t, *next;
+// Unregisters all non-emulator TCP transports.
+void unregister_all_tcp_transports() {
     adb_mutex_lock(&transport_lock);
-    for (t = transport_list.next; t != &transport_list; t = next) {
-        next = t->next;
+    for (auto it = transport_list.begin(); it != transport_list.end(); ) {
+        atransport* t = *it;
         if (t->type == kTransportLocal && t->adb_port == 0) {
-            t->next->prev = t->prev;
-            t->prev->next = next;
-            // we cannot call kick_transport when holding transport_lock
-            if (!t->kicked)
-            {
+            // We cannot call kick_transport when holding transport_lock.
+            if (!t->kicked) {
                 t->kicked = 1;
                 t->kick(t);
             }
             transport_unref_locked(t);
+
+            it = transport_list.erase(it);
+        } else {
+            ++it;
         }
-     }
+    }
 
     adb_mutex_unlock(&transport_lock);
 }
 
 #endif
 
-void register_usb_transport(usb_handle *usb, const char *serial, const char *devpath, unsigned writeable)
-{
-    atransport *t = reinterpret_cast<atransport*>(calloc(1, sizeof(atransport)));
-    if (t == nullptr) fatal("cannot allocate USB atransport");
+void register_usb_transport(usb_handle* usb, const char* serial,
+                            const char* devpath, unsigned writeable) {
+    atransport* t = new atransport();
+
     D("transport: %p init'ing for usb_handle %p (sn='%s')\n", t, usb,
       serial ? serial : "");
     init_usb_transport(t, usb, (writeable ? kCsOffline : kCsNoPerm));
     if(serial) {
         t->serial = strdup(serial);
     }
-    if(devpath) {
+
+    if (devpath) {
         t->devpath = strdup(devpath);
     }
 
     adb_mutex_lock(&transport_lock);
-    t->next = &pending_list;
-    t->prev = pending_list.prev;
-    t->next->prev = t;
-    t->prev->next = t;
+    pending_list.push_front(t);
     adb_mutex_unlock(&transport_lock);
 
     register_transport(t);
 }
 
 // This should only be used for transports with connection_state == kCsNoPerm.
-void unregister_usb_transport(usb_handle* usb) {
+void unregister_usb_transport(usb_handle *usb) {
     adb_mutex_lock(&transport_lock);
-    for (atransport* t = transport_list.next; t != &transport_list;
-         t = t->next) {
-        if (t->usb == usb && t->connection_state == kCsNoPerm) {
-            t->next->prev = t->prev;
-            t->prev->next = t->next;
-            break;
-        }
-    }
+    transport_list.remove_if([usb](atransport* t) {
+        return t->usb == usb && t->connection_state == kCsNoPerm;
+    });
     adb_mutex_unlock(&transport_lock);
 }
 
diff --git a/adb/transport_test.cpp b/adb/transport_test.cpp
index 2b3fe3c..4b74adf 100644
--- a/adb/transport_test.cpp
+++ b/adb/transport_test.cpp
@@ -20,34 +20,85 @@
 
 #include "adb.h"
 
+class TestTransport : public atransport {
+public:
+    bool operator==(const atransport& rhs) const {
+        EXPECT_EQ(read_from_remote, rhs.read_from_remote);
+        EXPECT_EQ(write_to_remote, rhs.write_to_remote);
+        EXPECT_EQ(close, rhs.close);
+        EXPECT_EQ(kick, rhs.kick);
+
+        EXPECT_EQ(fd, rhs.fd);
+        EXPECT_EQ(transport_socket, rhs.transport_socket);
+
+        EXPECT_EQ(
+            0, memcmp(&transport_fde, &rhs.transport_fde, sizeof(fdevent)));
+
+        EXPECT_EQ(ref_count, rhs.ref_count);
+        EXPECT_EQ(sync_token, rhs.sync_token);
+        EXPECT_EQ(connection_state, rhs.connection_state);
+        EXPECT_EQ(online, rhs.online);
+        EXPECT_EQ(type, rhs.type);
+
+        EXPECT_EQ(usb, rhs.usb);
+        EXPECT_EQ(sfd, rhs.sfd);
+
+        EXPECT_EQ(serial, rhs.serial);
+        EXPECT_EQ(product, rhs.product);
+        EXPECT_EQ(model, rhs.model);
+        EXPECT_EQ(device, rhs.device);
+        EXPECT_EQ(devpath, rhs.devpath);
+        EXPECT_EQ(adb_port, rhs.adb_port);
+        EXPECT_EQ(kicked, rhs.kicked);
+
+        EXPECT_EQ(
+            0, memcmp(&disconnects, &rhs.disconnects, sizeof(adisconnect)));
+
+        EXPECT_EQ(key, rhs.key);
+        EXPECT_EQ(0, memcmp(token, rhs.token, TOKEN_SIZE));
+        EXPECT_EQ(0, memcmp(&auth_fde, &rhs.auth_fde, sizeof(fdevent)));
+        EXPECT_EQ(failed_auth_attempts, rhs.failed_auth_attempts);
+
+        return true;
+    }
+};
+
 TEST(transport, kick_transport) {
-  atransport t = {};
+  TestTransport t;
+
   // Mutate some member so we can test that the function is run.
   t.kick = [](atransport* trans) { trans->fd = 42; };
-  atransport expected = t;
+
+  TestTransport expected;
+  expected.kick = t.kick;
   expected.fd = 42;
   expected.kicked = 1;
+
   kick_transport(&t);
   ASSERT_EQ(42, t.fd);
   ASSERT_EQ(1, t.kicked);
-  ASSERT_EQ(0, memcmp(&expected, &t, sizeof(atransport)));
+  ASSERT_EQ(expected, t);
 }
 
 TEST(transport, kick_transport_already_kicked) {
   // Ensure that the transport is not modified if the transport has already been
   // kicked.
-  atransport t = {};
+  TestTransport t;
   t.kicked = 1;
   t.kick = [](atransport*) { FAIL() << "Kick should not have been called"; };
-  atransport expected = t;
+
+  TestTransport expected;
+  expected.kicked = 1;
+  expected.kick = t.kick;
+
   kick_transport(&t);
-  ASSERT_EQ(0, memcmp(&expected, &t, sizeof(atransport)));
+  ASSERT_EQ(expected, t);
 }
 
 // Disabled because the function currently segfaults for a zeroed atransport. I
 // want to make sure I understand how this is working at all before I try fixing
 // that.
 TEST(transport, DISABLED_run_transport_disconnects_zeroed_atransport) {
-  atransport t = {};
+  atransport t;
   run_transport_disconnects(&t);
 }
diff --git a/base/include/base/strings.h b/base/include/base/strings.h
index 5dbc5fb..638f845 100644
--- a/base/include/base/strings.h
+++ b/base/include/base/strings.h
@@ -17,6 +17,7 @@
 #ifndef BASE_STRINGS_H
 #define BASE_STRINGS_H
 
+#include <sstream>
 #include <string>
 #include <vector>
 
@@ -34,9 +35,24 @@
 // Trims whitespace off both ends of the given string.
 std::string Trim(const std::string& s);
 
-// Joins a vector of strings into a single string, using the given separator.
-template <typename StringT>
-std::string Join(const std::vector<StringT>& strings, char separator);
+// Joins a container of things into a single string, using the given separator.
+template <typename ContainerT>
+std::string Join(const ContainerT& things, char separator) {
+  if (things.empty()) {
+    return "";
+  }
+
+  std::ostringstream result;
+  result << *things.begin();
+  for (auto it = std::next(things.begin()); it != things.end(); ++it) {
+    result << separator << *it;
+  }
+  return result.str();
+}
+
+// We instantiate the common cases in strings.cpp.
+extern template std::string Join(const std::vector<std::string>&, char);
+extern template std::string Join(const std::vector<const char*>&, char);
 
 // Tests whether 's' starts with 'prefix'.
 bool StartsWith(const std::string& s, const char* prefix);
diff --git a/base/strings.cpp b/base/strings.cpp
index d3375d9..bac983b 100644
--- a/base/strings.cpp
+++ b/base/strings.cpp
@@ -79,25 +79,10 @@
   return s.substr(start_index, end_index - start_index + 1);
 }
 
-template <typename StringT>
-std::string Join(const std::vector<StringT>& strings, char separator) {
-  if (strings.empty()) {
-    return "";
-  }
-
-  std::string result(strings[0]);
-  for (size_t i = 1; i < strings.size(); ++i) {
-    result += separator;
-    result += strings[i];
-  }
-  return result;
-}
-
-// Explicit instantiations.
-template std::string Join<std::string>(const std::vector<std::string>& strings,
-                                       char separator);
-template std::string Join<const char*>(const std::vector<const char*>& strings,
-                                       char separator);
+// These cases are probably the norm, so we mark them extern in the header to
+// aid compile time and binary size.
+template std::string Join(const std::vector<std::string>&, char);
+template std::string Join(const std::vector<const char*>&, char);
 
 bool StartsWith(const std::string& s, const char* prefix) {
   return s.compare(0, strlen(prefix), prefix) == 0;
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
index 46a1ab5..5f67575 100644
--- a/base/strings_test.cpp
+++ b/base/strings_test.cpp
@@ -20,6 +20,8 @@
 
 #include <string>
 #include <vector>
+#include <set>
+#include <unordered_set>
 
 TEST(strings, split_empty) {
   std::vector<std::string> parts = android::base::Split("", ",");
@@ -121,6 +123,17 @@
   ASSERT_EQ(",,,", android::base::Join(list, ','));
 }
 
+TEST(strings, join_simple_ints) {
+  std::set<int> list = {1, 2, 3};
+  ASSERT_EQ("1,2,3", android::base::Join(list, ','));
+}
+
+TEST(strings, join_unordered_set) {
+  std::unordered_set<int> list = {1, 2};
+  ASSERT_TRUE("1,2" == android::base::Join(list, ',') ||
+              "2,1" == android::base::Join(list, ','));
+}
+
 TEST(strings, startswith_empty) {
   ASSERT_FALSE(android::base::StartsWith("", "foo"));
   ASSERT_TRUE(android::base::StartsWith("", ""));
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk
index dd53296..1a5f05e 100644
--- a/debuggerd/Android.mk
+++ b/debuggerd/Android.mk
@@ -1,4 +1,12 @@
-LOCAL_PATH:= $(call my-dir)
+LOCAL_PATH := $(call my-dir)
+
+common_cppflags := \
+    -std=gnu++11 \
+    -W \
+    -Wall \
+    -Wextra \
+    -Wunused \
+    -Werror \
 
 include $(CLEAR_VARS)
 
@@ -17,11 +25,7 @@
 LOCAL_SRC_FILES_x86    := x86/machine.cpp
 LOCAL_SRC_FILES_x86_64 := x86_64/machine.cpp
 
-LOCAL_CPPFLAGS := \
-    -std=gnu++11 \
-    -W -Wall -Wextra \
-    -Wunused \
-    -Werror \
+LOCAL_CPPFLAGS := $(common_cppflags)
 
 ifeq ($(TARGET_IS_64_BIT),true)
 LOCAL_CPPFLAGS += -DTARGET_IS_64_BIT
@@ -70,3 +74,47 @@
 LOCAL_MULTILIB := both
 
 include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    utility.cpp \
+    test/dump_memory_test.cpp \
+    test/log_fake.cpp \
+
+LOCAL_MODULE := debuggerd_test
+
+LOCAL_SHARED_LIBRARIES := \
+    libbacktrace \
+    libbase \
+
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/test
+LOCAL_CPPFLAGS := $(common_cppflags)
+
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_MULTILIB := both
+
+include $(BUILD_HOST_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    utility.cpp \
+    test/dump_memory_test.cpp \
+    test/log_fake.cpp \
+
+LOCAL_MODULE := debuggerd_test
+
+LOCAL_SHARED_LIBRARIES := \
+    libbacktrace \
+    libbase \
+
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/test
+LOCAL_CPPFLAGS := $(common_cppflags)
+
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+LOCAL_MULTILIB := both
+
+include $(BUILD_NATIVE_TEST)
diff --git a/debuggerd/arm/machine.cpp b/debuggerd/arm/machine.cpp
index 50e78c5..b7d6997 100644
--- a/debuggerd/arm/machine.cpp
+++ b/debuggerd/arm/machine.cpp
@@ -16,53 +16,39 @@
  */
 
 #include <errno.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
+#include <stdint.h>
 #include <string.h>
 #include <sys/ptrace.h>
-#include <sys/types.h>
-#include <sys/user.h>
 
-#include "../utility.h"
-#include "../machine.h"
+#include <backtrace/Backtrace.h>
 
-void dump_memory_and_code(log_t* log, pid_t tid) {
+#include "machine.h"
+#include "utility.h"
+
+void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
   pt_regs regs;
-  if (ptrace(PTRACE_GETREGS, tid, 0, &regs)) {
+  if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &regs)) {
+    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
     return;
   }
 
-  static const char REG_NAMES[] = "r0r1r2r3r4r5r6r7r8r9slfpipsp";
+  static const char reg_names[] = "r0r1r2r3r4r5r6r7r8r9slfpipsp";
 
   for (int reg = 0; reg < 14; reg++) {
-    // this may not be a valid way to access, but it'll do for now
-    uintptr_t addr = regs.uregs[reg];
-
-    // Don't bother if it looks like a small int or ~= null, or if
-    // it's in the kernel area.
-    if (addr < 4096 || addr >= 0xc0000000) {
-      continue;
-    }
-
-    _LOG(log, logtype::MEMORY, "\nmemory near %.2s:\n", &REG_NAMES[reg * 2]);
-    dump_memory(log, tid, addr);
+    dump_memory(log, backtrace, regs.uregs[reg], "memory near %.2s:", &reg_names[reg * 2]);
   }
 
-  // explicitly allow upload of code dump logging
-  _LOG(log, logtype::MEMORY, "\ncode around pc:\n");
-  dump_memory(log, tid, static_cast<uintptr_t>(regs.ARM_pc));
+  dump_memory(log, backtrace, static_cast<uintptr_t>(regs.ARM_pc), "code around pc:");
 
   if (regs.ARM_pc != regs.ARM_lr) {
-    _LOG(log, logtype::MEMORY, "\ncode around lr:\n");
-    dump_memory(log, tid, static_cast<uintptr_t>(regs.ARM_lr));
+    dump_memory(log, backtrace, static_cast<uintptr_t>(regs.ARM_lr), "code around lr:");
   }
 }
 
 void dump_registers(log_t* log, pid_t tid) {
   pt_regs r;
   if (ptrace(PTRACE_GETREGS, tid, 0, &r)) {
-    _LOG(log, logtype::REGISTERS, "cannot get registers: %s\n", strerror(errno));
+    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
     return;
   }
 
@@ -82,7 +68,7 @@
 
   user_vfp vfp_regs;
   if (ptrace(PTRACE_GETVFPREGS, tid, 0, &vfp_regs)) {
-    _LOG(log, logtype::FP_REGISTERS, "cannot get FP registers: %s\n", strerror(errno));
+    _LOG(log, logtype::ERROR, "cannot get FP registers: %s\n", strerror(errno));
     return;
   }
 
diff --git a/debuggerd/arm64/machine.cpp b/debuggerd/arm64/machine.cpp
index 8b17d53..2e097da 100644
--- a/debuggerd/arm64/machine.cpp
+++ b/debuggerd/arm64/machine.cpp
@@ -17,50 +17,37 @@
 
 #include <elf.h>
 #include <errno.h>
-#include <inttypes.h>
+#include <stdint.h>
 #include <string.h>
-#include <sys/types.h>
 #include <sys/ptrace.h>
-#include <sys/user.h>
 #include <sys/uio.h>
 
-#include "../utility.h"
-#include "../machine.h"
+#include <backtrace/Backtrace.h>
 
-void dump_memory_and_code(log_t* log, pid_t tid) {
-    struct user_pt_regs regs;
-    struct iovec io;
-    io.iov_base = &regs;
-    io.iov_len = sizeof(regs);
+#include "machine.h"
+#include "utility.h"
 
-    if (ptrace(PTRACE_GETREGSET, tid, (void*)NT_PRSTATUS, &io) == -1) {
-        _LOG(log, logtype::ERROR, "%s: ptrace failed to get registers: %s\n",
-             __func__, strerror(errno));
-        return;
-    }
+void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
+  struct user_pt_regs regs;
+  struct iovec io;
+  io.iov_base = &regs;
+  io.iov_len = sizeof(regs);
 
-    for (int reg = 0; reg < 31; reg++) {
-        uintptr_t addr = regs.regs[reg];
+  if (ptrace(PTRACE_GETREGSET, backtrace->Tid(), reinterpret_cast<void*>(NT_PRSTATUS), &io) == -1) {
+    _LOG(log, logtype::ERROR, "%s: ptrace failed to get registers: %s",
+         __func__, strerror(errno));
+    return;
+  }
 
-        /*
-         * Don't bother if it looks like a small int or ~= null, or if
-         * it's in the kernel area.
-         */
-        if (addr < 4096 || addr >= (1UL<<63)) {
-            continue;
-        }
+  for (int reg = 0; reg < 31; reg++) {
+    dump_memory(log, backtrace, regs.regs[reg], "memory near x%d:", reg);
+  }
 
-        _LOG(log, logtype::MEMORY, "\nmemory near x%d:\n", reg);
-        dump_memory(log, tid, addr);
-    }
+  dump_memory(log, backtrace, static_cast<uintptr_t>(regs.pc), "code around pc:");
 
-    _LOG(log, logtype::MEMORY, "\ncode around pc:\n");
-    dump_memory(log, tid, (uintptr_t)regs.pc);
-
-    if (regs.pc != regs.sp) {
-        _LOG(log, logtype::MEMORY, "\ncode around sp:\n");
-        dump_memory(log, tid, (uintptr_t)regs.sp);
-    }
+  if (regs.pc != regs.sp) {
+    dump_memory(log, backtrace, static_cast<uintptr_t>(regs.sp), "code around sp:");
+  }
 }
 
 void dump_registers(log_t* log, pid_t tid) {
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 039b8ec..b84a4e5 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -279,7 +279,7 @@
 
   char ehdr[EI_NIDENT];
   ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, &ehdr, sizeof(ehdr)));
-  TEMP_FAILURE_RETRY(close(fd));
+  close(fd);
   if (bytes != (ssize_t) sizeof(ehdr) || memcmp(ELFMAG, ehdr, SELFMAG) != 0) {
     return false;
   }
@@ -304,14 +304,14 @@
 
   if (TEMP_FAILURE_RETRY(write(sock_fd, &msg, sizeof(msg))) != (ssize_t) sizeof(msg)) {
     ALOGE("Failed to write request to debuggerd32 socket: %s", strerror(errno));
-    TEMP_FAILURE_RETRY(close(sock_fd));
+    close(sock_fd);
     return;
   }
 
   char ack;
   if (TEMP_FAILURE_RETRY(read(sock_fd, &ack, 1)) == -1) {
     ALOGE("Failed to read ack from debuggerd32 socket: %s", strerror(errno));
-    TEMP_FAILURE_RETRY(close(sock_fd));
+    close(sock_fd);
     return;
   }
 
@@ -338,7 +338,7 @@
         break;
     }
   }
-  TEMP_FAILURE_RETRY(close(sock_fd));
+  close(sock_fd);
 }
 #endif
 
@@ -365,7 +365,7 @@
         ALOGE("debuggerd: Not allowed to redirect action %d to 32 bit debuggerd\n",
               request.action);
       }
-      TEMP_FAILURE_RETRY(close(fd));
+      close(fd);
       return;
     }
 #endif
diff --git a/debuggerd/machine.h b/debuggerd/machine.h
index fca9fbe..e65b147 100644
--- a/debuggerd/machine.h
+++ b/debuggerd/machine.h
@@ -19,9 +19,11 @@
 
 #include <sys/types.h>
 
+#include <backtrace/Backtrace.h>
+
 #include "utility.h"
 
-void dump_memory_and_code(log_t* log, pid_t tid);
+void dump_memory_and_code(log_t* log, Backtrace* backtrace);
 void dump_registers(log_t* log, pid_t tid);
 
 #endif // _DEBUGGERD_MACHINE_H
diff --git a/debuggerd/mips/machine.cpp b/debuggerd/mips/machine.cpp
index 1145963..f7b8a86 100644
--- a/debuggerd/mips/machine.cpp
+++ b/debuggerd/mips/machine.cpp
@@ -14,30 +14,29 @@
  * limitations under the License.
  */
 
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
 #include <errno.h>
-#include <sys/types.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <string.h>
 #include <sys/ptrace.h>
 
-#include <sys/user.h>
+#include <backtrace/Backtrace.h>
 
-#include "../utility.h"
-#include "../machine.h"
+#include "machine.h"
+#include "utility.h"
 
-#define R(x) (static_cast<unsigned int>(x))
+#define R(x) (static_cast<uintptr_t>(x))
 
 // If configured to do so, dump memory around *all* registers
 // for the crashing thread.
-void dump_memory_and_code(log_t* log, pid_t tid) {
+void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
   pt_regs r;
-  if (ptrace(PTRACE_GETREGS, tid, 0, &r)) {
+  if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r)) {
+    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
     return;
   }
 
-  static const char REG_NAMES[] = "$0atv0v1a0a1a2a3t0t1t2t3t4t5t6t7s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra";
+  static const char reg_names[] = "$0atv0v1a0a1a2a3t0t1t2t3t4t5t6t7s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra";
 
   for (int reg = 0; reg < 32; reg++) {
     // skip uninteresting registers
@@ -48,27 +47,14 @@
        )
       continue;
 
-    uintptr_t addr = R(r.regs[reg]);
-
-    // Don't bother if it looks like a small int or ~= null, or if
-    // it's in the kernel area.
-    if (addr < 4096 || addr >= 0x80000000) {
-      continue;
-    }
-
-    _LOG(log, logtype::MEMORY, "\nmemory near %.2s:\n", &REG_NAMES[reg * 2]);
-    dump_memory(log, tid, addr);
+    dump_memory(log, backtrace, R(r.regs[reg]), "memory near %.2s:", &reg_names[reg * 2]);
   }
 
-  unsigned int pc = R(r.cp0_epc);
-  unsigned int ra = R(r.regs[31]);
-
-  _LOG(log, logtype::MEMORY, "\ncode around pc:\n");
-  dump_memory(log, tid, (uintptr_t)pc);
-
+  uintptr_t pc = R(r.cp0_epc);
+  uintptr_t ra = R(r.regs[31]);
+  dump_memory(log, backtrace, pc, "code around pc:");
   if (pc != ra) {
-    _LOG(log, logtype::MEMORY, "\ncode around ra:\n");
-    dump_memory(log, tid, (uintptr_t)ra);
+    dump_memory(log, backtrace, ra, "code around ra:");
   }
 }
 
@@ -79,22 +65,31 @@
     return;
   }
 
-  _LOG(log, logtype::REGISTERS, " zr %08x  at %08x  v0 %08x  v1 %08x\n",
+  _LOG(log, logtype::REGISTERS, " zr %08" PRIxPTR "  at %08" PRIxPTR
+       "  v0 %08" PRIxPTR "  v1 %08" PRIxPTR "\n",
        R(r.regs[0]), R(r.regs[1]), R(r.regs[2]), R(r.regs[3]));
-  _LOG(log, logtype::REGISTERS, " a0 %08x  a1 %08x  a2 %08x  a3 %08x\n",
+  _LOG(log, logtype::REGISTERS, " a0 %08" PRIxPTR "  a1 %08" PRIxPTR
+       "  a2 %08" PRIxPTR "  a3 %08" PRIxPTR "\n",
        R(r.regs[4]), R(r.regs[5]), R(r.regs[6]), R(r.regs[7]));
-  _LOG(log, logtype::REGISTERS, " t0 %08x  t1 %08x  t2 %08x  t3 %08x\n",
+  _LOG(log, logtype::REGISTERS, " t0 %08" PRIxPTR "  t1 %08" PRIxPTR
+       "  t2 %08" PRIxPTR "  t3 %08" PRIxPTR "\n",
        R(r.regs[8]), R(r.regs[9]), R(r.regs[10]), R(r.regs[11]));
-  _LOG(log, logtype::REGISTERS, " t4 %08x  t5 %08x  t6 %08x  t7 %08x\n",
+  _LOG(log, logtype::REGISTERS, " t4 %08" PRIxPTR "  t5 %08" PRIxPTR
+       "  t6 %08" PRIxPTR "  t7 %08" PRIxPTR "\n",
        R(r.regs[12]), R(r.regs[13]), R(r.regs[14]), R(r.regs[15]));
-  _LOG(log, logtype::REGISTERS, " s0 %08x  s1 %08x  s2 %08x  s3 %08x\n",
+  _LOG(log, logtype::REGISTERS, " s0 %08" PRIxPTR "  s1 %08" PRIxPTR
+       "  s2 %08" PRIxPTR "  s3 %08" PRIxPTR "\n",
        R(r.regs[16]), R(r.regs[17]), R(r.regs[18]), R(r.regs[19]));
-  _LOG(log, logtype::REGISTERS, " s4 %08x  s5 %08x  s6 %08x  s7 %08x\n",
+  _LOG(log, logtype::REGISTERS, " s4 %08" PRIxPTR "  s5 %08" PRIxPTR
+       "  s6 %08" PRIxPTR "  s7 %08" PRIxPTR "\n",
        R(r.regs[20]), R(r.regs[21]), R(r.regs[22]), R(r.regs[23]));
-  _LOG(log, logtype::REGISTERS, " t8 %08x  t9 %08x  k0 %08x  k1 %08x\n",
+  _LOG(log, logtype::REGISTERS, " t8 %08" PRIxPTR "  t9 %08" PRIxPTR
+       "  k0 %08" PRIxPTR "  k1 %08" PRIxPTR "\n",
        R(r.regs[24]), R(r.regs[25]), R(r.regs[26]), R(r.regs[27]));
-  _LOG(log, logtype::REGISTERS, " gp %08x  sp %08x  s8 %08x  ra %08x\n",
+  _LOG(log, logtype::REGISTERS, " gp %08" PRIxPTR "  sp %08" PRIxPTR
+       "  s8 %08" PRIxPTR "  ra %08" PRIxPTR "\n",
        R(r.regs[28]), R(r.regs[29]), R(r.regs[30]), R(r.regs[31]));
-  _LOG(log, logtype::REGISTERS, " hi %08x  lo %08x bva %08x epc %08x\n",
+  _LOG(log, logtype::REGISTERS, " hi %08" PRIxPTR "  lo %08" PRIxPTR
+       " bva %08" PRIxPTR " epc %08" PRIxPTR "\n",
        R(r.hi), R(r.lo), R(r.cp0_badvaddr), R(r.cp0_epc));
 }
diff --git a/debuggerd/mips64/machine.cpp b/debuggerd/mips64/machine.cpp
index ef9092f..293dcf6 100644
--- a/debuggerd/mips64/machine.cpp
+++ b/debuggerd/mips64/machine.cpp
@@ -14,30 +14,29 @@
  * limitations under the License.
  */
 
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
 #include <errno.h>
-#include <sys/types.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <string.h>
 #include <sys/ptrace.h>
 
-#include <sys/user.h>
+#include <backtrace/Backtrace.h>
 
-#include "../utility.h"
-#include "../machine.h"
+#include "machine.h"
+#include "utility.h"
 
-#define R(x) (static_cast<unsigned long>(x))
+#define R(x) (static_cast<uintptr_t>(x))
 
 // If configured to do so, dump memory around *all* registers
 // for the crashing thread.
-void dump_memory_and_code(log_t* log, pid_t tid) {
+void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
   pt_regs r;
-  if (ptrace(PTRACE_GETREGS, tid, 0, &r)) {
+  if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r)) {
+    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
     return;
   }
 
-  static const char REG_NAMES[] = "$0atv0v1a0a1a2a3a4a5a6a7t0t1t2t3s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra";
+  static const char reg_names[] = "$0atv0v1a0a1a2a3a4a5a6a7t0t1t2t3s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra";
 
   for (int reg = 0; reg < 32; reg++) {
     // skip uninteresting registers
@@ -48,27 +47,14 @@
        )
       continue;
 
-    uintptr_t addr = R(r.regs[reg]);
-
-    // Don't bother if it looks like a small int or ~= null, or if
-    // it's in the kernel area.
-    if (addr < 4096 || addr >= 0x4000000000000000) {
-      continue;
-    }
-
-    _LOG(log, logtype::MEMORY, "\nmemory near %.2s:\n", &REG_NAMES[reg * 2]);
-    dump_memory(log, tid, addr);
+    dump_memory(log, backtrace, R(r.regs[reg]), "memory near %.2s:", &reg_names[reg * 2]);
   }
 
-  unsigned long pc = R(r.cp0_epc);
-  unsigned long ra = R(r.regs[31]);
-
-  _LOG(log, logtype::MEMORY, "\ncode around pc:\n");
-  dump_memory(log, tid, (uintptr_t)pc);
-
+  uintptr_t pc = R(r.cp0_epc);
+  uintptr_t ra = R(r.regs[31]);
+  dump_memory(log, backtrace, pc, "code around pc:");
   if (pc != ra) {
-    _LOG(log, logtype::MEMORY, "\ncode around ra:\n");
-    dump_memory(log, tid, (uintptr_t)ra);
+    dump_memory(log, backtrace, ra, "code around ra:");
   }
 }
 
@@ -79,22 +65,31 @@
     return;
   }
 
-  _LOG(log, logtype::REGISTERS, " zr %016lx  at %016lx  v0 %016lx  v1 %016lx\n",
+  _LOG(log, logtype::REGISTERS, " zr %016" PRIxPTR "  at %016" PRIxPTR
+       "  v0 %016" PRIxPTR "  v1 %016" PRIxPTR "\n",
        R(r.regs[0]), R(r.regs[1]), R(r.regs[2]), R(r.regs[3]));
-  _LOG(log, logtype::REGISTERS, " a0 %016lx  a1 %016lx  a2 %016lx  a3 %016lx\n",
+  _LOG(log, logtype::REGISTERS, " a0 %016" PRIxPTR "  a1 %016" PRIxPTR
+       "  a2 %016" PRIxPTR "  a3 %016" PRIxPTR "\n",
        R(r.regs[4]), R(r.regs[5]), R(r.regs[6]), R(r.regs[7]));
-  _LOG(log, logtype::REGISTERS, " a4 %016lx  a5 %016lx  a6 %016lx  a7 %016lx\n",
+  _LOG(log, logtype::REGISTERS, " a4 %016" PRIxPTR "  a5 %016" PRIxPTR
+       "  a6 %016" PRIxPTR "  a7 %016" PRIxPTR "\n",
        R(r.regs[8]), R(r.regs[9]), R(r.regs[10]), R(r.regs[11]));
-  _LOG(log, logtype::REGISTERS, " t0 %016lx  t1 %016lx  t2 %016lx  t3 %016lx\n",
+  _LOG(log, logtype::REGISTERS, " t0 %016" PRIxPTR "  t1 %016" PRIxPTR
+       "  t2 %016" PRIxPTR "  t3 %016" PRIxPTR "\n",
        R(r.regs[12]), R(r.regs[13]), R(r.regs[14]), R(r.regs[15]));
-  _LOG(log, logtype::REGISTERS, " s0 %016lx  s1 %016lx  s2 %016lx  s3 %016lx\n",
+  _LOG(log, logtype::REGISTERS, " s0 %016" PRIxPTR "  s1 %016" PRIxPTR
+       "  s2 %016" PRIxPTR "  s3 %016" PRIxPTR "\n",
        R(r.regs[16]), R(r.regs[17]), R(r.regs[18]), R(r.regs[19]));
-  _LOG(log, logtype::REGISTERS, " s4 %016lx  s5 %016lx  s6 %016lx  s7 %016lx\n",
+  _LOG(log, logtype::REGISTERS, " s4 %016" PRIxPTR "  s5 %016" PRIxPTR
+       "  s6 %016" PRIxPTR "  s7 %016" PRIxPTR "\n",
        R(r.regs[20]), R(r.regs[21]), R(r.regs[22]), R(r.regs[23]));
-  _LOG(log, logtype::REGISTERS, " t8 %016lx  t9 %016lx  k0 %016lx  k1 %016lx\n",
+  _LOG(log, logtype::REGISTERS, " t8 %016" PRIxPTR "  t9 %016" PRIxPTR
+       "  k0 %016" PRIxPTR "  k1 %016" PRIxPTR "\n",
        R(r.regs[24]), R(r.regs[25]), R(r.regs[26]), R(r.regs[27]));
-  _LOG(log, logtype::REGISTERS, " gp %016lx  sp %016lx  s8 %016lx  ra %016lx\n",
+  _LOG(log, logtype::REGISTERS, " gp %016" PRIxPTR "  sp %016" PRIxPTR
+       "  s8 %016" PRIxPTR "  ra %016" PRIxPTR "\n",
        R(r.regs[28]), R(r.regs[29]), R(r.regs[30]), R(r.regs[31]));
-  _LOG(log, logtype::REGISTERS, " hi %016lx  lo %016lx bva %016lx epc %016lx\n",
+  _LOG(log, logtype::REGISTERS, " hi %016" PRIxPTR "  lo %016" PRIxPTR
+       " bva %016" PRIxPTR " epc %016" PRIxPTR "\n",
        R(r.hi), R(r.lo), R(r.cp0_badvaddr), R(r.cp0_epc));
 }
diff --git a/debuggerd/test/BacktraceMock.h b/debuggerd/test/BacktraceMock.h
new file mode 100644
index 0000000..05ad12b
--- /dev/null
+++ b/debuggerd/test/BacktraceMock.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DEBUGGERD_TEST_BACKTRACE_MOCK_H
+#define _DEBUGGERD_TEST_BACKTRACE_MOCK_H
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ucontext.h>
+
+#include <string>
+#include <vector>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+class BacktraceMapMock : public BacktraceMap {
+ public:
+  BacktraceMapMock() : BacktraceMap(0) {}
+  virtual ~BacktraceMapMock() {}
+};
+
+
+class BacktraceMock : public Backtrace {
+ public:
+  BacktraceMock(BacktraceMapMock* map) : Backtrace(0, 0, map) {
+    if (map_ == nullptr) {
+      abort();
+    }
+  }
+  virtual ~BacktraceMock() {}
+
+  virtual bool Unwind(size_t, ucontext_t*) { return false; }
+  virtual bool ReadWord(uintptr_t, word_t*) { return false;}
+
+  virtual std::string GetFunctionNameRaw(uintptr_t, uintptr_t*) { return ""; }
+
+  virtual size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes) {
+    size_t offset = 0;
+    if (last_read_addr_ > 0) {
+      offset = addr - last_read_addr_;
+    }
+    size_t bytes_available = buffer_.size() - offset;
+
+    if (bytes_partial_read_ > 0) {
+      // Do a partial read.
+      if (bytes > bytes_partial_read_) {
+        bytes = bytes_partial_read_;
+      }
+      bytes_partial_read_ -= bytes;
+    } else if (bytes > bytes_available) {
+      bytes = bytes_available;
+    }
+
+    if (bytes > 0) {
+      memcpy(buffer, buffer_.data() + offset, bytes);
+    }
+
+    last_read_addr_ = addr;
+    return bytes;
+  }
+
+  void SetReadData(uint8_t* buffer, size_t bytes) {
+    buffer_.resize(bytes);
+    memcpy(buffer_.data(), buffer, bytes);
+    bytes_partial_read_ = 0;
+    last_read_addr_ = 0;
+  }
+
+  void SetPartialReadAmount(size_t bytes) {
+    if (bytes > buffer_.size()) {
+      abort();
+    }
+    bytes_partial_read_ = bytes;
+  }
+
+ private:
+  std::vector<uint8_t> buffer_;
+  size_t bytes_partial_read_ = 0;
+  uintptr_t last_read_addr_ = 0;
+};
+
+#endif //  _DEBUGGERD_TEST_BACKTRACE_MOCK_H
diff --git a/debuggerd/test/dump_memory_test.cpp b/debuggerd/test/dump_memory_test.cpp
new file mode 100644
index 0000000..fcb0108
--- /dev/null
+++ b/debuggerd/test/dump_memory_test.cpp
@@ -0,0 +1,504 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+
+#include <memory>
+#include <string>
+
+#include <gtest/gtest.h>
+#include <base/file.h>
+
+#include "BacktraceMock.h"
+#include "log_fake.h"
+#include "utility.h"
+
+const char g_expected_full_dump[] =
+"\nmemory near r1:\n"
+#if defined(__LP64__)
+"    0000000012345658 0706050403020100 0f0e0d0c0b0a0908  ................\n"
+"    0000000012345668 1716151413121110 1f1e1d1c1b1a1918  ................\n"
+"    0000000012345678 2726252423222120 2f2e2d2c2b2a2928   !\"#$%&'()*+,-./\n"
+"    0000000012345688 3736353433323130 3f3e3d3c3b3a3938  0123456789:;<=>?\n"
+"    0000000012345698 4746454443424140 4f4e4d4c4b4a4948  @ABCDEFGHIJKLMNO\n"
+"    00000000123456a8 5756555453525150 5f5e5d5c5b5a5958  PQRSTUVWXYZ[\\]^_\n"
+"    00000000123456b8 6766656463626160 6f6e6d6c6b6a6968  `abcdefghijklmno\n"
+"    00000000123456c8 7776757473727170 7f7e7d7c7b7a7978  pqrstuvwxyz{|}~.\n"
+"    00000000123456d8 8786858483828180 8f8e8d8c8b8a8988  ................\n"
+"    00000000123456e8 9796959493929190 9f9e9d9c9b9a9998  ................\n"
+"    00000000123456f8 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8  ................\n"
+"    0000000012345708 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8  ................\n"
+"    0000000012345718 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8  ................\n"
+"    0000000012345728 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8  ................\n"
+"    0000000012345738 e7e6e5e4e3e2e1e0 efeeedecebeae9e8  ................\n"
+"    0000000012345748 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8  ................\n";
+#else
+"    12345658 03020100 07060504 0b0a0908 0f0e0d0c  ................\n"
+"    12345668 13121110 17161514 1b1a1918 1f1e1d1c  ................\n"
+"    12345678 23222120 27262524 2b2a2928 2f2e2d2c   !\"#$%&'()*+,-./\n"
+"    12345688 33323130 37363534 3b3a3938 3f3e3d3c  0123456789:;<=>?\n"
+"    12345698 43424140 47464544 4b4a4948 4f4e4d4c  @ABCDEFGHIJKLMNO\n"
+"    123456a8 53525150 57565554 5b5a5958 5f5e5d5c  PQRSTUVWXYZ[\\]^_\n"
+"    123456b8 63626160 67666564 6b6a6968 6f6e6d6c  `abcdefghijklmno\n"
+"    123456c8 73727170 77767574 7b7a7978 7f7e7d7c  pqrstuvwxyz{|}~.\n"
+"    123456d8 83828180 87868584 8b8a8988 8f8e8d8c  ................\n"
+"    123456e8 93929190 97969594 9b9a9998 9f9e9d9c  ................\n"
+"    123456f8 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac  ................\n"
+"    12345708 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc  ................\n"
+"    12345718 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc  ................\n"
+"    12345728 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc  ................\n"
+"    12345738 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec  ................\n"
+"    12345748 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc  ................\n";
+#endif
+
+const char g_expected_partial_dump[] = \
+"\nmemory near pc:\n"
+#if defined(__LP64__)
+"    00000000123455e0 0706050403020100 0f0e0d0c0b0a0908  ................\n"
+"    00000000123455f0 1716151413121110 1f1e1d1c1b1a1918  ................\n"
+"    0000000012345600 2726252423222120 2f2e2d2c2b2a2928   !\"#$%&'()*+,-./\n"
+"    0000000012345610 3736353433323130 3f3e3d3c3b3a3938  0123456789:;<=>?\n"
+"    0000000012345620 4746454443424140 4f4e4d4c4b4a4948  @ABCDEFGHIJKLMNO\n"
+"    0000000012345630 5756555453525150 5f5e5d5c5b5a5958  PQRSTUVWXYZ[\\]^_\n"
+"    0000000012345640 6766656463626160 ----------------  `abcdefg........\n"
+"    0000000012345650 ---------------- ----------------  ................\n"
+"    0000000012345660 ---------------- ----------------  ................\n"
+"    0000000012345670 ---------------- ----------------  ................\n"
+"    0000000012345680 ---------------- ----------------  ................\n"
+"    0000000012345690 ---------------- ----------------  ................\n"
+"    00000000123456a0 ---------------- ----------------  ................\n"
+"    00000000123456b0 ---------------- ----------------  ................\n"
+"    00000000123456c0 ---------------- ----------------  ................\n"
+"    00000000123456d0 ---------------- ----------------  ................\n";
+#else
+"    123455e0 03020100 07060504 0b0a0908 0f0e0d0c  ................\n"
+"    123455f0 13121110 17161514 1b1a1918 1f1e1d1c  ................\n"
+"    12345600 23222120 27262524 2b2a2928 2f2e2d2c   !\"#$%&'()*+,-./\n"
+"    12345610 33323130 37363534 3b3a3938 3f3e3d3c  0123456789:;<=>?\n"
+"    12345620 43424140 47464544 4b4a4948 4f4e4d4c  @ABCDEFGHIJKLMNO\n"
+"    12345630 53525150 57565554 5b5a5958 5f5e5d5c  PQRSTUVWXYZ[\\]^_\n"
+"    12345640 63626160 67666564 -------- --------  `abcdefg........\n"
+"    12345650 -------- -------- -------- --------  ................\n"
+"    12345660 -------- -------- -------- --------  ................\n"
+"    12345670 -------- -------- -------- --------  ................\n"
+"    12345680 -------- -------- -------- --------  ................\n"
+"    12345690 -------- -------- -------- --------  ................\n"
+"    123456a0 -------- -------- -------- --------  ................\n"
+"    123456b0 -------- -------- -------- --------  ................\n"
+"    123456c0 -------- -------- -------- --------  ................\n"
+"    123456d0 -------- -------- -------- --------  ................\n";
+#endif
+
+class DumpMemoryTest : public ::testing::Test {
+ protected:
+  virtual void SetUp() {
+    map_mock_.reset(new BacktraceMapMock());
+    backtrace_mock_.reset(new BacktraceMock(map_mock_.get()));
+
+    char tmp_file[256];
+    const char data_template[] = "/data/local/tmp/debuggerd_memory_testXXXXXX";
+    memcpy(tmp_file, data_template, sizeof(data_template));
+    int tombstone_fd = mkstemp(tmp_file);
+    if (tombstone_fd == -1) {
+      const char tmp_template[] = "/tmp/debuggerd_memory_testXXXXXX";
+      memcpy(tmp_file, tmp_template, sizeof(tmp_template));
+      tombstone_fd = mkstemp(tmp_file);
+      if (tombstone_fd == -1) {
+        abort();
+      }
+    }
+    if (unlink(tmp_file) == -1) {
+      abort();
+    }
+
+    log_.tfd = tombstone_fd;
+    log_.amfd = -1;
+    log_.crashed_tid = 12;
+    log_.current_tid = 12;
+    log_.should_retrieve_logcat = false;
+
+    resetLogs();
+  }
+
+  virtual void TearDown() {
+    if (log_.tfd >= 0) {
+      close(log_.tfd);
+    }
+  }
+
+  std::unique_ptr<BacktraceMapMock> map_mock_;
+  std::unique_ptr<BacktraceMock> backtrace_mock_;
+
+  log_t log_;
+};
+
+TEST_F(DumpMemoryTest, aligned_addr) {
+  uint8_t buffer[256];
+  for (size_t i = 0; i < sizeof(buffer); i++) {
+    buffer[i] = i;
+  }
+  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+
+  dump_memory(&log_, backtrace_mock_.get(), 0x12345678, "memory near %.2s:", "r1");
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_STREQ(g_expected_full_dump, tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, partial_read) {
+  uint8_t buffer[256];
+  for (size_t i = 0; i < sizeof(buffer); i++) {
+    buffer[i] = i;
+  }
+  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  backtrace_mock_->SetPartialReadAmount(96);
+
+  dump_memory(&log_, backtrace_mock_.get(), 0x12345679, "memory near %.2s:", "r1");
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_STREQ(g_expected_full_dump, tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, unaligned_addr) {
+  uint8_t buffer[256];
+  for (size_t i = 0; i < sizeof(buffer); i++) {
+    buffer[i] = i;
+  }
+  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+
+  dump_memory(&log_, backtrace_mock_.get(), 0x12345679, "memory near %.2s:", "r1");
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_STREQ(g_expected_full_dump, tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, memory_unreadable) {
+  dump_memory(&log_, backtrace_mock_.get(), 0xa2345678, "memory near pc:");
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump = \
+"\nmemory near pc:\n"
+#if defined(__LP64__)
+"    00000000a2345658 ---------------- ----------------  ................\n"
+"    00000000a2345668 ---------------- ----------------  ................\n"
+"    00000000a2345678 ---------------- ----------------  ................\n"
+"    00000000a2345688 ---------------- ----------------  ................\n"
+"    00000000a2345698 ---------------- ----------------  ................\n"
+"    00000000a23456a8 ---------------- ----------------  ................\n"
+"    00000000a23456b8 ---------------- ----------------  ................\n"
+"    00000000a23456c8 ---------------- ----------------  ................\n"
+"    00000000a23456d8 ---------------- ----------------  ................\n"
+"    00000000a23456e8 ---------------- ----------------  ................\n"
+"    00000000a23456f8 ---------------- ----------------  ................\n"
+"    00000000a2345708 ---------------- ----------------  ................\n"
+"    00000000a2345718 ---------------- ----------------  ................\n"
+"    00000000a2345728 ---------------- ----------------  ................\n"
+"    00000000a2345738 ---------------- ----------------  ................\n"
+"    00000000a2345748 ---------------- ----------------  ................\n";
+#else
+"    a2345658 -------- -------- -------- --------  ................\n"
+"    a2345668 -------- -------- -------- --------  ................\n"
+"    a2345678 -------- -------- -------- --------  ................\n"
+"    a2345688 -------- -------- -------- --------  ................\n"
+"    a2345698 -------- -------- -------- --------  ................\n"
+"    a23456a8 -------- -------- -------- --------  ................\n"
+"    a23456b8 -------- -------- -------- --------  ................\n"
+"    a23456c8 -------- -------- -------- --------  ................\n"
+"    a23456d8 -------- -------- -------- --------  ................\n"
+"    a23456e8 -------- -------- -------- --------  ................\n"
+"    a23456f8 -------- -------- -------- --------  ................\n"
+"    a2345708 -------- -------- -------- --------  ................\n"
+"    a2345718 -------- -------- -------- --------  ................\n"
+"    a2345728 -------- -------- -------- --------  ................\n"
+"    a2345738 -------- -------- -------- --------  ................\n"
+"    a2345748 -------- -------- -------- --------  ................\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, memory_partially_unreadable) {
+  uint8_t buffer[104];
+  for (size_t i = 0; i < sizeof(buffer); i++) {
+    buffer[i] = i;
+  }
+  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+
+  dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:");
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, memory_partially_unreadable_unaligned_return) {
+  uint8_t buffer[104];
+  for (size_t i = 0; i < sizeof(buffer); i++) {
+    buffer[i] = i;
+  }
+  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  backtrace_mock_->SetPartialReadAmount(102);
+
+  dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:");
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str());
+
+#if defined(__LP64__)
+  ASSERT_STREQ("DEBUG Bytes read 102, is not a multiple of 8\n", getFakeLogPrint().c_str());
+#else
+  ASSERT_STREQ("DEBUG Bytes read 102, is not a multiple of 4\n", getFakeLogPrint().c_str());
+#endif
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+}
+
+TEST_F(DumpMemoryTest, memory_partially_unreadable_two_unaligned_reads) {
+  uint8_t buffer[106];
+  for (size_t i = 0; i < sizeof(buffer); i++) {
+    buffer[i] = i;
+  }
+  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  backtrace_mock_->SetPartialReadAmount(45);
+
+  dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:");
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str());
+
+#if defined(__LP64__)
+  ASSERT_STREQ("DEBUG Bytes read 45, is not a multiple of 8\n"
+               "DEBUG Bytes after second read 106, is not a multiple of 8\n",
+               getFakeLogPrint().c_str());
+#else
+  ASSERT_STREQ("DEBUG Bytes read 45, is not a multiple of 4\n"
+               "DEBUG Bytes after second read 106, is not a multiple of 4\n",
+               getFakeLogPrint().c_str());
+#endif
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+}
+
+TEST_F(DumpMemoryTest, address_low_fence) {
+  uint8_t buffer[256];
+  memset(buffer, 0, sizeof(buffer));
+  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+
+  dump_memory(&log_, backtrace_mock_.get(), 0x1000, "memory near %.2s:", "r1");
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump = \
+"\nmemory near r1:\n"
+#if defined(__LP64__)
+"    0000000000001000 0000000000000000 0000000000000000  ................\n"
+"    0000000000001010 0000000000000000 0000000000000000  ................\n"
+"    0000000000001020 0000000000000000 0000000000000000  ................\n"
+"    0000000000001030 0000000000000000 0000000000000000  ................\n"
+"    0000000000001040 0000000000000000 0000000000000000  ................\n"
+"    0000000000001050 0000000000000000 0000000000000000  ................\n"
+"    0000000000001060 0000000000000000 0000000000000000  ................\n"
+"    0000000000001070 0000000000000000 0000000000000000  ................\n"
+"    0000000000001080 0000000000000000 0000000000000000  ................\n"
+"    0000000000001090 0000000000000000 0000000000000000  ................\n"
+"    00000000000010a0 0000000000000000 0000000000000000  ................\n"
+"    00000000000010b0 0000000000000000 0000000000000000  ................\n"
+"    00000000000010c0 0000000000000000 0000000000000000  ................\n"
+"    00000000000010d0 0000000000000000 0000000000000000  ................\n"
+"    00000000000010e0 0000000000000000 0000000000000000  ................\n"
+"    00000000000010f0 0000000000000000 0000000000000000  ................\n";
+#else
+"    00001000 00000000 00000000 00000000 00000000  ................\n"
+"    00001010 00000000 00000000 00000000 00000000  ................\n"
+"    00001020 00000000 00000000 00000000 00000000  ................\n"
+"    00001030 00000000 00000000 00000000 00000000  ................\n"
+"    00001040 00000000 00000000 00000000 00000000  ................\n"
+"    00001050 00000000 00000000 00000000 00000000  ................\n"
+"    00001060 00000000 00000000 00000000 00000000  ................\n"
+"    00001070 00000000 00000000 00000000 00000000  ................\n"
+"    00001080 00000000 00000000 00000000 00000000  ................\n"
+"    00001090 00000000 00000000 00000000 00000000  ................\n"
+"    000010a0 00000000 00000000 00000000 00000000  ................\n"
+"    000010b0 00000000 00000000 00000000 00000000  ................\n"
+"    000010c0 00000000 00000000 00000000 00000000  ................\n"
+"    000010d0 00000000 00000000 00000000 00000000  ................\n"
+"    000010e0 00000000 00000000 00000000 00000000  ................\n"
+"    000010f0 00000000 00000000 00000000 00000000  ................\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, memory_address_too_low) {
+  uint8_t buffer[256];
+  memset(buffer, 0, sizeof(buffer));
+  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+
+  dump_memory(&log_, backtrace_mock_.get(), 0, "memory near %.2s:", "r1");
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_STREQ("", tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, memory_address_too_high) {
+  uint8_t buffer[256];
+  memset(buffer, 0, sizeof(buffer));
+  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+
+#if defined(__LP64__)
+  dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL, "memory near %.2s:", "r1");
+  dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 32, "memory near %.2s:", "r1");
+  dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 216, "memory near %.2s:", "r1");
+#else
+  dump_memory(&log_, backtrace_mock_.get(), 0xffff0000, "memory near %.2s:", "r1");
+  dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 32, "memory near %.2s:", "r1");
+  dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 220, "memory near %.2s:", "r1");
+#endif
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_STREQ("", tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, memory_address_would_overflow) {
+  uint8_t buffer[256];
+  memset(buffer, 0, sizeof(buffer));
+  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+
+#if defined(__LP64__)
+  dump_memory(&log_, backtrace_mock_.get(), 0xfffffffffffffff0, "memory near %.2s:", "r1");
+#else
+  dump_memory(&log_, backtrace_mock_.get(), 0xfffffff0, "memory near %.2s:", "r1");
+#endif
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_STREQ("", tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, memory_address_nearly_too_high) {
+  uint8_t buffer[256];
+  for (size_t i = 0; i < sizeof(buffer); i++) {
+    buffer[i] = i;
+  }
+  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+
+#if defined(__LP64__)
+  dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 224, "memory near %.2s:", "r4");
+#else
+  dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 224, "memory near %.2s:", "r4");
+#endif
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump = \
+"\nmemory near r4:\n"
+#if defined(__LP64__)
+"    3fffffffffffff00 0706050403020100 0f0e0d0c0b0a0908  ................\n"
+"    3fffffffffffff10 1716151413121110 1f1e1d1c1b1a1918  ................\n"
+"    3fffffffffffff20 2726252423222120 2f2e2d2c2b2a2928   !\"#$%&'()*+,-./\n"
+"    3fffffffffffff30 3736353433323130 3f3e3d3c3b3a3938  0123456789:;<=>?\n"
+"    3fffffffffffff40 4746454443424140 4f4e4d4c4b4a4948  @ABCDEFGHIJKLMNO\n"
+"    3fffffffffffff50 5756555453525150 5f5e5d5c5b5a5958  PQRSTUVWXYZ[\\]^_\n"
+"    3fffffffffffff60 6766656463626160 6f6e6d6c6b6a6968  `abcdefghijklmno\n"
+"    3fffffffffffff70 7776757473727170 7f7e7d7c7b7a7978  pqrstuvwxyz{|}~.\n"
+"    3fffffffffffff80 8786858483828180 8f8e8d8c8b8a8988  ................\n"
+"    3fffffffffffff90 9796959493929190 9f9e9d9c9b9a9998  ................\n"
+"    3fffffffffffffa0 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8  ................\n"
+"    3fffffffffffffb0 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8  ................\n"
+"    3fffffffffffffc0 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8  ................\n"
+"    3fffffffffffffd0 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8  ................\n"
+"    3fffffffffffffe0 e7e6e5e4e3e2e1e0 efeeedecebeae9e8  ................\n"
+"    3ffffffffffffff0 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8  ................\n";
+#else
+"    fffeff00 03020100 07060504 0b0a0908 0f0e0d0c  ................\n"
+"    fffeff10 13121110 17161514 1b1a1918 1f1e1d1c  ................\n"
+"    fffeff20 23222120 27262524 2b2a2928 2f2e2d2c   !\"#$%&'()*+,-./\n"
+"    fffeff30 33323130 37363534 3b3a3938 3f3e3d3c  0123456789:;<=>?\n"
+"    fffeff40 43424140 47464544 4b4a4948 4f4e4d4c  @ABCDEFGHIJKLMNO\n"
+"    fffeff50 53525150 57565554 5b5a5958 5f5e5d5c  PQRSTUVWXYZ[\\]^_\n"
+"    fffeff60 63626160 67666564 6b6a6968 6f6e6d6c  `abcdefghijklmno\n"
+"    fffeff70 73727170 77767574 7b7a7978 7f7e7d7c  pqrstuvwxyz{|}~.\n"
+"    fffeff80 83828180 87868584 8b8a8988 8f8e8d8c  ................\n"
+"    fffeff90 93929190 97969594 9b9a9998 9f9e9d9c  ................\n"
+"    fffeffa0 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac  ................\n"
+"    fffeffb0 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc  ................\n"
+"    fffeffc0 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc  ................\n"
+"    fffeffd0 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc  ................\n"
+"    fffeffe0 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec  ................\n"
+"    fffefff0 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc  ................\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
diff --git a/debuggerd/test/log_fake.cpp b/debuggerd/test/log_fake.cpp
new file mode 100644
index 0000000..1161afe
--- /dev/null
+++ b/debuggerd/test/log_fake.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015 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 <stdarg.h>
+
+#include <string>
+
+#include <base/stringprintf.h>
+
+std::string g_fake_log_buf;
+
+std::string g_fake_log_print;
+
+void resetLogs() {
+  g_fake_log_buf = "";
+  g_fake_log_print = "";
+}
+
+extern "C" int __android_log_buf_write(int, int, const char* tag, const char* msg) {
+  g_fake_log_buf += tag;
+  g_fake_log_buf += ' ';
+  g_fake_log_buf += msg;
+  return 1;
+}
+
+std::string getFakeLogBuf() {
+  return g_fake_log_buf;
+}
+
+extern "C" int __android_log_print(int, const char* tag, const char* fmt, ...) {
+  g_fake_log_print += tag;
+  g_fake_log_print += ' ';
+
+  va_list ap;
+  va_start(ap, fmt);
+  android::base::StringAppendV(&g_fake_log_print, fmt, ap);
+  va_end(ap);
+
+  g_fake_log_print += '\n';
+
+  return 1;
+}
+
+std::string getFakeLogPrint() {
+  return g_fake_log_print;
+}
diff --git a/debuggerd/test/log_fake.h b/debuggerd/test/log_fake.h
new file mode 100644
index 0000000..5418fce
--- /dev/null
+++ b/debuggerd/test/log_fake.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DEBUGGERD_TEST_LOG_FAKE_H
+#define _DEBUGGERD_TEST_LOG_FAKE_H
+
+#include <string>
+
+void resetLogs();
+std::string getFakeLogBuf();
+std::string getFakeLogPrint();
+
+#endif // _DEBUGGERD_TEST_LOG_FAKE_H
diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp
index ccdfe85..614edb6 100644
--- a/debuggerd/tombstone.cpp
+++ b/debuggerd/tombstone.cpp
@@ -654,7 +654,7 @@
   } else {
     ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid);
   }
-  dump_memory_and_code(log, tid);
+  dump_memory_and_code(log, backtrace.get());
   if (map.get() != nullptr) {
     dump_all_maps(backtrace.get(), map.get(), log, tid);
   }
diff --git a/debuggerd/utility.cpp b/debuggerd/utility.cpp
index e722f82..9f340a8 100644
--- a/debuggerd/utility.cpp
+++ b/debuggerd/utility.cpp
@@ -27,6 +27,7 @@
 
 #include <backtrace/Backtrace.h>
 #include <base/file.h>
+#include <base/stringprintf.h>
 #include <log/log.h>
 
 const int SLEEP_TIME_USEC = 50000;         // 0.05 seconds
@@ -118,68 +119,91 @@
   return -1;
 }
 
-void dump_memory(log_t* log, pid_t tid, uintptr_t addr) {
-    char code_buffer[64];
-    char ascii_buffer[32];
-    uintptr_t p, end;
+#define MEMORY_BYTES_TO_DUMP 256
+#define MEMORY_BYTES_PER_LINE 16
 
-    p = addr & ~(sizeof(long) - 1);
-    /* Dump 32 bytes before addr */
-    p -= 32;
-    if (p > addr) {
-        /* catch underflow */
-        p = 0;
-    }
-    /* Dump 256 bytes */
-    end = p + 256;
-    /* catch overflow; 'end - p' has to be multiples of 16 */
-    while (end < p) {
-        end -= 16;
-    }
+void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* fmt, ...) {
+  std::string log_msg;
+  va_list ap;
+  va_start(ap, fmt);
+  android::base::StringAppendV(&log_msg, fmt, ap);
+  va_end(ap);
 
-    /* Dump the code around PC as:
-     *  addr             contents                           ascii
-     *  0000000000008d34 ef000000e8bd0090 e1b00000512fff1e  ............../Q
-     *  0000000000008d44 ea00b1f9e92d0090 e3a070fcef000000  ......-..p......
-     * On 32-bit machines, there are still 16 bytes per line but addresses and
-     * words are of course presented differently.
-     */
-    while (p < end) {
-        char* asc_out = ascii_buffer;
+  // Align the address to sizeof(long) and start 32 bytes before the address.
+  addr &= ~(sizeof(long) - 1);
+  if (addr >= 4128) {
+    addr -= 32;
+  }
 
-        int len = snprintf(code_buffer, sizeof(code_buffer), "%" PRIPTR " ", p);
-
-        for (size_t i = 0; i < 16/sizeof(long); i++) {
-            long data = ptrace(PTRACE_PEEKTEXT, tid, (void*)p, NULL);
-            if (data == -1 && errno != 0) {
-                // ptrace failed, probably because we're dumping memory in an
-                // unmapped or inaccessible page.
-#ifdef __LP64__
-                len += sprintf(code_buffer + len, "---------------- ");
+  // Don't bother if the address looks too low, or looks too high.
+  if (addr < 4096 ||
+#if defined(__LP64__)
+      addr > 0x4000000000000000UL - MEMORY_BYTES_TO_DUMP) {
 #else
-                len += sprintf(code_buffer + len, "-------- ");
+      addr > 0xffff0000 - MEMORY_BYTES_TO_DUMP) {
 #endif
-            } else {
-                len += sprintf(code_buffer + len, "%" PRIPTR " ",
-                               static_cast<uintptr_t>(data));
-            }
+    return;
+  }
 
-            for (size_t j = 0; j < sizeof(long); j++) {
-                /*
-                 * Our isprint() allows high-ASCII characters that display
-                 * differently (often badly) in different viewers, so we
-                 * just use a simpler test.
-                 */
-                char val = (data >> (j*8)) & 0xff;
-                if (val >= 0x20 && val < 0x7f) {
-                    *asc_out++ = val;
-                } else {
-                    *asc_out++ = '.';
-                }
-            }
-            p += sizeof(long);
-        }
-        *asc_out = '\0';
-        _LOG(log, logtype::MEMORY, "    %s %s\n", code_buffer, ascii_buffer);
+  _LOG(log, logtype::MEMORY, "\n%s\n", log_msg.c_str());
+
+  // Dump 256 bytes
+  uintptr_t data[MEMORY_BYTES_TO_DUMP/sizeof(uintptr_t)];
+  memset(data, 0, MEMORY_BYTES_TO_DUMP);
+  size_t bytes = backtrace->Read(addr, reinterpret_cast<uint8_t*>(data), sizeof(data));
+  if (bytes % sizeof(uintptr_t) != 0) {
+    // This should never happen, but just in case.
+    ALOGE("Bytes read %zu, is not a multiple of %zu", bytes, sizeof(uintptr_t));
+    bytes &= ~(sizeof(uintptr_t) - 1);
+  }
+
+  if (bytes < MEMORY_BYTES_TO_DUMP && bytes > 0) {
+    // Try to do one more read. This could happen if a read crosses a map, but
+    // the maps do not have any break between them. Only requires one extra
+    // read because a map has to contain at least one page, and the total
+    // number of bytes to dump is smaller than a page.
+    size_t bytes2 = backtrace->Read(addr + bytes, reinterpret_cast<uint8_t*>(data) + bytes,
+                                    sizeof(data) - bytes);
+    bytes += bytes2;
+    if (bytes2 > 0 && bytes % sizeof(uintptr_t) != 0) {
+      // This should never happen, but we'll try and continue any way.
+      ALOGE("Bytes after second read %zu, is not a multiple of %zu", bytes, sizeof(uintptr_t));
+      bytes &= ~(sizeof(uintptr_t) - 1);
     }
+  }
+
+  // Dump the code around memory as:
+  //  addr             contents                           ascii
+  //  0000000000008d34 ef000000e8bd0090 e1b00000512fff1e  ............../Q
+  //  0000000000008d44 ea00b1f9e92d0090 e3a070fcef000000  ......-..p......
+  // On 32-bit machines, there are still 16 bytes per line but addresses and
+  // words are of course presented differently.
+  uintptr_t* data_ptr = data;
+  for (size_t line = 0; line < MEMORY_BYTES_TO_DUMP / MEMORY_BYTES_PER_LINE; line++) {
+    std::string logline;
+    android::base::StringAppendF(&logline, "    %" PRIPTR, addr);
+
+    addr += MEMORY_BYTES_PER_LINE;
+    std::string ascii;
+    for (size_t i = 0; i < MEMORY_BYTES_PER_LINE / sizeof(uintptr_t); i++, data_ptr++) {
+      if (bytes >= sizeof(uintptr_t)) {
+        bytes -= sizeof(uintptr_t);
+        android::base::StringAppendF(&logline, " %" PRIPTR, *data_ptr);
+
+        // Fill out the ascii string from the data.
+        uint8_t* ptr = reinterpret_cast<uint8_t*>(data_ptr);
+        for (size_t val = 0; val < sizeof(uintptr_t); val++, ptr++) {
+          if (*ptr >= 0x20 && *ptr < 0x7f) {
+            ascii += *ptr;
+          } else {
+            ascii += '.';
+          }
+        }
+      } else {
+        logline += ' ' + std::string(sizeof(uintptr_t) * 2, '-');
+        ascii += std::string(sizeof(uintptr_t), '.');
+      }
+    }
+    _LOG(log, logtype::MEMORY, "%s  %s\n", logline.c_str(), ascii.c_str());
+  }
 }
diff --git a/debuggerd/utility.h b/debuggerd/utility.h
index 49b46e8..263374d 100644
--- a/debuggerd/utility.h
+++ b/debuggerd/utility.h
@@ -21,6 +21,8 @@
 #include <stdbool.h>
 #include <sys/types.h>
 
+#include <backtrace/Backtrace.h>
+
 // Figure out the abi based on defined macros.
 #if defined(__arm__)
 #define ABI_STRING "arm"
@@ -75,6 +77,6 @@
 
 int wait_for_sigstop(pid_t, int*, bool*);
 
-void dump_memory(log_t* log, pid_t tid, uintptr_t addr);
+void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* fmt, ...);
 
 #endif // _DEBUGGERD_UTILITY_H
diff --git a/debuggerd/x86/machine.cpp b/debuggerd/x86/machine.cpp
index 57330c1..c5f9259 100644
--- a/debuggerd/x86/machine.cpp
+++ b/debuggerd/x86/machine.cpp
@@ -14,18 +14,31 @@
  * limitations under the License.
  */
 
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
 #include <errno.h>
-#include <sys/types.h>
+#include <stdint.h>
+#include <string.h>
 #include <sys/ptrace.h>
 
-#include "../utility.h"
-#include "../machine.h"
+#include <backtrace/Backtrace.h>
 
-void dump_memory_and_code(log_t*, pid_t) {
+#include "machine.h"
+#include "utility.h"
+
+void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
+  struct pt_regs r;
+  if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r) == -1) {
+    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    return;
+  }
+
+  dump_memory(log, backtrace, static_cast<uintptr_t>(r.eax), "memory near eax:");
+  dump_memory(log, backtrace, static_cast<uintptr_t>(r.ebx), "memory near ebx:");
+  dump_memory(log, backtrace, static_cast<uintptr_t>(r.ecx), "memory near ecx:");
+  dump_memory(log, backtrace, static_cast<uintptr_t>(r.edx), "memory near edx:");
+  dump_memory(log, backtrace, static_cast<uintptr_t>(r.esi), "memory near esi:");
+  dump_memory(log, backtrace, static_cast<uintptr_t>(r.edi), "memory near edi:");
+
+  dump_memory(log, backtrace, static_cast<uintptr_t>(r.eip), "code around eip:");
 }
 
 void dump_registers(log_t* log, pid_t tid) {
@@ -34,6 +47,7 @@
     _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
     return;
   }
+
   _LOG(log, logtype::REGISTERS, "    eax %08lx  ebx %08lx  ecx %08lx  edx %08lx\n",
        r.eax, r.ebx, r.ecx, r.edx);
   _LOG(log, logtype::REGISTERS, "    esi %08lx  edi %08lx\n",
diff --git a/debuggerd/x86_64/machine.cpp b/debuggerd/x86_64/machine.cpp
old mode 100755
new mode 100644
index af4f35a..4f09a5d
--- a/debuggerd/x86_64/machine.cpp
+++ b/debuggerd/x86_64/machine.cpp
@@ -14,38 +14,51 @@
 ** limitations under the License.
 */
 
-#include <stddef.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
 #include <errno.h>
-#include <sys/types.h>
+#include <stdint.h>
 #include <sys/ptrace.h>
+#include <string.h>
 #include <sys/user.h>
 
-#include "../utility.h"
-#include "../machine.h"
+#include <backtrace/Backtrace.h>
 
-void dump_memory_and_code(log_t*, pid_t) {
+#include "machine.h"
+#include "utility.h"
+
+void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
+  struct user_regs_struct r;
+  if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r) == -1) {
+    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    return;
+  }
+
+  dump_memory(log, backtrace, static_cast<uintptr_t>(r.rax), "memory near rax:");
+  dump_memory(log, backtrace, static_cast<uintptr_t>(r.rbx), "memory near rbx:");
+  dump_memory(log, backtrace, static_cast<uintptr_t>(r.rcx), "memory near rcx:");
+  dump_memory(log, backtrace, static_cast<uintptr_t>(r.rdx), "memory near rdx:");
+  dump_memory(log, backtrace, static_cast<uintptr_t>(r.rsi), "memory near rsi:");
+  dump_memory(log, backtrace, static_cast<uintptr_t>(r.rdi), "memory near rdi:");
+
+  dump_memory(log, backtrace, static_cast<uintptr_t>(r.rip), "code around rip:");
 }
 
 void dump_registers(log_t* log, pid_t tid) {
-    struct user_regs_struct r;
-    if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) {
-        _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
-        return;
-    }
-    _LOG(log, logtype::REGISTERS, "    rax %016lx  rbx %016lx  rcx %016lx  rdx %016lx\n",
-         r.rax, r.rbx, r.rcx, r.rdx);
-    _LOG(log, logtype::REGISTERS, "    rsi %016lx  rdi %016lx\n",
-         r.rsi, r.rdi);
-    _LOG(log, logtype::REGISTERS, "    r8  %016lx  r9  %016lx  r10 %016lx  r11 %016lx\n",
-         r.r8, r.r9, r.r10, r.r11);
-    _LOG(log, logtype::REGISTERS, "    r12 %016lx  r13 %016lx  r14 %016lx  r15 %016lx\n",
-         r.r12, r.r13, r.r14, r.r15);
-    _LOG(log, logtype::REGISTERS, "    cs  %016lx  ss  %016lx\n",
-         r.cs, r.ss);
-    _LOG(log, logtype::REGISTERS, "    rip %016lx  rbp %016lx  rsp %016lx  eflags %016lx\n",
-         r.rip, r.rbp, r.rsp, r.eflags);
+  struct user_regs_struct r;
+  if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) {
+    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    return;
+  }
+
+  _LOG(log, logtype::REGISTERS, "    rax %016lx  rbx %016lx  rcx %016lx  rdx %016lx\n",
+       r.rax, r.rbx, r.rcx, r.rdx);
+  _LOG(log, logtype::REGISTERS, "    rsi %016lx  rdi %016lx\n",
+       r.rsi, r.rdi);
+  _LOG(log, logtype::REGISTERS, "    r8  %016lx  r9  %016lx  r10 %016lx  r11 %016lx\n",
+       r.r8, r.r9, r.r10, r.r11);
+  _LOG(log, logtype::REGISTERS, "    r12 %016lx  r13 %016lx  r14 %016lx  r15 %016lx\n",
+       r.r12, r.r13, r.r14, r.r15);
+  _LOG(log, logtype::REGISTERS, "    cs  %016lx  ss  %016lx\n",
+       r.cs, r.ss);
+  _LOG(log, logtype::REGISTERS, "    rip %016lx  rbp %016lx  rsp %016lx  eflags %016lx\n",
+       r.rip, r.rbp, r.rsp, r.eflags);
 }
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index d5e94ac..bb2bed2 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -203,7 +203,7 @@
     }
 
     rc = ioctl(fd, BLKROSET, &ON);
-    TEMP_FAILURE_RETRY(close(fd));
+    close(fd);
 
     return rc;
 }
diff --git a/fs_mgr/fs_mgr_verity.c b/fs_mgr/fs_mgr_verity.c
index 63b23b9..81c7c9a 100644
--- a/fs_mgr/fs_mgr_verity.c
+++ b/fs_mgr/fs_mgr_verity.c
@@ -169,20 +169,20 @@
 
     if (TEMP_FAILURE_RETRY(lseek64(data_device, 1024, SEEK_SET)) < 0) {
         ERROR("Error seeking to superblock");
-        TEMP_FAILURE_RETRY(close(data_device));
+        close(data_device);
         return -1;
     }
 
     if (TEMP_FAILURE_RETRY(read(data_device, &sb, sizeof(sb))) != sizeof(sb)) {
         ERROR("Error reading superblock");
-        TEMP_FAILURE_RETRY(close(data_device));
+        close(data_device);
         return -1;
     }
 
     ext4_parse_sb(&sb, &info);
     *device_size = info.len;
 
-    TEMP_FAILURE_RETRY(close(data_device));
+    close(data_device);
     return 0;
 }
 
@@ -301,7 +301,7 @@
 
 out:
     if (device != -1)
-        TEMP_FAILURE_RETRY(close(device));
+        close(device);
 
     if (retval != FS_MGR_SETUP_VERITY_SUCCESS) {
         free(*signature);
@@ -470,7 +470,7 @@
 
 out:
     if (fd != -1) {
-        TEMP_FAILURE_RETRY(close(fd));
+        close(fd);
     }
 
     return rc;
@@ -622,7 +622,7 @@
 
 out:
     if (fd != -1) {
-        TEMP_FAILURE_RETRY(close(fd));
+        close(fd);
     }
 
     return rc;
@@ -670,7 +670,7 @@
 
 out:
     if (fd != -1) {
-        TEMP_FAILURE_RETRY(close(fd));
+        close(fd);
     }
 
     return rc;
@@ -745,7 +745,7 @@
     free(signature);
 
     if (fd != -1) {
-        TEMP_FAILURE_RETRY(close(fd));
+        close(fd);
     }
 
     return rc;
@@ -913,7 +913,7 @@
     }
 
     if (fd) {
-        TEMP_FAILURE_RETRY(close(fd));
+        close(fd);
     }
 
     return rc;
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 9e5f9ff..ca31c50 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -591,7 +591,13 @@
 
 int do_trigger(int nargs, char **args)
 {
-    action_for_each_trigger(args[1], action_add_queue_tail);
+    char prop_val[PROP_VALUE_MAX];
+    int res = expand_props(prop_val, args[1], sizeof(prop_val));
+    if (res) {
+        ERROR("trigger: cannot expand '%s'\n", args[1]);
+        return -EINVAL;
+    }
+    action_for_each_trigger(prop_val, action_add_queue_tail);
     return 0;
 }
 
diff --git a/init/util.cpp b/init/util.cpp
index 9c62f68..8216892 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -171,7 +171,7 @@
     }
 
     bool okay = android::base::ReadFdToString(fd, content);
-    TEMP_FAILURE_RETRY(close(fd));
+    close(fd);
     return okay;
 }
 
@@ -185,7 +185,7 @@
     if (result == -1) {
         NOTICE("write_file: Unable to write to '%s': %s\n", path, strerror(errno));
     }
-    TEMP_FAILURE_RETRY(close(fd));
+    close(fd);
     return result;
 }
 
diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp
index 95cd4d1..2714d93 100644
--- a/libbacktrace/BacktraceCurrent.cpp
+++ b/libbacktrace/BacktraceCurrent.cpp
@@ -96,7 +96,7 @@
 static void SignalHandler(int, siginfo_t*, void* sigcontext) {
   ThreadEntry* entry = ThreadEntry::Get(getpid(), gettid(), false);
   if (!entry) {
-    BACK_LOGW("Unable to find pid %d tid %d information", getpid(), gettid());
+    BACK_LOGE("pid %d, tid %d entry not found", getpid(), gettid());
     return;
   }
 
@@ -109,9 +109,14 @@
   // the thread run ahead causing problems.
   // The number indicates that we are waiting for the second Wake() call
   // overall which is made by the thread requesting an unwind.
-  entry->Wait(2);
-
-  ThreadEntry::Remove(entry);
+  if (entry->Wait(2)) {
+    // Do not remove the entry here because that can result in a deadlock
+    // if the code cannot properly send a signal to the thread under test.
+    entry->Wake();
+  } else {
+    // At this point, it is possible that entry has been freed, so just exit.
+    BACK_LOGE("Timed out waiting for unwind thread to indicate it completed.");
+  }
 }
 
 bool BacktraceCurrent::UnwindThread(size_t num_ignore_frames) {
@@ -128,17 +133,15 @@
   act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
   sigemptyset(&act.sa_mask);
   if (sigaction(THREAD_SIGNAL, &act, &oldact) != 0) {
-    BACK_LOGW("sigaction failed %s", strerror(errno));
-    entry->Unlock();
+    BACK_LOGE("sigaction failed: %s", strerror(errno));
     ThreadEntry::Remove(entry);
     pthread_mutex_unlock(&g_sigaction_mutex);
     return false;
   }
 
   if (tgkill(Pid(), Tid(), THREAD_SIGNAL) != 0) {
-    BACK_LOGW("tgkill %d failed: %s", Tid(), strerror(errno));
+    BACK_LOGE("tgkill %d failed: %s", Tid(), strerror(errno));
     sigaction(THREAD_SIGNAL, &oldact, nullptr);
-    entry->Unlock();
     ThreadEntry::Remove(entry);
     pthread_mutex_unlock(&g_sigaction_mutex);
     return false;
@@ -146,17 +149,30 @@
 
   // Wait for the thread to get the ucontext. The number indicates
   // that we are waiting for the first Wake() call made by the thread.
-  entry->Wait(1);
+  bool wait_completed = entry->Wait(1);
 
   // After the thread has received the signal, allow other unwinders to
   // continue.
   sigaction(THREAD_SIGNAL, &oldact, nullptr);
   pthread_mutex_unlock(&g_sigaction_mutex);
 
-  bool unwind_done = UnwindFromContext(num_ignore_frames, entry->GetUcontext());
+  bool unwind_done = false;
+  if (wait_completed) {
+    unwind_done = UnwindFromContext(num_ignore_frames, entry->GetUcontext());
 
-  // Tell the signal handler to exit and release the entry.
-  entry->Wake();
+    // Tell the signal handler to exit and release the entry.
+    entry->Wake();
+
+    // Wait for the thread to indicate it is done with the ThreadEntry.
+    if (!entry->Wait(3)) {
+      // Send a warning, but do not mark as a failure to unwind.
+      BACK_LOGW("Timed out waiting for signal handler to indicate it finished.");
+    }
+  } else {
+    BACK_LOGE("Timed out waiting for signal handler to get ucontext data.");
+  }
+
+  ThreadEntry::Remove(entry);
 
   return unwind_done;
 }
diff --git a/libbacktrace/BacktraceLog.h b/libbacktrace/BacktraceLog.h
index 1632ec2..5c39f1c 100644
--- a/libbacktrace/BacktraceLog.h
+++ b/libbacktrace/BacktraceLog.h
@@ -25,4 +25,7 @@
 #define BACK_LOGW(format, ...) \
   ALOGW("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__)
 
+#define BACK_LOGE(format, ...) \
+  ALOGE("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__)
+
 #endif // _LIBBACKTRACE_BACKTRACE_LOG_H
diff --git a/libbacktrace/ThreadEntry.cpp b/libbacktrace/ThreadEntry.cpp
index e8b60c8..084c1aa 100644
--- a/libbacktrace/ThreadEntry.cpp
+++ b/libbacktrace/ThreadEntry.cpp
@@ -69,7 +69,7 @@
 }
 
 void ThreadEntry::Remove(ThreadEntry* entry) {
-  pthread_mutex_unlock(&entry->mutex_);
+  entry->Unlock();
 
   pthread_mutex_lock(&ThreadEntry::list_mutex_);
   if (--entry->ref_count_ == 0) {
@@ -96,20 +96,24 @@
   pthread_cond_destroy(&wait_cond_);
 }
 
-void ThreadEntry::Wait(int value) {
+bool ThreadEntry::Wait(int value) {
   timespec ts;
   clock_gettime(CLOCK_MONOTONIC, &ts);
-  ts.tv_sec += 10;
+  ts.tv_sec += 5;
 
+  bool wait_completed = true;
   pthread_mutex_lock(&wait_mutex_);
   while (wait_value_ != value) {
     int ret = pthread_cond_timedwait(&wait_cond_, &wait_mutex_, &ts);
     if (ret != 0) {
-      BACK_LOGW("pthread_cond_timedwait failed: %s", strerror(ret));
+      BACK_LOGW("pthread_cond_timedwait for value %d failed: %s", value, strerror(ret));
+      wait_completed = false;
       break;
     }
   }
   pthread_mutex_unlock(&wait_mutex_);
+
+  return wait_completed;
 }
 
 void ThreadEntry::Wake() {
diff --git a/libbacktrace/ThreadEntry.h b/libbacktrace/ThreadEntry.h
index 94becf2..11924a3 100644
--- a/libbacktrace/ThreadEntry.h
+++ b/libbacktrace/ThreadEntry.h
@@ -29,7 +29,7 @@
 
   void Wake();
 
-  void Wait(int);
+  bool Wait(int);
 
   void CopyUcontextFromSigcontext(void*);
 
diff --git a/libcutils/debugger.c b/libcutils/debugger.c
index 4558719..3407ec3 100644
--- a/libcutils/debugger.c
+++ b/libcutils/debugger.c
@@ -68,7 +68,7 @@
   }
 
   if (send_request(sock_fd, &msg, sizeof(msg)) < 0) {
-    TEMP_FAILURE_RETRY(close(sock_fd));
+    close(sock_fd);
     return -1;
   }
 
@@ -95,7 +95,7 @@
       break;
     }
   }
-  TEMP_FAILURE_RETRY(close(sock_fd));
+  close(sock_fd);
   return result;
 }
 
@@ -124,6 +124,6 @@
       memcpy(pathbuf, buffer, n + 1);
     }
   }
-  TEMP_FAILURE_RETRY(close(sock_fd));
+  close(sock_fd);
   return result;
 }
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 24499ae..1da52d8 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -388,7 +388,6 @@
         bool kick = false;
         bool leading = true;
         LogBufferElementLast last;
-        log_time start(log_time::EPOCH);
         for(it = mLogElements.begin(); it != mLogElements.end();) {
             LogBufferElement *e = *it;
 
@@ -417,8 +416,6 @@
                 continue;
             }
 
-            leading = false;
-
             if (hasBlacklist && mPrune.naughty(e)) {
                 last.clear(e);
                 it = erase(it);
@@ -448,29 +445,24 @@
             }
 
             if (e->getUid() != worst) {
-                if (start != log_time::EPOCH) {
+                if (leading) {
                     static const timespec too_old = {
                         EXPIRE_HOUR_THRESHOLD * 60 * 60, 0
                     };
-                    start = e->getRealTime() + too_old;
+                    LogBufferElementCollection::iterator last;
+                    last = mLogElements.end();
+                    --last;
+                    if ((e->getRealTime() < ((*last)->getRealTime() - too_old))
+                            || (e->getRealTime() > (*last)->getRealTime())) {
+                        break;
+                    }
                 }
+                leading = false;
                 last.clear(e);
                 ++it;
                 continue;
             }
 
-            if ((start != log_time::EPOCH) && (e->getRealTime() > start)) {
-                // KISS. Really a heuristic rather than algorithmically strong,
-                // a crude mechanism, the following loops will move the oldest
-                // watermark possibly wiping out the extra EXPIRE_HOUR_THRESHOLD
-                // we just thought we were preserving. We count on the typical
-                // pruneRows of 10% of total not being a sledgehammer.
-                // A stronger algorithm would have us loop back to the top if
-                // we have worst-UID enabled and we start expiring messages
-                // below less than EXPIRE_HOUR_THRESHOLD old.
-                break;
-            }
-
             pruneRows--;
             if (pruneRows == 0) {
                 break;
@@ -479,15 +471,21 @@
             kick = true;
 
             unsigned short len = e->getMsgLen();
-            stats.drop(e);
-            e->setDropped(1);
-            if (last.merge(e, 1)) {
-                it = mLogElements.erase(it);
-                stats.erase(e);
-                delete e;
+
+            // do not create any leading drops
+            if (leading) {
+                it = erase(it);
             } else {
-                last.add(e);
-                ++it;
+                stats.drop(e);
+                e->setDropped(1);
+                if (last.merge(e, 1)) {
+                    it = mLogElements.erase(it);
+                    stats.erase(e);
+                    delete e;
+                } else {
+                    last.add(e);
+                    ++it;
+                }
             }
             if (worst_sizes < second_worst_sizes) {
                 break;
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 9fe1b4f..0966038 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -645,10 +645,7 @@
     disabled
     oneshot
 
-on property:ro.debuggable=1
-    start perfprofd
-
 service perfprofd /system/xbin/perfprofd
-    disabled
+    class late_start
     user root
     oneshot