Fix for Thread::QuickDeliverException.
We may have a switch interpreter frame, or the top of the call
stack as caller when calling QuickDeliverException.
Test: 844-exception
Change-Id: I07c2387e36d7cadb1dc4e778e569e662e8a08dea
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 5ce9d17..3369e5d 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -3941,30 +3941,35 @@
if (Dbg::IsForcedInterpreterNeededForException(this) || force_deopt || IsForceInterpreter()) {
NthCallerVisitor visitor(this, 0, false);
visitor.WalkStack();
- if (Runtime::Current()->IsAsyncDeoptimizeable(visitor.GetOuterMethod(), visitor.caller_pc)) {
- // method_type shouldn't matter due to exception handling.
- const DeoptimizationMethodType method_type = DeoptimizationMethodType::kDefault;
- // Save the exception into the deoptimization context so it can be restored
- // before entering the interpreter.
- if (force_deopt) {
- VLOG(deopt) << "Deopting " << cf->GetMethod()->PrettyMethod() << " for frame-pop";
- DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
- // Get rid of the exception since we are doing a framepop instead.
- LOG(WARNING) << "Suppressing pending exception for retry-instruction/frame-pop: "
- << exception->Dump();
- ClearException();
+ if (visitor.GetCurrentQuickFrame() != nullptr) {
+ if (Runtime::Current()->IsAsyncDeoptimizeable(visitor.GetOuterMethod(), visitor.caller_pc)) {
+ // method_type shouldn't matter due to exception handling.
+ const DeoptimizationMethodType method_type = DeoptimizationMethodType::kDefault;
+ // Save the exception into the deoptimization context so it can be restored
+ // before entering the interpreter.
+ if (force_deopt) {
+ VLOG(deopt) << "Deopting " << cf->GetMethod()->PrettyMethod() << " for frame-pop";
+ DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
+ // Get rid of the exception since we are doing a framepop instead.
+ LOG(WARNING) << "Suppressing pending exception for retry-instruction/frame-pop: "
+ << exception->Dump();
+ ClearException();
+ }
+ PushDeoptimizationContext(
+ JValue(),
+ /* is_reference= */ false,
+ (force_deopt ? nullptr : exception),
+ /* from_code= */ false,
+ method_type);
+ artDeoptimize(this);
+ UNREACHABLE();
+ } else {
+ LOG(WARNING) << "Got a deoptimization request on un-deoptimizable method "
+ << visitor.caller->PrettyMethod();
}
- PushDeoptimizationContext(
- JValue(),
- /* is_reference= */ false,
- (force_deopt ? nullptr : exception),
- /* from_code= */ false,
- method_type);
- artDeoptimize(this);
- UNREACHABLE();
- } else if (visitor.caller != nullptr) {
- LOG(WARNING) << "Got a deoptimization request on un-deoptimizable method "
- << visitor.caller->PrettyMethod();
+ } else {
+ // This is either top of call stack, or shadow frame.
+ DCHECK(visitor.caller == nullptr || visitor.IsShadowFrame());
}
}
diff --git a/test/844-exception/expected-stderr.txt b/test/844-exception/expected-stderr.txt
new file mode 100644
index 0000000..9804bd0
--- /dev/null
+++ b/test/844-exception/expected-stderr.txt
@@ -0,0 +1,5 @@
+Exception in thread "Thread-1" java.lang.Error:
+ at Main.callMethodThatThrows(Main.java:57)
+ at Main$Inner2.<clinit>(Main.java:38)
+ at Main$Inner.<clinit>(Main.java:32)
+ at Main$MyThread.run(Main.java:24)
diff --git a/test/844-exception/expected-stdout.txt b/test/844-exception/expected-stdout.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/844-exception/expected-stdout.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/844-exception/info.txt b/test/844-exception/info.txt
new file mode 100644
index 0000000..a825fbb
--- /dev/null
+++ b/test/844-exception/info.txt
@@ -0,0 +1,2 @@
+Regression test for Thread::QuickDeliverException which used to expect a quick
+frame to be the caller.
diff --git a/test/844-exception/src/Main.java b/test/844-exception/src/Main.java
new file mode 100644
index 0000000..79d61cc
--- /dev/null
+++ b/test/844-exception/src/Main.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ static Main empty;
+
+ static class MyThread extends Thread {
+ public void run() {
+ // This will throw at `callMethodThatThrows` and trigger deoptimization checks which we used
+ // to crash on.
+ new Inner();
+ }
+ }
+
+ public static class Inner {
+ // Have a <clinit> method invoke another <clinit> method to ensure we execute in the
+ // interpreter.
+ static {
+ new Inner2();
+ }
+ }
+
+ public static class Inner2 {
+ static {
+ Main.callMethodThatThrows();
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+ // Disables use of nterp.
+ Main.setAsyncExceptionsThrown();
+
+ // Execute the test in a different thread, to ensure we still
+ // return a 0 exit status.
+ Thread t = new MyThread();
+ t.start();
+ t.join();
+ }
+
+ public static void callMethodThatThrows() {
+ // Ensures we get deoptimization requests.
+ Main.forceInterpreterOnThread();
+ throw new Error("");
+ }
+
+ public static native void forceInterpreterOnThread();
+ public static native void setAsyncExceptionsThrown();
+
+}
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index a900714..e0e9641 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -451,4 +451,16 @@
return soa.Decode<mirror::Class>(c)->IsObsoleteObject();
}
+extern "C" JNIEXPORT void JNICALL Java_Main_forceInterpreterOnThread(JNIEnv* env,
+ jclass cls ATTRIBUTE_UNUSED) {
+ ScopedObjectAccess soa(env);
+ MutexLock thread_list_mu(soa.Self(), *Locks::thread_list_lock_);
+ soa.Self()->IncrementForceInterpreterCount();
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_setAsyncExceptionsThrown(JNIEnv* env ATTRIBUTE_UNUSED,
+ jclass cls ATTRIBUTE_UNUSED) {
+ Runtime::Current()->SetAsyncExceptionsThrown();
+}
+
} // namespace art
diff --git a/test/knownfailures.json b/test/knownfailures.json
index c370aaa..5e40eed 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1164,6 +1164,7 @@
"833-background-verification",
"836-32768classes",
"837-deopt",
+ "844-exception",
"999-redefine-hiddenapi",
"1000-non-moving-space-stress",
"1001-app-image-regions",