Append log data to tombstones
The Android Problem Report site shows tombstones uploaded from devices. We can see the native stack traces for every thread, but sometimes there's a very important bit of information sitting in the log, and without it we can't analyze the failure. This change modifies debuggerd so that the log contents for the crashing process are appended to the tombstone. The format matches the output of "logcat -v threadtime". Both "system" and "main" logs are included (but not interleaved -- we're not that fancy). This feature is only enabled when the "ro.debuggable" system property is set to 1 (indicating a development device). Bug 5456676 Change-Id: I3be1df59813ccf1058cec496a906f6d31fbc7b04
This commit is contained in:
parent
136dcc5ce6
commit
41e0cef301
|
@ -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 @@ static bool dump_sibling_thread_report(int tfd, unsigned pid, unsigned tid)
|
|||
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 @@ static bool engrave_tombstone(unsigned pid, unsigned tid, int debug_uid,
|
|||
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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue