diff options
Diffstat (limited to 'test/989-method-trace-throw/src')
| -rw-r--r-- | test/989-method-trace-throw/src/Main.java | 21 | ||||
| -rw-r--r-- | test/989-method-trace-throw/src/art/Test989.java | 465 | ||||
| -rw-r--r-- | test/989-method-trace-throw/src/art/Trace.java | 25 |
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); +} |