• 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 
doTestEvents()340   private static void doTestEvents() throws Exception {
341     enableThreadEvents(true);
342 
343     final CountDownLatch cdl1 = new CountDownLatch(1);
344     final CountDownLatch cdl2 = new CountDownLatch(1);
345 
346     Runnable r = new Runnable() {
347       @Override
348       public void run() {
349         try {
350           cdl1.countDown();
351           cdl2.await();
352         } catch (Exception e) {
353           throw new RuntimeException(e);
354         }
355       }
356     };
357     Thread t = new Thread(r, "EventTestThread");
358 
359     System.out.println("Constructed thread");
360     Thread.yield();
361     Thread.sleep(100);
362     System.out.println(Arrays.toString(getThreadEventMessages()));
363 
364     t.start();
365     cdl1.await();
366 
367     System.out.println(Arrays.toString(getThreadEventMessages()));
368 
369     cdl2.countDown();
370     t.join();
371     System.out.println(Arrays.toString(getThreadEventMessages()));
372 
373     System.out.println("Thread joined");
374 
375     enableThreadEvents(false);
376   }
377 
378   private final static Comparator<Thread> THREAD_COMP = new Comparator<Thread>() {
379     public int compare(Thread o1, Thread o2) {
380       return o1.getName().compareTo(o2.getName());
381     }
382   };
383 
384   private final static Map<Integer, String> STATE_NAMES = new HashMap<Integer, String>();
385   private final static List<Integer> STATE_KEYS = new ArrayList<Integer>();
386   static {
387     STATE_NAMES.put(0x1, "ALIVE");
388     STATE_NAMES.put(0x2, "TERMINATED");
389     STATE_NAMES.put(0x4, "RUNNABLE");
390     STATE_NAMES.put(0x400, "BLOCKED_ON_MONITOR_ENTER");
391     STATE_NAMES.put(0x80, "WAITING");
392     STATE_NAMES.put(0x10, "WAITING_INDEFINITELY");
393     STATE_NAMES.put(0x20, "WAITING_WITH_TIMEOUT");
394     STATE_NAMES.put(0x40, "SLEEPING");
395     STATE_NAMES.put(0x100, "IN_OBJECT_WAIT");
396     STATE_NAMES.put(0x200, "PARKED");
397     STATE_NAMES.put(0x100000, "SUSPENDED");
398     STATE_NAMES.put(0x200000, "INTERRUPTED");
399     STATE_NAMES.put(0x400000, "IN_NATIVE");
STATE_NAMES.keySet()400     STATE_KEYS.addAll(STATE_NAMES.keySet());
401     Collections.sort(STATE_KEYS);
402   }
403 
404   // Call getThreadState 'votes' times waiting 'wait' millis between calls and print the most common
405   // result.
printMajorityThreadState(Thread t, int votes, int wait)406   private static void printMajorityThreadState(Thread t, int votes, int wait) throws Exception {
407     Map<Integer, Integer> states = new HashMap<>();
408     for (int i = 0; i < votes; i++) {
409       int cur_state = getThreadState(t);
410       states.put(cur_state, states.getOrDefault(cur_state, 0) + 1);
411       Thread.sleep(wait);  // Wait a little bit.
412     }
413     int best_state = -1;
414     int highest_count = 0;
415     for (Map.Entry<Integer, Integer> e : states.entrySet()) {
416       if (e.getValue() > highest_count) {
417         highest_count = e.getValue();
418         best_state = e.getKey();
419       }
420     }
421     printThreadState(best_state);
422   }
423 
printThreadState(Thread t)424   private static void printThreadState(Thread t) {
425     printThreadState(getThreadState(t));
426   }
427 
printThreadState(int state)428   private static void printThreadState(int state) {
429     StringBuilder sb = new StringBuilder();
430 
431     for (Integer i : STATE_KEYS) {
432       if ((state & i) != 0) {
433         if (sb.length()>0) {
434           sb.append('|');
435         }
436         sb.append(STATE_NAMES.get(i));
437       }
438     }
439 
440     if (sb.length() == 0) {
441       sb.append("NEW");
442     }
443 
444     System.out.println(Integer.toHexString(state) + " = " + sb.toString());
445   }
446 
printThreadInfo(Thread t)447   private static void printThreadInfo(Thread t) {
448     Object[] threadInfo = getThreadInfo(t);
449     if (threadInfo == null || threadInfo.length != 5) {
450       System.out.println(Arrays.toString(threadInfo));
451       throw new RuntimeException("threadInfo length wrong");
452     }
453 
454     System.out.println(threadInfo[0]);  // Name
455     System.out.println(threadInfo[1]);  // Priority
456     System.out.println(threadInfo[2]);  // Daemon
457     System.out.println(threadInfo[3]);  // Threadgroup
458     System.out.println(threadInfo[4] == null ? "null" : threadInfo[4].getClass());  // Context CL.
459   }
460 
461   public static final class NativeWaiter {
462     public long struct;
NativeWaiter()463     public NativeWaiter() {
464       struct = nativeWaiterStructAlloc();
465     }
waitForNative()466     public void waitForNative() {
467       if (struct == 0l) {
468         throw new Error("Already resumed from native!");
469       }
470       nativeWaiterStructWaitForNative(struct);
471     }
finish()472     public void finish() {
473       if (struct == 0l) {
474         throw new Error("Already resumed from native!");
475       }
476       nativeWaiterStructFinish(struct);
477       struct = 0;
478     }
479   }
480 
nativeWaiterStructAlloc()481   private static native long nativeWaiterStructAlloc();
nativeWaiterStructWaitForNative(long struct)482   private static native void nativeWaiterStructWaitForNative(long struct);
nativeWaiterStructFinish(long struct)483   private static native void nativeWaiterStructFinish(long struct);
nativeLoop(long w)484   private static native void nativeLoop(long w);
485 
getCurrentThread()486   private static native Thread getCurrentThread();
getThreadInfo(Thread t)487   private static native Object[] getThreadInfo(Thread t);
getThreadState(Thread t)488   private static native int getThreadState(Thread t);
getAllThreads()489   private static native Thread[] getAllThreads();
setTLS(Thread t, long l)490   private static native void setTLS(Thread t, long l);
getTLS(Thread t)491   private static native long getTLS(Thread t);
enableThreadEvents(boolean b)492   private static native void enableThreadEvents(boolean b);
getThreadEventMessages()493   private static native String[] getThreadEventMessages();
494 }
495