• 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 
31 public class Test924 {
run()32   public static void run() throws Exception {
33     // Run the test on its own thread, so we have a known state for the "current" thread.
34     Thread t = new Thread("TestThread") {
35       @Override
36       public void run() {
37         try {
38           doTest();
39         } catch (Exception e) {
40           throw new RuntimeException(e);
41         }
42       }
43     };
44     t.start();
45     t.join();
46   }
47 
doTest()48   private static void doTest() throws Exception {
49     Thread t1 = Thread.currentThread();
50     Thread t2 = getCurrentThread();
51 
52     // Need to adjust priority, as on-device this may be unexpected (and we prefer not
53     // to special-case this.)
54     t1.setPriority(5);
55 
56     if (t1 != t2) {
57       throw new RuntimeException("Expected " + t1 + " but got " + t2);
58     }
59     System.out.println("currentThread OK");
60 
61     printThreadInfo(t1);
62     printThreadInfo(null);
63 
64     Thread t3 = new Thread("Daemon Thread");
65     t3.setDaemon(true);
66     // Do not start this thread, yet.
67     printThreadInfo(t3);
68     // Start, and wait for it to die.
69     t3.start();
70     t3.join();
71     Thread.sleep(500);  // Wait a little bit.
72     // Thread has died, check that we can still get info.
73     printThreadInfo(t3);
74 
75     // Try a subclass of thread.
76     Thread t4 = new Thread("Subclass") {
77     };
78     printThreadInfo(t4);
79 
80     doCurrentThreadStateTests();
81     doStateTests(Thread::new);
82     doStateTests(ExtThread::new);
83 
84     doAllThreadsTests();
85 
86     doTLSTests();
87 
88     doTestEvents();
89   }
90 
91   private static final class ExtThread extends Thread {
ExtThread(Runnable r)92     public ExtThread(Runnable r) { super(r); }
93   }
94 
95   private static class Holder {
96     volatile boolean flag = false;
97   }
98 
doCurrentThreadStateTests()99   private static void doCurrentThreadStateTests() throws Exception {
100     System.out.println(Integer.toHexString(getThreadState(null)));
101     System.out.println(Integer.toHexString(getThreadState(Thread.currentThread())));
102   }
103 
doStateTests(Function<Runnable, Thread> mkThread)104   private static void doStateTests(Function<Runnable, Thread> mkThread) throws Exception {
105     final CountDownLatch cdl1 = new CountDownLatch(1);
106     final CountDownLatch cdl2 = new CountDownLatch(1);
107     final CountDownLatch cdl3_1 = new CountDownLatch(1);
108     final CountDownLatch cdl3_2 = new CountDownLatch(1);
109     final CountDownLatch cdl4 = new CountDownLatch(1);
110     final CountDownLatch cdl5 = new CountDownLatch(1);
111     final Holder h = new Holder();
112     final NativeWaiter w = new NativeWaiter();
113     Runnable r = new Runnable() {
114       @Override
115       public void run() {
116         try {
117           cdl1.countDown();
118           synchronized(cdl1) {
119             cdl1.wait();
120           }
121 
122           cdl2.countDown();
123           synchronized(cdl2) {
124             cdl2.wait(1000);  // Wait a second.
125           }
126 
127           cdl3_1.await();
128           cdl3_2.countDown();
129           synchronized(cdl3_2) {
130             // Nothing, just wanted to block on cdl3.
131           }
132 
133           cdl4.countDown();
134           Thread.sleep(1000);
135 
136           cdl5.countDown();
137           while (!h.flag) {
138             // Busy-loop.
139           }
140 
141           nativeLoop(w.struct);
142         } catch (Exception e) {
143           throw new RuntimeException(e);
144         }
145       }
146     };
147 
148     Thread t = mkThread.apply(r);
149     System.out.println("Thread type is " + t.getClass());
150     printThreadState(t);
151     t.start();
152 
153     // Waiting.
154     cdl1.await();
155     Thread.yield();
156     Thread.sleep(100);
157     printThreadState(t);
158     synchronized(cdl1) {
159       cdl1.notifyAll();
160     }
161 
162     // Timed waiting.
163     cdl2.await();
164     Thread.yield();
165     Thread.sleep(100);
166     printThreadState(t);
167     synchronized(cdl2) {
168       cdl2.notifyAll();
169     }
170 
171     // Blocked on monitor.
172     synchronized(cdl3_2) {
173       cdl3_1.countDown();
174       cdl3_2.await();
175       // While the latch improves the chances to make good progress, scheduling might still be
176       // messy. Wait till we get the right Java-side Thread state.
177       do {
178         Thread.yield();
179       } while (t.getState() != Thread.State.BLOCKED);
180       // Since internal thread suspension (For GC or other cases) can happen at any time and changes
181       // the thread state we just have it print the majority thread state across 11 calls over 55
182       // milliseconds.
183       printMajorityThreadState(t, 11, 5);
184     }
185 
186     // Sleeping.
187     cdl4.await();
188     Thread.yield();
189     Thread.sleep(100);
190     printThreadState(t);
191 
192     // Running.
193     cdl5.await();
194     Thread.yield();
195     Thread.sleep(100);
196     printThreadState(t);
197     h.flag = true;
198 
199     // Native
200     w.waitForNative();
201     printThreadState(t);
202     w.finish();
203 
204     // Dying.
205     t.join();
206     Thread.yield();
207     Thread.sleep(100);
208 
209     printThreadState(t);
210   }
211 
doAllThreadsTests()212   private static void doAllThreadsTests() {
213     Thread[] threads = getAllThreads();
214     List<Thread> threadList = new ArrayList<>(Arrays.asList(threads));
215 
216     // Filter out JIT thread. It may or may not be there depending on configuration.
217     Iterator<Thread> it = threadList.iterator();
218     while (it.hasNext()) {
219       Thread t = it.next();
220       if (t.getName().startsWith("Jit thread pool worker")) {
221         it.remove();
222         break;
223       }
224     }
225 
226     Collections.sort(threadList, THREAD_COMP);
227 
228     List<Thread> expectedList = new ArrayList<>();
229     Set<Thread> threadsFromTraces = Thread.getAllStackTraces().keySet();
230 
231     expectedList.add(findThreadByName(threadsFromTraces, "FinalizerDaemon"));
232     expectedList.add(findThreadByName(threadsFromTraces, "FinalizerWatchdogDaemon"));
233     expectedList.add(findThreadByName(threadsFromTraces, "HeapTaskDaemon"));
234     expectedList.add(findThreadByName(threadsFromTraces, "ReferenceQueueDaemon"));
235     // We can't get the signal catcher through getAllStackTraces. So ignore it.
236     // expectedList.add(findThreadByName(threadsFromTraces, "Signal Catcher"));
237     expectedList.add(findThreadByName(threadsFromTraces, "TestThread"));
238     expectedList.add(findThreadByName(threadsFromTraces, "main"));
239 
240     if (!threadList.containsAll(expectedList)) {
241       throw new RuntimeException("Expected " + expectedList + " as subset, got " + threadList);
242     }
243     System.out.println(expectedList);
244   }
245 
findThreadByName(Set<Thread> threads, String name)246   private static Thread findThreadByName(Set<Thread> threads, String name) {
247     for (Thread t : threads) {
248         if (t.getName().equals(name)) {
249             return t;
250         }
251     }
252     throw new RuntimeException("Did not find thread " + name + ": " + threads);
253   }
254 
doTLSTests()255   private static void doTLSTests() throws Exception {
256     doTLSNonLiveTests();
257     doTLSLiveTests();
258   }
259 
doTLSNonLiveTests()260   private static void doTLSNonLiveTests() throws Exception {
261     Thread t = new Thread();
262     try {
263       setTLS(t, 1);
264       System.out.println("Expected failure setting TLS for non-live thread");
265     } catch (Exception e) {
266       System.out.println(e.getMessage());
267     }
268     t.start();
269     t.join();
270     try {
271       setTLS(t, 1);
272       System.out.println("Expected failure setting TLS for non-live thread");
273     } catch (Exception e) {
274       System.out.println(e.getMessage());
275     }
276   }
277 
doTLSLiveTests()278   private static void doTLSLiveTests() throws Exception {
279     setTLS(Thread.currentThread(), 1);
280 
281     long l = getTLS(Thread.currentThread());
282     if (l != 1) {
283       throw new RuntimeException("Unexpected TLS value: " + l);
284     };
285 
286     final CountDownLatch cdl1 = new CountDownLatch(1);
287     final CountDownLatch cdl2 = new CountDownLatch(1);
288 
289     Runnable r = new Runnable() {
290       @Override
291       public void run() {
292         try {
293           cdl1.countDown();
294           cdl2.await();
295           setTLS(Thread.currentThread(), 2);
296           if (getTLS(Thread.currentThread()) != 2) {
297             throw new RuntimeException("Different thread issue");
298           }
299         } catch (Exception e) {
300           throw new RuntimeException(e);
301         }
302       }
303     };
304 
305     Thread t = new Thread(r);
306     t.start();
307     cdl1.await();
308     setTLS(Thread.currentThread(), 1);
309     cdl2.countDown();
310 
311     t.join();
312     if (getTLS(Thread.currentThread()) != 1) {
313       throw new RuntimeException("Got clobbered");
314     }
315   }
316 
doTestEvents()317   private static void doTestEvents() throws Exception {
318     enableThreadEvents(true);
319 
320     final CountDownLatch cdl1 = new CountDownLatch(1);
321     final CountDownLatch cdl2 = new CountDownLatch(1);
322 
323     Runnable r = new Runnable() {
324       @Override
325       public void run() {
326         try {
327           cdl1.countDown();
328           cdl2.await();
329         } catch (Exception e) {
330           throw new RuntimeException(e);
331         }
332       }
333     };
334     Thread t = new Thread(r, "EventTestThread");
335 
336     System.out.println("Constructed thread");
337     Thread.yield();
338     Thread.sleep(100);
339     System.out.println(Arrays.toString(getThreadEventMessages()));
340 
341     t.start();
342     cdl1.await();
343 
344     System.out.println(Arrays.toString(getThreadEventMessages()));
345 
346     cdl2.countDown();
347     t.join();
348     System.out.println(Arrays.toString(getThreadEventMessages()));
349 
350     System.out.println("Thread joined");
351 
352     enableThreadEvents(false);
353   }
354 
355   private final static Comparator<Thread> THREAD_COMP = new Comparator<Thread>() {
356     public int compare(Thread o1, Thread o2) {
357       return o1.getName().compareTo(o2.getName());
358     }
359   };
360 
361   private final static Map<Integer, String> STATE_NAMES = new HashMap<Integer, String>();
362   private final static List<Integer> STATE_KEYS = new ArrayList<Integer>();
363   static {
364     STATE_NAMES.put(0x1, "ALIVE");
365     STATE_NAMES.put(0x2, "TERMINATED");
366     STATE_NAMES.put(0x4, "RUNNABLE");
367     STATE_NAMES.put(0x400, "BLOCKED_ON_MONITOR_ENTER");
368     STATE_NAMES.put(0x80, "WAITING");
369     STATE_NAMES.put(0x10, "WAITING_INDEFINITELY");
370     STATE_NAMES.put(0x20, "WAITING_WITH_TIMEOUT");
371     STATE_NAMES.put(0x40, "SLEEPING");
372     STATE_NAMES.put(0x100, "IN_OBJECT_WAIT");
373     STATE_NAMES.put(0x200, "PARKED");
374     STATE_NAMES.put(0x100000, "SUSPENDED");
375     STATE_NAMES.put(0x200000, "INTERRUPTED");
376     STATE_NAMES.put(0x400000, "IN_NATIVE");
STATE_NAMES.keySet()377     STATE_KEYS.addAll(STATE_NAMES.keySet());
378     Collections.sort(STATE_KEYS);
379   }
380 
381   // Call getThreadState 'votes' times waiting 'wait' millis between calls and print the most common
382   // result.
printMajorityThreadState(Thread t, int votes, int wait)383   private static void printMajorityThreadState(Thread t, int votes, int wait) throws Exception {
384     Map<Integer, Integer> states = new HashMap<>();
385     for (int i = 0; i < votes; i++) {
386       int cur_state = getThreadState(t);
387       states.put(cur_state, states.getOrDefault(cur_state, 0) + 1);
388       Thread.sleep(wait);  // Wait a little bit.
389     }
390     int best_state = -1;
391     int highest_count = 0;
392     for (Map.Entry<Integer, Integer> e : states.entrySet()) {
393       if (e.getValue() > highest_count) {
394         highest_count = e.getValue();
395         best_state = e.getKey();
396       }
397     }
398     printThreadState(best_state);
399   }
400 
printThreadState(Thread t)401   private static void printThreadState(Thread t) {
402     printThreadState(getThreadState(t));
403   }
404 
printThreadState(int state)405   private static void printThreadState(int state) {
406     StringBuilder sb = new StringBuilder();
407 
408     for (Integer i : STATE_KEYS) {
409       if ((state & i) != 0) {
410         if (sb.length()>0) {
411           sb.append('|');
412         }
413         sb.append(STATE_NAMES.get(i));
414       }
415     }
416 
417     if (sb.length() == 0) {
418       sb.append("NEW");
419     }
420 
421     System.out.println(Integer.toHexString(state) + " = " + sb.toString());
422   }
423 
printThreadInfo(Thread t)424   private static void printThreadInfo(Thread t) {
425     Object[] threadInfo = getThreadInfo(t);
426     if (threadInfo == null || threadInfo.length != 5) {
427       System.out.println(Arrays.toString(threadInfo));
428       throw new RuntimeException("threadInfo length wrong");
429     }
430 
431     System.out.println(threadInfo[0]);  // Name
432     System.out.println(threadInfo[1]);  // Priority
433     System.out.println(threadInfo[2]);  // Daemon
434     System.out.println(threadInfo[3]);  // Threadgroup
435     System.out.println(threadInfo[4] == null ? "null" : threadInfo[4].getClass());  // Context CL.
436   }
437 
438   public static final class NativeWaiter {
439     public long struct;
NativeWaiter()440     public NativeWaiter() {
441       struct = nativeWaiterStructAlloc();
442     }
waitForNative()443     public void waitForNative() {
444       if (struct == 0l) {
445         throw new Error("Already resumed from native!");
446       }
447       nativeWaiterStructWaitForNative(struct);
448     }
finish()449     public void finish() {
450       if (struct == 0l) {
451         throw new Error("Already resumed from native!");
452       }
453       nativeWaiterStructFinish(struct);
454       struct = 0;
455     }
456   }
457 
nativeWaiterStructAlloc()458   private static native long nativeWaiterStructAlloc();
nativeWaiterStructWaitForNative(long struct)459   private static native void nativeWaiterStructWaitForNative(long struct);
nativeWaiterStructFinish(long struct)460   private static native void nativeWaiterStructFinish(long struct);
nativeLoop(long w)461   private static native void nativeLoop(long w);
462 
getCurrentThread()463   private static native Thread getCurrentThread();
getThreadInfo(Thread t)464   private static native Object[] getThreadInfo(Thread t);
getThreadState(Thread t)465   private static native int getThreadState(Thread t);
getAllThreads()466   private static native Thread[] getAllThreads();
setTLS(Thread t, long l)467   private static native void setTLS(Thread t, long l);
getTLS(Thread t)468   private static native long getTLS(Thread t);
enableThreadEvents(boolean b)469   private static native void enableThreadEvents(boolean b);
getThreadEventMessages()470   private static native String[] getThreadEventMessages();
471 }
472