diff options
author | Andreas Gampe <agampe@google.com> | 2014-07-14 16:21:44 -0700 |
---|---|---|
committer | Andreas Gampe <agampe@google.com> | 2014-07-21 23:36:31 -0700 |
commit | 7ea6f79bbddd69d5db86a8656a31aaaf64ae2582 (patch) | |
tree | c64f89b15ca71e87317f6dd405ef4a5560b73e01 /runtime/entrypoints/entrypoint_utils.cc | |
parent | e72ff8022968b23efedc56c0afdc1d24e8a928c2 (diff) | |
download | android_art-7ea6f79bbddd69d5db86a8656a31aaaf64ae2582.tar.gz android_art-7ea6f79bbddd69d5db86a8656a31aaaf64ae2582.tar.bz2 android_art-7ea6f79bbddd69d5db86a8656a31aaaf64ae2582.zip |
ART: Throw StackOverflowError in native code
Initialize stack-overflow errors in native code to be able to reduce
the preserved area size of the stack.
Includes a refactoring away from constexpr in instruction_set.h to allow
for easy changing of the values.
Change-Id: I117cc8485f43da5f0a470f0f5e5b3dc3b5a06246
Diffstat (limited to 'runtime/entrypoints/entrypoint_utils.cc')
-rw-r--r-- | runtime/entrypoints/entrypoint_utils.cc | 97 |
1 files changed, 86 insertions, 11 deletions
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index 0fa0e410bf..c1c7631b35 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -110,8 +110,8 @@ mirror::Array* CheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx, mirror: void ThrowStackOverflowError(Thread* self) { if (self->IsHandlingStackOverflow()) { - LOG(ERROR) << "Recursive stack overflow."; - // We don't fail here because SetStackEndForStackOverflow will print better diagnostics. + LOG(ERROR) << "Recursive stack overflow."; + // We don't fail here because SetStackEndForStackOverflow will print better diagnostics. } if (Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) { @@ -123,15 +123,90 @@ void ThrowStackOverflowError(Thread* self) { JNIEnvExt* env = self->GetJniEnv(); std::string msg("stack size "); msg += PrettySize(self->GetStackSize()); - // Use low-level JNI routine and pre-baked error class to avoid class linking operations that - // would consume more stack. - int rc = ::art::ThrowNewException(env, WellKnownClasses::java_lang_StackOverflowError, - msg.c_str(), NULL); - if (rc != JNI_OK) { - // TODO: ThrowNewException failed presumably because of an OOME, we continue to throw the OOME - // or die in the CHECK below. We may want to throw a pre-baked StackOverflowError - // instead. - LOG(ERROR) << "Couldn't throw new StackOverflowError because JNI ThrowNew failed."; + + // Avoid running Java code for exception initialization. + // TODO: Checks to make this a bit less brittle. + + std::string error_msg; + + // Allocate an uninitialized object. + ScopedLocalRef<jobject> exc(env, + env->AllocObject(WellKnownClasses::java_lang_StackOverflowError)); + if (exc.get() != nullptr) { + // "Initialize". + // StackOverflowError -> VirtualMachineError -> Error -> Throwable -> Object. + // Only Throwable has "custom" fields: + // String detailMessage. + // Throwable cause (= this). + // List<Throwable> suppressedExceptions (= Collections.emptyList()). + // Object stackState; + // StackTraceElement[] stackTrace; + // Only Throwable has a non-empty constructor: + // this.stackTrace = EmptyArray.STACK_TRACE_ELEMENT; + // fillInStackTrace(); + + // detailMessage. + // TODO: Use String::FromModifiedUTF...? + ScopedLocalRef<jstring> s(env, env->NewStringUTF(msg.c_str())); + if (s.get() != nullptr) { + jfieldID detail_message_id = env->GetFieldID(WellKnownClasses::java_lang_Throwable, + "detailMessage", "Ljava/lang/String;"); + env->SetObjectField(exc.get(), detail_message_id, s.get()); + + // cause. + jfieldID cause_id = env->GetFieldID(WellKnownClasses::java_lang_Throwable, + "cause", "Ljava/lang/Throwable;"); + env->SetObjectField(exc.get(), cause_id, exc.get()); + + // suppressedExceptions. + jfieldID emptylist_id = env->GetStaticFieldID(WellKnownClasses::java_util_Collections, + "EMPTY_LIST", "Ljava/util/List;"); + ScopedLocalRef<jobject> emptylist(env, env->GetStaticObjectField( + WellKnownClasses::java_util_Collections, emptylist_id)); + CHECK(emptylist.get() != nullptr); + jfieldID suppressed_id = env->GetFieldID(WellKnownClasses::java_lang_Throwable, + "suppressedExceptions", "Ljava/util/List;"); + env->SetObjectField(exc.get(), suppressed_id, emptylist.get()); + + // stackState is set as result of fillInStackTrace. fillInStackTrace calls + // nativeFillInStackTrace. + ScopedLocalRef<jobject> stack_state_val(env, nullptr); + { + ScopedObjectAccessUnchecked soa(env); + stack_state_val.reset(soa.Self()->CreateInternalStackTrace<false>(soa)); + } + if (stack_state_val.get() != nullptr) { + jfieldID stackstateID = env->GetFieldID(WellKnownClasses::java_lang_Throwable, + "stackState", "Ljava/lang/Object;"); + env->SetObjectField(exc.get(), stackstateID, stack_state_val.get()); + + // stackTrace. + jfieldID stack_trace_elem_id = env->GetStaticFieldID( + WellKnownClasses::libcore_util_EmptyArray, "STACK_TRACE_ELEMENT", + "[Ljava/lang/StackTraceElement;"); + ScopedLocalRef<jobject> stack_trace_elem(env, env->GetStaticObjectField( + WellKnownClasses::libcore_util_EmptyArray, stack_trace_elem_id)); + jfieldID stacktrace_id = env->GetFieldID( + WellKnownClasses::java_lang_Throwable, "stackTrace", "[Ljava/lang/StackTraceElement;"); + env->SetObjectField(exc.get(), stacktrace_id, stack_trace_elem.get()); + + // Throw the exception. + ThrowLocation throw_location = self->GetCurrentLocationForThrow(); + self->SetException(throw_location, + reinterpret_cast<mirror::Throwable*>(self->DecodeJObject(exc.get()))); + } else { + error_msg = "Could not create stack trace."; + } + } else { + // Could not allocate a string object. + error_msg = "Couldn't throw new StackOverflowError because JNI NewStringUTF failed."; + } + } else { + error_msg = "Could not allocate StackOverflowError object."; + } + + if (!error_msg.empty()) { + LOG(ERROR) << error_msg; CHECK(self->IsExceptionPending()); } |