blob: 500e945ddd04b9b3660261306781552b548545b7 [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 Gampea1a27c62017-01-11 16:37:16 -0800119 public static void doTestAllStackTraces() throws Exception {
120 System.out.println();
121 System.out.println("################################");
122 System.out.println("### Other threads (suspended) ###");
123 System.out.println("################################");
124
125 final int N = 10;
126
127 final ControlData data = new ControlData(N);
128 data.waitFor = new Object();
129
130 Thread threads[] = new Thread[N];
131
132 for (int i = 0; i < N; i++) {
133 Thread t = new Thread() {
134 public void run() {
135 Recurse.foo(4, 0, 0, data);
136 }
137 };
138 t.start();
139 threads[i] = t;
140 }
141 data.reached.await();
142 Thread.yield();
143 Thread.sleep(500); // A little bit of time...
144
145 printAll(0);
146
147 printAll(5);
148
149 printAll(25);
150
151 // Let the thread make progress and die.
152 synchronized(data.waitFor) {
153 data.waitFor.notifyAll();
154 }
155 for (int i = 0; i < N; i++) {
156 threads[i].join();
157 }
158 }
159
Andreas Gampeceafe352016-12-12 18:49:33 -0800160 public static void print(String[][] stack) {
Andreas Gampeb5eb94a2016-10-27 19:23:09 -0700161 System.out.println("---------");
Andreas Gampeceafe352016-12-12 18:49:33 -0800162 for (String[] stackElement : stack) {
163 for (String part : stackElement) {
164 System.out.print(' ');
165 System.out.print(part);
166 }
167 System.out.println();
Andreas Gampeb5eb94a2016-10-27 19:23:09 -0700168 }
169 }
170
171 public static void print(Thread t, int start, int max) {
172 print(getStackTrace(t, start, max));
173 }
174
Andreas Gampea1a27c62017-01-11 16:37:16 -0800175 public static void printAll(Object[][] stacks) {
176 List<String> stringified = new ArrayList<String>(stacks.length);
177
178 for (Object[] stackInfo : stacks) {
179 Thread t = (Thread)stackInfo[0];
180 String name = (t != null) ? t.getName() : "null";
181 String stackSerialization;
182 if (name.contains("Daemon")) {
183 // Do not print daemon stacks, as they're non-deterministic.
184 stackSerialization = "<not printed>";
185 } else {
186 StringBuilder sb = new StringBuilder();
187 for (String[] stackElement : (String[][])stackInfo[1]) {
188 for (String part : stackElement) {
189 sb.append(' ');
190 sb.append(part);
191 }
192 sb.append('\n');
193 }
194 stackSerialization = sb.toString();
195 }
196 stringified.add(name + "\n" + stackSerialization);
197 }
198
199 Collections.sort(stringified);
200
201 for (String s : stringified) {
202 System.out.println("---------");
203 System.out.println(s);
204 }
205 }
206
207 public static void printAll(int max) {
208 printAll(getAllStackTraces(max));
209 }
210
Andreas Gampeb5eb94a2016-10-27 19:23:09 -0700211 // Wrap generated stack traces into a class to separate them nicely.
212 public static class Recurse {
213
214 public static int foo(int x, int start, int max, ControlData data) {
215 bar(x, start, max, data);
216 return 0;
217 }
218
219 private static long bar(int x, int start, int max, ControlData data) {
220 baz(x, start, max, data);
221 return 0;
222 }
223
224 private static Object baz(int x, int start, int max, ControlData data) {
225 if (x == 0) {
226 printOrWait(start, max, data);
227 } else {
228 foo(x - 1, start, max, data);
229 }
230 return null;
231 }
232
233 private static void printOrWait(int start, int max, ControlData data) {
234 if (data == null) {
235 print(Thread.currentThread(), start, max);
236 } else {
237 if (data.waitFor != null) {
238 synchronized (data.waitFor) {
239 data.reached.countDown();
240 try {
241 data.waitFor.wait(); // Use wait() as it doesn't have a "hidden" Java call-graph.
242 } catch (Throwable t) {
243 throw new RuntimeException(t);
244 }
245 }
246 } else {
247 data.reached.countDown();
248 while (!data.stop) {
249 // Busy-loop.
250 }
251 }
252 }
253 }
254 }
255
256 public static class ControlData {
Andreas Gampea1a27c62017-01-11 16:37:16 -0800257 CountDownLatch reached;
Andreas Gampeb5eb94a2016-10-27 19:23:09 -0700258 Object waitFor = null;
259 volatile boolean stop = false;
Andreas Gampea1a27c62017-01-11 16:37:16 -0800260
261 public ControlData() {
262 this(1);
263 }
264
265 public ControlData(int latchCount) {
266 reached = new CountDownLatch(latchCount);
267 }
Andreas Gampeb5eb94a2016-10-27 19:23:09 -0700268 }
269
Andreas Gampeceafe352016-12-12 18:49:33 -0800270 public static native String[][] getStackTrace(Thread thread, int start, int max);
Andreas Gampea1a27c62017-01-11 16:37:16 -0800271 // Get all stack traces. This will return an array with an element for each thread. The element
272 // is an array itself with the first element being the thread, and the second element a nested
273 // String array as in getStackTrace.
274 public static native Object[][] getAllStackTraces(int max);
Andreas Gampeb5eb94a2016-10-27 19:23:09 -0700275}