summaryrefslogtreecommitdiff
path: root/test/989-method-trace-throw/src
diff options
context:
space:
mode:
Diffstat (limited to 'test/989-method-trace-throw/src')
-rw-r--r--test/989-method-trace-throw/src/Main.java21
-rw-r--r--test/989-method-trace-throw/src/art/Test989.java465
-rw-r--r--test/989-method-trace-throw/src/art/Trace.java25
3 files changed, 511 insertions, 0 deletions
diff --git a/test/989-method-trace-throw/src/Main.java b/test/989-method-trace-throw/src/Main.java
new file mode 100644
index 0000000000..29b9de1027
--- /dev/null
+++ b/test/989-method-trace-throw/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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 {
+ public static void main(String[] args) throws Exception {
+ art.Test989.run();
+ }
+}
diff --git a/test/989-method-trace-throw/src/art/Test989.java b/test/989-method-trace-throw/src/art/Test989.java
new file mode 100644
index 0000000000..18421bd08b
--- /dev/null
+++ b/test/989-method-trace-throw/src/art/Test989.java
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+package art;
+
+import java.lang.reflect.Method;
+import java.util.Set;
+import java.util.HashSet;
+
+public class Test989 {
+ static boolean PRINT_STACK_TRACE = false;
+ static Set<Method> testMethods = new HashSet<>();
+
+ static MethodTracer currentTracer = new MethodTracer() {
+ public void methodEntry(Object o) { return; }
+ public void methodExited(Object o, boolean e, Object r) { return; }
+ };
+
+ private static boolean DISABLE_TRACING = false;
+
+ static {
+ try {
+ testMethods.add(Test989.class.getDeclaredMethod("doNothing"));
+ testMethods.add(Test989.class.getDeclaredMethod("doNothingNative"));
+ testMethods.add(Test989.class.getDeclaredMethod("throwA"));
+ testMethods.add(Test989.class.getDeclaredMethod("throwANative"));
+ testMethods.add(Test989.class.getDeclaredMethod("returnFloat"));
+ testMethods.add(Test989.class.getDeclaredMethod("returnFloatNative"));
+ testMethods.add(Test989.class.getDeclaredMethod("returnDouble"));
+ testMethods.add(Test989.class.getDeclaredMethod("returnDoubleNative"));
+ testMethods.add(Test989.class.getDeclaredMethod("returnValue"));
+ testMethods.add(Test989.class.getDeclaredMethod("returnValueNative"));
+ testMethods.add(Test989.class.getDeclaredMethod("acceptValue", Object.class));
+ testMethods.add(Test989.class.getDeclaredMethod("acceptValueNative", Object.class));
+ testMethods.add(Test989.class.getDeclaredMethod("tryCatchExit"));
+ } catch (Exception e) {
+ throw new Error("Bad static!", e);
+ }
+ }
+
+ // Disables tracing only on RI. Used to work around an annoying piece of behavior where in the
+ // RI throwing an exception in an exit hook causes the exit hook to be re-executed. This leads
+ // to an infinite loop on the RI.
+ private static void disableTraceForRI() {
+ if (!System.getProperty("java.vm.name").equals("Dalvik")) {
+ Trace.disableMethodTracing(Thread.currentThread());
+ }
+ }
+
+ private static String getInfo(Object m, boolean exception, Object result) {
+ String out = m.toString() + " returned ";
+ if (exception) {
+ out += "<exception>";
+ } else {
+ out += result;
+ }
+ return out;
+ }
+
+ public static interface MethodTracer {
+ public void methodEntry(Object m);
+ public void methodExited(Object m, boolean exception, Object result);
+ public default Class<?> entryException() { return null; }
+ public default Class<?> exitException() { return null; }
+ }
+
+ public static class NormalTracer implements MethodTracer {
+ public void methodEntry(Object m) {
+ if (testMethods.contains(m)) {
+ System.out.println("Normal: Entering " + m);
+ }
+ }
+ public void methodExited(Object m, boolean exception, Object result) {
+ if (testMethods.contains(m)) {
+ System.out.println("Normal: Leaving " + getInfo(m, exception, result));
+ }
+ }
+ }
+
+ public static class ThrowEnterTracer implements MethodTracer {
+ public void methodEntry(Object m) {
+ if (testMethods.contains(m)) {
+ System.out.println("ThrowEnter: Entering " + m);
+ throw new ErrorB("Throwing error while entering " + m);
+ }
+ }
+ public void methodExited(Object m, boolean exception, Object result) {
+ if (testMethods.contains(m)) {
+ System.out.println("ThrowEnter: Leaving " + getInfo(m, exception, result));
+ }
+ }
+ public Class<?> entryException() { return ErrorB.class; }
+ }
+
+ public static class ThrowExitTracer implements MethodTracer {
+ public void methodEntry(Object m) {
+ if (testMethods.contains(m)) {
+ System.out.println("ThrowExit: Entering " + m);
+ }
+ }
+ public void methodExited(Object m, boolean exception, Object result) {
+ if (testMethods.contains(m)) {
+ // The RI goes into an infinite loop if we throw exceptions in an ExitHook. See
+ // disableTraceForRI for explanation.
+ disableTraceForRI();
+ System.out.println("ThrowExit: Leaving " + getInfo(m, exception, result));
+ throw new ErrorB("Throwing error while exit " + getInfo(m, exception, result));
+ }
+ }
+ public Class<?> exitException() { return ErrorB.class; }
+ }
+
+ public static class ThrowBothTracer implements MethodTracer {
+ public void methodEntry(Object m) {
+ if (testMethods.contains(m)) {
+ System.out.println("ThrowBoth: Entering " + m);
+ throw new ErrorB("Throwing error while entering " + m);
+ }
+ }
+ public void methodExited(Object m, boolean exception, Object result) {
+ if (testMethods.contains(m)) {
+ // The RI goes into an infinite loop if we throw exceptions in an ExitHook. See
+ // disableTraceForRI for explanation.
+ disableTraceForRI();
+ System.out.println("ThrowBoth: Leaving " + getInfo(m, exception, result));
+ throw new ErrorC("Throwing error while exit " + getInfo(m, exception, result));
+ }
+ }
+ public Class<?> entryException() { return ErrorB.class; }
+ public Class<?> exitException() { return ErrorC.class; }
+ }
+
+ public static class ForceGCTracer implements MethodTracer {
+ public void methodEntry(Object m) {
+ if (System.getProperty("java.vm.name").equals("Dalvik")) {
+ System.gc();
+ }
+ }
+ public void methodExited(Object m, boolean exception, Object result) {
+ if (System.getProperty("java.vm.name").equals("Dalvik")) {
+ System.gc();
+ }
+ }
+ }
+
+ private static void maybeDisableTracing() throws Exception {
+ if (DISABLE_TRACING) {
+ Trace.disableMethodTracing(Thread.currentThread());
+ }
+ }
+
+ public static void baseNotifyMethodEntry(Object o) {
+ currentTracer.methodEntry(o);
+ }
+ public static void baseNotifyMethodExit(Object o, boolean exception, Object res) {
+ currentTracer.methodExited(o, exception, res);
+ }
+
+ private static void setupTracing() throws Exception {
+ Trace.enableMethodTracing(
+ Test989.class,
+ Test989.class.getDeclaredMethod("baseNotifyMethodEntry", Object.class),
+ Test989.class.getDeclaredMethod(
+ "baseNotifyMethodExit", Object.class, Boolean.TYPE, Object.class),
+ Thread.currentThread());
+ }
+ private static void setEntry(MethodTracer type) throws Exception {
+ if (DISABLE_TRACING || !System.getProperty("java.vm.name").equals("Dalvik")) {
+ Trace.disableMethodTracing(Thread.currentThread());
+ setupTracing();
+ }
+ currentTracer = type;
+ }
+
+ private static String testDescription(MethodTracer type, Runnable test) {
+ return "test[" + type.getClass() + ", " + test.getClass() + "]";
+ }
+
+ private static Class<?> getExpectedError(MethodTracer t, MyRunnable r) {
+ if (t.exitException() != null) {
+ return t.exitException();
+ } else if (t.entryException() != null) {
+ return t.entryException();
+ } else {
+ return r.expectedThrow();
+ }
+ }
+
+ private static void doTest(MethodTracer type, MyRunnable test) throws Exception {
+ Class<?> expected = getExpectedError(type, test);
+
+ setEntry(type);
+ try {
+ test.run();
+ // Disabling method tracing just makes this test somewhat faster.
+ maybeDisableTracing();
+ if (expected == null) {
+ System.out.println(
+ "Received no exception as expected for " + testDescription(type, test) + ".");
+ return;
+ }
+ } catch (Error t) {
+ // Disabling method tracing just makes this test somewhat faster.
+ maybeDisableTracing();
+ if (expected == null) {
+ throw new Error("Unexpected error occured: " + t + " for " + testDescription(type, test), t);
+ } else if (!expected.isInstance(t)) {
+ throw new Error("Expected error of type " + expected + " not " + t +
+ " for " + testDescription(type, test), t);
+ } else {
+ System.out.println(
+ "Received expected error for " + testDescription(type, test) + " - " + t);
+ if (PRINT_STACK_TRACE) {
+ t.printStackTrace();
+ }
+ return;
+ }
+ }
+ System.out.println("Expected an error of type " + expected + " but got no exception for "
+ + testDescription(type, test));
+ // throw new Error("Expected an error of type " + expected + " but got no exception for "
+ // + testDescription(type, test));
+ }
+
+ public static interface MyRunnable extends Runnable {
+ public default Class<?> expectedThrow() {
+ return null;
+ }
+ }
+
+ public static void run() throws Exception {
+ MyRunnable[] testCases = new MyRunnable[] {
+ new doNothingClass(),
+ new doNothingNativeClass(),
+ new throwAClass(),
+ new throwANativeClass(),
+ new returnValueClass(),
+ new returnValueNativeClass(),
+ new acceptValueClass(),
+ new acceptValueNativeClass(),
+ new tryCatchExitClass(),
+ new returnFloatClass(),
+ new returnFloatNativeClass(),
+ new returnDoubleClass(),
+ new returnDoubleNativeClass(),
+ };
+ MethodTracer[] tracers = new MethodTracer[] {
+ new NormalTracer(),
+ new ThrowEnterTracer(),
+ new ThrowExitTracer(),
+ new ThrowBothTracer(),
+ new ForceGCTracer(),
+ };
+
+ setupTracing();
+ for (MethodTracer t : tracers) {
+ for (MyRunnable r : testCases) {
+ doTest(t, r);
+ }
+ }
+
+ maybeDisableTracing();
+ System.out.println("Finished!");
+ Trace.disableMethodTracing(Thread.currentThread());
+ }
+
+ private static final class throwAClass implements MyRunnable {
+ public void run() {
+ throwA();
+ }
+ @Override
+ public Class<?> expectedThrow() {
+ return ErrorA.class;
+ }
+ }
+
+ private static final class throwANativeClass implements MyRunnable {
+ public void run() {
+ throwANative();
+ }
+ @Override
+ public Class<?> expectedThrow() {
+ return ErrorA.class;
+ }
+ }
+
+ private static final class tryCatchExitClass implements MyRunnable {
+ public void run() {
+ tryCatchExit();
+ }
+ }
+
+ private static final class doNothingClass implements MyRunnable {
+ public void run() {
+ doNothing();
+ }
+ }
+
+ private static final class doNothingNativeClass implements MyRunnable {
+ public void run() {
+ doNothingNative();
+ }
+ }
+
+ private static final class acceptValueClass implements MyRunnable {
+ public void run() {
+ acceptValue(mkTestObject());
+ }
+ }
+
+ private static final class acceptValueNativeClass implements MyRunnable {
+ public void run() {
+ acceptValueNative(mkTestObject());
+ }
+ }
+
+ private static final class returnValueClass implements MyRunnable {
+ public void run() {
+ Object o = returnValue();
+ System.out.println("returnValue returned: " + o);
+ }
+ }
+
+ private static final class returnValueNativeClass implements MyRunnable {
+ public void run() {
+ Object o = returnValueNative();
+ System.out.println("returnValueNative returned: " + o);
+ }
+ }
+
+ private static final class returnFloatClass implements MyRunnable {
+ public void run() {
+ float d = returnFloat();
+ System.out.println("returnFloat returned: " + d);
+ }
+ }
+
+ private static final class returnFloatNativeClass implements MyRunnable {
+ public void run() {
+ float d = returnFloatNative();
+ System.out.println("returnFloatNative returned: " + d);
+ }
+ }
+
+ private static final class returnDoubleClass implements MyRunnable {
+ public void run() {
+ double d = returnDouble();
+ System.out.println("returnDouble returned: " + d);
+ }
+ }
+
+ private static final class returnDoubleNativeClass implements MyRunnable {
+ public void run() {
+ double d = returnDoubleNative();
+ System.out.println("returnDoubleNative returned: " + d);
+ }
+ }
+
+ private static class ErrorA extends Error {
+ private static final long serialVersionUID = 0;
+ public ErrorA(String s) { super(s); }
+ }
+
+ private static class ErrorB extends Error {
+ private static final long serialVersionUID = 1;
+ public ErrorB(String s) { super(s); }
+ }
+
+ private static class ErrorC extends Error {
+ private static final long serialVersionUID = 2;
+ public ErrorC(String s) { super(s); }
+ }
+
+ // Does nothing.
+ public static void doNothing() { }
+
+ public static void tryCatchExit() {
+ try {
+ Object o = mkTestObject();
+ return;
+ } catch (ErrorB b) {
+ System.out.println("ERROR: Caught " + b);
+ b.printStackTrace();
+ } catch (ErrorC c) {
+ System.out.println("ERROR: Caught " + c);
+ c.printStackTrace();
+ }
+ }
+
+ public static float returnFloat() {
+ return doGetFloat();
+ }
+
+ public static double returnDouble() {
+ return doGetDouble();
+ }
+
+ // Throws an ErrorA.
+ public static void throwA() {
+ doThrowA();
+ }
+
+ public static void doThrowA() {
+ throw new ErrorA("Throwing Error A");
+ }
+
+ static final class TestObject {
+ private int idx;
+ public TestObject(int v) {
+ this.idx = v;
+ }
+ @Override
+ public String toString() {
+ return "TestObject(" + idx + ")";
+ }
+ }
+
+ static int counter = 0;
+ public static Object mkTestObject() {
+ return new TestObject(counter++);
+ }
+
+ public static void printObject(Object o) {
+ System.out.println("Recieved " + o);
+ }
+
+ // Returns a newly allocated value.
+ public static Object returnValue() {
+ return mkTestObject();
+ }
+
+ public static void acceptValue(Object o) {
+ printObject(o);
+ }
+
+ public static float doGetFloat() {
+ return 1.618f;
+ }
+
+ public static double doGetDouble() {
+ return 3.14159628;
+ }
+
+ // Calls mkTestObject from native code and returns it.
+ public static native Object returnValueNative();
+ // Calls printObject from native code.
+ public static native void acceptValueNative(Object t);
+ public static native void doNothingNative();
+ public static native void throwANative();
+ public static native float returnFloatNative();
+ public static native double returnDoubleNative();
+}
diff --git a/test/989-method-trace-throw/src/art/Trace.java b/test/989-method-trace-throw/src/art/Trace.java
new file mode 100644
index 0000000000..3370996df3
--- /dev/null
+++ b/test/989-method-trace-throw/src/art/Trace.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.lang.reflect.Method;
+
+public class Trace {
+ public static native void enableMethodTracing(
+ Class<?> methodClass, Method entryMethod, Method exitMethod, Thread thr);
+ public static native void disableMethodTracing(Thread thr);
+}