summaryrefslogtreecommitdiffstats
path: root/test/802-deoptimization
diff options
context:
space:
mode:
authorSebastien Hertz <shertz@google.com>2015-01-16 19:49:09 +0100
committerSebastien Hertz <shertz@google.com>2015-01-23 17:26:45 +0100
commit270a0e16c3b8e5b95cbfdbd8996ac137c7c6322b (patch)
tree2800649d19ffcdc891118a458f6b0169022d93e0 /test/802-deoptimization
parent951d70b42400453f9d1746d155b8337c07c86acc (diff)
downloadart-270a0e16c3b8e5b95cbfdbd8996ac137c7c6322b.zip
art-270a0e16c3b8e5b95cbfdbd8996ac137c7c6322b.tar.gz
art-270a0e16c3b8e5b95cbfdbd8996ac137c7c6322b.tar.bz2
Fix exception handling during deoptimization
When interpreting a deoptimized shadow frame, we may start with a pending exception thrown by a previous deoptimized shadow frame (from a previous invoke). Therefore, we need to handle it before executing any instruction, otherwise we execute incorrect code. Because we need the DEX pc of the throwing instruction to find a matching catch handler, we initialize deoptimized shadow frames with the current DEX pc at the time the stack is deoptimized. When we are about to interpret a deoptimized shadow frame, we need to update the shadow frame with the DEX pc of the next instruction to interpret. There are three cases: - if there is no pending exception, this is the instruction following the current one. - if there is a pending exception and we found a matching catch handler, this is the first instruction of this handler. - if there is a pending exception but there is no matching catch handler, we do not execute the deoptimized shadow frame and continue to its caller. The verifier now fails when a method starts with a move-exception instruction. Indeed we cannot start executing a method with a pending exception. Bug: 19057915 Bug: 19041195 Bug: 18607595 Change-Id: I355ac81e6ac098edc7e3cc8c13dbfa24a2969ab2
Diffstat (limited to 'test/802-deoptimization')
-rw-r--r--test/802-deoptimization/expected.txt1
-rw-r--r--test/802-deoptimization/info.txt1
-rw-r--r--test/802-deoptimization/smali/catch_handler_on_entry.smali29
-rw-r--r--test/802-deoptimization/src/CatchHandlerOnEntryHelper.java30
-rw-r--r--test/802-deoptimization/src/DeoptimizationController.java86
-rw-r--r--test/802-deoptimization/src/Main.java43
6 files changed, 190 insertions, 0 deletions
diff --git a/test/802-deoptimization/expected.txt b/test/802-deoptimization/expected.txt
new file mode 100644
index 0000000..d5f1f08
--- /dev/null
+++ b/test/802-deoptimization/expected.txt
@@ -0,0 +1 @@
+CatchHandlerOnEntryWithoutMoveException OK
diff --git a/test/802-deoptimization/info.txt b/test/802-deoptimization/info.txt
new file mode 100644
index 0000000..104d40f
--- /dev/null
+++ b/test/802-deoptimization/info.txt
@@ -0,0 +1 @@
+Tests related to deoptimization
diff --git a/test/802-deoptimization/smali/catch_handler_on_entry.smali b/test/802-deoptimization/smali/catch_handler_on_entry.smali
new file mode 100644
index 0000000..836101e
--- /dev/null
+++ b/test/802-deoptimization/smali/catch_handler_on_entry.smali
@@ -0,0 +1,29 @@
+.class public LCatchHandlerOnEntry;
+
+.super Ljava/lang/Object;
+
+# Test we can execute a method starting with a catch handler (without
+# move-exception instruction). This method must be called with parameter
+# initialized to 0.
+#
+# We execute the catch handler (Label1) for the first time with p0 == 0.
+# We save its value in v0, increment p0 to 1 and execute the div-int
+# instruction (Label2) which throws an ArithmeticException (division by zero).
+# That exception is caught by the catch handler so we execute it a second time.
+# Now p0 == 1. When we we execute the div-int instruction, it succeeds and we
+# return its result: this is the initial value of v1 because "v1 = v1 / 1".
+.method public static catchHandlerOnEntry(I)I
+.registers 4
+:Label1
+ const v1, 100
+ move v0, p0
+ add-int/lit8 p0, p0, 1
+
+:Label2
+ invoke-static {v0}, LCatchHandlerOnEntryHelper;->throwExceptionDuringDeopt(I)V
+
+:Label3
+ return v1
+
+.catchall {:Label2 .. :Label3} :Label1
+.end method
diff --git a/test/802-deoptimization/src/CatchHandlerOnEntryHelper.java b/test/802-deoptimization/src/CatchHandlerOnEntryHelper.java
new file mode 100644
index 0000000..a88d31b
--- /dev/null
+++ b/test/802-deoptimization/src/CatchHandlerOnEntryHelper.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+/**
+ * Helper class used by smali test classes.
+ */
+public class CatchHandlerOnEntryHelper {
+
+ public static void throwExceptionDuringDeopt(int i) {
+ if (i == 0) {
+ DeoptimizationController.startDeoptomization();
+ throw new RuntimeException("Test exception");
+ } else {
+ DeoptimizationController.stopDeoptomization();
+ }
+ }
+}
diff --git a/test/802-deoptimization/src/DeoptimizationController.java b/test/802-deoptimization/src/DeoptimizationController.java
new file mode 100644
index 0000000..c031c07
--- /dev/null
+++ b/test/802-deoptimization/src/DeoptimizationController.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Method;
+
+/**
+ * Controls deoptimization using dalvik.system.VMDebug class.
+ */
+public class DeoptimizationController {
+ public static void startDeoptomization() {
+ try {
+ File tempFile;
+ try {
+ tempFile = File.createTempFile("test", ".trace");
+ } catch (IOException e) {
+ System.setProperty("java.io.tmpdir", "/sdcard");
+ tempFile = File.createTempFile("test", ".trace");
+ }
+ tempFile.deleteOnExit();
+ String tempFileName = tempFile.getPath();
+
+ VMDebug.startMethodTracing(tempFileName, 0, 0, false, 1000);
+ if (VMDebug.getMethodTracingMode() == 0) {
+ throw new IllegalStateException("Not tracing.");
+ }
+ } catch (Exception exc) {
+ exc.printStackTrace(System.err);
+ }
+ }
+
+ public static void stopDeoptomization() {
+ try {
+ VMDebug.stopMethodTracing();
+ if (VMDebug.getMethodTracingMode() != 0) {
+ throw new IllegalStateException("Still tracing.");
+ }
+ } catch (Exception exc) {
+ exc.printStackTrace(System.err);
+ }
+ }
+
+ private static class VMDebug {
+ private static final Method startMethodTracingMethod;
+ private static final Method stopMethodTracingMethod;
+ private static final Method getMethodTracingModeMethod;
+
+ static {
+ try {
+ Class<?> c = Class.forName("dalvik.system.VMDebug");
+ startMethodTracingMethod = c.getDeclaredMethod("startMethodTracing", String.class,
+ Integer.TYPE, Integer.TYPE, Boolean.TYPE, Integer.TYPE);
+ stopMethodTracingMethod = c.getDeclaredMethod("stopMethodTracing");
+ getMethodTracingModeMethod = c.getDeclaredMethod("getMethodTracingMode");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void startMethodTracing(String filename, int bufferSize, int flags,
+ boolean samplingEnabled, int intervalUs) throws Exception {
+ startMethodTracingMethod.invoke(null, filename, bufferSize, flags, samplingEnabled,
+ intervalUs);
+ }
+ public static void stopMethodTracing() throws Exception {
+ stopMethodTracingMethod.invoke(null);
+ }
+ public static int getMethodTracingMode() throws Exception {
+ return (int) getMethodTracingModeMethod.invoke(null);
+ }
+ }
+}
diff --git a/test/802-deoptimization/src/Main.java b/test/802-deoptimization/src/Main.java
new file mode 100644
index 0000000..c8780de
--- /dev/null
+++ b/test/802-deoptimization/src/Main.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+import java.lang.reflect.Method;
+
+public class Main {
+ private static final int EXPECTED_RESULT = 100;
+ private static final int PARAMETER_VALUE = 0;
+
+ public static void main(String[] args) throws Throwable {
+ testCatchHandlerOnEntryWithoutMoveException();
+ }
+
+ /**
+ * Tests we correctly execute a method starting with a catch handler without
+ * move-exception instruction when throwing an exception during deoptimization.
+ */
+ private static void testCatchHandlerOnEntryWithoutMoveException() throws Throwable {
+ Class<?> c = Class.forName("CatchHandlerOnEntry");
+ Method m = c.getMethod("catchHandlerOnEntry", int.class);
+ Object result = m.invoke(null, new Object[]{PARAMETER_VALUE});
+ int intResult = ((Integer) result).intValue();
+ if (intResult == EXPECTED_RESULT) {
+ System.out.println("CatchHandlerOnEntryWithoutMoveException OK");
+ } else {
+ System.out.println("CatchHandlerOnEntryWithoutMoveException KO: result==" + intResult);
+ }
+ }
+}
+