Detect bug 294339122 and crash with debugging output.

(cherry picked from commit 32967de2c88fc675d9d3f43d6b5613f4fd93f769)

Test: run-gtests.sh
Test: testrunner.py --target --64 --optimizing
Bug: 294339122
Merged-In: I418783b7e80f7fb2ef54cab5d52717ab3bf039c8
Change-Id: Iec745e0969649f161e42484b9f9af09955f75696
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index a3c1f3b..652368c 100644
--- a/runtime/fault_handler.cc
+++ b/runtime/fault_handler.cc
@@ -236,6 +236,47 @@
   return Runtime::Current()->GetHeap()->MarkCompactCollector()->SigbusHandler(info);
 }
 
+inline void FaultManager::CheckForUnrecognizedImplicitSuspendCheckInBootImage(
+    siginfo_t* siginfo, void* context) {
+  CHECK_EQ(kRuntimeISA, InstructionSet::kArm64);
+  uintptr_t fault_pc = GetFaultPc(siginfo, context);
+  if (fault_pc == 0u || !IsUint<32>(fault_pc) || !IsAligned<4u>(fault_pc)) {
+    return;
+  }
+  Runtime* runtime = Runtime::Current();
+  if (runtime == nullptr) {
+    return;
+  }
+  gc::Heap* heap = runtime->GetHeap();
+  if (heap == nullptr ||
+      fault_pc < heap->GetBootImagesStartAddress() ||
+      fault_pc - heap->GetBootImagesStartAddress() >= heap->GetBootImagesSize() ||
+      reinterpret_cast<uint32_t*>(fault_pc)[0] != /*LDR x21. [x21]*/ 0xf94002b5u) {
+    return;
+  }
+  std::ostringstream oss;
+  oss << "Failed to recognize implicit suspend check at 0x" << std::hex << fault_pc << "; ";
+  Thread* thread = Thread::Current();
+  if (thread == nullptr) {
+    oss << "null thread";
+  } else {
+    oss << "thread state = " << thread->GetState() << std::boolalpha
+        << "; mutator lock shared held = " << Locks::mutator_lock_->IsSharedHeld(thread);
+  }
+  oss << "; code ranges = {";
+  GeneratedCodeRange* range = generated_code_ranges_.load(std::memory_order_acquire);
+  const char* s = "";
+  while (range != nullptr) {
+    oss << s << "{" << range->start << ", " << range->size << "}";
+    s = ", ";
+    range = range->next.load(std::memory_order_relaxed);
+  }
+  oss << "}";
+  LOG(FATAL) << oss.str();
+  UNREACHABLE();
+}
+
+
 bool FaultManager::HandleSigsegvFault(int sig, siginfo_t* info, void* context) {
   if (VLOG_IS_ON(signals)) {
     PrintSignalInfo(VLOG_STREAM(signals) << "Handling SIGSEGV fault:\n", info);
@@ -256,6 +297,8 @@
         return true;
       }
     }
+  } else if (kRuntimeISA == InstructionSet::kArm64) {
+    CheckForUnrecognizedImplicitSuspendCheckInBootImage(info, context);
   }
 
   // We hit a signal we didn't handle.  This might be something for which
diff --git a/runtime/fault_handler.h b/runtime/fault_handler.h
index 1ed6526..6be1743 100644
--- a/runtime/fault_handler.h
+++ b/runtime/fault_handler.h
@@ -89,6 +89,13 @@
   bool HandleFaultByOtherHandlers(int sig, siginfo_t* info, void* context)
                                   NO_THREAD_SAFETY_ANALYSIS;
 
+  // Check if this is an implicit suspend check that was somehow not recognized as being
+  // in the compiled code. If that's the case, collect debugging data for the abort message
+  // and crash. Focus on suspend checks in the boot image. Bug: 294339122
+  // NO_THREAD_SAFETY_ANALYSIS: Same as `IsInGeneratedCode()`.
+  void CheckForUnrecognizedImplicitSuspendCheckInBootImage(siginfo_t* siginfo, void* context)
+      NO_THREAD_SAFETY_ANALYSIS;
+
   // Note: The lock guards modifications of the ranges but the function `IsInGeneratedCode()`
   // walks the list in the context of a signal handler without holding the lock.
   Mutex generated_code_ranges_lock_;