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);