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 Test1924 {
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 
118   public static class RecursionError extends Error {
RecursionError(String s)119     public RecursionError(String s) { super(s); }
120   }
recurTimesK(int times, Runnable safepoint)121   public static void recurTimesK(int times, Runnable safepoint) {
122     if (times == 0) {
123       safepoint.run();
124       return;
125     }
126     safepoint.run();
127     throw new RecursionError("Unable recur further. Still " + times + " outstanding!");
128   }
129 
130   public static class ThreadPauser implements Runnable {
131     public final Semaphore sem_wakeup_main;
132     public final Semaphore sem_wait;
133 
ThreadPauser()134     public ThreadPauser() {
135       sem_wakeup_main = new Semaphore(0);
136       sem_wait = new Semaphore(0);
137     }
138 
run()139     public void run() {
140       try {
141         sem_wakeup_main.release();
142         sem_wait.acquire();
143       } catch (Exception e) {
144         throw new Error("Error with semaphores!", e);
145       }
146     }
147 
waitForOtherThreadToPause()148     public void waitForOtherThreadToPause() throws Exception {
149       sem_wakeup_main.acquire();
150     }
151 
wakeupOtherThread()152     public void wakeupOtherThread() throws Exception {
153       sem_wait.release();
154     }
155   }
156 
doRecurTestWith(final int times, int watch_frame)157   public static void doRecurTestWith(final int times, int watch_frame) throws Exception {
158     final String target_method_name_start = "recurTimes";
159     final ThreadPauser safepoint = new ThreadPauser();
160     Thread target = new Thread(null,
161         () -> {
162           try {
163             recurTimesA(times, safepoint);
164             System.out.println("Ran recurTimes(" + times + ") without errors!");
165           } catch (RecursionError e) {
166             System.out.println("Caught exception " + e + " while running recurTimes(" + times + ")");
167           }
168         },
169         "RecurTimes(" + times + ") watching: " + watch_frame + " runner.",
170         // 4000 kb stack since ASAN can make us stack-overflow otherwise
171         4000 * 1024);
172     target.start();
173     safepoint.waitForOtherThreadToPause();
174     Suspension.suspend(target);
175     // Safe block
176     int cnt = 0;
177     StackTrace.StackFrameData target_frame = null;
178     for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(target)) {
179       if (frame.method.getName().startsWith(target_method_name_start)) {
180         if (times - cnt == watch_frame) {
181           target_frame = frame;
182           break;
183         } else {
184           cnt++;
185         }
186       }
187     }
188     if (target_frame != null) {
189       FramePop.notifyFramePop(target, target_frame.depth);
190     } else {
191       System.out.println(
192           "Unable to find stack frame for " + watch_frame + " depth of "
193           + target_method_name_start);
194     }
195     Suspension.resume(target);
196     toggleFramePop(null);
197     safepoint.wakeupOtherThread();
198     target.join();
199   }
200 
run()201   public static void run() throws Exception {
202     // TODO Investigate what thread argument means for FramePop event enable.
203     // Listen for events on all threads.
204     FramePop.enableFramePopEvent(
205         Test1924.class,
206         Test1924.class.getDeclaredMethod(
207             "handleFramePop", Executable.class, Boolean.TYPE, Long.TYPE),
208         null);
209     doRecurTestWith(10, 0);
210     doRecurTestWith(10, 5);
211     doRecurTestWith(10, 10);
212     doRecurTestWith(100, 95);
213   }
214 
toggleFramePop(Thread thr)215   public static native void toggleFramePop(Thread thr);
216 }
217