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