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