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
17package art;
18
19import java.io.PrintWriter;
20import java.io.StringWriter;
21import java.util.concurrent.Semaphore;
22import java.util.Arrays;
23import java.lang.reflect.Executable;
24import java.lang.reflect.Method;
25import java.util.List;
26import java.util.Set;
27import java.util.ArrayList;
28import java.util.HashSet;
29import java.util.function.IntUnaryOperator;
30import java.util.function.Function;
31
32public class Test1923 {
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
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
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
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
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
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
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
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
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
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
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 {
119    public RecursionError(String s) { super(s); }
120  }
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
134    public ThreadPauser() {
135      sem_wakeup_main = new Semaphore(0);
136      sem_wait = new Semaphore(0);
137    }
138
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
148    public void waitForOtherThreadToPause() throws Exception {
149      sem_wakeup_main.acquire();
150    }
151
152    public void wakeupOtherThread() throws Exception {
153      sem_wait.release();
154    }
155  }
156
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(
161        null,
162        () -> {
163          try {
164            recurTimesA(times, safepoint);
165            System.out.println("Ran recurTimes(" + times + ") without errors!");
166          } catch (RecursionError e) {
167            System.out.println("Caught exception " + e + " while running recurTimes(" + times + ")");
168          }
169        },
170        "RecurTimes(" + times + ") watching: " + watch_frame + " runner.",
171        // 4000 kb stack since ASAN can make us stack-overflow otherwise
172        4000 * 1024);
173    target.start();
174    safepoint.waitForOtherThreadToPause();
175    Suspension.suspend(target);
176    // Safe block
177    int cnt = 0;
178    StackTrace.StackFrameData target_frame = null;
179    for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(target)) {
180      if (frame.method.getName().startsWith(target_method_name_start)) {
181        if (times - cnt == watch_frame) {
182          target_frame = frame;
183          break;
184        } else {
185          cnt++;
186        }
187      }
188    }
189    if (target_frame != null) {
190      FramePop.notifyFramePop(target, target_frame.depth);
191    } else {
192      System.out.println(
193          "Unable to find stack frame for " + watch_frame + " depth of "
194          + target_method_name_start);
195    }
196    Suspension.resume(target);
197    safepoint.wakeupOtherThread();
198    target.join();
199  }
200
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        Test1923.class,
206        Test1923.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}
215