Add timed versions of stack dumping functions.

Under some unknown circumstances, debuggerd could become unresponsive.
If you try and take a bugreport during this time, it will hang forever.
Adding functions that have a timeout will allow dumpstate to stop if
dumping is taking too long.

Bug: 18766581
Change-Id: I85053b8dcfe6224e2b64b4d8f7f2ef448b3cda34
diff --git a/include/cutils/debugger.h b/include/cutils/debugger.h
index 4bcc8e6..bae687d 100644
--- a/include/cutils/debugger.h
+++ b/include/cutils/debugger.h
@@ -64,11 +64,26 @@
  */
 int dump_tombstone(pid_t tid, char* pathbuf, size_t pathlen);
 
+/* Dumps a process backtrace, registers, and stack to a tombstone file (requires root).
+ * Stores the tombstone path in the provided buffer.
+ * If reading debugger data from debuggerd ever takes longer than timeout_secs
+ * seconds, then stop and return an error.
+ * Returns 0 on success, -1 on error.
+ */
+int dump_tombstone_timeout(pid_t tid, char* pathbuf, size_t pathlen, int timeout_secs);
+
 /* Dumps a process backtrace only to the specified file (requires root).
  * Returns 0 on success, -1 on error.
  */
 int dump_backtrace_to_file(pid_t tid, int fd);
 
+/* Dumps a process backtrace only to the specified file (requires root).
+ * If reading debugger data from debuggerd ever takes longer than timeout_secs
+ * seconds, then stop and return an error.
+ * Returns 0 on success, -1 on error.
+ */
+int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libcutils/debugger.c b/libcutils/debugger.c
index 4035ee1..b8a2efc 100644
--- a/libcutils/debugger.c
+++ b/libcutils/debugger.c
@@ -19,11 +19,16 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
 #include <unistd.h>
 
 #include <cutils/debugger.h>
 #include <cutils/sockets.h>
 
+#define LOG_TAG "DEBUG"
+#include <log/log.h>
+
 #if defined(__LP64__)
 #include <elf.h>
 
@@ -64,7 +69,7 @@
   return result;
 }
 
-static int make_dump_request(debugger_action_t action, pid_t tid) {
+static int make_dump_request(debugger_action_t action, pid_t tid, int timeout_secs) {
   const char* socket_name;
   debugger_msg_t msg;
   size_t msg_len;
@@ -98,6 +103,19 @@
     return -1;
   }
 
+  if (timeout_secs > 0) {
+    struct timeval tm;
+    tm.tv_sec = timeout_secs;
+    tm.tv_usec = 0;
+    if (setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tm, sizeof(tm)) == -1) {
+      ALOGE("WARNING: Cannot set receive timeout value on socket: %s", strerror(errno));
+    }
+
+    if (setsockopt(sock_fd, SOL_SOCKET, SO_SNDTIMEO, &tm, sizeof(tm)) == -1) {
+      ALOGE("WARNING: Cannot set send timeout value on socket: %s", strerror(errno));
+    }
+  }
+
   if (send_request(sock_fd, msg_ptr, msg_len) < 0) {
     TEMP_FAILURE_RETRY(close(sock_fd));
     return -1;
@@ -107,7 +125,11 @@
 }
 
 int dump_backtrace_to_file(pid_t tid, int fd) {
-  int sock_fd = make_dump_request(DEBUGGER_ACTION_DUMP_BACKTRACE, tid);
+  return dump_backtrace_to_file_timeout(tid, fd, 0);
+}
+
+int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs) {
+  int sock_fd = make_dump_request(DEBUGGER_ACTION_DUMP_BACKTRACE, tid, timeout_secs);
   if (sock_fd < 0) {
     return -1;
   }
@@ -127,7 +149,11 @@
 }
 
 int dump_tombstone(pid_t tid, char* pathbuf, size_t pathlen) {
-  int sock_fd = make_dump_request(DEBUGGER_ACTION_DUMP_TOMBSTONE, tid);
+  return dump_tombstone_timeout(tid, pathbuf, pathlen, 0);
+}
+
+int dump_tombstone_timeout(pid_t tid, char* pathbuf, size_t pathlen, int timeout_secs) {
+  int sock_fd = make_dump_request(DEBUGGER_ACTION_DUMP_TOMBSTONE, tid, timeout_secs);
   if (sock_fd < 0) {
     return -1;
   }