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;
}
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);
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 03f17bc..3d4b596 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -291,7 +291,12 @@
// Get the special object used to mark a cleared JNI weak global.
mirror::Object* GetClearedJniWeakGlobal() REQUIRES_SHARED(Locks::mutator_lock_);
- mirror::Throwable* GetPreAllocatedOutOfMemoryError() REQUIRES_SHARED(Locks::mutator_lock_);
+ mirror::Throwable* GetPreAllocatedOutOfMemoryErrorWhenThrowingException()
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ mirror::Throwable* GetPreAllocatedOutOfMemoryErrorWhenThrowingOOME()
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ mirror::Throwable* GetPreAllocatedOutOfMemoryErrorWhenHandlingStackOverflow()
+ REQUIRES_SHARED(Locks::mutator_lock_);
mirror::Throwable* GetPreAllocatedNoClassDefFoundError()
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -764,6 +769,11 @@
bool Init(RuntimeArgumentMap&& runtime_options)
SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_);
+ void InitPreAllocatedException(Thread* self,
+ GcRoot<mirror::Throwable> Runtime::* exception,
+ const char* exception_class_descriptor,
+ const char* msg)
+ REQUIRES_SHARED(Locks::mutator_lock_);
void InitNativeMethods() REQUIRES(!Locks::mutator_lock_);
void RegisterRuntimeNativeMethods(JNIEnv* env);
@@ -796,7 +806,10 @@
// 64 bit so that we can share the same asm offsets for both 32 and 64 bits.
uint64_t callee_save_methods_[kCalleeSaveSize];
- GcRoot<mirror::Throwable> pre_allocated_OutOfMemoryError_;
+ // Pre-allocated exceptions (see Runtime::Init).
+ GcRoot<mirror::Throwable> pre_allocated_OutOfMemoryError_when_throwing_exception_;
+ GcRoot<mirror::Throwable> pre_allocated_OutOfMemoryError_when_throwing_oome_;
+ GcRoot<mirror::Throwable> pre_allocated_OutOfMemoryError_when_handling_stack_overflow_;
GcRoot<mirror::Throwable> pre_allocated_NoClassDefFoundError_;
ArtMethod* resolution_method_;
ArtMethod* imt_conflict_method_;
diff --git a/runtime/thread.cc b/runtime/thread.cc
index d17f409..f6ac64f 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -3021,7 +3021,8 @@
// If we couldn't allocate the exception, throw the pre-allocated out of memory exception.
if (exception == nullptr) {
- SetException(Runtime::Current()->GetPreAllocatedOutOfMemoryError());
+ Dump(LOG_STREAM(WARNING)); // The pre-allocated OOME has no stack, so help out and log one.
+ SetException(Runtime::Current()->GetPreAllocatedOutOfMemoryErrorWhenThrowingException());
return;
}
@@ -3101,7 +3102,7 @@
tls32_.throwing_OutOfMemoryError = false;
} else {
Dump(LOG_STREAM(WARNING)); // The pre-allocated OOME has no stack, so help out and log one.
- SetException(Runtime::Current()->GetPreAllocatedOutOfMemoryError());
+ SetException(Runtime::Current()->GetPreAllocatedOutOfMemoryErrorWhenThrowingOOME());
}
}