Use GDB to dump threads in test timeouts, if available.

GDB is able to dump threads which may not be responding to the SIGQUIT
thread dump.

Bug: 23485091
Change-Id: Ib80db4e63258887f3b7c313ed5e6eb9e6300ac4c
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 39dc030..f45e6f5 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -37,9 +37,9 @@
 QUIET="n"
 RELOCATE="y"
 SECONDARY_DEX=""
-TIME_OUT="y"
-# Value in minutes.
-TIME_OUT_VALUE=10
+TIME_OUT="gdb"  # "n" (disabled), "timeout" (use timeout), "gdb" (use gdb)
+# Value in seconds
+TIME_OUT_VALUE=600  # 10 minutes.
 USE_GDB="n"
 USE_JVM="n"
 VERIFY="y" # y=yes,n=no,s=softfail
@@ -459,15 +459,29 @@
 
     cmdline="$dalvikvm_cmdline"
 
-    if [ "$TIME_OUT" = "y" ]; then
+    if [ "$TIME_OUT" = "gdb" ]; then
+      if [ `uname` = "Darwin" ]; then
+        # Fall back to timeout on Mac.
+        TIME_OUT="timeout"
+      else
+        # Check if gdb is available.
+        gdb --eval-command="quit" > /dev/null 2>&1
+        if [ $? != 0 ]; then
+          # gdb isn't available. Fall back to timeout.
+          TIME_OUT="timeout"
+        fi
+      fi
+    fi
+
+    if [ "$TIME_OUT" = "timeout" ]; then
       # Add timeout command if time out is desired.
       #
       # Note: We use nested timeouts. The inner timeout sends SIGRTMIN+2 (usually 36) to ART, which
       #       will induce a full thread dump before abort. However, dumping threads might deadlock,
       #       so the outer timeout sends the regular SIGTERM after an additional minute to ensure
       #       termination (without dumping all threads).
-      TIME_PLUS_ONE=$(($TIME_OUT_VALUE + 1))
-      cmdline="timeout ${TIME_PLUS_ONE}m timeout -s SIGRTMIN+2 ${TIME_OUT_VALUE}m $cmdline"
+      TIME_PLUS_ONE=$(($TIME_OUT_VALUE + 60))
+      cmdline="timeout ${TIME_PLUS_ONE}s timeout -s SIGRTMIN+2 ${TIME_OUT_VALUE}s $cmdline"
     fi
 
     if [ "$DEV_MODE" = "y" ]; then
@@ -502,12 +516,37 @@
       # When running under gdb, we cannot do piping and grepping...
       $cmdline "$@"
     else
-      trap 'kill -INT -$pid' INT
-      $cmdline "$@" 2>&1 & pid=$!
-      wait $pid
-      # Add extra detail if time out is enabled.
-      if [ ${PIPESTATUS[0]} = 124 ] && [ "$TIME_OUT" = "y" ]; then
-        echo -e "\e[91mTEST TIMED OUT!\e[0m" >&2
+      if [ "$TIME_OUT" != "gdb" ]; then
+        trap 'kill -INT -$pid' INT
+        $cmdline "$@" 2>&1 & pid=$!
+        wait $pid
+        # Add extra detail if time out is enabled.
+        if [ ${PIPESTATUS[0]} = 124 ] && [ "$TIME_OUT" = "timeout" ]; then
+          echo -e "\e[91mTEST TIMED OUT!\e[0m" >&2
+        fi
+      else
+        # With a thread dump that uses gdb if a timeout.
+        trap 'kill -INT -$pid' INT
+        $cmdline "$@" 2>&1 & pid=$!
+        # Spawn a watcher process.
+        ( sleep $TIME_OUT_VALUE && \
+          echo "##### Thread dump using gdb on test timeout" && \
+          ( gdb -q -p $pid --eval-command="info thread" --eval-command="thread apply all bt" \
+                           --eval-command="call exit(124)" --eval-command=quit || \
+            kill $pid )) 2> /dev/null & watcher=$!
+        wait $pid
+        test_exit_status=$?
+        pkill -P $watcher 2> /dev/null # kill the sleep which will in turn end the watcher as well
+        if [ $test_exit_status = 0 ]; then
+          # The test finished normally.
+          exit 0
+        else
+          # The test failed or timed out.
+          if [ $test_exit_status = 124 ]; then
+            # The test timed out.
+            echo -e "\e[91mTEST TIMED OUT!\e[0m" >&2
+          fi
+        fi
       fi
     fi
 fi