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