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/gc/heap.cc b/runtime/gc/heap.cc
index 6849220..e85824d 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -1203,7 +1203,8 @@
   // If we're in a stack overflow, do not create a new exception. It would require running the
   // constructor, which will of course still be in a stack overflow.
   if (self->IsHandlingStackOverflow()) {
-    self->SetException(Runtime::Current()->GetPreAllocatedOutOfMemoryError());
+    self->SetException(
+        Runtime::Current()->GetPreAllocatedOutOfMemoryErrorWhenHandlingStackOverflow());
     return;
   }