diff options
author | Christopher Tate <ctate@google.com> | 2013-03-19 13:12:23 -0700 |
---|---|---|
committer | Christopher Tate <ctate@google.com> | 2013-04-01 10:37:24 -0700 |
commit | ded2e5acfcf0c705f08577d0ccb720bd5037f4ba (patch) | |
tree | 909be0b0bf450f3fcc1f5a6313f799b151e0572b /debuggerd | |
parent | d67bbab6d873c073fa5c3bdb93e8f32106898123 (diff) | |
download | system_core-ded2e5acfcf0c705f08577d0ccb720bd5037f4ba.zip system_core-ded2e5acfcf0c705f08577d0ccb720bd5037f4ba.tar.gz system_core-ded2e5acfcf0c705f08577d0ccb720bd5037f4ba.tar.bz2 |
debuggerd now notifies the Activity Manager about native crashes
The Activity Manager sets up a permission-guarded domain socket, which
debuggerd connects to when a crash happens. If this is successful,
the daemon then mirrors the logged crash report to that socket, then
closes it.
Bug 8322568
Change-Id: Ife0c772a628ef82e8457094e511ce1edbfe57460
Diffstat (limited to 'debuggerd')
-rw-r--r-- | debuggerd/backtrace.c | 3 | ||||
-rw-r--r-- | debuggerd/backtrace.h | 2 | ||||
-rw-r--r-- | debuggerd/debuggerd.c | 3 | ||||
-rw-r--r-- | debuggerd/tombstone.c | 59 | ||||
-rw-r--r-- | debuggerd/utility.c | 42 | ||||
-rw-r--r-- | debuggerd/utility.h | 4 |
6 files changed, 106 insertions, 7 deletions
diff --git a/debuggerd/backtrace.c b/debuggerd/backtrace.c index ba76e7d..08f8836 100644 --- a/debuggerd/backtrace.c +++ b/debuggerd/backtrace.c @@ -112,10 +112,11 @@ static void dump_thread(log_t* log, pid_t tid, ptrace_context_t* context, bool a } } -void dump_backtrace(int fd, pid_t pid, pid_t tid, bool* detach_failed, +void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed, int* total_sleep_time_usec) { log_t log; log.tfd = fd; + log.amfd = amfd; log.quiet = true; ptrace_context_t* context = load_ptrace_context(tid); diff --git a/debuggerd/backtrace.h b/debuggerd/backtrace.h index ec7d20f..c5c786a 100644 --- a/debuggerd/backtrace.h +++ b/debuggerd/backtrace.h @@ -25,7 +25,7 @@ /* Dumps a backtrace using a format similar to what Dalvik uses so that the result * can be intermixed in a bug report. */ -void dump_backtrace(int fd, pid_t pid, pid_t tid, bool* detach_failed, +void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed, int* total_sleep_time_usec); #endif // _DEBUGGERD_BACKTRACE_H diff --git a/debuggerd/debuggerd.c b/debuggerd/debuggerd.c index 65ff0f6..2736f30 100644 --- a/debuggerd/debuggerd.c +++ b/debuggerd/debuggerd.c @@ -314,7 +314,8 @@ static void handle_request(int fd) { &total_sleep_time_usec); } else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) { XLOG("stopped -- dumping to fd\n"); - dump_backtrace(fd, request.pid, request.tid, &detach_failed, + dump_backtrace(fd, -1, + request.pid, request.tid, &detach_failed, &total_sleep_time_usec); } else { XLOG("stopped -- continuing\n"); diff --git a/debuggerd/tombstone.c b/debuggerd/tombstone.c index e8b3e24..7c9cb6c 100644 --- a/debuggerd/tombstone.c +++ b/debuggerd/tombstone.c @@ -35,6 +35,9 @@ #include <corkscrew/demangle.h> #include <corkscrew/backtrace.h> +#include <sys/socket.h> +#include <linux/un.h> + #include <selinux/android.h> #include "machine.h" @@ -47,6 +50,9 @@ #define MAX_TOMBSTONES 10 #define TOMBSTONE_DIR "/data/tombstones" +/* Must match the path defined in NativeCrashListener.java */ +#define NCRASH_SOCKET_PATH "/data/system/ndebugsocket" + #define typecheck(x,y) { \ typeof(x) __dummy1; \ typeof(y) __dummy2; \ @@ -627,6 +633,18 @@ static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, property_get("ro.debuggable", value, "0"); bool want_logs = (value[0] == '1'); + if (log->amfd >= 0) { + /* + * Activity Manager protocol: binary 32-bit network-byte-order ints for the + * pid and signal number, followed by the raw text of the dump, culminating + * in a zero byte that marks end-of-data. + */ + uint32_t datum = htonl(pid); + TEMP_FAILURE_RETRY( write(log->amfd, &datum, 4) ); + datum = htonl(signal); + TEMP_FAILURE_RETRY( write(log->amfd, &datum, 4) ); + } + _LOG(log, false, "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"); dump_build_info(log); @@ -653,6 +671,16 @@ static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, if (want_logs) { dump_logs(log, pid, false); } + + /* send EOD to the Activity Manager, then wait for its ack to avoid racing ahead + * and killing the target out from under it */ + if (log->amfd >= 0) { + uint8_t eodMarker = 0; + TEMP_FAILURE_RETRY( write(log->amfd, &eodMarker, 1) ); + /* 3 sec timeout reading the ack; we're fine if that happens */ + TEMP_FAILURE_RETRY( read(log->amfd, &eodMarker, 1) ); + } + return detach_failed; } @@ -712,6 +740,35 @@ static char* find_and_open_tombstone(int* fd) return strdup(path); } +static int activity_manager_connect() { + int amfd = socket(PF_UNIX, SOCK_STREAM, 0); + if (amfd >= 0) { + struct sockaddr_un address; + int err; + + memset(&address, 0, sizeof(address)); + address.sun_family = AF_UNIX; + strncpy(address.sun_path, NCRASH_SOCKET_PATH, sizeof(address.sun_path)); + err = TEMP_FAILURE_RETRY( connect(amfd, (struct sockaddr*) &address, sizeof(address)) ); + if (!err) { + struct timeval tv; + memset(&tv, 0, sizeof(tv)); + tv.tv_sec = 1; // tight leash + err = setsockopt(amfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + if (!err) { + tv.tv_sec = 3; // 3 seconds on handshake read + err = setsockopt(amfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + } + } + if (err) { + close(amfd); + amfd = -1; + } + } + + return amfd; +} + char* engrave_tombstone(pid_t pid, pid_t tid, int signal, bool dump_sibling_threads, bool quiet, bool* detach_failed, int* total_sleep_time_usec) { @@ -732,10 +789,12 @@ char* engrave_tombstone(pid_t pid, pid_t tid, int signal, log_t log; log.tfd = fd; + log.amfd = activity_manager_connect(); log.quiet = quiet; *detach_failed = dump_crash(&log, pid, tid, signal, dump_sibling_threads, total_sleep_time_usec); + close(log.amfd); close(fd); return path; } diff --git a/debuggerd/utility.c b/debuggerd/utility.c index aabaf74..de9200a 100644 --- a/debuggerd/utility.c +++ b/debuggerd/utility.c @@ -25,27 +25,63 @@ #include <cutils/logd.h> #include <sys/ptrace.h> #include <sys/wait.h> +#include <arpa/inet.h> +#include <assert.h> #include "utility.h" const int sleep_time_usec = 50000; /* 0.05 seconds */ const int max_total_sleep_usec = 10000000; /* 10 seconds */ +static int write_to_am(int fd, const char* buf, int len) { + int to_write = len; + while (to_write > 0) { + int written = TEMP_FAILURE_RETRY( write(fd, buf + len - to_write, to_write) ); + if (written < 0) { + /* hard failure */ + return -1; + } + to_write -= written; + } + return len; +} + void _LOG(log_t* log, bool in_tombstone_only, const char *fmt, ...) { char buf[512]; + bool want_tfd_write; + bool want_log_write; + bool want_amfd_write; + int len; va_list ap; va_start(ap, fmt); - if (log && log->tfd >= 0) { - int len; + // where is the information going to go? + want_tfd_write = log && log->tfd >= 0; // write to the tombstone fd? + want_log_write = !in_tombstone_only && (!log || !log->quiet); + want_amfd_write = log && log->amfd >= 0; // only used when want_log_write is true + + // if we're going to need the literal string, generate it once here + if (want_tfd_write || want_amfd_write) { vsnprintf(buf, sizeof(buf), fmt, ap); len = strlen(buf); + } + + if (want_tfd_write) { write(log->tfd, buf, len); } - if (!in_tombstone_only && (!log || !log->quiet)) { + if (want_log_write) { + // whatever goes to logcat also goes to the Activity Manager __android_log_vprint(ANDROID_LOG_INFO, "DEBUG", fmt, ap); + if (want_amfd_write && len > 0) { + int written = write_to_am(log->amfd, buf, len); + if (written <= 0) { + // timeout or other failure on write; stop informing the activity manager + LOG("AM write failure, giving up\n"); + log->amfd = -1; + } + } } va_end(ap); } diff --git a/debuggerd/utility.h b/debuggerd/utility.h index 136f46d..8d31e5e 100644 --- a/debuggerd/utility.h +++ b/debuggerd/utility.h @@ -24,7 +24,9 @@ typedef struct { /* tombstone file descriptor */ int tfd; - /* if true, does not log anything to the Android logcat */ + /* Activity Manager socket file descriptor */ + int amfd; + /* if true, does not log anything to the Android logcat or Activity Manager */ bool quiet; } log_t; |