blob: 355572044f4694b11680778e401f7bba6d62a20d [file] [log] [blame]
Andreas Gampeaf13ab92017-01-11 20:57:40 -08001/*
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
Andreas Gampe46651672017-04-07 09:00:04 -070017package art;
18
Andreas Gampeaf13ab92017-01-11 20:57:40 -080019import java.util.Arrays;
Andreas Gampe72c19832017-01-12 13:22:16 -080020import java.util.ArrayList;
21import java.util.Collections;
Andreas Gampe85807442017-01-13 14:40:58 -080022import java.util.Comparator;
Andreas Gampe72c19832017-01-12 13:22:16 -080023import java.util.concurrent.CountDownLatch;
Alex Lightba461c32017-09-22 14:19:18 -070024import java.util.function.Function;
Andreas Gampe72c19832017-01-12 13:22:16 -080025import java.util.HashMap;
Andreas Gampe4471e4f2017-01-30 16:40:49 +000026import java.util.Iterator;
Andreas Gampe72c19832017-01-12 13:22:16 -080027import java.util.List;
28import java.util.Map;
Andreas Gampe447c1af2017-04-12 08:42:16 -070029import java.util.Set;
Alex Lighte0592382018-11-30 14:08:03 -080030import java.time.Instant;
Andreas Gampeaf13ab92017-01-11 20:57:40 -080031
Andreas Gampe46651672017-04-07 09:00:04 -070032public class Test924 {
33 public static void run() throws Exception {
Andreas Gampe447c1af2017-04-12 08:42:16 -070034 // Run the test on its own thread, so we have a known state for the "current" thread.
35 Thread t = new Thread("TestThread") {
36 @Override
37 public void run() {
38 try {
39 doTest();
40 } catch (Exception e) {
41 throw new RuntimeException(e);
42 }
43 }
44 };
45 t.start();
46 t.join();
Andreas Gampeaf13ab92017-01-11 20:57:40 -080047 }
48
49 private static void doTest() throws Exception {
50 Thread t1 = Thread.currentThread();
51 Thread t2 = getCurrentThread();
52
Andreas Gampe447c1af2017-04-12 08:42:16 -070053 // Need to adjust priority, as on-device this may be unexpected (and we prefer not
54 // to special-case this.)
55 t1.setPriority(5);
56
Andreas Gampeaf13ab92017-01-11 20:57:40 -080057 if (t1 != t2) {
58 throw new RuntimeException("Expected " + t1 + " but got " + t2);
59 }
60 System.out.println("currentThread OK");
61
62 printThreadInfo(t1);
63 printThreadInfo(null);
64
65 Thread t3 = new Thread("Daemon Thread");
66 t3.setDaemon(true);
67 // Do not start this thread, yet.
68 printThreadInfo(t3);
69 // Start, and wait for it to die.
70 t3.start();
71 t3.join();
Andreas Gampe72c19832017-01-12 13:22:16 -080072 Thread.sleep(500); // Wait a little bit.
Andreas Gampeaf13ab92017-01-11 20:57:40 -080073 // Thread has died, check that we can still get info.
74 printThreadInfo(t3);
Andreas Gampe72c19832017-01-12 13:22:16 -080075
Andreas Gampedb6c2ab2017-03-28 17:28:32 -070076 // Try a subclass of thread.
77 Thread t4 = new Thread("Subclass") {
78 };
79 printThreadInfo(t4);
80
Alex Lightba461c32017-09-22 14:19:18 -070081 doCurrentThreadStateTests();
82 doStateTests(Thread::new);
83 doStateTests(ExtThread::new);
Andreas Gampe85807442017-01-13 14:40:58 -080084
85 doAllThreadsTests();
Andreas Gampe7b3b3262017-01-19 20:40:42 -080086
87 doTLSTests();
Andreas Gampeeafaf572017-01-20 12:34:15 -080088
89 doTestEvents();
Andreas Gampe72c19832017-01-12 13:22:16 -080090 }
91
Alex Lightba461c32017-09-22 14:19:18 -070092 private static final class ExtThread extends Thread {
93 public ExtThread(Runnable r) { super(r); }
94 }
95
Andreas Gampe72c19832017-01-12 13:22:16 -080096 private static class Holder {
97 volatile boolean flag = false;
98 }
99
Alex Lightba461c32017-09-22 14:19:18 -0700100 private static void doCurrentThreadStateTests() throws Exception {
Andreas Gampe72c19832017-01-12 13:22:16 -0800101 System.out.println(Integer.toHexString(getThreadState(null)));
102 System.out.println(Integer.toHexString(getThreadState(Thread.currentThread())));
Alex Lightba461c32017-09-22 14:19:18 -0700103 }
Andreas Gampe72c19832017-01-12 13:22:16 -0800104
Alex Lightba461c32017-09-22 14:19:18 -0700105 private static void doStateTests(Function<Runnable, Thread> mkThread) throws Exception {
Andreas Gampe72c19832017-01-12 13:22:16 -0800106 final CountDownLatch cdl1 = new CountDownLatch(1);
107 final CountDownLatch cdl2 = new CountDownLatch(1);
108 final CountDownLatch cdl3_1 = new CountDownLatch(1);
109 final CountDownLatch cdl3_2 = new CountDownLatch(1);
110 final CountDownLatch cdl4 = new CountDownLatch(1);
111 final CountDownLatch cdl5 = new CountDownLatch(1);
112 final Holder h = new Holder();
Alex Lighte0592382018-11-30 14:08:03 -0800113 final long ALMOST_INFINITE = 100000000; // 1.1 days!
Alex Light597adad2017-10-16 16:11:42 -0700114 final NativeWaiter w = new NativeWaiter();
Andreas Gampe72c19832017-01-12 13:22:16 -0800115 Runnable r = new Runnable() {
116 @Override
117 public void run() {
118 try {
119 cdl1.countDown();
120 synchronized(cdl1) {
121 cdl1.wait();
122 }
123
124 cdl2.countDown();
125 synchronized(cdl2) {
Alex Lighte0592382018-11-30 14:08:03 -0800126 cdl2.wait(ALMOST_INFINITE);
Andreas Gampe72c19832017-01-12 13:22:16 -0800127 }
128
129 cdl3_1.await();
130 cdl3_2.countDown();
131 synchronized(cdl3_2) {
132 // Nothing, just wanted to block on cdl3.
133 }
134
135 cdl4.countDown();
Alex Lighte0592382018-11-30 14:08:03 -0800136 try {
137 Thread.sleep(ALMOST_INFINITE);
138 } catch (InterruptedException e) { }
Andreas Gampe72c19832017-01-12 13:22:16 -0800139
140 cdl5.countDown();
141 while (!h.flag) {
142 // Busy-loop.
143 }
Alex Light597adad2017-10-16 16:11:42 -0700144
145 nativeLoop(w.struct);
Andreas Gampe72c19832017-01-12 13:22:16 -0800146 } catch (Exception e) {
147 throw new RuntimeException(e);
148 }
149 }
150 };
151
Alex Lightba461c32017-09-22 14:19:18 -0700152 Thread t = mkThread.apply(r);
153 System.out.println("Thread type is " + t.getClass());
Andreas Gampe72c19832017-01-12 13:22:16 -0800154 printThreadState(t);
155 t.start();
156
157 // Waiting.
158 cdl1.await();
Alex Lighte0592382018-11-30 14:08:03 -0800159 // This is super inconsistent so just wait for the desired state for up to 5 minutes then give
160 // up and continue
161 final int WAITING_INDEF = 0x191;
162 waitForState(t, WAITING_INDEF);
Andreas Gampe72c19832017-01-12 13:22:16 -0800163 synchronized(cdl1) {
164 cdl1.notifyAll();
165 }
166
167 // Timed waiting.
168 cdl2.await();
Alex Lighte0592382018-11-30 14:08:03 -0800169 // This is super inconsistent so just wait for the desired state for up to 5 minutes then give
170 // up and continue
171 final int WAITING_TIMED = 0x1a1;
172 waitForState(t, WAITING_TIMED);
Andreas Gampe72c19832017-01-12 13:22:16 -0800173 synchronized(cdl2) {
174 cdl2.notifyAll();
175 }
176
177 // Blocked on monitor.
178 synchronized(cdl3_2) {
179 cdl3_1.countDown();
180 cdl3_2.await();
Andreas Gamped59695c2017-03-09 10:21:58 -0800181 // While the latch improves the chances to make good progress, scheduling might still be
182 // messy. Wait till we get the right Java-side Thread state.
183 do {
184 Thread.yield();
185 } while (t.getState() != Thread.State.BLOCKED);
Alex Lighted3a3572017-07-20 11:42:12 -0700186 // Since internal thread suspension (For GC or other cases) can happen at any time and changes
187 // the thread state we just have it print the majority thread state across 11 calls over 55
188 // milliseconds.
189 printMajorityThreadState(t, 11, 5);
Andreas Gampe72c19832017-01-12 13:22:16 -0800190 }
191
192 // Sleeping.
193 cdl4.await();
Alex Lighte0592382018-11-30 14:08:03 -0800194 // This is super inconsistent so just wait for the desired state for up to 5 minutes then give
195 // up and continue
196 final int WAITING_SLEEP = 0xe1;
197 waitForState(t, WAITING_SLEEP);
198 t.interrupt();
Andreas Gampe72c19832017-01-12 13:22:16 -0800199
200 // Running.
201 cdl5.await();
202 Thread.yield();
Alex Lighte0592382018-11-30 14:08:03 -0800203 Thread.sleep(1000);
Andreas Gampe72c19832017-01-12 13:22:16 -0800204 printThreadState(t);
205 h.flag = true;
206
Alex Light597adad2017-10-16 16:11:42 -0700207 // Native
208 w.waitForNative();
209 printThreadState(t);
210 w.finish();
211
Andreas Gampe72c19832017-01-12 13:22:16 -0800212 // Dying.
213 t.join();
214 Thread.yield();
Alex Lighte0592382018-11-30 14:08:03 -0800215 Thread.sleep(1000);
Andreas Gampe72c19832017-01-12 13:22:16 -0800216
217 printThreadState(t);
218 }
219
Alex Lighte0592382018-11-30 14:08:03 -0800220 private static void waitForState(Thread t, int desired) throws Exception {
221 Thread.yield();
222 Thread.sleep(1000);
223 // This is super inconsistent so just wait for the desired state for up to 5 minutes then give
224 // up and continue
225 int state;
226 Instant deadline = Instant.now().plusSeconds(60 * 5);
227 while ((state = getThreadState(t)) != desired && deadline.isAfter(Instant.now())) {
228 Thread.yield();
229 Thread.sleep(100);
230 Thread.yield();
231 }
232 printThreadState(state);
233 }
234
Andreas Gampe85807442017-01-13 14:40:58 -0800235 private static void doAllThreadsTests() {
236 Thread[] threads = getAllThreads();
Andreas Gampe4471e4f2017-01-30 16:40:49 +0000237 List<Thread> threadList = new ArrayList<>(Arrays.asList(threads));
238
239 // Filter out JIT thread. It may or may not be there depending on configuration.
240 Iterator<Thread> it = threadList.iterator();
241 while (it.hasNext()) {
242 Thread t = it.next();
243 if (t.getName().startsWith("Jit thread pool worker")) {
244 it.remove();
245 break;
246 }
247 }
248
249 Collections.sort(threadList, THREAD_COMP);
Andreas Gampe447c1af2017-04-12 08:42:16 -0700250
251 List<Thread> expectedList = new ArrayList<>();
252 Set<Thread> threadsFromTraces = Thread.getAllStackTraces().keySet();
253
254 expectedList.add(findThreadByName(threadsFromTraces, "FinalizerDaemon"));
255 expectedList.add(findThreadByName(threadsFromTraces, "FinalizerWatchdogDaemon"));
256 expectedList.add(findThreadByName(threadsFromTraces, "HeapTaskDaemon"));
257 expectedList.add(findThreadByName(threadsFromTraces, "ReferenceQueueDaemon"));
258 // We can't get the signal catcher through getAllStackTraces. So ignore it.
259 // expectedList.add(findThreadByName(threadsFromTraces, "Signal Catcher"));
260 expectedList.add(findThreadByName(threadsFromTraces, "TestThread"));
261 expectedList.add(findThreadByName(threadsFromTraces, "main"));
262
263 if (!threadList.containsAll(expectedList)) {
264 throw new RuntimeException("Expected " + expectedList + " as subset, got " + threadList);
265 }
266 System.out.println(expectedList);
267 }
268
269 private static Thread findThreadByName(Set<Thread> threads, String name) {
270 for (Thread t : threads) {
271 if (t.getName().equals(name)) {
272 return t;
273 }
274 }
275 throw new RuntimeException("Did not find thread " + name + ": " + threads);
Andreas Gampe85807442017-01-13 14:40:58 -0800276 }
277
Andreas Gampe7b3b3262017-01-19 20:40:42 -0800278 private static void doTLSTests() throws Exception {
279 doTLSNonLiveTests();
280 doTLSLiveTests();
281 }
282
283 private static void doTLSNonLiveTests() throws Exception {
284 Thread t = new Thread();
285 try {
286 setTLS(t, 1);
287 System.out.println("Expected failure setting TLS for non-live thread");
288 } catch (Exception e) {
289 System.out.println(e.getMessage());
290 }
291 t.start();
292 t.join();
293 try {
294 setTLS(t, 1);
295 System.out.println("Expected failure setting TLS for non-live thread");
296 } catch (Exception e) {
297 System.out.println(e.getMessage());
298 }
299 }
300
301 private static void doTLSLiveTests() throws Exception {
302 setTLS(Thread.currentThread(), 1);
303
304 long l = getTLS(Thread.currentThread());
305 if (l != 1) {
306 throw new RuntimeException("Unexpected TLS value: " + l);
307 };
308
309 final CountDownLatch cdl1 = new CountDownLatch(1);
310 final CountDownLatch cdl2 = new CountDownLatch(1);
311
312 Runnable r = new Runnable() {
313 @Override
314 public void run() {
315 try {
316 cdl1.countDown();
317 cdl2.await();
318 setTLS(Thread.currentThread(), 2);
319 if (getTLS(Thread.currentThread()) != 2) {
320 throw new RuntimeException("Different thread issue");
321 }
322 } catch (Exception e) {
323 throw new RuntimeException(e);
324 }
325 }
326 };
327
328 Thread t = new Thread(r);
329 t.start();
330 cdl1.await();
331 setTLS(Thread.currentThread(), 1);
332 cdl2.countDown();
333
334 t.join();
335 if (getTLS(Thread.currentThread()) != 1) {
336 throw new RuntimeException("Got clobbered");
337 }
338 }
339
Mythri Alle1989aa42022-09-21 12:33:36 +0000340 private static List<String> filterForThread(Object[] thread_messages, String thread_name) {
341 List<String> messageListForThread = new ArrayList<String>();
342
343 for (int i = 0; i < thread_messages.length; i++) {
344 String message = (String)thread_messages[i];
345 if (message.startsWith("Thread(" + thread_name + ")")) {
346 messageListForThread.add(message);
347 }
348 }
349
350 return messageListForThread;
351 }
352
Andreas Gampeeafaf572017-01-20 12:34:15 -0800353 private static void doTestEvents() throws Exception {
354 enableThreadEvents(true);
355
Andreas Gampe447c1af2017-04-12 08:42:16 -0700356 final CountDownLatch cdl1 = new CountDownLatch(1);
357 final CountDownLatch cdl2 = new CountDownLatch(1);
358
359 Runnable r = new Runnable() {
360 @Override
361 public void run() {
362 try {
363 cdl1.countDown();
364 cdl2.await();
365 } catch (Exception e) {
366 throw new RuntimeException(e);
367 }
368 }
369 };
Mythri Alle1989aa42022-09-21 12:33:36 +0000370 String thread_name = "EventTestThread";
371 Thread t = new Thread(r, thread_name);
Andreas Gampeeafaf572017-01-20 12:34:15 -0800372
373 System.out.println("Constructed thread");
374 Thread.yield();
Andreas Gampe447c1af2017-04-12 08:42:16 -0700375 Thread.sleep(100);
Mythri Alle1989aa42022-09-21 12:33:36 +0000376
377 // Check that there are no events related to EventTestThread that we just created.
378 System.out.println(filterForThread(getThreadEventMessages(), thread_name).toString());
Andreas Gampeeafaf572017-01-20 12:34:15 -0800379
380 t.start();
Andreas Gampe447c1af2017-04-12 08:42:16 -0700381 cdl1.await();
382
Mythri Alle1989aa42022-09-21 12:33:36 +0000383 System.out.println(filterForThread(getThreadEventMessages(), thread_name).toString());
Andreas Gampe447c1af2017-04-12 08:42:16 -0700384
385 cdl2.countDown();
Andreas Gampeeafaf572017-01-20 12:34:15 -0800386 t.join();
Mythri Alle1989aa42022-09-21 12:33:36 +0000387 System.out.println(filterForThread(getThreadEventMessages(), thread_name).toString());
Andreas Gampeeafaf572017-01-20 12:34:15 -0800388
389 System.out.println("Thread joined");
390
391 enableThreadEvents(false);
392 }
393
Andreas Gampe85807442017-01-13 14:40:58 -0800394 private final static Comparator<Thread> THREAD_COMP = new Comparator<Thread>() {
395 public int compare(Thread o1, Thread o2) {
396 return o1.getName().compareTo(o2.getName());
397 }
398 };
399
Andreas Gampe72c19832017-01-12 13:22:16 -0800400 private final static Map<Integer, String> STATE_NAMES = new HashMap<Integer, String>();
401 private final static List<Integer> STATE_KEYS = new ArrayList<Integer>();
402 static {
403 STATE_NAMES.put(0x1, "ALIVE");
404 STATE_NAMES.put(0x2, "TERMINATED");
405 STATE_NAMES.put(0x4, "RUNNABLE");
406 STATE_NAMES.put(0x400, "BLOCKED_ON_MONITOR_ENTER");
407 STATE_NAMES.put(0x80, "WAITING");
408 STATE_NAMES.put(0x10, "WAITING_INDEFINITELY");
409 STATE_NAMES.put(0x20, "WAITING_WITH_TIMEOUT");
410 STATE_NAMES.put(0x40, "SLEEPING");
411 STATE_NAMES.put(0x100, "IN_OBJECT_WAIT");
412 STATE_NAMES.put(0x200, "PARKED");
413 STATE_NAMES.put(0x100000, "SUSPENDED");
414 STATE_NAMES.put(0x200000, "INTERRUPTED");
415 STATE_NAMES.put(0x400000, "IN_NATIVE");
416 STATE_KEYS.addAll(STATE_NAMES.keySet());
417 Collections.sort(STATE_KEYS);
418 }
Andreas Gampe72c19832017-01-12 13:22:16 -0800419
Alex Lighted3a3572017-07-20 11:42:12 -0700420 // Call getThreadState 'votes' times waiting 'wait' millis between calls and print the most common
421 // result.
422 private static void printMajorityThreadState(Thread t, int votes, int wait) throws Exception {
423 Map<Integer, Integer> states = new HashMap<>();
424 for (int i = 0; i < votes; i++) {
425 int cur_state = getThreadState(t);
426 states.put(cur_state, states.getOrDefault(cur_state, 0) + 1);
427 Thread.sleep(wait); // Wait a little bit.
428 }
429 int best_state = -1;
430 int highest_count = 0;
431 for (Map.Entry<Integer, Integer> e : states.entrySet()) {
432 if (e.getValue() > highest_count) {
433 highest_count = e.getValue();
434 best_state = e.getKey();
435 }
436 }
437 printThreadState(best_state);
438 }
439
440 private static void printThreadState(Thread t) {
441 printThreadState(getThreadState(t));
442 }
443
444 private static void printThreadState(int state) {
Andreas Gampe72c19832017-01-12 13:22:16 -0800445 StringBuilder sb = new StringBuilder();
446
447 for (Integer i : STATE_KEYS) {
448 if ((state & i) != 0) {
449 if (sb.length()>0) {
450 sb.append('|');
451 }
452 sb.append(STATE_NAMES.get(i));
453 }
454 }
455
456 if (sb.length() == 0) {
457 sb.append("NEW");
458 }
459
460 System.out.println(Integer.toHexString(state) + " = " + sb.toString());
Andreas Gampeaf13ab92017-01-11 20:57:40 -0800461 }
462
463 private static void printThreadInfo(Thread t) {
464 Object[] threadInfo = getThreadInfo(t);
465 if (threadInfo == null || threadInfo.length != 5) {
466 System.out.println(Arrays.toString(threadInfo));
467 throw new RuntimeException("threadInfo length wrong");
468 }
469
470 System.out.println(threadInfo[0]); // Name
471 System.out.println(threadInfo[1]); // Priority
472 System.out.println(threadInfo[2]); // Daemon
473 System.out.println(threadInfo[3]); // Threadgroup
474 System.out.println(threadInfo[4] == null ? "null" : threadInfo[4].getClass()); // Context CL.
475 }
476
Alex Light597adad2017-10-16 16:11:42 -0700477 public static final class NativeWaiter {
478 public long struct;
479 public NativeWaiter() {
480 struct = nativeWaiterStructAlloc();
481 }
482 public void waitForNative() {
483 if (struct == 0l) {
484 throw new Error("Already resumed from native!");
485 }
486 nativeWaiterStructWaitForNative(struct);
487 }
488 public void finish() {
489 if (struct == 0l) {
490 throw new Error("Already resumed from native!");
491 }
492 nativeWaiterStructFinish(struct);
493 struct = 0;
494 }
495 }
496
497 private static native long nativeWaiterStructAlloc();
498 private static native void nativeWaiterStructWaitForNative(long struct);
499 private static native void nativeWaiterStructFinish(long struct);
500 private static native void nativeLoop(long w);
501
Andreas Gampeaf13ab92017-01-11 20:57:40 -0800502 private static native Thread getCurrentThread();
503 private static native Object[] getThreadInfo(Thread t);
Andreas Gampe72c19832017-01-12 13:22:16 -0800504 private static native int getThreadState(Thread t);
Andreas Gampe85807442017-01-13 14:40:58 -0800505 private static native Thread[] getAllThreads();
Andreas Gampe7b3b3262017-01-19 20:40:42 -0800506 private static native void setTLS(Thread t, long l);
507 private static native long getTLS(Thread t);
Andreas Gampeeafaf572017-01-20 12:34:15 -0800508 private static native void enableThreadEvents(boolean b);
Andreas Gampe447c1af2017-04-12 08:42:16 -0700509 private static native String[] getThreadEventMessages();
Andreas Gampeaf13ab92017-01-11 20:57:40 -0800510}