diff options
Diffstat (limited to 'debuggerd/debuggerd.cpp')
-rw-r--r-- | debuggerd/debuggerd.cpp | 228 |
1 files changed, 87 insertions, 141 deletions
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp index de8ba9d..61805c9 100644 --- a/debuggerd/debuggerd.cpp +++ b/debuggerd/debuggerd.cpp @@ -30,7 +30,6 @@ #include <sys/stat.h> #include <sys/poll.h> -#include <log/logd.h> #include <log/logger.h> #include <cutils/sockets.h> @@ -46,105 +45,55 @@ #include "tombstone.h" #include "utility.h" -typedef struct { +struct debugger_request_t { debugger_action_t action; pid_t pid, tid; uid_t uid, gid; uintptr_t abort_msg_address; -} debugger_request_t; - -static int write_string(const char* file, const char* string) { - int len; - int fd; - ssize_t amt; - fd = open(file, O_RDWR); - len = strlen(string); - if (fd < 0) - return -errno; - amt = write(fd, string, len); - close(fd); - return amt >= 0 ? 0 : -errno; -} - -static void init_debug_led() { - // trout leds - write_string("/sys/class/leds/red/brightness", "0"); - write_string("/sys/class/leds/green/brightness", "0"); - write_string("/sys/class/leds/blue/brightness", "0"); - write_string("/sys/class/leds/red/device/blink", "0"); - // sardine leds - write_string("/sys/class/leds/left/cadence", "0,0"); -} + int32_t original_si_code; +}; -static void enable_debug_led() { - // trout leds - write_string("/sys/class/leds/red/brightness", "255"); - // sardine leds - write_string("/sys/class/leds/left/cadence", "1,0"); -} +static void wait_for_user_action(const debugger_request_t &request) { + // Find out the name of the process that crashed. + char path[64]; + snprintf(path, sizeof(path), "/proc/%d/exe", request.pid); -static void disable_debug_led() { - // trout leds - write_string("/sys/class/leds/red/brightness", "0"); - // sardine leds - write_string("/sys/class/leds/left/cadence", "0,0"); -} + char exe[PATH_MAX]; + int count; + if ((count = readlink(path, exe, sizeof(exe) - 1)) == -1) { + ALOGE("readlink('%s') failed: %s", path, strerror(errno)); + strlcpy(exe, "unknown", sizeof(exe)); + } else { + exe[count] = '\0'; + } -static void wait_for_user_action(pid_t pid) { - // First log a helpful message - LOG( "********************************************************\n" - "* Process %d has been suspended while crashing. To\n" - "* attach gdbserver for a gdb connection on port 5039\n" - "* and start gdbclient:\n" - "*\n" - "* gdbclient app_process :5039 %d\n" - "*\n" - "* Wait for gdb to start, then press HOME or VOLUME DOWN key\n" - "* to let the process continue crashing.\n" - "********************************************************\n", - pid, pid); - - // wait for HOME or VOLUME DOWN key + // Explain how to attach the debugger. + ALOGI("********************************************************\n" + "* Process %d has been suspended while crashing.\n" + "* To attach gdbserver for a gdb connection on port 5039\n" + "* and start gdbclient:\n" + "*\n" + "* gdbclient %s :5039 %d\n" + "*\n" + "* Wait for gdb to start, then press the VOLUME DOWN key\n" + "* to let the process continue crashing.\n" + "********************************************************\n", + request.pid, exe, request.tid); + + // Wait for VOLUME DOWN. if (init_getevent() == 0) { - int ms = 1200 / 10; - int dit = 1; - int dah = 3*dit; - int _ = -dit; - int ___ = 3*_; - int _______ = 7*_; - const int codes[] = { - dit,_,dit,_,dit,___,dah,_,dah,_,dah,___,dit,_,dit,_,dit,_______ - }; - size_t s = 0; - struct input_event e; - bool done = false; - init_debug_led(); - enable_debug_led(); - do { - int timeout = abs(codes[s]) * ms; - int res = get_event(&e, timeout); - if (res == 0) { - if (e.type == EV_KEY - && (e.code == KEY_HOME || e.code == KEY_VOLUMEDOWN) - && e.value == 0) { - done = true; - } - } else if (res == 1) { - if (++s >= sizeof(codes)/sizeof(*codes)) - s = 0; - if (codes[s] > 0) { - enable_debug_led(); - } else { - disable_debug_led(); + while (true) { + input_event e; + if (get_event(&e, -1) == 0) { + if (e.type == EV_KEY && e.code == KEY_VOLUMEDOWN && e.value == 0) { + break; } } - } while (!done); + } uninit_getevent(); } - // don't forget to turn debug led off - disable_debug_led(); - LOG("debuggerd resuming process %d", pid); + ALOGI("debuggerd resuming process %d", request.pid); } static int get_process_info(pid_t tid, pid_t* out_pid, uid_t* out_uid, uid_t* out_gid) { @@ -176,24 +125,24 @@ static int get_process_info(pid_t tid, pid_t* out_pid, uid_t* out_uid, uid_t* ou } static int read_request(int fd, debugger_request_t* out_request) { - struct ucred cr; - int len = sizeof(cr); + ucred cr; + socklen_t len = sizeof(cr); int status = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len); if (status != 0) { - LOG("cannot get credentials\n"); + ALOGE("cannot get credentials\n"); return -1; } - XLOG("reading tid\n"); + ALOGV("reading tid\n"); fcntl(fd, F_SETFL, O_NONBLOCK); - struct pollfd pollfds[1]; + pollfd pollfds[1]; pollfds[0].fd = fd; pollfds[0].events = POLLIN; pollfds[0].revents = 0; status = TEMP_FAILURE_RETRY(poll(pollfds, 1, 3000)); if (status != 1) { - LOG("timed out reading tid (from pid=%d uid=%d)\n", cr.pid, cr.uid); + ALOGE("timed out reading tid (from pid=%d uid=%d)\n", cr.pid, cr.uid); return -1; } @@ -201,13 +150,11 @@ static int read_request(int fd, debugger_request_t* out_request) { memset(&msg, 0, sizeof(msg)); status = TEMP_FAILURE_RETRY(read(fd, &msg, sizeof(msg))); if (status < 0) { - LOG("read failure? %s (pid=%d uid=%d)\n", strerror(errno), cr.pid, cr.uid); + ALOGE("read failure? %s (pid=%d uid=%d)\n", strerror(errno), cr.pid, cr.uid); return -1; } - if (status == sizeof(debugger_msg_t)) { - XLOG("crash request of size %d abort_msg_address=%#08x\n", status, msg.abort_msg_address); - } else { - LOG("invalid crash request of size %d (from pid=%d uid=%d)\n", status, cr.pid, cr.uid); + if (status != sizeof(debugger_msg_t)) { + ALOGE("invalid crash request of size %d (from pid=%d uid=%d)\n", status, cr.pid, cr.uid); return -1; } @@ -217,6 +164,7 @@ static int read_request(int fd, debugger_request_t* out_request) { out_request->uid = cr.uid; out_request->gid = cr.gid; out_request->abort_msg_address = msg.abort_msg_address; + out_request->original_si_code = msg.original_si_code; if (msg.action == DEBUGGER_ACTION_CRASH) { // Ensure that the tid reported by the crashing process is valid. @@ -224,7 +172,7 @@ static int read_request(int fd, debugger_request_t* out_request) { struct stat s; snprintf(buf, sizeof buf, "/proc/%d/task/%d", out_request->pid, out_request->tid); if (stat(buf, &s)) { - LOG("tid %d does not exist in pid %d. ignoring debug request\n", + ALOGE("tid %d does not exist in pid %d. ignoring debug request\n", out_request->tid, out_request->pid); return -1; } @@ -235,7 +183,7 @@ static int read_request(int fd, debugger_request_t* out_request) { status = get_process_info(out_request->tid, &out_request->pid, &out_request->uid, &out_request->gid); if (status < 0) { - LOG("tid %d does not exist. ignoring explicit dump request\n", out_request->tid); + ALOGE("tid %d does not exist. ignoring explicit dump request\n", out_request->tid); return -1; } } else { @@ -256,13 +204,13 @@ static bool should_attach_gdb(debugger_request_t* request) { } static void handle_request(int fd) { - XLOG("handle_request(%d)\n", fd); + ALOGV("handle_request(%d)\n", fd); debugger_request_t request; memset(&request, 0, sizeof(request)); int status = read_request(fd, &request); if (!status) { - XLOG("BOOM: pid=%d uid=%d gid=%d tid=%d\n", + ALOGV("BOOM: pid=%d uid=%d gid=%d tid=%d\n", request.pid, request.uid, request.gid, request.tid); // At this point, the thread that made the request is blocked in @@ -276,12 +224,12 @@ static void handle_request(int fd) { // See details in bionic/libc/linker/debugger.c, in function // debugger_signal_handler(). if (ptrace(PTRACE_ATTACH, request.tid, 0, 0)) { - LOG("ptrace attach failed: %s\n", strerror(errno)); + ALOGE("ptrace attach failed: %s\n", strerror(errno)); } else { bool detach_failed = false; bool attach_gdb = should_attach_gdb(&request); if (TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) { - LOG("failed responding to client: %s\n", strerror(errno)); + ALOGE("failed responding to client: %s\n", strerror(errno)); } else { char* tombstone_path = NULL; @@ -300,34 +248,36 @@ static void handle_request(int fd) { switch (signal) { case SIGSTOP: if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) { - XLOG("stopped -- dumping to tombstone\n"); - tombstone_path = engrave_tombstone( - request.pid, request.tid, signal, request.abort_msg_address, true, true, - &detach_failed, &total_sleep_time_usec); + ALOGV("stopped -- dumping to tombstone\n"); + tombstone_path = engrave_tombstone(request.pid, request.tid, + signal, request.original_si_code, + request.abort_msg_address, true, + &detach_failed, &total_sleep_time_usec); } else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) { - XLOG("stopped -- dumping to fd\n"); + ALOGV("stopped -- dumping to fd\n"); dump_backtrace(fd, -1, request.pid, request.tid, &detach_failed, &total_sleep_time_usec); } else { - XLOG("stopped -- continuing\n"); + ALOGV("stopped -- continuing\n"); status = ptrace(PTRACE_CONT, request.tid, 0, 0); if (status) { - LOG("ptrace continue failed: %s\n", strerror(errno)); + ALOGE("ptrace continue failed: %s\n", strerror(errno)); } continue; // loop again } break; - case SIGILL: case SIGABRT: case SIGBUS: case SIGFPE: - case SIGSEGV: + case SIGILL: case SIGPIPE: + case SIGSEGV: #ifdef SIGSTKFLT case SIGSTKFLT: #endif - XLOG("stopped -- fatal signal\n"); + case SIGTRAP: + ALOGV("stopped -- fatal signal\n"); // Send a SIGSTOP to the process to make all of // the non-signaled threads stop moving. Without // this we get a lot of "ptrace detach failed: @@ -335,14 +285,14 @@ static void handle_request(int fd) { kill(request.pid, SIGSTOP); // don't dump sibling threads when attaching to GDB because it // makes the process less reliable, apparently... - tombstone_path = engrave_tombstone( - request.pid, request.tid, signal, request.abort_msg_address, !attach_gdb, - false, &detach_failed, &total_sleep_time_usec); + tombstone_path = engrave_tombstone(request.pid, request.tid, + signal, request.original_si_code, + request.abort_msg_address, !attach_gdb, + &detach_failed, &total_sleep_time_usec); break; default: - XLOG("stopped -- unexpected signal\n"); - LOG("process stopped due to unexpected signal %d\n", signal); + ALOGE("process stopped due to unexpected signal %d\n", signal); break; } break; @@ -358,14 +308,14 @@ static void handle_request(int fd) { free(tombstone_path); } - XLOG("detaching\n"); + ALOGV("detaching\n"); if (attach_gdb) { // stop the process so we can debug kill(request.pid, SIGSTOP); // detach so we can attach gdbserver if (ptrace(PTRACE_DETACH, request.tid, 0, 0)) { - LOG("ptrace detach from %d failed: %s\n", request.tid, strerror(errno)); + ALOGE("ptrace detach from %d failed: %s\n", request.tid, strerror(errno)); detach_failed = true; } @@ -373,11 +323,11 @@ static void handle_request(int fd) { // for user action for the crashing process. // in this case, we log a message and turn the debug LED on // waiting for a gdb connection (for instance) - wait_for_user_action(request.pid); + wait_for_user_action(request); } else { // just detach if (ptrace(PTRACE_DETACH, request.tid, 0, 0)) { - LOG("ptrace detach from %d failed: %s\n", request.tid, strerror(errno)); + ALOGE("ptrace detach from %d failed: %s\n", request.tid, strerror(errno)); detach_failed = true; } } @@ -389,7 +339,7 @@ static void handle_request(int fd) { // actual parent won't receive a death notification via wait(2). At this point // there's not much we can do about that. if (detach_failed) { - LOG("debuggerd committing suicide to free the zombie!\n"); + ALOGE("debuggerd committing suicide to free the zombie!\n"); kill(getpid(), SIGKILL); } } @@ -401,54 +351,50 @@ static void handle_request(int fd) { } static int do_server() { - int s; - struct sigaction act; - int logsocket = -1; - - // debuggerd crashes can't be reported to debuggerd. Reset all of the - // crash handlers. - signal(SIGILL, SIG_DFL); + // debuggerd crashes can't be reported to debuggerd. + // Reset all of the crash handlers. signal(SIGABRT, SIG_DFL); signal(SIGBUS, SIG_DFL); signal(SIGFPE, SIG_DFL); + signal(SIGILL, SIG_DFL); signal(SIGSEGV, SIG_DFL); #ifdef SIGSTKFLT signal(SIGSTKFLT, SIG_DFL); #endif + signal(SIGTRAP, SIG_DFL); // Ignore failed writes to closed sockets signal(SIGPIPE, SIG_IGN); - logsocket = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_DGRAM); + int logsocket = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_DGRAM); if (logsocket < 0) { logsocket = -1; } else { fcntl(logsocket, F_SETFD, FD_CLOEXEC); } + struct sigaction act; act.sa_handler = SIG_DFL; sigemptyset(&act.sa_mask); sigaddset(&act.sa_mask,SIGCHLD); act.sa_flags = SA_NOCLDWAIT; sigaction(SIGCHLD, &act, 0); - s = socket_local_server(DEBUGGER_SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); + int s = socket_local_server(DEBUGGER_SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); if (s < 0) return 1; fcntl(s, F_SETFD, FD_CLOEXEC); - LOG("debuggerd: " __DATE__ " " __TIME__ "\n"); + ALOGI("debuggerd: " __DATE__ " " __TIME__ "\n"); for (;;) { - struct sockaddr addr; - socklen_t alen; - int fd; + sockaddr addr; + socklen_t alen = sizeof(addr); - alen = sizeof(addr); - XLOG("waiting for connection\n"); - fd = accept(s, &addr, &alen); + ALOGV("waiting for connection\n"); + int fd = accept(s, &addr, &alen); if (fd < 0) { - XLOG("accept failed: %s\n", strerror(errno)); + ALOGV("accept failed: %s\n", strerror(errno)); continue; } |