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. disableTraceForRI()57 private static void disableTraceForRI() { 58 if (!System.getProperty("java.vm.name").equals("Dalvik")) { 59 Trace.disableTracing(Thread.currentThread()); 60 } 61 } 62 getInfo(Object m, boolean exception, Object result)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 { methodEntry(Object m)74 public void methodEntry(Object m); methodExited(Object m, boolean exception, Object result)75 public void methodExited(Object m, boolean exception, Object result); entryException()76 public default Class<?> entryException() { return null; } exitException()77 public default Class<?> exitException() { return null; } 78 } 79 80 public static class NormalTracer implements MethodTracer { methodEntry(Object m)81 public void methodEntry(Object m) { 82 if (testMethods.contains(m)) { 83 System.out.println("Normal: Entering " + m); 84 } 85 } methodExited(Object m, boolean exception, Object result)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 { methodEntry(Object m)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 } methodExited(Object m, boolean exception, Object result)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 } entryException()105 public Class<?> entryException() { return ErrorB.class; } 106 } 107 108 public static class ThrowExitTracer implements MethodTracer { methodEntry(Object m)109 public void methodEntry(Object m) { 110 if (testMethods.contains(m)) { 111 System.out.println("ThrowExit: Entering " + m); 112 } 113 } methodExited(Object m, boolean exception, Object result)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 } exitException()123 public Class<?> exitException() { return ErrorB.class; } 124 } 125 126 public static class ThrowBothTracer implements MethodTracer { methodEntry(Object m)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 } methodExited(Object m, boolean exception, Object result)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 } entryException()142 public Class<?> entryException() { return ErrorB.class; } exitException()143 public Class<?> exitException() { return ErrorC.class; } 144 } 145 146 public static class ForceGCTracer implements MethodTracer { methodEntry(Object m)147 public void methodEntry(Object m) { 148 if (System.getProperty("java.vm.name").equals("Dalvik")) { 149 System.gc(); 150 } 151 } methodExited(Object m, boolean exception, Object result)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 maybeDisableTracing()159 private static void maybeDisableTracing() throws Exception { 160 if (DISABLE_TRACING) { 161 Trace.disableTracing(Thread.currentThread()); 162 } 163 } 164 baseNotifyMethodEntry(Object o)165 public static void baseNotifyMethodEntry(Object o) { 166 currentTracer.methodEntry(o); 167 } baseNotifyMethodExit(Object o, boolean exception, Object res)168 public static void baseNotifyMethodExit(Object o, boolean exception, Object res) { 169 currentTracer.methodExited(o, exception, res); 170 } 171 setupTracing()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 } setEntry(MethodTracer type)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 testDescription(MethodTracer type, Runnable test)188 private static String testDescription(MethodTracer type, Runnable test) { 189 return "test[" + type.getClass() + ", " + test.getClass() + "]"; 190 } 191 getExpectedError(MethodTracer t, MyRunnable r)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 doTest(MethodTracer type, MyRunnable test)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 { expectedThrow()239 public default Class<?> expectedThrow() { 240 return null; 241 } 242 } 243 run()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 { run()281 public void run() { 282 throwA(); 283 } 284 @Override expectedThrow()285 public Class<?> expectedThrow() { 286 return ErrorA.class; 287 } 288 } 289 290 private static final class throwANativeClass implements MyRunnable { run()291 public void run() { 292 throwANative(); 293 } 294 @Override expectedThrow()295 public Class<?> expectedThrow() { 296 return ErrorA.class; 297 } 298 } 299 300 private static final class tryCatchExitClass implements MyRunnable { run()301 public void run() { 302 tryCatchExit(); 303 } 304 } 305 306 private static final class doNothingClass implements MyRunnable { run()307 public void run() { 308 doNothing(); 309 } 310 } 311 312 private static final class doNothingNativeClass implements MyRunnable { run()313 public void run() { 314 doNothingNative(); 315 } 316 } 317 318 private static final class acceptValueClass implements MyRunnable { run()319 public void run() { 320 acceptValue(mkTestObject()); 321 } 322 } 323 324 private static final class acceptValueNativeClass implements MyRunnable { run()325 public void run() { 326 acceptValueNative(mkTestObject()); 327 } 328 } 329 330 private static final class returnValueClass implements MyRunnable { run()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 { run()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 { run()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 { run()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 { run()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 { run()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; ErrorA(String s)374 public ErrorA(String s) { super(s); } 375 } 376 377 private static class ErrorB extends Error { 378 private static final long serialVersionUID = 1; ErrorB(String s)379 public ErrorB(String s) { super(s); } 380 } 381 382 private static class ErrorC extends Error { 383 private static final long serialVersionUID = 2; ErrorC(String s)384 public ErrorC(String s) { super(s); } 385 } 386 387 // Does nothing. doNothing()388 public static void doNothing() { } 389 tryCatchExit()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 returnFloat()403 public static float returnFloat() { 404 return doGetFloat(); 405 } 406 returnDouble()407 public static double returnDouble() { 408 return doGetDouble(); 409 } 410 411 // Throws an ErrorA. throwA()412 public static void throwA() { 413 doThrowA(); 414 } 415 doThrowA()416 public static void doThrowA() { 417 throw new ErrorA("Throwing Error A"); 418 } 419 420 static final class TestObject { 421 private int idx; TestObject(int v)422 public TestObject(int v) { 423 this.idx = v; 424 } 425 @Override toString()426 public String toString() { 427 return "TestObject(" + idx + ")"; 428 } 429 } 430 431 static int counter = 0; mkTestObject()432 public static Object mkTestObject() { 433 return new TestObject(counter++); 434 } 435 printObject(Object o)436 public static void printObject(Object o) { 437 System.out.println("Recieved " + o); 438 } 439 440 // Returns a newly allocated value. returnValue()441 public static Object returnValue() { 442 return mkTestObject(); 443 } 444 acceptValue(Object o)445 public static void acceptValue(Object o) { 446 printObject(o); 447 } 448 doGetFloat()449 public static float doGetFloat() { 450 return 1.618f; 451 } 452 doGetDouble()453 public static double doGetDouble() { 454 return 3.14159628; 455 } 456 457 // Calls mkTestObject from native code and returns it. returnValueNative()458 public static native Object returnValueNative(); 459 // Calls printObject from native code. acceptValueNative(Object t)460 public static native void acceptValueNative(Object t); doNothingNative()461 public static native void doNothingNative(); throwANative()462 public static native void throwANative(); returnFloatNative()463 public static native float returnFloatNative(); returnDoubleNative()464 public static native double returnDoubleNative(); 465 } 466