• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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("throwNativeException"));
41       testMethods.add(Test989.class.getDeclaredMethod("returnFloat"));
42       testMethods.add(Test989.class.getDeclaredMethod("returnFloatNative"));
43       testMethods.add(Test989.class.getDeclaredMethod("returnDouble"));
44       testMethods.add(Test989.class.getDeclaredMethod("returnDoubleNative"));
45       testMethods.add(Test989.class.getDeclaredMethod("returnValue"));
46       testMethods.add(Test989.class.getDeclaredMethod("returnValueNative"));
47       testMethods.add(Test989.class.getDeclaredMethod("acceptValue", Object.class));
48       testMethods.add(Test989.class.getDeclaredMethod("acceptValueNative", Object.class));
49       testMethods.add(Test989.class.getDeclaredMethod("tryCatchExit"));
50       testMethods.add(Test989.class.getDeclaredMethod("doThrowNative"));
51     } catch (Exception e) {
52       throw new Error("Bad static!", e);
53     }
54   }
55 
56   // Disables tracing only on RI. Used to work around an annoying piece of behavior where in the
57   // RI throwing an exception in an exit hook causes the exit hook to be re-executed. This leads
58   // to an infinite loop on the RI.
disableTraceForRI()59   private static void disableTraceForRI() {
60     if (!System.getProperty("java.vm.name").equals("Dalvik")) {
61       Trace.disableTracing(Thread.currentThread());
62     }
63   }
64 
getInfo(Object m, boolean exception, Object result)65   private static String getInfo(Object m, boolean exception, Object result) {
66     String out = m.toString() + " returned ";
67     if (exception) {
68       out += "<exception>";
69     } else {
70       out += result;
71     }
72     return out;
73   }
74 
75   public static interface MethodTracer {
methodEntry(Object m)76     public void methodEntry(Object m);
methodExited(Object m, boolean exception, Object result)77     public void methodExited(Object m, boolean exception, Object result);
entryException()78     public default Class<?> entryException() { return null; }
exitException()79     public default Class<?> exitException() { return null; }
80   }
81 
82   public static class NormalTracer implements MethodTracer {
methodEntry(Object m)83     public void methodEntry(Object m) {
84       if (testMethods.contains(m)) {
85         System.out.println("Normal: Entering " + m);
86       }
87     }
methodExited(Object m, boolean exception, Object result)88     public void methodExited(Object m, boolean exception, Object result) {
89       if (testMethods.contains(m)) {
90         System.out.println("Normal: Leaving " + getInfo(m, exception, result));
91       }
92     }
93   }
94 
95   public static class ThrowEnterTracer implements MethodTracer {
methodEntry(Object m)96     public void methodEntry(Object m) {
97       if (testMethods.contains(m)) {
98         System.out.println("ThrowEnter: Entering " + m);
99         throw new ErrorB("Throwing error while entering " + m);
100       }
101     }
methodExited(Object m, boolean exception, Object result)102     public void methodExited(Object m, boolean exception, Object result) {
103       if (testMethods.contains(m)) {
104         System.out.println("ThrowEnter: Leaving " + getInfo(m, exception, result));
105       }
106     }
entryException()107     public Class<?> entryException() { return ErrorB.class; }
108   }
109 
110   public static class ThrowExitTracer implements MethodTracer {
methodEntry(Object m)111     public void methodEntry(Object m) {
112       if (testMethods.contains(m)) {
113         System.out.println("ThrowExit: Entering " + m);
114       }
115     }
methodExited(Object m, boolean exception, Object result)116     public void methodExited(Object m, boolean exception, Object result) {
117       if (testMethods.contains(m)) {
118         // The RI goes into an infinite loop if we throw exceptions in an ExitHook. See
119         // disableTraceForRI for explanation.
120         disableTraceForRI();
121         System.out.println("ThrowExit: Leaving " + getInfo(m, exception, result));
122         throw new ErrorB("Throwing error while exit " + getInfo(m, exception, result));
123       }
124     }
exitException()125     public Class<?> exitException() { return ErrorB.class; }
126   }
127 
128   public static class ThrowBothTracer implements MethodTracer {
methodEntry(Object m)129     public void methodEntry(Object m) {
130       if (testMethods.contains(m)) {
131         System.out.println("ThrowBoth: Entering " + m);
132         throw new ErrorB("Throwing error while entering " + m);
133       }
134     }
methodExited(Object m, boolean exception, Object result)135     public void methodExited(Object m, boolean exception, Object result) {
136       if (testMethods.contains(m)) {
137         // The RI goes into an infinite loop if we throw exceptions in an ExitHook. See
138         // disableTraceForRI for explanation.
139         disableTraceForRI();
140         System.out.println("ThrowBoth: Leaving " + getInfo(m, exception, result));
141         throw new ErrorC("Throwing error while exit " + getInfo(m, exception, result));
142       }
143     }
entryException()144     public Class<?> entryException() { return ErrorB.class; }
exitException()145     public Class<?> exitException() { return ErrorC.class; }
146   }
147 
148   public static class ForceGCTracer implements MethodTracer {
methodEntry(Object m)149     public void methodEntry(Object m) {
150       if (System.getProperty("java.vm.name").equals("Dalvik") &&
151           testMethods.contains(m)) {
152         Runtime.getRuntime().gc();
153       }
154     }
methodExited(Object m, boolean exception, Object result)155     public void methodExited(Object m, boolean exception, Object result) {
156       if (System.getProperty("java.vm.name").equals("Dalvik") &&
157           testMethods.contains(m)) {
158         Runtime.getRuntime().gc();
159       }
160     }
161   }
162 
maybeDisableTracing()163   private static void maybeDisableTracing() throws Exception {
164     if (DISABLE_TRACING) {
165       Trace.disableTracing(Thread.currentThread());
166     }
167   }
168 
baseNotifyMethodEntry(Object o)169   public static void baseNotifyMethodEntry(Object o) {
170     currentTracer.methodEntry(o);
171   }
baseNotifyMethodExit(Object o, boolean exception, Object res)172   public static void baseNotifyMethodExit(Object o, boolean exception, Object res) {
173     currentTracer.methodExited(o, exception, res);
174   }
175 
setupTracing()176   private static void setupTracing() throws Exception {
177     Trace.enableMethodTracing(
178         Test989.class,
179         Test989.class.getDeclaredMethod("baseNotifyMethodEntry", Object.class),
180         Test989.class.getDeclaredMethod(
181             "baseNotifyMethodExit", Object.class, Boolean.TYPE, Object.class),
182         Thread.currentThread());
183   }
setEntry(MethodTracer type)184   private static void setEntry(MethodTracer type) throws Exception {
185     if (DISABLE_TRACING || !System.getProperty("java.vm.name").equals("Dalvik")) {
186       Trace.disableTracing(Thread.currentThread());
187       setupTracing();
188     }
189     currentTracer = type;
190   }
191 
testDescription(MethodTracer type, Runnable test)192   private static String testDescription(MethodTracer type, Runnable test) {
193     return "test[" + type.getClass() + ", " + test.getClass() + "]";
194   }
195 
getExpectedError(MethodTracer t, MyRunnable r)196   private static Class<?> getExpectedError(MethodTracer t, MyRunnable r) {
197     if (t.exitException() != null) {
198       return t.exitException();
199     } else if (t.entryException() != null) {
200       return t.entryException();
201     } else {
202       return r.expectedThrow();
203     }
204   }
205 
doTest(MethodTracer type, MyRunnable test)206   private static void doTest(MethodTracer type, MyRunnable test) throws Exception {
207     Class<?> expected = getExpectedError(type, test);
208 
209     setEntry(type);
210     try {
211       test.run();
212       // Disabling method tracing just makes this test somewhat faster.
213       maybeDisableTracing();
214       if (expected == null) {
215         System.out.println(
216             "Received no exception as expected for " + testDescription(type, test) + ".");
217         return;
218       }
219     } catch (Error t) {
220       // Disabling method tracing just makes this test somewhat faster.
221       maybeDisableTracing();
222       if (expected == null) {
223         throw new Error("Unexpected error occured: " + t + " for " + testDescription(type, test), t);
224       } else if (!expected.isInstance(t)) {
225         throw new Error("Expected error of type " + expected + " not " + t +
226             " for " + testDescription(type, test), t);
227       } else {
228         System.out.println(
229             "Received expected error for " + testDescription(type, test) + " - " + t);
230         if (PRINT_STACK_TRACE) {
231           t.printStackTrace();
232         }
233         return;
234       }
235     }
236     System.out.println("Expected an error of type " + expected + " but got no exception for "
237         + testDescription(type, test));
238     // throw new Error("Expected an error of type " + expected + " but got no exception for "
239     //     + testDescription(type, test));
240   }
241 
242   public static interface MyRunnable extends Runnable {
expectedThrow()243     public default Class<?> expectedThrow() {
244       return null;
245     }
246   }
247 
run()248   public static void run() throws Exception {
249       MyRunnable[] testCases = new MyRunnable[] {
250               new doNothingClass(),
251               new doNothingNativeClass(),
252               new throwAClass(),
253               new throwANativeClass(),
254               new throwNativeExceptionClass(),
255               new returnValueClass(),
256               new returnValueNativeClass(),
257               new acceptValueClass(),
258               new acceptValueNativeClass(),
259               new tryCatchExitClass(),
260               new returnFloatClass(),
261               new returnFloatNativeClass(),
262               new returnDoubleClass(),
263               new returnDoubleNativeClass(),
264       };
265       MethodTracer[] tracers = new MethodTracer[] {
266               new NormalTracer(),
267               new ThrowEnterTracer(),
268               new ThrowExitTracer(),
269               new ThrowBothTracer(),
270               new ForceGCTracer(),
271       };
272 
273       setupTracing();
274       for (MethodTracer t : tracers) {
275           for (MyRunnable r : testCases) {
276               doTest(t, r);
277           }
278       }
279 
280       maybeDisableTracing();
281       System.out.println("Finished - without non-standard exits!");
282       Trace.disableTracing(Thread.currentThread());
283 
284       // Enabling frame pop events force a different path and deoptimize more often. So redo the
285       // tests by enabling frame pop events.
286       Trace.enableFramePopEvents();
287       setupTracing();
288       for (MethodTracer t : tracers) {
289           for (MyRunnable r : testCases) {
290               doTest(t, r);
291           }
292       }
293 
294     maybeDisableTracing();
295     System.out.println("Finished!");
296     Trace.disableTracing(Thread.currentThread());
297   }
298 
299   private static final class throwAClass implements MyRunnable {
run()300     public void run() {
301       throwA();
302     }
303     @Override
expectedThrow()304     public Class<?> expectedThrow() {
305       return ErrorA.class;
306     }
307   }
308 
309   private static final class throwANativeClass implements MyRunnable {
run()310     public void run() {
311       throwANative();
312     }
313     @Override
expectedThrow()314     public Class<?> expectedThrow() {
315       return ErrorA.class;
316     }
317   }
318 
319   private static final class throwNativeExceptionClass implements MyRunnable {
run()320     public void run() {
321         throwNativeException();
322     }
323 
324     @Override
expectedThrow()325     public Class<?> expectedThrow() {
326       return Error.class;
327     }
328   }
329 
330   private static final class tryCatchExitClass implements MyRunnable {
run()331     public void run() {
332       tryCatchExit();
333     }
334   }
335 
336   private static final class doNothingClass implements MyRunnable {
run()337     public void run() {
338       doNothing();
339     }
340   }
341 
342   private static final class doNothingNativeClass implements MyRunnable {
run()343     public void run() {
344       doNothingNative();
345     }
346   }
347 
348   private static final class acceptValueClass implements MyRunnable {
run()349     public void run() {
350       acceptValue(mkTestObject());
351     }
352   }
353 
354   private static final class acceptValueNativeClass implements MyRunnable {
run()355     public void run() {
356       acceptValueNative(mkTestObject());
357     }
358   }
359 
360   private static final class returnValueClass implements MyRunnable {
run()361     public void run() {
362       Object o = returnValue();
363       System.out.println("returnValue returned: " + o);
364     }
365   }
366 
367   private static final class returnValueNativeClass implements MyRunnable {
run()368     public void run() {
369       Object o = returnValueNative();
370       System.out.println("returnValueNative returned: " + o);
371     }
372   }
373 
374   private static final class returnFloatClass implements MyRunnable {
run()375     public void run() {
376       float d = returnFloat();
377       System.out.println("returnFloat returned: " + d);
378     }
379   }
380 
381   private static final class returnFloatNativeClass implements MyRunnable {
run()382     public void run() {
383       float d = returnFloatNative();
384       System.out.println("returnFloatNative returned: " + d);
385     }
386   }
387 
388   private static final class returnDoubleClass implements MyRunnable {
run()389     public void run() {
390       double d = returnDouble();
391       System.out.println("returnDouble returned: " + d);
392     }
393   }
394 
395   private static final class returnDoubleNativeClass implements MyRunnable {
run()396     public void run() {
397       double d = returnDoubleNative();
398       System.out.println("returnDoubleNative returned: " + d);
399     }
400   }
401 
402   private static class ErrorA extends Error {
403     private static final long serialVersionUID = 0;
ErrorA(String s)404     public ErrorA(String s) { super(s); }
405   }
406 
407   private static class ErrorB extends Error {
408     private static final long serialVersionUID = 1;
ErrorB(String s)409     public ErrorB(String s) { super(s); }
410   }
411 
412   private static class ErrorC extends Error {
413     private static final long serialVersionUID = 2;
ErrorC(String s)414     public ErrorC(String s) { super(s); }
415   }
416 
417   // Does nothing.
doNothing()418   public static void doNothing() { }
419 
tryCatchExit()420   public static void tryCatchExit() {
421     try {
422       Object o = mkTestObject();
423       return;
424     } catch (ErrorB b) {
425       System.out.println("ERROR: Caught " + b);
426       b.printStackTrace();
427     } catch (ErrorC c) {
428       System.out.println("ERROR: Caught " + c);
429       c.printStackTrace();
430     }
431   }
432 
returnFloat()433   public static float returnFloat() {
434     return doGetFloat();
435   }
436 
returnDouble()437   public static double returnDouble() {
438     return doGetDouble();
439   }
440 
441   // Throws an ErrorA.
throwA()442   public static void throwA() {
443     doThrowA();
444   }
445 
doThrowA()446   public static void doThrowA() {
447     throw new ErrorA("Throwing Error A");
448   }
449 
throwNativeException()450   public static void throwNativeException() {
451     doThrowNative();
452   }
453 
454   static final class TestObject {
455     private int idx;
TestObject(int v)456     public TestObject(int v) {
457       this.idx = v;
458     }
459     @Override
toString()460     public String toString() {
461       return "TestObject(" + idx + ")";
462     }
463   }
464 
465   static int counter = 0;
mkTestObject()466   public static Object mkTestObject() {
467     return new TestObject(counter++);
468   }
469 
printObject(Object o)470   public static void printObject(Object o) {
471     System.out.println("Recieved " + o);
472   }
473 
474   // Returns a newly allocated value.
returnValue()475   public static Object returnValue() {
476     return mkTestObject();
477   }
478 
acceptValue(Object o)479   public static void acceptValue(Object o) {
480     printObject(o);
481   }
482 
doGetFloat()483   public static float doGetFloat() {
484     return 1.618f;
485   }
486 
doGetDouble()487   public static double doGetDouble() {
488     return 3.14159628;
489   }
490 
491   // Calls mkTestObject from native code and returns it.
returnValueNative()492   public static native Object returnValueNative();
493   // Calls printObject from native code.
acceptValueNative(Object t)494   public static native void acceptValueNative(Object t);
doNothingNative()495   public static native void doNothingNative();
throwANative()496   public static native void throwANative();
returnFloatNative()497   public static native float returnFloatNative();
returnDoubleNative()498   public static native double returnDoubleNative();
doThrowNative()499   public static native void doThrowNative();
500 }
501