1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 
15 package art;
16 
17 import static art.SuspendEvents.EVENT_TYPE_CLASS_LOAD;
18 import static art.SuspendEvents.setupFieldSuspendFor;
19 import static art.SuspendEvents.setupSuspendBreakpointFor;
20 import static art.SuspendEvents.setupSuspendClassEvent;
21 import static art.SuspendEvents.setupSuspendExceptionEvent;
22 import static art.SuspendEvents.setupSuspendMethodEvent;
23 import static art.SuspendEvents.setupSuspendPopFrameEvent;
24 import static art.SuspendEvents.setupSuspendSingleStepAt;
25 import static art.SuspendEvents.setupTest;
26 import static art.SuspendEvents.waitForSuspendHit;
27 
28 import java.io.*;
29 import java.lang.reflect.Executable;
30 import java.lang.reflect.Field;
31 import java.lang.reflect.Method;
32 import java.util.concurrent.CountDownLatch;
33 import java.util.function.Consumer;
34 import java.util.function.Supplier;
35 
36 public class Test1969 {
37   public static final boolean PRINT_STACK_TRACE = false;
38 
39   public final boolean canRunClassLoadTests;
40 
doNothing()41   public static void doNothing() {}
42 
43   public static interface TestSuspender {
setupForceReturnRun(Thread thr)44     public void setupForceReturnRun(Thread thr);
45 
waitForSuspend(Thread thr)46     public void waitForSuspend(Thread thr);
47 
cleanup(Thread thr)48     public void cleanup(Thread thr);
49 
performForceReturn(Thread thr)50     public default void performForceReturn(Thread thr) {
51       System.out.println("Will force return of " + thr.getName());
52       NonStandardExit.forceEarlyReturnVoid(thr);
53     }
54 
setupNormalRun(Thread thr)55     public default void setupNormalRun(Thread thr) {}
56   }
57 
58   public static interface ThreadRunnable {
run(Thread thr)59     public void run(Thread thr);
60   }
61 
makeSuspend(final ThreadRunnable setup, final ThreadRunnable clean)62   public static TestSuspender makeSuspend(final ThreadRunnable setup, final ThreadRunnable clean) {
63     return new TestSuspender() {
64       public void setupForceReturnRun(Thread thr) {
65         setup.run(thr);
66       }
67 
68       public void waitForSuspend(Thread thr) {
69         waitForSuspendHit(thr);
70       }
71 
72       public void cleanup(Thread thr) {
73         clean.run(thr);
74       }
75     };
76   }
77 
78   public void runTestOn(Supplier<Runnable> testObj, ThreadRunnable su, ThreadRunnable cl)
79       throws Exception {
80     runTestOn(testObj, makeSuspend(su, cl));
81   }
82 
83   private static void SafePrintStackTrace(StackTraceElement st[]) {
84     System.out.println(safeDumpStackTrace(st, "\t"));
85   }
86 
87   private static String safeDumpStackTrace(StackTraceElement st[], String prefix) {
88     ByteArrayOutputStream baos = new ByteArrayOutputStream();
89     PrintStream os = new PrintStream(baos);
90     for (StackTraceElement e : st) {
91       os.println(
92           prefix
93               + e.getClassName()
94               + "."
95               + e.getMethodName()
96               + "("
97               + (e.isNativeMethod() ? "Native Method" : e.getFileName())
98               + ")");
99       if (e.getClassName().equals("art.Test1969") && e.getMethodName().equals("runTests")) {
100         os.println(prefix + "<Additional frames hidden>");
101         break;
102       }
103     }
104     os.flush();
105     return baos.toString();
106   }
107 
108   static long ID_COUNTER = 0;
109 
110   public Runnable Id(final Runnable tr) {
111     final long my_id = ID_COUNTER++;
112     return new Runnable() {
113       public void run() {
114         tr.run();
115       }
116 
117       public String toString() {
118         return "(ID: " + my_id + ") " + tr.toString();
119       }
120     };
121   }
122 
123   public static long THREAD_COUNT = 0;
124 
125   public Thread mkThread(Runnable r) {
126     Thread t = new Thread(r, "Test1969 target thread - " + THREAD_COUNT++);
127     t.setUncaughtExceptionHandler(
128         (thr, e) -> {
129           System.out.println(
130               "Uncaught exception in thread "
131                   + thr
132                   + " - "
133                   + e.getClass().getName()
134                   + ": "
135                   + e.getLocalizedMessage());
136           SafePrintStackTrace(e.getStackTrace());
137         });
138     return t;
139   }
140 
141   final class TestConfig {
142     public final Runnable testObj;
143     public final TestSuspender suspender;
144 
145     public TestConfig(Runnable obj, TestSuspender su) {
146       this.testObj = obj;
147       this.suspender = su;
148     }
149   }
150 
151   public void runTestOn(Supplier<Runnable> testObjGen, TestSuspender su) throws Exception {
152     runTestOn(() -> new TestConfig(testObjGen.get(), su));
153   }
154 
155   public void runTestOn(Supplier<TestConfig> config) throws Exception {
156     TestConfig normal_config = config.get();
157     Runnable normal_run = Id(normal_config.testObj);
158     try {
159       System.out.println("NORMAL RUN: Single call with no interference on " + normal_run);
160       Thread normal_thread = mkThread(normal_run);
161       normal_config.suspender.setupNormalRun(normal_thread);
162       normal_thread.start();
163       normal_thread.join();
164       System.out.println("NORMAL RUN: result for " + normal_run + " on " + normal_thread.getName());
165     } catch (Exception e) {
166       System.out.println("NORMAL RUN: Ended with exception for " + normal_run + "!");
167       e.printStackTrace(System.out);
168     }
169 
170     TestConfig force_return_config = config.get();
171     Runnable testObj = Id(force_return_config.testObj);
172     TestSuspender su = force_return_config.suspender;
173     System.out.println("Single call with force-early-return on " + testObj);
174     final CountDownLatch continue_latch = new CountDownLatch(1);
175     final CountDownLatch startup_latch = new CountDownLatch(1);
176     Runnable await =
177         () -> {
178           try {
179             startup_latch.countDown();
180             continue_latch.await();
181           } catch (Exception e) {
182             throw new Error("Failed to await latch", e);
183           }
184         };
185     Thread thr =
186         mkThread(
187             () -> {
188               await.run();
189               testObj.run();
190             });
191     thr.start();
192 
193     // Wait until the other thread is started.
194     startup_latch.await();
195 
196     // Setup suspension method on the thread.
197     su.setupForceReturnRun(thr);
198 
199     // Let the other thread go.
200     continue_latch.countDown();
201 
202     // Wait for the other thread to hit the breakpoint/watchpoint/whatever and
203     // suspend itself
204     // (without re-entering java)
205     su.waitForSuspend(thr);
206 
207     // Cleanup the breakpoint/watchpoint/etc.
208     su.cleanup(thr);
209 
210     try {
211       // Pop the frame.
212       su.performForceReturn(thr);
213     } catch (Exception e) {
214       System.out.println("Failed to force-return due to " + e);
215       SafePrintStackTrace(e.getStackTrace());
216     }
217 
218     // Start the other thread going again.
219     Suspension.resume(thr);
220 
221     // Wait for the other thread to finish.
222     thr.join();
223 
224     // See how many times calledFunction was called.
225     System.out.println("result for " + testObj + " on " + thr.getName());
226   }
227 
228   public abstract static class AbstractTestObject implements Runnable {
229     public AbstractTestObject() {}
230 
231     public void run() {
232       // This function should be force-early-returned.
233       calledFunction();
234     }
235 
236     public abstract void calledFunction();
237   }
238 
239   public static class FieldBasedTestObject extends AbstractTestObject implements Runnable {
240     public int TARGET_FIELD;
241     public int cnt = 0;
242 
243     public FieldBasedTestObject() {
244       super();
245       TARGET_FIELD = 0;
246     }
247 
248     public void calledFunction() {
249       cnt++;
250       // We put a watchpoint here and force-early-return when we are at it.
251       TARGET_FIELD += 10;
252       cnt++;
253     }
254 
255     public String toString() {
256       return "FieldBasedTestObject { TARGET_FIELD: " + TARGET_FIELD + ", cnt: " + cnt + " }";
257     }
258   }
259 
260   public static class StandardTestObject extends AbstractTestObject implements Runnable {
261     public int cnt;
262 
263     public StandardTestObject() {
264       super();
265       cnt = 0;
266     }
267 
268     public void calledFunction() {
269       cnt++; // line +0
270       // We put a breakpoint here and force-early-return when we are at it.
271       doNothing(); // line +2
272       cnt++; // line +3
273       return; // line +4
274     }
275 
276     public String toString() {
277       return "StandardTestObject { cnt: " + cnt + " }";
278     }
279   }
280 
281   public static class SynchronizedFunctionTestObject extends AbstractTestObject
282       implements Runnable {
283     public int cnt;
284 
285     public SynchronizedFunctionTestObject() {
286       super();
287       cnt = 0;
288     }
289 
290     public synchronized void calledFunction() {
291       cnt++; // line +0
292       // We put a breakpoint here and PopFrame when we are at it.
293       doNothing(); // line +2
294       cnt++; // line +3
295       return;
296     }
297 
298     public String toString() {
299       return "SynchronizedFunctionTestObject { cnt: " + cnt + " }";
300     }
301   }
302 
303   public static class SynchronizedTestObject extends AbstractTestObject implements Runnable {
304     public final Object lock;
305     public int cnt;
306 
307     public SynchronizedTestObject() {
308       super();
309       lock = new Object();
310       cnt = 0;
311     }
312 
313     public void calledFunction() {
314       synchronized (lock) { // line +0
315         cnt++; // line +1
316         // We put a breakpoint here and PopFrame when we are at it.
317         doNothing(); // line +3
318         cnt++; // line +4
319         return; // line +5
320       }
321     }
322 
323     public String toString() {
324       return "SynchronizedTestObject { cnt: " + cnt + " }";
325     }
326   }
327 
328   public static class ExceptionCatchTestObject extends AbstractTestObject implements Runnable {
329     public static class TestError extends Error {}
330 
331     public int cnt;
332 
333     public ExceptionCatchTestObject() {
334       super();
335       cnt = 0;
336     }
337 
338     public void calledFunction() {
339       cnt++;
340       try {
341         doThrow();
342         cnt += 100;
343       } catch (TestError e) {
344         System.out.println(e.getClass().getName() + " caught in called function.");
345         cnt++;
346       }
347       return;
348     }
349 
350     public Object doThrow() {
351       throw new TestError();
352     }
353 
354     public String toString() {
355       return "ExceptionCatchTestObject { cnt: " + cnt + " }";
356     }
357   }
358 
359   public static class ExceptionThrowFarTestObject implements Runnable {
360     public static class TestError extends Error {}
361 
362     public int cnt;
363     public int baseCallCnt;
364     public final boolean catchInCalled;
365 
366     public ExceptionThrowFarTestObject(boolean catchInCalled) {
367       super();
368       cnt = 0;
369       baseCallCnt = 0;
370       this.catchInCalled = catchInCalled;
371     }
372 
373     public void run() {
374       baseCallCnt++;
375       try {
376         callingFunction();
377       } catch (TestError e) {
378         System.out.println(e.getClass().getName() + " thrown and caught!");
379       }
380       baseCallCnt++;
381     }
382 
383     public void callingFunction() {
384       calledFunction();
385     }
386 
387     public void calledFunction() {
388       cnt++;
389       if (catchInCalled) {
390         try {
391           cnt += 100;
392           throw new TestError(); // We put a watch here.
393         } catch (TestError e) {
394           System.out.println(e.getClass().getName() + " caught in same function.");
395           doNothing();
396           cnt += 10;
397           return;
398         }
399       } else {
400         cnt++;
401         throw new TestError(); // We put a watch here.
402       }
403     }
404 
405     public String toString() {
406       return "ExceptionThrowFarTestObject { cnt: " + cnt + ", baseCnt: " + baseCallCnt + " }";
407     }
408   }
409 
410   public static class ExceptionOnceObject extends AbstractTestObject {
411     public static final class TestError extends Error {}
412 
413     public int cnt;
414     public final boolean throwInSub;
415 
416     public ExceptionOnceObject(boolean throwInSub) {
417       super();
418       cnt = 0;
419       this.throwInSub = throwInSub;
420     }
421 
422     public void calledFunction() {
423       cnt++;
424       if (cnt == 1) {
425         if (throwInSub) {
426           doThrow();
427         } else {
428           throw new TestError();
429         }
430       }
431       return;
432     }
433 
434     public void doThrow() {
435       throw new TestError();
436     }
437 
438     public String toString() {
439       return "ExceptionOnceObject { cnt: " + cnt + ", throwInSub: " + throwInSub + " }";
440     }
441   }
442 
443   public static class ExceptionThrowTestObject implements Runnable {
444     public static class TestError extends Error {}
445 
446     public int cnt;
447     public int baseCallCnt;
448     public final boolean catchInCalled;
449 
450     public ExceptionThrowTestObject(boolean catchInCalled) {
451       super();
452       cnt = 0;
453       baseCallCnt = 0;
454       this.catchInCalled = catchInCalled;
455     }
456 
457     public void run() {
458       baseCallCnt++;
459       try {
460         calledFunction();
461       } catch (TestError e) {
462         System.out.println(e.getClass().getName() + " thrown and caught!");
463       }
464       baseCallCnt++;
465     }
466 
467     public void calledFunction() {
468       cnt++;
469       if (catchInCalled) {
470         try {
471           cnt += 10;
472           throw new TestError(); // We put a watch here.
473         } catch (TestError e) {
474           System.out.println(e.getClass().getName() + " caught in same function.");
475           doNothing();
476           cnt += 100;
477           return;
478         }
479       } else {
480         cnt += 1;
481         throw new TestError(); // We put a watch here.
482       }
483     }
484 
485     public String toString() {
486       return "ExceptionThrowTestObject { cnt: " + cnt + ", baseCnt: " + baseCallCnt + " }";
487     }
488   }
489 
490   public static class NativeCalledObject extends AbstractTestObject {
491     public int cnt = 0;
492 
493     public native void calledFunction();
494 
495     public String toString() {
496       return "NativeCalledObject { cnt: " + cnt + " }";
497     }
498   }
499 
500   public static class NativeCallerObject implements Runnable {
501     public Object returnValue = null;
502     public int cnt = 0;
503 
504     public Object getReturnValue() {
505       return returnValue;
506     }
507 
508     public native void run();
509 
510     public void calledFunction() {
511       cnt++;
512       // We will stop using a MethodExit event.
513       doNothing();
514       cnt++;
515       return;
516     }
517 
518     public String toString() {
519       return "NativeCallerObject { cnt: " + cnt + " }";
520     }
521   }
522 
523   public static class ClassLoadObject implements Runnable {
524     public int cnt;
525 
526     public static final String[] CLASS_NAMES =
527         new String[] {
528           "Lart/Test1969$ClassLoadObject$TC0;",
529           "Lart/Test1969$ClassLoadObject$TC1;",
530           "Lart/Test1969$ClassLoadObject$TC2;",
531           "Lart/Test1969$ClassLoadObject$TC3;",
532           "Lart/Test1969$ClassLoadObject$TC4;",
533           "Lart/Test1969$ClassLoadObject$TC5;",
534           "Lart/Test1969$ClassLoadObject$TC6;",
535           "Lart/Test1969$ClassLoadObject$TC7;",
536           "Lart/Test1969$ClassLoadObject$TC8;",
537           "Lart/Test1969$ClassLoadObject$TC9;",
538         };
539 
540     private static int curClass = 0;
541 
542     private static class TC0 { public static int foo; static { foo = 100 + curClass; } }
543 
544     private static class TC1 { public static int foo; static { foo = 200 + curClass; } }
545 
546     private static class TC2 { public static int foo; static { foo = 300 + curClass; } }
547 
548     private static class TC3 { public static int foo; static { foo = 400 + curClass; } }
549 
550     private static class TC4 { public static int foo; static { foo = 500 + curClass; } }
551 
552     private static class TC5 { public static int foo; static { foo = 600 + curClass; } }
553 
554     private static class TC6 { public static int foo; static { foo = 700 + curClass; } }
555 
556     private static class TC7 { public static int foo; static { foo = 800 + curClass; } }
557 
558     private static class TC8 { public static int foo; static { foo = 900 + curClass; } }
559 
560     private static class TC9 { public static int foo; static { foo = 1000 + curClass; } }
561 
562     public ClassLoadObject() {
563       super();
564       cnt = 0;
565     }
566 
567     public void run() {
568       if (curClass == 0) {
569         calledFunction0();
570       } else if (curClass == 1) {
571         calledFunction1();
572       } else if (curClass == 2) {
573         calledFunction2();
574       } else if (curClass == 3) {
575         calledFunction3();
576       } else if (curClass == 4) {
577         calledFunction4();
578       } else if (curClass == 5) {
579         calledFunction5();
580       } else if (curClass == 6) {
581         calledFunction6();
582       } else if (curClass == 7) {
583         calledFunction7();
584       } else if (curClass == 8) {
585         calledFunction8();
586       } else if (curClass == 9) {
587         calledFunction9();
588       }
589       curClass++;
590     }
591 
592     public void calledFunction0() {
593       cnt++;
594       System.out.println("TC0.foo == " + TC0.foo);
595     }
596 
597     public void calledFunction1() {
598       cnt++;
599       System.out.println("TC1.foo == " + TC1.foo);
600     }
601 
602     public void calledFunction2() {
603       cnt++;
604       System.out.println("TC2.foo == " + TC2.foo);
605     }
606 
607     public void calledFunction3() {
608       cnt++;
609       System.out.println("TC3.foo == " + TC3.foo);
610     }
611 
612     public void calledFunction4() {
613       cnt++;
614       System.out.println("TC4.foo == " + TC4.foo);
615     }
616 
617     public void calledFunction5() {
618       cnt++;
619       System.out.println("TC5.foo == " + TC5.foo);
620     }
621 
622     public void calledFunction6() {
623       cnt++;
624       System.out.println("TC6.foo == " + TC6.foo);
625     }
626 
627     public void calledFunction7() {
628       cnt++;
629       System.out.println("TC7.foo == " + TC7.foo);
630     }
631 
632     public void calledFunction8() {
633       cnt++;
634       System.out.println("TC8.foo == " + TC8.foo);
635     }
636 
637     public void calledFunction9() {
638       cnt++;
639       System.out.println("TC9.foo == " + TC9.foo);
640     }
641 
642     public String toString() {
643       return "ClassLoadObject { cnt: " + cnt + ", curClass: " + curClass + "}";
644     }
645   }
646 
647   public static class ObjectInitTestObject implements Runnable {
648     // TODO How do we do this for <clinit>
649     public int cnt = 0;
650     public static final class ObjectInitTarget {
651       public ObjectInitTarget(Runnable r) {
652         super();  // line +0
653         r.run(); // line +1
654         // We set a breakpoint here and force-early-return
655         doNothing();  // line +3
656         r.run();  // line +4
657       }
658     }
659 
660     public void run() {
661       new ObjectInitTarget(() -> cnt++);
662     }
663 
664     public String toString() {
665       return "ObjectInitTestObject { cnt: " + cnt + " }";
666     }
667   }
668 
669   public static class SuspendSuddenlyObject extends AbstractTestObject {
670     public volatile boolean should_spin = true;
671     public volatile boolean is_spinning = false;
672     public int cnt = 0;
673 
674     public void calledFunction() {
675       cnt++;
676       do {
677         is_spinning = true;
678       } while (should_spin);
679       cnt++;
680       return;
681     }
682 
683     public String toString() {
684       return "SuspendSuddenlyObject { cnt: " + cnt + ", spun: " + is_spinning + " }";
685     }
686   }
687 
688   public static final class StaticMethodObject implements Runnable {
689     public int cnt = 0;
690 
691     public static void calledFunction(Runnable incr) {
692       incr.run();   // line +0
693       // We put a breakpoint here to force the return.
694       doNothing();  // line +2
695       incr.run();   // line +3
696       return;       // line +4
697     }
698 
699     public void run() {
700       calledFunction(() -> cnt++);
701     }
702 
703     public final String toString() {
704       return "StaticMethodObject { cnt: " + cnt + " }";
705     }
706   }
707 
708   // Only used by CTS to run without class-load tests.
709   public static void run() throws Exception {
710     new Test1969(false).runTests();
711   }
712   public static void run(boolean canRunClassLoadTests) throws Exception {
713     new Test1969(canRunClassLoadTests).runTests();
714   }
715 
716   public Test1969(boolean canRunClassLoadTests) {
717     this.canRunClassLoadTests = canRunClassLoadTests;
718   }
719 
720   public static void no_runTestOn(Supplier<Object> a, ThreadRunnable b, ThreadRunnable c) {}
721 
722   public void runTests() throws Exception {
723     setupTest();
724 
725     final Method calledFunction = StandardTestObject.class.getDeclaredMethod("calledFunction");
726     // Add a breakpoint on the second line after the start of the function
727     final int line = Breakpoint.locationToLine(calledFunction, 0) + 2;
728     final long loc = Breakpoint.lineToLocation(calledFunction, line);
729     System.out.println("Test stopped using breakpoint");
730     runTestOn(
731         StandardTestObject::new,
732         (thr) -> setupSuspendBreakpointFor(calledFunction, loc, thr),
733         SuspendEvents::clearSuspendBreakpointFor);
734 
735     final Method syncFunctionCalledFunction =
736         SynchronizedFunctionTestObject.class.getDeclaredMethod("calledFunction");
737     // Add a breakpoint on the second line after the start of the function
738     // Annoyingly r8 generally
739     // has the first instruction (a monitor enter) not be marked as being on any
740     // line but javac has
741     // it marked as being on the first line of the function. Just use the second
742     // entry on the
743     // line-number table to get the breakpoint. This should be good for both.
744     final long syncFunctionLoc =
745         Breakpoint.getLineNumberTable(syncFunctionCalledFunction)[1].location;
746     System.out.println("Test stopped using breakpoint with declared synchronized function");
747     runTestOn(
748         SynchronizedFunctionTestObject::new,
749         (thr) -> setupSuspendBreakpointFor(syncFunctionCalledFunction, syncFunctionLoc, thr),
750         SuspendEvents::clearSuspendBreakpointFor);
751 
752     final Method syncCalledFunction =
753         SynchronizedTestObject.class.getDeclaredMethod("calledFunction");
754     // Add a breakpoint on the second line after the start of the function
755     final int syncLine = Breakpoint.locationToLine(syncCalledFunction, 0) + 3;
756     final long syncLoc = Breakpoint.lineToLocation(syncCalledFunction, syncLine);
757     System.out.println("Test stopped using breakpoint with synchronized block");
758     runTestOn(
759         SynchronizedTestObject::new,
760         (thr) -> setupSuspendBreakpointFor(syncCalledFunction, syncLoc, thr),
761         SuspendEvents::clearSuspendBreakpointFor);
762 
763     System.out.println("Test stopped on single step");
764     runTestOn(
765         StandardTestObject::new,
766         (thr) -> setupSuspendSingleStepAt(calledFunction, loc, thr),
767         SuspendEvents::clearSuspendSingleStepFor);
768 
769     final Field target_field = FieldBasedTestObject.class.getDeclaredField("TARGET_FIELD");
770     System.out.println("Test stopped on field access");
771     runTestOn(
772         FieldBasedTestObject::new,
773         (thr) -> setupFieldSuspendFor(FieldBasedTestObject.class, target_field, true, thr),
774         SuspendEvents::clearFieldSuspendFor);
775 
776     System.out.println("Test stopped on field modification");
777     runTestOn(
778         FieldBasedTestObject::new,
779         (thr) -> setupFieldSuspendFor(FieldBasedTestObject.class, target_field, false, thr),
780         SuspendEvents::clearFieldSuspendFor);
781 
782     System.out.println("Test stopped during Method Exit of calledFunction");
783     runTestOn(
784         StandardTestObject::new,
785         (thr) -> setupSuspendMethodEvent(calledFunction, /* enter */ false, thr),
786         SuspendEvents::clearSuspendMethodEvent);
787 
788     System.out.println("Test stopped during Method Enter of calledFunction");
789     runTestOn(
790         StandardTestObject::new,
791         (thr) -> setupSuspendMethodEvent(calledFunction, /* enter */ true, thr),
792         SuspendEvents::clearSuspendMethodEvent);
793 
794     final Method exceptionOnceCalledMethod =
795         ExceptionOnceObject.class.getDeclaredMethod("calledFunction");
796     System.out.println("Test stopped during Method Exit due to exception thrown in same function");
797     runTestOn(
798         () -> new ExceptionOnceObject(/* throwInSub */ false),
799         (thr) -> setupSuspendMethodEvent(exceptionOnceCalledMethod, /* enter */ false, thr),
800         SuspendEvents::clearSuspendMethodEvent);
801 
802     System.out.println("Test stopped during Method Exit due to exception thrown in subroutine");
803     runTestOn(
804         () -> new ExceptionOnceObject(/* throwInSub */ true),
805         (thr) -> setupSuspendMethodEvent(exceptionOnceCalledMethod, /* enter */ false, thr),
806         SuspendEvents::clearSuspendMethodEvent);
807 
808     final Method exceptionThrowCalledMethod =
809         ExceptionThrowTestObject.class.getDeclaredMethod("calledFunction");
810     System.out.println(
811         "Test stopped during notifyFramePop with exception on pop of calledFunction");
812     runTestOn(
813         () -> new ExceptionThrowTestObject(false),
814         (thr) -> setupSuspendPopFrameEvent(0, exceptionThrowCalledMethod, thr),
815         SuspendEvents::clearSuspendPopFrameEvent);
816 
817     final Method exceptionCatchThrowMethod =
818         ExceptionCatchTestObject.class.getDeclaredMethod("doThrow");
819     System.out.println("Test stopped during notifyFramePop with exception on pop of doThrow");
820     runTestOn(
821         ExceptionCatchTestObject::new,
822         (thr) -> setupSuspendPopFrameEvent(0, exceptionCatchThrowMethod, thr),
823         SuspendEvents::clearSuspendPopFrameEvent);
824 
825     System.out.println(
826         "Test stopped during ExceptionCatch event of calledFunction "
827             + "(catch in called function, throw in called function)");
828     runTestOn(
829         () -> new ExceptionThrowTestObject(true),
830         (thr) -> setupSuspendExceptionEvent(exceptionThrowCalledMethod, /* catch */ true, thr),
831         SuspendEvents::clearSuspendExceptionEvent);
832 
833     final Method exceptionCatchCalledMethod =
834         ExceptionCatchTestObject.class.getDeclaredMethod("calledFunction");
835     System.out.println(
836         "Test stopped during ExceptionCatch event of calledFunction "
837             + "(catch in called function, throw in subroutine)");
838     runTestOn(
839         ExceptionCatchTestObject::new,
840         (thr) -> setupSuspendExceptionEvent(exceptionCatchCalledMethod, /* catch */ true, thr),
841         SuspendEvents::clearSuspendExceptionEvent);
842 
843     System.out.println(
844         "Test stopped during Exception event of calledFunction " + "(catch in calling function)");
845     runTestOn(
846         () -> new ExceptionThrowTestObject(false),
847         (thr) -> setupSuspendExceptionEvent(exceptionThrowCalledMethod, /* catch */ false, thr),
848         SuspendEvents::clearSuspendExceptionEvent);
849 
850     System.out.println(
851         "Test stopped during Exception event of calledFunction (catch in called function)");
852     runTestOn(
853         () -> new ExceptionThrowTestObject(true),
854         (thr) -> setupSuspendExceptionEvent(exceptionThrowCalledMethod, /* catch */ false, thr),
855         SuspendEvents::clearSuspendExceptionEvent);
856 
857     final Method exceptionThrowFarCalledMethod =
858         ExceptionThrowFarTestObject.class.getDeclaredMethod("calledFunction");
859     System.out.println(
860         "Test stopped during Exception event of calledFunction "
861             + "(catch in parent of calling function)");
862     runTestOn(
863         () -> new ExceptionThrowFarTestObject(false),
864         (thr) -> setupSuspendExceptionEvent(exceptionThrowFarCalledMethod, /* catch */ false, thr),
865         SuspendEvents::clearSuspendExceptionEvent);
866 
867     System.out.println(
868         "Test stopped during Exception event of calledFunction " + "(catch in called function)");
869     runTestOn(
870         () -> new ExceptionThrowFarTestObject(true),
871         (thr) -> setupSuspendExceptionEvent(exceptionThrowFarCalledMethod, /* catch */ false, thr),
872         SuspendEvents::clearSuspendExceptionEvent);
873 
874     System.out.println("Test stopped during random Suspend.");
875     runTestOn(
876         () -> {
877           final SuspendSuddenlyObject sso = new SuspendSuddenlyObject();
878           return new TestConfig(
879               sso,
880               new TestSuspender() {
881                 public void setupForceReturnRun(Thread thr) {}
882 
883                 public void setupNormalRun(Thread thr) {
884                   sso.should_spin = false;
885                 }
886 
887                 public void waitForSuspend(Thread thr) {
888                   while (!sso.is_spinning) {}
889                   Suspension.suspend(thr);
890                 }
891 
892                 public void cleanup(Thread thr) {}
893               });
894         });
895 
896     System.out.println("Test stopped during a native method fails");
897     runTestOn(
898         NativeCalledObject::new,
899         SuspendEvents::setupWaitForNativeCall,
900         SuspendEvents::clearWaitForNativeCall);
901 
902     System.out.println("Test stopped in a method called by native succeeds");
903     final Method nativeCallerMethod = NativeCallerObject.class.getDeclaredMethod("calledFunction");
904     runTestOn(
905         NativeCallerObject::new,
906         (thr) -> setupSuspendMethodEvent(nativeCallerMethod, /* enter */ false, thr),
907         SuspendEvents::clearSuspendMethodEvent);
908 
909 
910     System.out.println("Test stopped in a static method");
911     final Method staticCalledMethod = StaticMethodObject.class.getDeclaredMethod("calledFunction", Runnable.class);
912     final int staticFunctionLine= Breakpoint.locationToLine(staticCalledMethod, 0) + 2;
913     final long staticFunctionLoc = Breakpoint.lineToLocation(staticCalledMethod, staticFunctionLine);
914     runTestOn(
915         StaticMethodObject::new,
916         (thr) -> setupSuspendBreakpointFor(staticCalledMethod, staticFunctionLoc, thr),
917         SuspendEvents::clearSuspendMethodEvent);
918 
919     System.out.println("Test stopped in a Object <init> method");
920     final Executable initCalledMethod = ObjectInitTestObject.ObjectInitTarget.class.getConstructor(Runnable.class);
921     final int initFunctionLine= Breakpoint.locationToLine(initCalledMethod, 0) + 3;
922     final long initFunctionLoc = Breakpoint.lineToLocation(initCalledMethod, initFunctionLine);
923     runTestOn(
924         ObjectInitTestObject::new,
925         (thr) -> setupSuspendBreakpointFor(initCalledMethod, initFunctionLoc, thr),
926         SuspendEvents::clearSuspendMethodEvent);
927 
928     if (canRunClassLoadTests && CanRunClassLoadingTests()) {
929       System.out.println("Test stopped during class-load.");
930       runTestOn(
931           ClassLoadObject::new,
932           (thr) -> setupSuspendClassEvent(EVENT_TYPE_CLASS_LOAD, ClassLoadObject.CLASS_NAMES, thr),
933           SuspendEvents::clearSuspendClassEvent);
934       System.out.println("Test stopped during class-load.");
935       runTestOn(
936           ClassLoadObject::new,
937           (thr) -> setupSuspendClassEvent(EVENT_TYPE_CLASS_LOAD, ClassLoadObject.CLASS_NAMES, thr),
938           SuspendEvents::clearSuspendClassEvent);
939     }
940   }
941 
942 
943   // Volatile is to prevent any future optimizations that could invalidate this test by doing
944   // constant propagation and eliminating the failing paths before the verifier is able to load the
945   // class.
946   static volatile boolean ranClassLoadTest = false;
947   static boolean classesPreverified = false;
948   private static final class RCLT0 { public void foo() {} }
949   private static final class RCLT1 { public void foo() {} }
950   // If classes are not preverified for some reason (interp-ac, no-image, etc) the verifier will
951   // actually load classes as it runs. This means that we cannot use the class-load tests as they
952   // are written. TODO Support this.
953   public boolean CanRunClassLoadingTests() {
954     if (ranClassLoadTest) {
955       return classesPreverified;
956     }
957     if (!ranClassLoadTest) {
958       // Only this will ever be executed.
959       new RCLT0().foo();
960     } else {
961       // This will never be executed. If classes are not preverified the verifier will load RCLT1
962       // when the enclosing method is run. This behavior makes the class-load/prepare test cases
963       // impossible to successfully run (they will deadlock).
964       new RCLT1().foo();
965       System.out.println("FAILURE: UNREACHABLE Location!");
966     }
967     classesPreverified = !isClassLoaded("Lart/Test1969$RCLT1;");
968     ranClassLoadTest = true;
969     return classesPreverified;
970   }
971 
972   public static native boolean isClassLoaded(String name);
973 }
974