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
This commit is contained in:
parent
d67bbab6d8
commit
ded2e5acfc
6 changed files with 106 additions and 7 deletions
|
|
@ -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) {
|
int* total_sleep_time_usec) {
|
||||||
log_t log;
|
log_t log;
|
||||||
log.tfd = fd;
|
log.tfd = fd;
|
||||||
|
log.amfd = amfd;
|
||||||
log.quiet = true;
|
log.quiet = true;
|
||||||
|
|
||||||
ptrace_context_t* context = load_ptrace_context(tid);
|
ptrace_context_t* context = load_ptrace_context(tid);
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
/* Dumps a backtrace using a format similar to what Dalvik uses so that the result
|
/* Dumps a backtrace using a format similar to what Dalvik uses so that the result
|
||||||
* can be intermixed in a bug report. */
|
* 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);
|
int* total_sleep_time_usec);
|
||||||
|
|
||||||
#endif // _DEBUGGERD_BACKTRACE_H
|
#endif // _DEBUGGERD_BACKTRACE_H
|
||||||
|
|
|
||||||
|
|
@ -314,7 +314,8 @@ static void handle_request(int fd) {
|
||||||
&total_sleep_time_usec);
|
&total_sleep_time_usec);
|
||||||
} else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) {
|
} else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) {
|
||||||
XLOG("stopped -- dumping to fd\n");
|
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);
|
&total_sleep_time_usec);
|
||||||
} else {
|
} else {
|
||||||
XLOG("stopped -- continuing\n");
|
XLOG("stopped -- continuing\n");
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,9 @@
|
||||||
#include <corkscrew/demangle.h>
|
#include <corkscrew/demangle.h>
|
||||||
#include <corkscrew/backtrace.h>
|
#include <corkscrew/backtrace.h>
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <linux/un.h>
|
||||||
|
|
||||||
#include <selinux/android.h>
|
#include <selinux/android.h>
|
||||||
|
|
||||||
#include "machine.h"
|
#include "machine.h"
|
||||||
|
|
@ -47,6 +50,9 @@
|
||||||
#define MAX_TOMBSTONES 10
|
#define MAX_TOMBSTONES 10
|
||||||
#define TOMBSTONE_DIR "/data/tombstones"
|
#define TOMBSTONE_DIR "/data/tombstones"
|
||||||
|
|
||||||
|
/* Must match the path defined in NativeCrashListener.java */
|
||||||
|
#define NCRASH_SOCKET_PATH "/data/system/ndebugsocket"
|
||||||
|
|
||||||
#define typecheck(x,y) { \
|
#define typecheck(x,y) { \
|
||||||
typeof(x) __dummy1; \
|
typeof(x) __dummy1; \
|
||||||
typeof(y) __dummy2; \
|
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");
|
property_get("ro.debuggable", value, "0");
|
||||||
bool want_logs = (value[0] == '1');
|
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,
|
_LOG(log, false,
|
||||||
"*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
|
"*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
|
||||||
dump_build_info(log);
|
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) {
|
if (want_logs) {
|
||||||
dump_logs(log, pid, false);
|
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;
|
return detach_failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -712,6 +740,35 @@ static char* find_and_open_tombstone(int* fd)
|
||||||
return strdup(path);
|
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,
|
char* engrave_tombstone(pid_t pid, pid_t tid, int signal,
|
||||||
bool dump_sibling_threads, bool quiet, bool* detach_failed,
|
bool dump_sibling_threads, bool quiet, bool* detach_failed,
|
||||||
int* total_sleep_time_usec) {
|
int* total_sleep_time_usec) {
|
||||||
|
|
@ -732,10 +789,12 @@ char* engrave_tombstone(pid_t pid, pid_t tid, int signal,
|
||||||
|
|
||||||
log_t log;
|
log_t log;
|
||||||
log.tfd = fd;
|
log.tfd = fd;
|
||||||
|
log.amfd = activity_manager_connect();
|
||||||
log.quiet = quiet;
|
log.quiet = quiet;
|
||||||
*detach_failed = dump_crash(&log, pid, tid, signal, dump_sibling_threads,
|
*detach_failed = dump_crash(&log, pid, tid, signal, dump_sibling_threads,
|
||||||
total_sleep_time_usec);
|
total_sleep_time_usec);
|
||||||
|
|
||||||
|
close(log.amfd);
|
||||||
close(fd);
|
close(fd);
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,27 +25,63 @@
|
||||||
#include <cutils/logd.h>
|
#include <cutils/logd.h>
|
||||||
#include <sys/ptrace.h>
|
#include <sys/ptrace.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
|
|
||||||
const int sleep_time_usec = 50000; /* 0.05 seconds */
|
const int sleep_time_usec = 50000; /* 0.05 seconds */
|
||||||
const int max_total_sleep_usec = 10000000; /* 10 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, ...) {
|
void _LOG(log_t* log, bool in_tombstone_only, const char *fmt, ...) {
|
||||||
char buf[512];
|
char buf[512];
|
||||||
|
bool want_tfd_write;
|
||||||
|
bool want_log_write;
|
||||||
|
bool want_amfd_write;
|
||||||
|
int len;
|
||||||
|
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
|
|
||||||
if (log && log->tfd >= 0) {
|
// where is the information going to go?
|
||||||
int len;
|
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);
|
vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||||
len = strlen(buf);
|
len = strlen(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (want_tfd_write) {
|
||||||
write(log->tfd, buf, len);
|
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);
|
__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);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,9 @@
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* tombstone file descriptor */
|
/* tombstone file descriptor */
|
||||||
int tfd;
|
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;
|
bool quiet;
|
||||||
} log_t;
|
} log_t;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue