summaryrefslogtreecommitdiffstats
path: root/debuggerd
diff options
context:
space:
mode:
authorJeff Brown <jeffbrown@google.com>2011-10-24 11:10:16 -0700
committerJeff Brown <jeffbrown@google.com>2011-10-25 17:00:17 -0700
commit9524e4158fbb988b6a5e4f5be68ee10b7e4dd6d8 (patch)
treef694d3c1041c0a94a987b9d0b6f1011ea9b3d962 /debuggerd
parent13e715b491e876865e752a3a69dd6f347049a488 (diff)
downloadsystem_core-9524e4158fbb988b6a5e4f5be68ee10b7e4dd6d8.zip
system_core-9524e4158fbb988b6a5e4f5be68ee10b7e4dd6d8.tar.gz
system_core-9524e4158fbb988b6a5e4f5be68ee10b7e4dd6d8.tar.bz2
Add support for explicitly dumping native stacks.
This change modifies debuggerd so that it can be used to grab the native stacks of a process that has hung and not just crashed. Note that only the root user can do this (for now). adb shell debuggerd <tid> Then use logcat to find the tombstone file that was generated which will have the native stacks of all threads in the requested process. The specified thread will be shown first and will also appear in the main log. Also made some minor tweaks to libcorkscrew so that we could handle statically compiled executables in the future if we compiled the library statically. Improved the "wait_for_user_action" function to support volume down as an alternative for devices that do not have home keys. Removed a mess of gotos. Change-Id: Ic149653986b0c2f503c7f0e8b7cb1f3be7c84d1e
Diffstat (limited to 'debuggerd')
-rw-r--r--debuggerd/arm/machine.c2
-rw-r--r--debuggerd/debuggerd.c521
2 files changed, 317 insertions, 206 deletions
diff --git a/debuggerd/arm/machine.c b/debuggerd/arm/machine.c
index 891b1ef..d941684 100644
--- a/debuggerd/arm/machine.c
+++ b/debuggerd/arm/machine.c
@@ -121,7 +121,7 @@ void dump_registers(ptrace_context_t* context __attribute((unused)),
_LOG(tfd, only_in_tombstone, " d%-2d %016llx d%-2d %016llx\n",
i, vfp_regs.fpregs[i], i+1, vfp_regs.fpregs[i+1]);
}
- _LOG(tfd, only_in_tombstone, " scr %08lx\n\n", vfp_regs.fpscr);
+ _LOG(tfd, only_in_tombstone, " scr %08lx\n", vfp_regs.fpscr);
#endif
}
diff --git a/debuggerd/debuggerd.c b/debuggerd/debuggerd.c
index 1599439..5a180f1 100644
--- a/debuggerd/debuggerd.c
+++ b/debuggerd/debuggerd.c
@@ -28,6 +28,7 @@
#include <sys/wait.h>
#include <sys/exec_elf.h>
#include <sys/stat.h>
+#include <sys/poll.h>
#include <cutils/sockets.h>
#include <cutils/logd.h>
@@ -64,6 +65,7 @@ static const char *get_signame(int sig)
case SIGFPE: return "SIGFPE";
case SIGSEGV: return "SIGSEGV";
case SIGSTKFLT: return "SIGSTKFLT";
+ case SIGSTOP: return "SIGSTOP";
default: return "?";
}
}
@@ -156,49 +158,50 @@ static void dump_crash_banner(int tfd, pid_t pid, pid_t tid, int sig)
/* Return true if some thread is not detached cleanly */
static bool dump_sibling_thread_report(ptrace_context_t* context,
- int tfd, pid_t pid, pid_t tid)
-{
- char task_path[1024];
-
- sprintf(task_path, "/proc/%d/task", pid);
- DIR *d;
- struct dirent *de;
- int need_cleanup = 0;
+ int tfd, pid_t pid, pid_t tid) {
+ char task_path[64];
+ snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
- d = opendir(task_path);
+ DIR* 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;
}
+
+ bool detach_failed = false;
+ struct dirent *de;
while ((de = readdir(d)) != NULL) {
pid_t new_tid;
/* Ignore "." and ".." */
- if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+ 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)
+ if (new_tid == tid) {
continue;
+ }
/* Skip this thread if cannot ptrace it */
- if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0)
+ if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0) {
continue;
+ }
- _LOG(tfd, true,
- "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
+ _LOG(tfd, true, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
_LOG(tfd, true, "pid: %d, tid: %d\n", pid, new_tid);
dump_thread(context, tfd, new_tid, false);
if (ptrace(PTRACE_DETACH, new_tid, 0, 0) != 0) {
- XLOG("detach of tid %d failed: %s\n", new_tid, strerror(errno));
- need_cleanup = 1;
+ LOG("ptrace detach from %d failed: %s\n", new_tid, strerror(errno));
+ detach_failed = true;
}
}
- closedir(d);
- return need_cleanup != 0;
+ closedir(d);
+ return detach_failed;
}
/*
@@ -355,7 +358,6 @@ static bool dump_crash(int tfd, pid_t pid, pid_t tid, int signal,
char value[PROPERTY_VALUE_MAX];
property_get("ro.debuggable", value, "0");
bool wantLogs = (value[0] == '1');
- bool need_cleanup = false;
dump_crash_banner(tfd, pid, tid, signal);
@@ -367,8 +369,9 @@ static bool dump_crash(int tfd, pid_t pid, pid_t tid, int signal,
dump_logs(tfd, pid, true);
}
+ bool detach_failed = false;
if (dump_sibling_threads) {
- need_cleanup = dump_sibling_thread_report(context, tfd, pid, tid);
+ detach_failed = dump_sibling_thread_report(context, tfd, pid, tid);
}
free_ptrace_context(context);
@@ -376,7 +379,7 @@ static bool dump_crash(int tfd, pid_t pid, pid_t tid, int signal,
if (wantLogs) {
dump_logs(tfd, pid, false);
}
- return need_cleanup;
+ return detach_failed;
}
#define MAX_TOMBSTONES 10
@@ -443,20 +446,18 @@ static int find_and_open_tombstone(void)
static bool engrave_tombstone(pid_t pid, pid_t tid, int signal,
bool dump_sibling_threads)
{
- 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;
+ int fd = find_and_open_tombstone();
+ if (fd < 0) {
+ return false;
+ }
- need_cleanup = dump_crash(fd, pid, tid, signal, dump_sibling_threads);
+ bool detach_failed = dump_crash(fd, pid, tid, signal, dump_sibling_threads);
close(fd);
- return need_cleanup;
+ return detach_failed;
}
static int
@@ -504,21 +505,21 @@ void disable_debug_led(void)
write_string("/sys/class/leds/left/cadence", "0,0");
}
-static void wait_for_user_action(pid_t tid, struct ucred* cr)
-{
- (void)tid;
+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"
+ "* attach gdbserver for a gdb connection on port 5039\n"
+ "* and start gdbclient:\n"
"*\n"
- "* adb shell gdbserver :5039 --attach %d &\n"
+ "* gdbclient app_process :5039 %d\n"
"*\n"
- "* Press HOME key to let the process continue crashing.\n"
+ "* Wait for gdb to start, then press HOME or VOLUME DOWN key\n"
+ "* to let the process continue crashing.\n"
"********************************************************\n",
- cr->pid, cr->pid);
+ pid, pid);
- /* wait for HOME key (TODO: something useful for devices w/o HOME key) */
+ /* wait for HOME or VOLUME DOWN key */
if (init_getevent() == 0) {
int ms = 1200 / 10;
int dit = 1;
@@ -531,15 +532,18 @@ static void wait_for_user_action(pid_t tid, struct ucred* cr)
};
size_t s = 0;
struct input_event e;
- int home = 0;
+ bool done = false;
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;
+ 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;
@@ -549,209 +553,281 @@ static void wait_for_user_action(pid_t tid, struct ucred* cr)
disable_debug_led();
}
}
- } while (!home);
+ } while (!done);
uninit_getevent();
}
/* don't forget to turn debug led off */
disable_debug_led();
+ LOG("debuggerd resuming process %d", pid);
+}
- /* close filedescriptor */
- LOG("debuggerd resuming process %d", cr->pid);
- }
+static int get_process_info(pid_t tid, pid_t* out_pid, uid_t* out_uid, uid_t* out_gid) {
+ char path[64];
+ snprintf(path, sizeof(path), "/proc/%d/status", tid);
-static void handle_crashing_process(int fd)
-{
- char buf[64];
- struct stat s;
- pid_t tid;
- struct ucred cr;
- int n, len, status;
- int tid_attach_status = -1;
- unsigned retry = 30;
- bool need_cleanup = false;
+ FILE* fp = fopen(path, "r");
+ if (!fp) {
+ return -1;
+ }
- XLOG("handle_crashing_process(%d)\n", fd);
+ int fields = 0;
+ char line[1024];
+ while (fgets(line, sizeof(line), fp)) {
+ size_t len = strlen(line);
+ if (len > 6 && !memcmp(line, "Tgid:\t", 6)) {
+ *out_pid = atoi(line + 6);
+ fields |= 1;
+ } else if (len > 5 && !memcmp(line, "Uid:\t", 5)) {
+ *out_uid = atoi(line + 5);
+ fields |= 2;
+ } else if (len > 5 && !memcmp(line, "Gid:\t", 5)) {
+ *out_gid = atoi(line + 5);
+ fields |= 4;
+ }
+ }
+ fclose(fp);
+ return fields == 7 ? 0 : -1;
+}
- char value[PROPERTY_VALUE_MAX];
- property_get("debug.db.uid", value, "-1");
- int debug_uid = atoi(value);
+static int wait_for_signal(pid_t tid, int* total_sleep_time_usec) {
+ const int sleep_time_usec = 200000; /* 0.2 seconds */
+ const int max_total_sleep_usec = 3000000; /* 3 seconds */
+ for (;;) {
+ int status;
+ pid_t n = waitpid(tid, &status, __WALL | WNOHANG);
+ if (n < 0) {
+ if(errno == EAGAIN) continue;
+ LOG("waitpid failed: %s\n", strerror(errno));
+ return -1;
+ } else if (n > 0) {
+ XLOG("waitpid: n=%d status=%08x\n", n, status);
+ if (WIFSTOPPED(status)) {
+ return WSTOPSIG(status);
+ } else {
+ LOG("unexpected waitpid response: n=%d, status=%08x\n", n, status);
+ return -1;
+ }
+ }
+
+ if (*total_sleep_time_usec > max_total_sleep_usec) {
+ LOG("timed out waiting for tid=%d to die\n", tid);
+ return -1;
+ }
- len = sizeof(cr);
- n = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
- if(n != 0) {
+ /* not ready yet */
+ XLOG("not ready yet\n");
+ usleep(sleep_time_usec);
+ *total_sleep_time_usec += sleep_time_usec;
+ }
+}
+
+enum {
+ REQUEST_TYPE_CRASH,
+ REQUEST_TYPE_DUMP,
+};
+
+typedef struct {
+ int type;
+ pid_t pid, tid;
+ uid_t uid, gid;
+} request_t;
+
+static int read_request(int fd, request_t* out_request) {
+ struct ucred cr;
+ int len = sizeof(cr);
+ int status = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
+ if (status != 0) {
LOG("cannot get credentials\n");
- goto done;
+ return -1;
}
XLOG("reading tid\n");
fcntl(fd, F_SETFL, O_NONBLOCK);
- while((n = read(fd, &tid, sizeof(pid_t))) != sizeof(pid_t)) {
- 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;
+
+ struct 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\n");
+ return -1;
}
- snprintf(buf, sizeof buf, "/proc/%d/task/%d", cr.pid, tid);
- if(stat(buf, &s)) {
- LOG("tid %d does not exist in pid %d. ignoring debug request\n",
- tid, cr.pid);
- close(fd);
- return;
+ status = TEMP_FAILURE_RETRY(read(fd, &out_request->tid, sizeof(pid_t)));
+ if (status < 0) {
+ LOG("read failure? %s\n", strerror(errno));
+ return -1;
+ }
+ if (status != sizeof(pid_t)) {
+ LOG("invalid crash request of size %d\n", status);
+ return -1;
}
- XLOG("BOOM: pid=%d uid=%d gid=%d tid=%d\n", cr.pid, cr.uid, cr.gid, tid);
+ if (out_request->tid < 0 && cr.uid == 0) {
+ /* Root can ask us to attach to any process and dump it explicitly. */
+ out_request->type = REQUEST_TYPE_DUMP;
+ out_request->tid = -out_request->tid;
+ 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);
+ return -1;
+ }
+ return 0;
+ }
- /*
- * 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.
- */
- bool dump_sibling_threads = (signed)cr.pid > debug_uid;
-
- /* Note that at this point, the target thread's signal handler
- * is blocked in a read() call. This gives us the time to PTRACE_ATTACH
- * to it before it has a chance to really fault.
- *
- * The PTRACE_ATTACH sends a SIGSTOP to the target process, but it
- * won't necessarily have stopped by the time ptrace() returns. (We
- * currently assume it does.) We write to the file descriptor to
- * ensure that it can run as soon as we call PTRACE_CONT below.
- * See details in bionic/libc/linker/debugger.c, in function
- * debugger_signal_handler().
- */
- tid_attach_status = ptrace(PTRACE_ATTACH, tid, 0, 0);
- int ptrace_error = errno;
+ /* Ensure that the tid reported by the crashing process is valid. */
+ out_request->type = REQUEST_TYPE_CRASH;
+ out_request->pid = cr.pid;
+ out_request->uid = cr.uid;
+ out_request->gid = cr.gid;
- if (TEMP_FAILURE_RETRY(write(fd, &tid, 1)) != 1) {
- XLOG("failed responding to client: %s\n",
- strerror(errno));
- goto done;
+ char buf[64];
+ 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",
+ out_request->tid, out_request->pid);
+ return -1;
}
+ return 0;
+}
- if(tid_attach_status < 0) {
- LOG("ptrace attach failed: %s\n", strerror(ptrace_error));
- goto done;
+static bool should_attach_gdb(request_t* request) {
+ if (request->type == REQUEST_TYPE_CRASH) {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("debug.db.uid", value, "-1");
+ int debug_uid = atoi(value);
+ return debug_uid >= 0 && request->uid <= (uid_t)debug_uid;
}
+ return false;
+}
- close(fd);
- fd = -1;
+static void handle_request(int fd) {
+ XLOG("handle_request(%d)\n", fd);
- const int sleep_time_usec = 200000; /* 0.2 seconds */
- const int max_total_sleep_usec = 3000000; /* 3 seconds */
- int loop_limit = max_total_sleep_usec / sleep_time_usec;
- for(;;) {
- if (loop_limit-- == 0) {
- LOG("timed out waiting for pid=%d tid=%d uid=%d to die\n",
- cr.pid, tid, cr.uid);
- goto done;
- }
- n = waitpid(tid, &status, __WALL | WNOHANG);
+ request_t request;
+ int status = read_request(fd, &request);
+ if (!status) {
+ XLOG("BOOM: pid=%d uid=%d gid=%d tid=%d\n", pid, uid, gid, tid);
- if (n == 0) {
- /* not ready yet */
- XLOG("not ready yet\n");
- usleep(sleep_time_usec);
- continue;
- }
+ /* At this point, the thread that made the request is blocked in
+ * a read() call. If the thread has crashed, then this gives us
+ * time to PTRACE_ATTACH to it before it has a chance to really fault.
+ *
+ * The PTRACE_ATTACH sends a SIGSTOP to the target process, but it
+ * won't necessarily have stopped by the time ptrace() returns. (We
+ * currently assume it does.) We write to the file descriptor to
+ * ensure that it can run as soon as we call PTRACE_CONT below.
+ * 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));
+ } else {
+ bool detach_failed = false;
+ bool attach_gdb = should_attach_gdb(&request);
+ char response = 0;
+ if (TEMP_FAILURE_RETRY(write(fd, &response, 1)) != 1) {
+ LOG("failed responding to client: %s\n", strerror(errno));
+ } else {
+ close(fd);
+ fd = -1;
+
+ int total_sleep_time_usec = 0;
+ for (;;) {
+ int signal = wait_for_signal(request.tid, &total_sleep_time_usec);
+ if (signal < 0) {
+ break;
+ }
+
+ switch (signal) {
+ case SIGSTOP:
+ if (request.type == REQUEST_TYPE_DUMP) {
+ XLOG("stopped -- dumping\n");
+ detach_failed = engrave_tombstone(request.pid, request.tid,
+ signal, true);
+ } else {
+ XLOG("stopped -- continuing\n");
+ status = ptrace(PTRACE_CONT, request.tid, 0, 0);
+ if (status) {
+ LOG("ptrace continue failed: %s\n", strerror(errno));
+ }
+ continue; /* loop again */
+ }
+ break;
+
+ case SIGILL:
+ case SIGABRT:
+ case SIGBUS:
+ case SIGFPE:
+ case SIGSEGV:
+ case SIGSTKFLT: {
+ XLOG("stopped -- fatal signal\n");
+ /* don't dump sibling threads when attaching to GDB because it
+ * makes the process less reliable, apparently... */
+ detach_failed = engrave_tombstone(request.pid, request.tid,
+ signal, !attach_gdb);
+ break;
+ }
+
+ default:
+ XLOG("stopped -- unexpected signal\n");
+ LOG("process stopped due to unexpected signal %d\n", signal);
+ break;
+ }
+ break;
+ }
+ }
- if(n < 0) {
- if(errno == EAGAIN) continue;
- LOG("waitpid failed: %s\n", strerror(errno));
- goto done;
- }
+ XLOG("detaching\n");
+ if (attach_gdb) {
+ /* stop the process so we can debug */
+ kill(request.pid, SIGSTOP);
- 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;
+ /* 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));
+ detach_failed = true;
}
- 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, n,
- dump_sibling_threads);
- kill(tid, SIGSTOP);
- goto done;
+ /*
+ * 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)
+ */
+ wait_for_user_action(request.pid);
+ } else {
+ /* just detach */
+ if (ptrace(PTRACE_DETACH, request.tid, 0, 0)) {
+ LOG("ptrace detach from %d failed: %s\n", request.tid, strerror(errno));
+ detach_failed = true;
+ }
}
- default:
- XLOG("stopped -- unexpected signal\n");
- goto done;
+ /* resume stopped process (so it can crash in peace). */
+ kill(request.pid, SIGCONT);
+
+ /* If we didn't successfully detach, we're still the parent, and the
+ * 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");
+ kill(getpid(), SIGKILL);
}
- } 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). If we didn't
- * successfully detach, we're still the parent, and the actual parent
- * won't receive a death notification via wait(2). At this point
- * there's not much we can do about that.
- */
- kill(cr.pid, SIGCONT);
-
- if (need_cleanup) {
- LOG("debuggerd committing suicide to free the zombie!\n");
- kill(getpid(), SIGKILL);
+ if (fd >= 0) {
+ close(fd);
}
-
- if(fd != -1) close(fd);
}
-
-int main()
-{
+static int do_server() {
int s;
struct sigaction act;
int logsocket = -1;
@@ -784,7 +860,7 @@ int main()
s = socket_local_server("android:debuggerd",
ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
- if(s < 0) return -1;
+ if(s < 0) return 1;
fcntl(s, F_SETFD, FD_CLOEXEC);
LOG("debuggerd: " __DATE__ " " __TIME__ "\n");
@@ -804,7 +880,42 @@ int main()
fcntl(fd, F_SETFD, FD_CLOEXEC);
- handle_crashing_process(fd);
+ handle_request(fd);
+ }
+ return 0;
+}
+
+static int do_explicit_dump(pid_t tid) {
+ fprintf(stdout, "Sending request to dump task %d.\n", tid);
+
+ int fd = socket_local_client("android:debuggerd",
+ ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
+ if (fd < 0) {
+ fputs("Error opening local socket to debuggerd.\n", stderr);
+ return 1;
+ }
+
+ pid_t request = -tid;
+ write(fd, &request, sizeof(pid_t));
+ if (read(fd, &request, 1) != 1) {
+ /* did not get expected reply, debuggerd must have closed the socket */
+ fputs("Error sending request. Did not receive reply from debuggerd.\n", stderr);
}
+ close(fd);
return 0;
}
+
+int main(int argc, char** argv) {
+ if (argc == 2) {
+ pid_t tid = atoi(argv[1]);
+ if (!tid) {
+ fputs("Usage: [<tid>]\n"
+ "\n"
+ "If tid specified, sends a request to debuggerd to dump that task.\n"
+ "Otherwise, starts the debuggerd server.\n", stderr);
+ return 1;
+ }
+ return do_explicit_dump(tid);
+ }
+ return do_server();
+}