📄 debuggerd.c
字号:
exit(execv(args[0], args)); } else { LOG("gdbserver pid=%d port=%d targetpid=%d\n", p, port, pid); sleep(5); }}#endif#define MAX_TOMBSTONES 10#define typecheck(x,y) { \ typeof(x) __dummy1; \ typeof(y) __dummy2; \ (void)(&__dummy1 == &__dummy2); }#define TOMBSTONE_DIR "/data/tombstones"/* * find_and_open_tombstone - find an available tombstone slot, if any, of the * form tombstone_XX where XX is 00 to MAX_TOMBSTONES-1, inclusive. If no * file is available, we reuse the least-recently-modified file. */static int find_and_open_tombstone(void){ unsigned long mtime = ULONG_MAX; struct stat sb; char path[128]; int fd, i, oldest = 0; /* * XXX: Our stat.st_mtime isn't time_t. If it changes, as it probably ought * to, our logic breaks. This check will generate a warning if that happens. */ typecheck(mtime, sb.st_mtime); /* * In a single wolf-like pass, find an available slot and, in case none * exist, find and record the least-recently-modified file. */ for (i = 0; i < MAX_TOMBSTONES; i++) { snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", i); if (!stat(path, &sb)) { if (sb.st_mtime < mtime) { oldest = i; mtime = sb.st_mtime; } continue; } if (errno != ENOENT) continue; fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0600); if (fd < 0) continue; /* raced ? */ fchown(fd, AID_SYSTEM, AID_SYSTEM); return fd; } /* we didn't find an available file, so we clobber the oldest one */ snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", oldest); fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600); fchown(fd, AID_SYSTEM, AID_SYSTEM); return fd;}/* Return true if some thread is not detached cleanly */static bool dump_sibling_thread_report(int tfd, unsigned pid, unsigned tid){ char task_path[1024]; sprintf(task_path, "/proc/%d/task", pid); DIR *d; struct dirent *de; int need_cleanup = 0; d = opendir(task_path); /* Bail early if cannot open the task directory */ if (d == NULL) { XLOG("Cannot open /proc/%d/task\n", pid); return false; } while ((de = readdir(d)) != NULL) { unsigned new_tid; /* Ignore "." and ".." */ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; new_tid = atoi(de->d_name); /* The main thread at fault has been handled individually */ if (new_tid == tid) continue; /* Skip this thread if cannot ptrace it */ if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0) continue; dump_crash_report(tfd, pid, new_tid, false); need_cleanup |= ptrace(PTRACE_DETACH, new_tid, 0, 0); } closedir(d); return need_cleanup != 0;}/* Return true if some thread is not detached cleanly */static bool engrave_tombstone(unsigned pid, unsigned tid, int debug_uid, int signal){ int fd; bool need_cleanup = false; mkdir(TOMBSTONE_DIR, 0755); chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM); fd = find_and_open_tombstone(); if (fd < 0) return need_cleanup; dump_crash_banner(fd, pid, tid, signal); dump_crash_report(fd, pid, tid, true); /* * If the user has requested to attach gdb, don't collect the per-thread * information as it increases the chance to lose track of the process. */ if ((signed)pid > debug_uid) { need_cleanup = dump_sibling_thread_report(fd, pid, tid); } close(fd); return need_cleanup;}static intwrite_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;}staticvoid init_debug_led(void){ // 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");}staticvoid enable_debug_led(void){ // trout leds write_string("/sys/class/leds/red/brightness", "255"); // sardine leds write_string("/sys/class/leds/left/cadence", "1,0");}staticvoid disable_debug_led(void){ // trout leds write_string("/sys/class/leds/red/brightness", "0"); // sardine leds write_string("/sys/class/leds/left/cadence", "0,0");}extern int init_getevent();extern void uninit_getevent();extern int get_event(struct input_event* event, int timeout);static void wait_for_user_action(unsigned tid, struct ucred* cr){ (void)tid; /* First log a helpful message */ LOG( "********************************************************\n" "* process %d crashed. debuggerd waiting for gdbserver \n" "* \n" "* adb shell gdbserver :port --attach %d & \n" "* \n" "* and press the HOME key. \n" "********************************************************\n", cr->pid, cr->pid); /* wait for HOME key */ if (init_getevent() == 0) { int ms = 1200 / 10; int dit = 1; int dah = 3*dit; int _ = -dit; int ___ = 3*_; int _______ = 7*_; const signed char codes[] = { dit,_,dit,_,dit,___,dah,_,dah,_,dah,___,dit,_,dit,_,dit,_______ }; size_t s = 0; struct input_event e; int home = 0; init_debug_led(); enable_debug_led(); do { int timeout = abs((int)(codes[s])) * ms; int res = get_event(&e, timeout); if (res == 0) { if (e.type==EV_KEY && e.code==KEY_HOME && e.value==0) home = 1; } else if (res == 1) { if (++s >= sizeof(codes)/sizeof(*codes)) s = 0; if (codes[s] > 0) { enable_debug_led(); } else { disable_debug_led(); } } } while (!home); uninit_getevent(); } /* don't forget to turn debug led off */ disable_debug_led(); /* close filedescriptor */ LOG("debuggerd resuming process %d", cr->pid); }static void handle_crashing_process(int fd){ char buf[64]; struct stat s; unsigned tid; struct ucred cr; int n, len, status; int tid_attach_status = -1; unsigned retry = 30; bool need_cleanup = false; char value[PROPERTY_VALUE_MAX]; property_get("debug.db.uid", value, "-1"); int debug_uid = atoi(value); XLOG("handle_crashing_process(%d)\n", fd); len = sizeof(cr); n = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len); if(n != 0) { LOG("cannot get credentials\n"); goto done; } XLOG("reading tid\n"); fcntl(fd, F_SETFL, O_NONBLOCK); while((n = read(fd, &tid, sizeof(unsigned))) != sizeof(unsigned)) { if(errno == EINTR) continue; if(errno == EWOULDBLOCK) { if(retry-- > 0) { usleep(100 * 1000); continue; } LOG("timed out reading tid\n"); goto done; } LOG("read failure? %s\n", strerror(errno)); goto done; } sprintf(buf,"/proc/%d/task/%d", cr.pid, tid); if(stat(buf, &s)) { LOG("tid %d does not exist in pid %d. ignorning debug request\n", tid, cr.pid); close(fd); return; } XLOG("BOOM: pid=%d uid=%d gid=%d tid=%d\n", cr.pid, cr.uid, cr.gid, tid); tid_attach_status = ptrace(PTRACE_ATTACH, tid, 0, 0); if(tid_attach_status < 0) { LOG("ptrace attach failed: %s\n", strerror(errno)); goto done; } close(fd); fd = -1; for(;;) { n = waitpid(tid, &status, __WALL); if(n < 0) { if(errno == EAGAIN) continue; LOG("waitpid failed: %s\n", strerror(errno)); goto done; } XLOG("waitpid: n=%d status=%08x\n", n, status); if(WIFSTOPPED(status)){ n = WSTOPSIG(status); switch(n) { case SIGSTOP: XLOG("stopped -- continuing\n"); n = ptrace(PTRACE_CONT, tid, 0, 0); if(n) { LOG("ptrace failed: %s\n", strerror(errno)); goto done; } continue; case SIGILL: case SIGABRT: case SIGBUS: case SIGFPE: case SIGSEGV: case SIGSTKFLT: { XLOG("stopped -- fatal signal\n"); need_cleanup = engrave_tombstone(cr.pid, tid, debug_uid, n); kill(tid, SIGSTOP); goto done; } default: XLOG("stopped -- unexpected signal\n"); goto done; } } else { XLOG("unexpected waitpid response\n"); goto done; } } done: XLOG("detaching\n"); /* stop the process so we can debug */ kill(cr.pid, SIGSTOP); /* * If a thread has been attached by ptrace, make sure it is detached * successfully otherwise we will get a zombie. */ if (tid_attach_status == 0) { int detach_status; /* detach so we can attach gdbserver */ detach_status = ptrace(PTRACE_DETACH, tid, 0, 0); need_cleanup |= (detach_status != 0); } /* * if debug.db.uid is set, its value indicates if we should wait * 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) */ if ((signed)cr.uid <= debug_uid) { wait_for_user_action(tid, &cr); } /* resume stopped process (so it can crash in peace) */ kill(cr.pid, SIGCONT); if (need_cleanup) { LOG("debuggerd committing suicide to free the zombie!\n"); kill(getpid(), SIGKILL); } if(fd != -1) close(fd);}int main(int argc, char **argv){ int s; struct sigaction act; process_name_ptr = argv; logsocket = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_DGRAM); if(logsocket < 0) { logsocket = -1; } else { fcntl(logsocket, F_SETFD, FD_CLOEXEC); } 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("android:debuggerd", ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); if(s < 0) return -1; fcntl(s, F_SETFD, FD_CLOEXEC); LOG("debuggerd: " __DATE__ " " __TIME__ "\n"); for(;;) { struct sockaddr addr; socklen_t alen; int fd; alen = sizeof(addr); fd = accept(s, &addr, &alen); if(fd < 0) continue; fcntl(fd, F_SETFD, FD_CLOEXEC); handle_crashing_process(fd); } return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -