blob: 3479abbeae016064829009871bee85b7acfd8296 [file] [log] [blame]
Andreas Gampeb5eb94a2016-10-27 19:23:09 -07001/*
2 * Copyright (C) 2016 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
Andreas Gampea1a27c62017-01-11 16:37:16 -080017import java.util.ArrayList;
Andreas Gampeb5eb94a2016-10-27 19:23:09 -070018import java.util.Arrays;
Andreas Gampea1a27c62017-01-11 16:37:16 -080019import java.util.Collections;
20import java.util.List;
Andreas Gampeb5eb94a2016-10-27 19:23:09 -070021import java.util.concurrent.CountDownLatch;
22
23public class Main {
24 public static void main(String[] args) throws Exception {
25 System.loadLibrary(args[1]);
26
27 doTest();
28 doTestOtherThreadWait();
29 doTestOtherThreadBusyLoop();
Andreas Gampea1a27c62017-01-11 16:37:16 -080030
31 doTestAllStackTraces();
32
33 System.out.println("Done");
Andreas Gampeb5eb94a2016-10-27 19:23:09 -070034 }
35
36 public static void doTest() throws Exception {
37 System.out.println("###################");
38 System.out.println("### Same thread ###");
39 System.out.println("###################");
40 System.out.println("From top");
41 Recurse.foo(4, 0, 25, null);
42 Recurse.foo(4, 1, 25, null);
43 Recurse.foo(4, 0, 5, null);
44 Recurse.foo(4, 2, 5, null);
45
46 System.out.println("From bottom");
47 Recurse.foo(4, -1, 25, null);
48 Recurse.foo(4, -5, 5, null);
49 Recurse.foo(4, -7, 5, null);
50 }
51
52 public static void doTestOtherThreadWait() throws Exception {
53 System.out.println();
54 System.out.println("################################");
55 System.out.println("### Other thread (suspended) ###");
56 System.out.println("################################");
57 final ControlData data = new ControlData();
58 data.waitFor = new Object();
59 Thread t = new Thread() {
60 public void run() {
61 Recurse.foo(4, 0, 0, data);
62 }
63 };
64 t.start();
65 data.reached.await();
66 Thread.yield();
67 Thread.sleep(500); // A little bit of time...
68
69 System.out.println("From top");
70 print(t, 0, 25);
71 print(t, 1, 25);
72 print(t, 0, 5);
73 print(t, 2, 5);
74
75 System.out.println("From bottom");
76 print(t, -1, 25);
77 print(t, -5, 5);
78 print(t, -7, 5);
79
80 // Let the thread make progress and die.
81 synchronized(data.waitFor) {
82 data.waitFor.notifyAll();
83 }
84 t.join();
85 }
86
87 public static void doTestOtherThreadBusyLoop() throws Exception {
88 System.out.println();
89 System.out.println("###########################");
90 System.out.println("### Other thread (live) ###");
91 System.out.println("###########################");
92 final ControlData data = new ControlData();
93 Thread t = new Thread() {
94 public void run() {
95 Recurse.foo(4, 0, 0, data);
96 }
97 };
98 t.start();
99 data.reached.await();
100 Thread.yield();
101 Thread.sleep(500); // A little bit of time...
102
103 System.out.println("From top");
104 print(t, 0, 25);
105 print(t, 1, 25);
106 print(t, 0, 5);
107 print(t, 2, 5);
108
109 System.out.println("From bottom");
110 print(t, -1, 25);
111 print(t, -5, 5);
112 print(t, -7, 5);
113
114 // Let the thread stop looping and die.
115 data.stop = true;
116 t.join();
117 }
118
Andreas Gampe984efb52017-01-12 17:43:13 -0800119 private final static List<Object> RETAIN = new ArrayList<Object>();
120
Andreas Gampea1a27c62017-01-11 16:37:16 -0800121 public static void doTestAllStackTraces() throws Exception {
122 System.out.println();
123 System.out.println("################################");
124 System.out.println("### Other threads (suspended) ###");
125 System.out.println("################################");
126
Andreas Gampe984efb52017-01-12 17:43:13 -0800127 // Also create an unstarted and a dead thread.
128 RETAIN.add(new Thread());
129 Thread deadThread = new Thread();
130 RETAIN.add(deadThread);
131 deadThread.start();
132 deadThread.join();
133
Andreas Gampea1a27c62017-01-11 16:37:16 -0800134 final int N = 10;
135
136 final ControlData data = new ControlData(N);
137 data.waitFor = new Object();
138
139 Thread threads[] = new Thread[N];
140
141 for (int i = 0; i < N; i++) {
142 Thread t = new Thread() {
143 public void run() {
144 Recurse.foo(4, 0, 0, data);
145 }
146 };
147 t.start();
148 threads[i] = t;
149 }
150 data.reached.await();
151 Thread.yield();
152 Thread.sleep(500); // A little bit of time...
153
154 printAll(0);
155
156 printAll(5);
157
158 printAll(25);
159
160 // Let the thread make progress and die.
161 synchronized(data.waitFor) {
162 data.waitFor.notifyAll();
163 }
164 for (int i = 0; i < N; i++) {
165 threads[i].join();
166 }
Andreas Gampe984efb52017-01-12 17:43:13 -0800167
168 RETAIN.clear();
Andreas Gampea1a27c62017-01-11 16:37:16 -0800169 }
170
Andreas Gampeceafe352016-12-12 18:49:33 -0800171 public static void print(String[][] stack) {
Andreas Gampeb5eb94a2016-10-27 19:23:09 -0700172 System.out.println("---------");
Andreas Gampeceafe352016-12-12 18:49:33 -0800173 for (String[] stackElement : stack) {
174 for (String part : stackElement) {
175 System.out.print(' ');
176 System.out.print(part);
177 }
178 System.out.println();
Andreas Gampeb5eb94a2016-10-27 19:23:09 -0700179 }
180 }
181
182 public static void print(Thread t, int start, int max) {
183 print(getStackTrace(t, start, max));
184 }
185
Andreas Gampea1a27c62017-01-11 16:37:16 -0800186 public static void printAll(Object[][] stacks) {
187 List<String> stringified = new ArrayList<String>(stacks.length);
188
189 for (Object[] stackInfo : stacks) {
190 Thread t = (Thread)stackInfo[0];
191 String name = (t != null) ? t.getName() : "null";
192 String stackSerialization;
193 if (name.contains("Daemon")) {
194 // Do not print daemon stacks, as they're non-deterministic.
195 stackSerialization = "<not printed>";
196 } else {
197 StringBuilder sb = new StringBuilder();
198 for (String[] stackElement : (String[][])stackInfo[1]) {
199 for (String part : stackElement) {
200 sb.append(' ');
201 sb.append(part);
202 }
203 sb.append('\n');
204 }
205 stackSerialization = sb.toString();
206 }
207 stringified.add(name + "\n" + stackSerialization);
208 }
209
210 Collections.sort(stringified);
211
212 for (String s : stringified) {
213 System.out.println("---------");
214 System.out.println(s);
215 }
216 }
217
218 public static void printAll(int max) {
219 printAll(getAllStackTraces(max));
220 }
221
Andreas Gampeb5eb94a2016-10-27 19:23:09 -0700222 // Wrap generated stack traces into a class to separate them nicely.
223 public static class Recurse {
224
225 public static int foo(int x, int start, int max, ControlData data) {
226 bar(x, start, max, data);
227 return 0;
228 }
229
230 private static long bar(int x, int start, int max, ControlData data) {
231 baz(x, start, max, data);
232 return 0;
233 }
234
235 private static Object baz(int x, int start, int max, ControlData data) {
236 if (x == 0) {
237 printOrWait(start, max, data);
238 } else {
239 foo(x - 1, start, max, data);
240 }
241 return null;
242 }
243
244 private static void printOrWait(int start, int max, ControlData data) {
245 if (data == null) {
246 print(Thread.currentThread(), start, max);
247 } else {
248 if (data.waitFor != null) {
249 synchronized (data.waitFor) {
250 data.reached.countDown();
251 try {
252 data.waitFor.wait(); // Use wait() as it doesn't have a "hidden" Java call-graph.
253 } catch (Throwable t) {
254 throw new RuntimeException(t);
255 }
256 }
257 } else {
258 data.reached.countDown();
259 while (!data.stop) {
260 // Busy-loop.
261 }
262 }
263 }
264 }
265 }
266
267 public static class ControlData {
Andreas Gampea1a27c62017-01-11 16:37:16 -0800268 CountDownLatch reached;
Andreas Gampeb5eb94a2016-10-27 19:23:09 -0700269 Object waitFor = null;
270 volatile boolean stop = false;
Andreas Gampea1a27c62017-01-11 16:37:16 -0800271
272 public ControlData() {
273 this(1);
274 }
275
276 public ControlData(int latchCount) {
277 reached = new CountDownLatch(latchCount);
278 }
Andreas Gampeb5eb94a2016-10-27 19:23:09 -0700279 }
280
Andreas Gampeceafe352016-12-12 18:49:33 -0800281 public static native String[][] getStackTrace(Thread thread, int start, int max);
Andreas Gampea1a27c62017-01-11 16:37:16 -0800282 // Get all stack traces. This will return an array with an element for each thread. The element
283 // is an array itself with the first element being the thread, and the second element a nested
284 // String array as in getStackTrace.
285 public static native Object[][] getAllStackTraces(int max);
Andreas Gampeb5eb94a2016-10-27 19:23:09 -0700286}