1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package art;
18 
19 import java.io.PrintWriter;
20 import java.io.StringWriter;
21 import java.util.concurrent.Semaphore;
22 import java.util.Arrays;
23 import java.lang.reflect.Executable;
24 import java.lang.reflect.Method;
25 import java.util.List;
26 import java.util.Set;
27 import java.util.ArrayList;
28 import java.util.HashSet;
29 import java.util.function.IntUnaryOperator;
30 import java.util.function.Function;
31 
32 public class Test1926 {
handleFramePop(Executable m, boolean exception, long location)33   public static void handleFramePop(Executable m, boolean exception, long location) {
34     System.out.println(
35         m + " pop. Line=" + Breakpoint.locationToLine(m, location) + " exception:" + exception);
36   }
37 
recurTimesA(int times, Runnable safepoint)38   public static void recurTimesA(int times, Runnable safepoint) {
39     if (times == 0) {
40       safepoint.run();
41       return;
42     }
43     recurTimesB(times - 1, safepoint);
44   }
45 
recurTimesB(int times, Runnable safepoint)46   public static void recurTimesB(int times, Runnable safepoint) {
47     if (times == 0) {
48       safepoint.run();
49       return;
50     }
51     recurTimesC(times - 1, safepoint);
52   }
53 
recurTimesC(int times, Runnable safepoint)54   public static void recurTimesC(int times, Runnable safepoint) {
55     if (times == 0) {
56       safepoint.run();
57       return;
58     }
59     recurTimesD(times - 1, safepoint);
60   }
61 
recurTimesD(int times, Runnable safepoint)62   public static void recurTimesD(int times, Runnable safepoint) {
63     if (times == 0) {
64       safepoint.run();
65       return;
66     }
67     recurTimesE(times - 1, safepoint);
68   }
69 
recurTimesE(int times, Runnable safepoint)70   public static void recurTimesE(int times, Runnable safepoint) {
71     if (times == 0) {
72       safepoint.run();
73       return;
74     }
75     recurTimesF(times - 1, safepoint);
76   }
77 
recurTimesF(int times, Runnable safepoint)78   public static void recurTimesF(int times, Runnable safepoint) {
79     if (times == 0) {
80       safepoint.run();
81       return;
82     }
83     recurTimesG(times - 1, safepoint);
84   }
85 
recurTimesG(int times, Runnable safepoint)86   public static void recurTimesG(int times, Runnable safepoint) {
87     if (times == 0) {
88       safepoint.run();
89       return;
90     }
91     recurTimesH(times - 1, safepoint);
92   }
93 
recurTimesH(int times, Runnable safepoint)94   public static void recurTimesH(int times, Runnable safepoint) {
95     if (times == 0) {
96       safepoint.run();
97       return;
98     }
99     recurTimesI(times - 1, safepoint);
100   }
101 
recurTimesI(int times, Runnable safepoint)102   public static void recurTimesI(int times, Runnable safepoint) {
103     if (times == 0) {
104       safepoint.run();
105       return;
106     }
107     recurTimesJ(times - 1, safepoint);
108   }
109 
recurTimesJ(int times, Runnable safepoint)110   public static void recurTimesJ(int times, Runnable safepoint) {
111     if (times == 0) {
112       safepoint.run();
113       return;
114     }
115     recurTimesK(times - 1, safepoint);
116   }
117 
recurTimesK(int times, Runnable safepoint)118   public static void recurTimesK(int times, Runnable safepoint) {
119     if (times == 0) {
120       safepoint.run();
121       return;
122     }
123     recurTimesL(times - 1, safepoint);
124   }
125 
126   public static class RecursionError extends Error {
RecursionError(String s)127     public RecursionError(String s) { super(s); }
128   }
129 
recurTimesL(int times, Runnable safepoint)130   public static void recurTimesL(int times, Runnable safepoint) {
131     if (times == 0) {
132       safepoint.run();
133       return;
134     }
135     safepoint.run();
136     throw new RecursionError("Unable recur further. Still " + times + " outstanding!");
137   }
138 
139   public static class ThreadPauser implements Runnable {
140     public final Semaphore sem_wakeup_main;
141     public final Semaphore sem_wait;
142 
ThreadPauser()143     public ThreadPauser() {
144       sem_wakeup_main = new Semaphore(0);
145       sem_wait = new Semaphore(0);
146     }
147 
run()148     public void run() {
149       try {
150         sem_wakeup_main.release();
151         sem_wait.acquire();
152       } catch (Exception e) {
153         throw new Error("Error with semaphores!", e);
154       }
155     }
156 
waitForOtherThreadToPause()157     public void waitForOtherThreadToPause() throws Exception {
158       sem_wakeup_main.acquire();
159     }
160 
wakeupOtherThread()161     public void wakeupOtherThread() throws Exception {
162       sem_wait.release();
163     }
164   }
165 
doRecurTestWith(final int times, int watch_frame)166   public static void doRecurTestWith(final int times, int watch_frame) throws Exception {
167     final String target_method_name_start = "recurTimes";
168     final ThreadPauser safepoint = new ThreadPauser();
169     // We need to make the stack bigger since ASAN causes SOE if we don't.
170     Thread target = new Thread(
171         /*thread Group*/ null,
172         () -> {
173           recurTimesA(times, () -> {
174             safepoint.run();
175             disableFramePop(null);
176           });
177           System.out.println("Ran recurTimes(" + times + ") without errors after disabling " +
178               "frame pop event!");
179           System.out.println("renabling frame pop event with similar stack.");
180           recurTimesB(times, () -> { reenableFramePop(null); });
181           System.out.println("Ran recurTimes(" + times + ") without errors!");
182         },
183         "Test1926-Thread",
184         /*10 mb stack*/10 * 1024 * 1024);
185     target.start();
186     safepoint.waitForOtherThreadToPause();
187     Suspension.suspend(target);
188     // Safe block
189     int cnt = 0;
190     StackTrace.StackFrameData target_frame = null;
191     for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(target)) {
192       if (frame.method.getName().startsWith(target_method_name_start)) {
193         if (times - cnt == watch_frame) {
194           target_frame = frame;
195           break;
196         } else {
197           cnt++;
198         }
199       }
200     }
201     if (target_frame != null) {
202       FramePop.notifyFramePop(target, target_frame.depth);
203     } else {
204       System.out.println(
205           "Unable to find stack frame for " + watch_frame + " depth of "
206           + target_method_name_start);
207     }
208     Suspension.resume(target);
209     safepoint.wakeupOtherThread();
210     target.join();
211   }
212 
run()213   public static void run() throws Exception {
214     // Listen for events on all threads.
215     FramePop.enableFramePopEvent(
216         Test1926.class,
217         Test1926.class.getDeclaredMethod(
218             "handleFramePop", Executable.class, Boolean.TYPE, Long.TYPE),
219         null);
220     doRecurTestWith(10, 0);
221     doRecurTestWith(10, 5);
222     doRecurTestWith(10, 10);
223   }
224 
disableFramePop(Thread thr)225   public static native void disableFramePop(Thread thr);
reenableFramePop(Thread thr)226   public static native void reenableFramePop(Thread thr);
227 }
228