Distinguish the various pre-allocated OutOfMemoryError use cases.

The pre-allocated exception "OutOfMemoryError thrown while trying to
throw OutOfMemoryError; no stack trace available", can be thrown in
three occasions:
1. OOME thrown while throwing an exception (not necessarily an OOME);
2. OOME thrown while throwing an OOME;
3. OOME thrown while handling a stack overflow.

To improve error reports, replace the unique pre-allocated OOME
exception with three different exceptions for these three use cases.

Noe that we were already manually dumping a stack trace in case #2.
This change implements this for case #1 too.

Test: art/test.py
Bug: 68251879
Bug: 77567088
Bug: 77844013
Change-Id: I84cd15b729b4d0bd7bf6c831caeb0eb93c458114
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index f550a15..c394fef 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1503,19 +1503,34 @@
   // TODO: move this to just be an Trace::Start argument
   Trace::SetDefaultClockSource(runtime_options.GetOrDefault(Opt::ProfileClock));
 
+  // Pre-allocate an OutOfMemoryError for the case when we fail to
+  // allocate the exception to be thrown.
+  InitPreAllocatedException(self,
+                            &Runtime::pre_allocated_OutOfMemoryError_when_throwing_exception_,
+                            "Ljava/lang/OutOfMemoryError;",
+                            "OutOfMemoryError thrown while trying to throw an exception; "
+                            "no stack trace available");
   // Pre-allocate an OutOfMemoryError for the double-OOME case.
-  self->ThrowNewException("Ljava/lang/OutOfMemoryError;",
-                          "OutOfMemoryError thrown while trying to throw OutOfMemoryError; "
-                          "no stack trace available");
-  pre_allocated_OutOfMemoryError_ = GcRoot<mirror::Throwable>(self->GetException());
-  self->ClearException();
+  InitPreAllocatedException(self,
+                            &Runtime::pre_allocated_OutOfMemoryError_when_throwing_oome_,
+                            "Ljava/lang/OutOfMemoryError;",
+                            "OutOfMemoryError thrown while trying to throw OutOfMemoryError; "
+                            "no stack trace available");
+  // Pre-allocate an OutOfMemoryError for the case when we fail to
+  // allocate while handling a stack overflow.
+  InitPreAllocatedException(self,
+                            &Runtime::pre_allocated_OutOfMemoryError_when_handling_stack_overflow_,
+                            "Ljava/lang/OutOfMemoryError;",
+                            "OutOfMemoryError thrown while trying to handle a stack overflow; "
+                            "no stack trace available");
 
   // Pre-allocate a NoClassDefFoundError for the common case of failing to find a system class
   // ahead of checking the application's class loader.
-  self->ThrowNewException("Ljava/lang/NoClassDefFoundError;",
-                          "Class not found using the boot class loader; no stack trace available");
-  pre_allocated_NoClassDefFoundError_ = GcRoot<mirror::Throwable>(self->GetException());
-  self->ClearException();
+  InitPreAllocatedException(self,
+                            &Runtime::pre_allocated_NoClassDefFoundError_,
+                            "Ljava/lang/NoClassDefFoundError;",
+                            "Class not found using the boot class loader; "
+                            "no stack trace available");
 
   // Runtime initialization is largely done now.
   // We load plugins first since that can modify the runtime state slightly.
@@ -1666,6 +1681,16 @@
   }
 }
 
+void Runtime::InitPreAllocatedException(Thread* self,
+                                        GcRoot<mirror::Throwable> Runtime::* exception,
+                                        const char* exception_class_descriptor,
+                                        const char* msg) {
+  DCHECK_EQ(self, Thread::Current());
+  self->ThrowNewException(exception_class_descriptor, msg);
+  this->*exception = GcRoot<mirror::Throwable>(self->GetException());
+  self->ClearException();
+}
+
 void Runtime::InitNativeMethods() {
   VLOG(startup) << "Runtime::InitNativeMethods entering";
   Thread* self = Thread::Current();
@@ -1917,10 +1942,26 @@
   thread_list_->Unregister(self);
 }
 
-mirror::Throwable* Runtime::GetPreAllocatedOutOfMemoryError() {
-  mirror::Throwable* oome = pre_allocated_OutOfMemoryError_.Read();
+mirror::Throwable* Runtime::GetPreAllocatedOutOfMemoryErrorWhenThrowingException() {
+  mirror::Throwable* oome = pre_allocated_OutOfMemoryError_when_throwing_exception_.Read();
   if (oome == nullptr) {
-    LOG(ERROR) << "Failed to return pre-allocated OOME";
+    LOG(ERROR) << "Failed to return pre-allocated OOME-when-throwing-exception";
+  }
+  return oome;
+}
+
+mirror::Throwable* Runtime::GetPreAllocatedOutOfMemoryErrorWhenThrowingOOME() {
+  mirror::Throwable* oome = pre_allocated_OutOfMemoryError_when_throwing_oome_.Read();
+  if (oome == nullptr) {
+    LOG(ERROR) << "Failed to return pre-allocated OOME-when-throwing-OOME";
+  }
+  return oome;
+}
+
+mirror::Throwable* Runtime::GetPreAllocatedOutOfMemoryErrorWhenHandlingStackOverflow() {
+  mirror::Throwable* oome = pre_allocated_OutOfMemoryError_when_handling_stack_overflow_.Read();
+  if (oome == nullptr) {
+    LOG(ERROR) << "Failed to return pre-allocated OOME-when-handling-stack-overflow";
   }
   return oome;
 }
@@ -2005,7 +2046,12 @@
 void Runtime::VisitNonThreadRoots(RootVisitor* visitor) {
   java_vm_->VisitRoots(visitor);
   sentinel_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
-  pre_allocated_OutOfMemoryError_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
+  pre_allocated_OutOfMemoryError_when_throwing_exception_
+      .VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
+  pre_allocated_OutOfMemoryError_when_throwing_oome_
+      .VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
+  pre_allocated_OutOfMemoryError_when_handling_stack_overflow_
+      .VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
   pre_allocated_NoClassDefFoundError_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
   verifier::MethodVerifier::VisitStaticRoots(visitor);
   VisitTransactionRoots(visitor);