ART: Experiment with timeout dumping

In an attempt to diagnose some timeout dumping issues, allow a recursive
unexpected signal. Also print the new signal number irrespectively.

Test: m test-art-host
Test: manual: send timeout signal to run-test
Test: manual: send timeout signal to run-test, then send sigbus to run-test
Change-Id: Idf198b264a7e5868bdf444f323921c946584c650
diff --git a/runtime/runtime_common.cc b/runtime/runtime_common.cc
index 41bfb58..010c6e7 100644
--- a/runtime/runtime_common.cc
+++ b/runtime/runtime_common.cc
@@ -370,30 +370,11 @@
 #pragma GCC diagnostic ignored "-Wframe-larger-than="
 #endif
 
-void HandleUnexpectedSignalCommon(int signal_number,
-                                  siginfo_t* info,
-                                  void* raw_context,
-                                  bool handle_timeout_signal,
-                                  bool dump_on_stderr) {
-  static bool handling_unexpected_signal = false;
-  if (handling_unexpected_signal) {
-    LogHelper::LogLineLowStack(__FILE__,
-                               __LINE__,
-                               ::android::base::FATAL_WITHOUT_ABORT,
-                               "HandleUnexpectedSignal reentered\n");
-    if (handle_timeout_signal) {
-      if (IsTimeoutSignal(signal_number)) {
-        // Ignore a recursive timeout.
-        return;
-      }
-    }
-    _exit(1);
-  }
-  handling_unexpected_signal = true;
-
-  gAborting++;  // set before taking any locks
-  MutexLock mu(Thread::Current(), *Locks::unexpected_signal_lock_);
-
+static void HandleUnexpectedSignalCommonDump(int signal_number,
+                                             siginfo_t* info,
+                                             void* raw_context,
+                                             bool handle_timeout_signal,
+                                             bool dump_on_stderr) {
   auto logger = [&](auto& stream) {
     bool has_address = (signal_number == SIGILL || signal_number == SIGBUS ||
                         signal_number == SIGFPE || signal_number == SIGSEGV);
@@ -452,6 +433,71 @@
   }
 }
 
+void HandleUnexpectedSignalCommon(int signal_number,
+                                  siginfo_t* info,
+                                  void* raw_context,
+                                  bool handle_timeout_signal,
+                                  bool dump_on_stderr) {
+  // Local _static_ storing the currently handled signal (or -1).
+  static int handling_unexpected_signal = -1;
+
+  // Whether the dump code should be run under the unexpected-signal lock. For diagnostics we
+  // allow recursive unexpected-signals in certain cases - avoid a deadlock.
+  bool grab_lock = true;
+
+  if (handling_unexpected_signal != -1) {
+    LogHelper::LogLineLowStack(__FILE__,
+                               __LINE__,
+                               ::android::base::FATAL_WITHOUT_ABORT,
+                               "HandleUnexpectedSignal reentered\n");
+    // Print the signal number. Don't use any standard functions, just some arithmetic. Just best
+    // effort, with a minimal buffer.
+    if (0 < signal_number && signal_number < 100) {
+      char buf[] = { ' ',
+                     'S',
+                     static_cast<char>('0' + (signal_number / 10)),
+                     static_cast<char>('0' + (signal_number % 10)),
+                     '\n',
+                     0 };
+      LogHelper::LogLineLowStack(__FILE__,
+                                 __LINE__,
+                                 ::android::base::FATAL_WITHOUT_ABORT,
+                                 buf);
+    }
+    if (handle_timeout_signal) {
+      if (IsTimeoutSignal(signal_number)) {
+        // Ignore a recursive timeout.
+        return;
+      }
+    }
+    // If we were handling a timeout signal, try to go on. Otherwise hard-exit.
+    // This relies on the expectation that we'll only ever get one timeout signal.
+    if (!handle_timeout_signal || handling_unexpected_signal != GetTimeoutSignal()) {
+      _exit(1);
+    }
+    grab_lock = false;  // The "outer" handling instance already holds the lock.
+  }
+  handling_unexpected_signal = signal_number;
+
+  gAborting++;  // set before taking any locks
+
+  if (grab_lock) {
+    MutexLock mu(Thread::Current(), *Locks::unexpected_signal_lock_);
+
+    HandleUnexpectedSignalCommonDump(signal_number,
+                                     info,
+                                     raw_context,
+                                     handle_timeout_signal,
+                                     dump_on_stderr);
+  } else {
+    HandleUnexpectedSignalCommonDump(signal_number,
+                                     info,
+                                     raw_context,
+                                     handle_timeout_signal,
+                                     dump_on_stderr);
+  }
+}
+
 #if defined(__APPLE__)
 #pragma GCC diagnostic pop
 #endif