• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.util.Arrays;
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.Comparator;
23 import java.util.concurrent.CountDownLatch;
24 import java.util.function.Function;
25 import java.util.HashMap;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30 import java.time.Instant;
31 
32 public class Test924 {
run()33   public static void run() throws Exception {
34     // 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();
47   }
48 
doTest()49   private static void doTest() throws Exception {
50     Thread t1 = Thread.currentThread();
51     Thread t2 = getCurrentThread();
52 
53     // 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 
57     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();
72     Thread.sleep(500);  // Wait a little bit.
73     // Thread has died, check that we can still get info.
74     printThreadInfo(t3);
75 
76     // Try a subclass of thread.
77     Thread t4 = new Thread("Subclass") {
78     };
79     printThreadInfo(t4);
80 
81     doCurrentThreadStateTests();
82     doStateTests(Thread::new);
83     doStateTests(ExtThread::new);
84 
85     doAllThreadsTests();
86 
87     doTLSTests();
88 
89     doTestEvents();
90   }
91 
92   private static final class ExtThread extends Thread {
ExtThread(Runnable r)93     public ExtThread(Runnable r) { super(r); }
94   }
95 
96   private static class Holder {
97     volatile boolean flag = false;
98   }
99 
doCurrentThreadStateTests()100   private static void doCurrentThreadStateTests() throws Exception {
101     System.out.println(Integer.toHexString(getThreadState(null)));
102     System.out.println(Integer.toHexString(getThreadState(Thread.currentThread())));
103   }
104 
doStateTests(Function<Runnable, Thread> mkThread)105   private static void doStateTests(Function<Runnable, Thread> mkThread) throws Exception {
106     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();
113     final long ALMOST_INFINITE = 100000000;  // 1.1 days!
114     final NativeWaiter w = new NativeWaiter();
115     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) {
126             cdl2.wait(ALMOST_INFINITE);
127           }
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();
136           try {
137             Thread.sleep(ALMOST_INFINITE);
138           } catch (InterruptedException e) { }
139 
140           cdl5.countDown();
141           while (!h.flag) {
142             // Busy-loop.
143           }
144 
145           nativeLoop(w.struct);
146         } catch (Exception e) {
147           throw new RuntimeException(e);
148         }
149       }
150     };
151 
152     Thread t = mkThread.apply(r);
153     System.out.println("Thread type is " + t.getClass());
154     printThreadState(t);
155     t.start();
156 
157     // Waiting.
158     cdl1.await();
159     // 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);
163     synchronized(cdl1) {
164       cdl1.notifyAll();
165     }
166 
167     // Timed waiting.
168     cdl2.await();
169     // 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);
173     synchronized(cdl2) {
174       cdl2.notifyAll();
175     }
176 
177     // Blocked on monitor.
178     synchronized(cdl3_2) {
179       cdl3_1.countDown();
180       cdl3_2.await();
181       // 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);
186       // 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);
190     }
191 
192     // Sleeping.
193     cdl4.await();
194     // 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();
199 
200     // Running.
201     cdl5.await();
202     Thread.yield();
203     Thread.sleep(1000);
204     printThreadState(t);
205     h.flag = true;
206 
207     // Native
208     w.waitForNative();
209     printThreadState(t);
210     w.finish();
211 
212     // Dying.
213     t.join();
214     Thread.yield();
215     Thread.sleep(1000);
216 
217     printThreadState(t);
218   }
219 
waitForState(Thread t, int desired)220   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 
doAllThreadsTests()235   private static void doAllThreadsTests() {
236     Thread[] threads = getAllThreads();
237     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);
250 
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 
findThreadByName(Set<Thread> threads, String name)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);
276   }
277 
doTLSTests()278   private static void doTLSTests() throws Exception {
279     doTLSNonLiveTests();
280     doTLSLiveTests();
281   }
282 
doTLSNonLiveTests()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 
doTLSLiveTests()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 
filterForThread(Object[] thread_messages, String thread_name)340   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 
doTestEvents()353   private static void doTestEvents() throws Exception {
354     enableThreadEvents(true);
355 
356     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     };
370     String thread_name = "EventTestThread";
371     Thread t = new Thread(r, thread_name);
372 
373     System.out.println("Constructed thread");
374     Thread.yield();
375     Thread.sleep(100);
376 
377     // Check that there are no events related to EventTestThread that we just created.
378     System.out.println(filterForThread(getThreadEventMessages(), thread_name).toString());
379 
380     t.start();
381     cdl1.await();
382 
383     System.out.println(filterForThread(getThreadEventMessages(), thread_name).toString());
384 
385     cdl2.countDown();
386     t.join();
387     System.out.println(filterForThread(getThreadEventMessages(), thread_name).toString());
388 
389     System.out.println("Thread joined");
390 
391     enableThreadEvents(false);
392   }
393 
394   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 
400   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");
STATE_NAMES.keySet()416     STATE_KEYS.addAll(STATE_NAMES.keySet());
417     Collections.sort(STATE_KEYS);
418   }
419 
420   // Call getThreadState 'votes' times waiting 'wait' millis between calls and print the most common
421   // result.
printMajorityThreadState(Thread t, int votes, int wait)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 
printThreadState(Thread t)440   private static void printThreadState(Thread t) {
441     printThreadState(getThreadState(t));
442   }
443 
printThreadState(int state)444   private static void printThreadState(int state) {
445     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());
461   }
462 
printThreadInfo(Thread t)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 
477   public static final class NativeWaiter {
478     public long struct;
NativeWaiter()479     public NativeWaiter() {
480       struct = nativeWaiterStructAlloc();
481     }
waitForNative()482     public void waitForNative() {
483       if (struct == 0l) {
484         throw new Error("Already resumed from native!");
485       }
486       nativeWaiterStructWaitForNative(struct);
487     }
finish()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 
nativeWaiterStructAlloc()497   private static native long nativeWaiterStructAlloc();
nativeWaiterStructWaitForNative(long struct)498   private static native void nativeWaiterStructWaitForNative(long struct);
nativeWaiterStructFinish(long struct)499   private static native void nativeWaiterStructFinish(long struct);
nativeLoop(long w)500   private static native void nativeLoop(long w);
501 
getCurrentThread()502   private static native Thread getCurrentThread();
getThreadInfo(Thread t)503   private static native Object[] getThreadInfo(Thread t);
getThreadState(Thread t)504   private static native int getThreadState(Thread t);
getAllThreads()505   private static native Thread[] getAllThreads();
setTLS(Thread t, long l)506   private static native void setTLS(Thread t, long l);
getTLS(Thread t)507   private static native long getTLS(Thread t);
enableThreadEvents(boolean b)508   private static native void enableThreadEvents(boolean b);
getThreadEventMessages()509   private static native String[] getThreadEventMessages();
510 }
511