summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristopher Tate <ctate@google.com>2013-03-19 13:12:23 -0700
committerChristopher Tate <ctate@google.com>2013-04-01 10:37:24 -0700
commitded2e5acfcf0c705f08577d0ccb720bd5037f4ba (patch)
tree909be0b0bf450f3fcc1f5a6313f799b151e0572b
parentd67bbab6d873c073fa5c3bdb93e8f32106898123 (diff)
downloadsystem_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
-rw-r--r--debuggerd/backtrace.c3
-rw-r--r--debuggerd/backtrace.h2
-rw-r--r--debuggerd/debuggerd.c3
-rw-r--r--debuggerd/tombstone.c59
-rw-r--r--debuggerd/utility.c42
-rw-r--r--debuggerd/utility.h4
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;