Home | History | Annotate | Download | only in art
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package art;
     18 
     19 import java.lang.reflect.Method;
     20 import java.util.Set;
     21 import java.util.HashSet;
     22 
     23 public class Test989 {
     24   static boolean PRINT_STACK_TRACE = false;
     25   static Set<Method> testMethods = new HashSet<>();
     26 
     27   static MethodTracer currentTracer = new MethodTracer() {
     28     public void methodEntry(Object o) { return; }
     29     public void methodExited(Object o, boolean e, Object r) { return; }
     30   };
     31 
     32   private static boolean DISABLE_TRACING = false;
     33 
     34   static {
     35     try {
     36       testMethods.add(Test989.class.getDeclaredMethod("doNothing"));
     37       testMethods.add(Test989.class.getDeclaredMethod("doNothingNative"));
     38       testMethods.add(Test989.class.getDeclaredMethod("throwA"));
     39       testMethods.add(Test989.class.getDeclaredMethod("throwANative"));
     40       testMethods.add(Test989.class.getDeclaredMethod("returnFloat"));
     41       testMethods.add(Test989.class.getDeclaredMethod("returnFloatNative"));
     42       testMethods.add(Test989.class.getDeclaredMethod("returnDouble"));
     43       testMethods.add(Test989.class.getDeclaredMethod("returnDoubleNative"));
     44       testMethods.add(Test989.class.getDeclaredMethod("returnValue"));
     45       testMethods.add(Test989.class.getDeclaredMethod("returnValueNative"));
     46       testMethods.add(Test989.class.getDeclaredMethod("acceptValue", Object.class));
     47       testMethods.add(Test989.class.getDeclaredMethod("acceptValueNative", Object.class));
     48       testMethods.add(Test989.class.getDeclaredMethod("tryCatchExit"));
     49     } catch (Exception e) {
     50       throw new Error("Bad static!", e);
     51     }
     52   }
     53 
     54   // Disables tracing only on RI. Used to work around an annoying piece of behavior where in the
     55   // RI throwing an exception in an exit hook causes the exit hook to be re-executed. This leads
     56   // to an infinite loop on the RI.
     57   private static void disableTraceForRI() {
     58     if (!System.getProperty("java.vm.name").equals("Dalvik")) {
     59       Trace.disableTracing(Thread.currentThread());
     60     }
     61   }
     62 
     63   private static String getInfo(Object m, boolean exception, Object result) {
     64     String out = m.toString() + " returned ";
     65     if (exception) {
     66       out += "<exception>";
     67     } else {
     68       out += result;
     69     }
     70     return out;
     71   }
     72 
     73   public static interface MethodTracer {
     74     public void methodEntry(Object m);
     75     public void methodExited(Object m, boolean exception, Object result);
     76     public default Class<?> entryException() { return null; }
     77     public default Class<?> exitException() { return null; }
     78   }
     79 
     80   public static class NormalTracer implements MethodTracer {
     81     public void methodEntry(Object m) {
     82       if (testMethods.contains(m)) {
     83         System.out.println("Normal: Entering " + m);
     84       }
     85     }
     86     public void methodExited(Object m, boolean exception, Object result) {
     87       if (testMethods.contains(m)) {
     88         System.out.println("Normal: Leaving " + getInfo(m, exception, result));
     89       }
     90     }
     91   }
     92 
     93   public static class ThrowEnterTracer implements MethodTracer {
     94     public void methodEntry(Object m) {
     95       if (testMethods.contains(m)) {
     96         System.out.println("ThrowEnter: Entering " + m);
     97         throw new ErrorB("Throwing error while entering " + m);
     98       }
     99     }
    100     public void methodExited(Object m, boolean exception, Object result) {
    101       if (testMethods.contains(m)) {
    102         System.out.println("ThrowEnter: Leaving " + getInfo(m, exception, result));
    103       }
    104     }
    105     public Class<?> entryException() { return ErrorB.class; }
    106   }
    107 
    108   public static class ThrowExitTracer implements MethodTracer {
    109     public void methodEntry(Object m) {
    110       if (testMethods.contains(m)) {
    111         System.out.println("ThrowExit: Entering " + m);
    112       }
    113     }
    114     public void methodExited(Object m, boolean exception, Object result) {
    115       if (testMethods.contains(m)) {
    116         // The RI goes into an infinite loop if we throw exceptions in an ExitHook. See
    117         // disableTraceForRI for explanation.
    118         disableTraceForRI();
    119         System.out.println("ThrowExit: Leaving " + getInfo(m, exception, result));
    120         throw new ErrorB("Throwing error while exit " + getInfo(m, exception, result));
    121       }
    122     }
    123     public Class<?> exitException() { return ErrorB.class; }
    124   }
    125 
    126   public static class ThrowBothTracer implements MethodTracer {
    127     public void methodEntry(Object m) {
    128       if (testMethods.contains(m)) {
    129         System.out.println("ThrowBoth: Entering " + m);
    130         throw new ErrorB("Throwing error while entering " + m);
    131       }
    132     }
    133     public void methodExited(Object m, boolean exception, Object result) {
    134       if (testMethods.contains(m)) {
    135         // The RI goes into an infinite loop if we throw exceptions in an ExitHook. See
    136         // disableTraceForRI for explanation.
    137         disableTraceForRI();
    138         System.out.println("ThrowBoth: Leaving " + getInfo(m, exception, result));
    139         throw new ErrorC("Throwing error while exit " + getInfo(m, exception, result));
    140       }
    141     }
    142     public Class<?> entryException() { return ErrorB.class; }
    143     public Class<?> exitException() { return ErrorC.class; }
    144   }
    145 
    146   public static class ForceGCTracer implements MethodTracer {
    147     public void methodEntry(Object m) {
    148       if (System.getProperty("java.vm.name").equals("Dalvik")) {
    149         System.gc();
    150       }
    151     }
    152     public void methodExited(Object m, boolean exception, Object result) {
    153       if (System.getProperty("java.vm.name").equals("Dalvik")) {
    154         System.gc();
    155       }
    156     }
    157   }
    158 
    159   private static void maybeDisableTracing() throws Exception {
    160     if (DISABLE_TRACING) {
    161       Trace.disableTracing(Thread.currentThread());
    162     }
    163   }
    164 
    165   public static void baseNotifyMethodEntry(Object o) {
    166     currentTracer.methodEntry(o);
    167   }
    168   public static void baseNotifyMethodExit(Object o, boolean exception, Object res) {
    169     currentTracer.methodExited(o, exception, res);
    170   }
    171 
    172   private static void setupTracing() throws Exception {
    173     Trace.enableMethodTracing(
    174         Test989.class,
    175         Test989.class.getDeclaredMethod("baseNotifyMethodEntry", Object.class),
    176         Test989.class.getDeclaredMethod(
    177             "baseNotifyMethodExit", Object.class, Boolean.TYPE, Object.class),
    178         Thread.currentThread());
    179   }
    180   private static void setEntry(MethodTracer type) throws Exception {
    181     if (DISABLE_TRACING || !System.getProperty("java.vm.name").equals("Dalvik")) {
    182       Trace.disableTracing(Thread.currentThread());
    183       setupTracing();
    184     }
    185     currentTracer = type;
    186   }
    187 
    188   private static String testDescription(MethodTracer type, Runnable test) {
    189     return "test[" + type.getClass() + ", " + test.getClass() + "]";
    190   }
    191 
    192   private static Class<?> getExpectedError(MethodTracer t, MyRunnable r) {
    193     if (t.exitException() != null) {
    194       return t.exitException();
    195     } else if (t.entryException() != null) {
    196       return t.entryException();
    197     } else {
    198       return r.expectedThrow();
    199     }
    200   }
    201 
    202   private static void doTest(MethodTracer type, MyRunnable test) throws Exception {
    203     Class<?> expected = getExpectedError(type, test);
    204 
    205     setEntry(type);
    206     try {
    207       test.run();
    208       // Disabling method tracing just makes this test somewhat faster.
    209       maybeDisableTracing();
    210       if (expected == null) {
    211         System.out.println(
    212             "Received no exception as expected for " + testDescription(type, test) + ".");
    213         return;
    214       }
    215     } catch (Error t) {
    216       // Disabling method tracing just makes this test somewhat faster.
    217       maybeDisableTracing();
    218       if (expected == null) {
    219         throw new Error("Unexpected error occured: " + t + " for " + testDescription(type, test), t);
    220       } else if (!expected.isInstance(t)) {
    221         throw new Error("Expected error of type " + expected + " not " + t +
    222             " for " + testDescription(type, test), t);
    223       } else {
    224         System.out.println(
    225             "Received expected error for " + testDescription(type, test) + " - " + t);
    226         if (PRINT_STACK_TRACE) {
    227           t.printStackTrace();
    228         }
    229         return;
    230       }
    231     }
    232     System.out.println("Expected an error of type " + expected + " but got no exception for "
    233         + testDescription(type, test));
    234     // throw new Error("Expected an error of type " + expected + " but got no exception for "
    235     //     + testDescription(type, test));
    236   }
    237 
    238   public static interface MyRunnable extends Runnable {
    239     public default Class<?> expectedThrow() {
    240       return null;
    241     }
    242   }
    243 
    244   public static void run() throws Exception {
    245     MyRunnable[] testCases = new MyRunnable[] {
    246       new doNothingClass(),
    247       new doNothingNativeClass(),
    248       new throwAClass(),
    249       new throwANativeClass(),
    250       new returnValueClass(),
    251       new returnValueNativeClass(),
    252       new acceptValueClass(),
    253       new acceptValueNativeClass(),
    254       new tryCatchExitClass(),
    255       new returnFloatClass(),
    256       new returnFloatNativeClass(),
    257       new returnDoubleClass(),
    258       new returnDoubleNativeClass(),
    259     };
    260     MethodTracer[] tracers = new MethodTracer[] {
    261       new NormalTracer(),
    262       new ThrowEnterTracer(),
    263       new ThrowExitTracer(),
    264       new ThrowBothTracer(),
    265       new ForceGCTracer(),
    266     };
    267 
    268     setupTracing();
    269     for (MethodTracer t : tracers) {
    270       for (MyRunnable r : testCases) {
    271         doTest(t, r);
    272       }
    273     }
    274 
    275     maybeDisableTracing();
    276     System.out.println("Finished!");
    277     Trace.disableTracing(Thread.currentThread());
    278   }
    279 
    280   private static final class throwAClass implements MyRunnable {
    281     public void run() {
    282       throwA();
    283     }
    284     @Override
    285     public Class<?> expectedThrow() {
    286       return ErrorA.class;
    287     }
    288   }
    289 
    290   private static final class throwANativeClass implements MyRunnable {
    291     public void run() {
    292       throwANative();
    293     }
    294     @Override
    295     public Class<?> expectedThrow() {
    296       return ErrorA.class;
    297     }
    298   }
    299 
    300   private static final class tryCatchExitClass implements MyRunnable {
    301     public void run() {
    302       tryCatchExit();
    303     }
    304   }
    305 
    306   private static final class doNothingClass implements MyRunnable {
    307     public void run() {
    308       doNothing();
    309     }
    310   }
    311 
    312   private static final class doNothingNativeClass implements MyRunnable {
    313     public void run() {
    314       doNothingNative();
    315     }
    316   }
    317 
    318   private static final class acceptValueClass implements MyRunnable {
    319     public void run() {
    320       acceptValue(mkTestObject());
    321     }
    322   }
    323 
    324   private static final class acceptValueNativeClass implements MyRunnable {
    325     public void run() {
    326       acceptValueNative(mkTestObject());
    327     }
    328   }
    329 
    330   private static final class returnValueClass implements MyRunnable {
    331     public void run() {
    332       Object o = returnValue();
    333       System.out.println("returnValue returned: " + o);
    334     }
    335   }
    336 
    337   private static final class returnValueNativeClass implements MyRunnable {
    338     public void run() {
    339       Object o = returnValueNative();
    340       System.out.println("returnValueNative returned: " + o);
    341     }
    342   }
    343 
    344   private static final class returnFloatClass implements MyRunnable {
    345     public void run() {
    346       float d = returnFloat();
    347       System.out.println("returnFloat returned: " + d);
    348     }
    349   }
    350 
    351   private static final class returnFloatNativeClass implements MyRunnable {
    352     public void run() {
    353       float d = returnFloatNative();
    354       System.out.println("returnFloatNative returned: " + d);
    355     }
    356   }
    357 
    358   private static final class returnDoubleClass implements MyRunnable {
    359     public void run() {
    360       double d = returnDouble();
    361       System.out.println("returnDouble returned: " + d);
    362     }
    363   }
    364 
    365   private static final class returnDoubleNativeClass implements MyRunnable {
    366     public void run() {
    367       double d = returnDoubleNative();
    368       System.out.println("returnDoubleNative returned: " + d);
    369     }
    370   }
    371 
    372   private static class ErrorA extends Error {
    373     private static final long serialVersionUID = 0;
    374     public ErrorA(String s) { super(s); }
    375   }
    376 
    377   private static class ErrorB extends Error {
    378     private static final long serialVersionUID = 1;
    379     public ErrorB(String s) { super(s); }
    380   }
    381 
    382   private static class ErrorC extends Error {
    383     private static final long serialVersionUID = 2;
    384     public ErrorC(String s) { super(s); }
    385   }
    386 
    387   // Does nothing.
    388   public static void doNothing() { }
    389 
    390   public static void tryCatchExit() {
    391     try {
    392       Object o = mkTestObject();
    393       return;
    394     } catch (ErrorB b) {
    395       System.out.println("ERROR: Caught " + b);
    396       b.printStackTrace();
    397     } catch (ErrorC c) {
    398       System.out.println("ERROR: Caught " + c);
    399       c.printStackTrace();
    400     }
    401   }
    402 
    403   public static float returnFloat() {
    404     return doGetFloat();
    405   }
    406 
    407   public static double returnDouble() {
    408     return doGetDouble();
    409   }
    410 
    411   // Throws an ErrorA.
    412   public static void throwA() {
    413     doThrowA();
    414   }
    415 
    416   public static void doThrowA() {
    417     throw new ErrorA("Throwing Error A");
    418   }
    419 
    420   static final class TestObject {
    421     private int idx;
    422     public TestObject(int v) {
    423       this.idx = v;
    424     }
    425     @Override
    426     public String toString() {
    427       return "TestObject(" + idx + ")";
    428     }
    429   }
    430 
    431   static int counter = 0;
    432   public static Object mkTestObject() {
    433     return new TestObject(counter++);
    434   }
    435 
    436   public static void printObject(Object o) {
    437     System.out.println("Recieved " + o);
    438   }
    439 
    440   // Returns a newly allocated value.
    441   public static Object returnValue() {
    442     return mkTestObject();
    443   }
    444 
    445   public static void acceptValue(Object o) {
    446     printObject(o);
    447   }
    448 
    449   public static float doGetFloat() {
    450     return 1.618f;
    451   }
    452 
    453   public static double doGetDouble() {
    454     return 3.14159628;
    455   }
    456 
    457   // Calls mkTestObject from native code and returns it.
    458   public static native Object returnValueNative();
    459   // Calls printObject from native code.
    460   public static native void acceptValueNative(Object t);
    461   public static native void doNothingNative();
    462   public static native void throwANative();
    463   public static native float returnFloatNative();
    464   public static native double returnDoubleNative();
    465 }
    466