Merge "Append log data to tombstones"
diff --git a/debuggerd/debuggerd.c b/debuggerd/debuggerd.c
index 97a6b9e..91d9dda 100644
--- a/debuggerd/debuggerd.c
+++ b/debuggerd/debuggerd.c
@@ -31,6 +31,7 @@
 
 #include <cutils/sockets.h>
 #include <cutils/logd.h>
+#include <cutils/logger.h>
 #include <cutils/properties.h>
 
 #include <linux/input.h>
@@ -413,6 +414,101 @@
     return need_cleanup != 0;
 }
 
+/*
+ * Reads the contents of the specified log device, filters out the entries
+ * that don't match the specified pid, and writes them to the tombstone file.
+ */
+static void dump_log_file(int tfd, unsigned pid, const char* filename)
+{
+    int logfd = open(filename, O_RDONLY | O_NONBLOCK);
+    if (logfd < 0) {
+        XLOG("Unable to open %s: %s\n", filename, strerror(errno));
+        return;
+    }
+    _LOG(tfd, true, "--------- log %s\n", filename);
+
+    union {
+        unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1];
+        struct logger_entry entry;
+    } log_entry;
+
+    while (true) {
+        ssize_t actual = read(logfd, log_entry.buf, LOGGER_ENTRY_MAX_LEN);
+        if (actual < 0) {
+            if (errno == EINTR) {
+                /* interrupted by signal, retry */
+                continue;
+            } else if (errno == EAGAIN) {
+                /* non-blocking EOF; we're done */
+                break;
+            } else {
+                _LOG(tfd, true, "Error while reading log: %s\n",
+                    strerror(errno));
+                break;
+            }
+        } else if (actual == 0) {
+            _LOG(tfd, true, "Got zero bytes while reading log: %s\n",
+                strerror(errno));
+            break;
+        }
+
+        /*
+         * NOTE: if you XLOG something here, this will spin forever,
+         * because you will be writing as fast as you're reading.  Any
+         * high-frequency debug diagnostics should just be written to
+         * the tombstone file.
+         */
+
+        struct logger_entry* entry = &log_entry.entry;
+
+        if (entry->pid != (int32_t) pid) {
+            /* wrong pid, ignore */
+            continue;
+        }
+
+        /*
+         * Msg format is: <priority:1><tag:N>\0<message:N>\0
+         *
+         * We want to display it in the same format as "logcat -v threadtime"
+         * (although in this case the pid is redundant).
+         *
+         * TODO: scan for line breaks ('\n') and display each text line
+         * on a separate line, prefixed with the header, like logcat does.
+         */
+        static const char* kPrioChars = "!.VDIWEFS";
+        unsigned char prio = entry->msg[0];
+        const char* tag = entry->msg + 1;
+        const char* msg = tag + strlen(tag) + 1;
+
+        log_entry.entry.msg[entry->len] = '\0';
+
+        char timeBuf[32];
+        time_t sec = (time_t) entry->sec;
+        struct tm tmBuf;
+        struct tm* ptm;
+        ptm = localtime_r(&sec, &tmBuf);
+        strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
+
+        _LOG(tfd, true, "%s.%03ld %5d %5d %c %-8s: %s\n",
+            timeBuf, entry->nsec / 1000000,
+            entry->pid, entry->tid,
+            (prio < strlen(kPrioChars) ? kPrioChars[prio] : '?'),
+            tag, msg);
+    }
+
+    close(logfd);
+}
+
+/*
+ * Dumps the logs generated by the specified pid to the tombstone, from both
+ * "system" and "main" log devices.  Ideally we'd interleave the output.
+ */
+static void dump_logs(int tfd, unsigned pid)
+{
+    dump_log_file(tfd, pid, "/dev/log/system");
+    dump_log_file(tfd, pid, "/dev/log/main");
+}
+
 /* Return true if some thread is not detached cleanly */
 static bool engrave_tombstone(unsigned pid, unsigned tid, int debug_uid,
                               int signal)
@@ -437,6 +533,13 @@
         need_cleanup = dump_sibling_thread_report(fd, pid, tid);
     }
 
+    /* don't copy log to tombstone unless this is a dev device */
+    char value[PROPERTY_VALUE_MAX];
+    property_get("ro.debuggable", value, "0");
+    if (value[0] == '1') {
+        dump_logs(fd, pid);
+    }
+
     close(fd);
     return need_cleanup;
 }