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