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