1 /* 2 * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 4530538 27 * @summary Basic unit test of ThreadInfo.getStackTrace() and 28 * ThreadInfo.getThreadState() 29 * @author Mandy Chung 30 * 31 * @modules java.management 32 * @run build Semaphore Utils 33 * @run main ThreadStackTrace 34 */ 35 36 import java.lang.management.*; 37 38 public class ThreadStackTrace { 39 private static ThreadMXBean mbean 40 = ManagementFactory.getThreadMXBean(); 41 private static boolean notified = false; 42 private static Object lockA = new Object(); 43 private static Object lockB = new Object(); 44 private static volatile boolean testFailed = false; 45 private static String[] blockedStack = {"run", "test", "A", "B", "C", "D"}; 46 private static int bsDepth = 6; 47 private static int methodB = 4; 48 private static String[] examinerStack = {"run", "examine1", "examine2"}; 49 private static int esDepth = 3; 50 private static int methodExamine1= 2; 51 52 private static void checkNullThreadInfo(Thread t) throws Exception { 53 ThreadInfo ti = mbean.getThreadInfo(t.getId()); 54 if (ti != null) { 55 ThreadInfo info = 56 mbean.getThreadInfo(t.getId(), Integer.MAX_VALUE); 57 System.out.println(INDENT + "TEST FAILED:"); 58 if (info != null) { 59 printStack(t, info.getStackTrace()); 60 System.out.println(INDENT + "Thread state: " + info.getThreadState()); 61 } 62 throw new RuntimeException("TEST FAILED: " + 63 "getThreadInfo() is expected to return null for " + t); 64 } 65 } 66 67 private static boolean trace = false; 68 public static void main(String args[]) throws Exception { 69 if (args.length > 0 && args[0].equals("trace")) { 70 trace = true; 71 } 72 73 Examiner examiner = new Examiner("Examiner"); 74 BlockedThread blocked = new BlockedThread("BlockedThread"); 75 examiner.setThread(blocked); 76 77 checkNullThreadInfo(examiner); 78 checkNullThreadInfo(blocked); 79 80 // Start the threads and check them in Blocked and Waiting states 81 examiner.start(); 82 83 // block until examiner begins doing its real work 84 examiner.waitForStarted(); 85 86 System.out.println("Checking stack trace for the examiner thread " + 87 "is waiting to begin."); 88 89 // The Examiner should be waiting to be notified by the BlockedThread 90 Utils.checkThreadState(examiner, Thread.State.WAITING); 91 92 // Check that the stack is returned correctly for a new thread 93 checkStack(examiner, examinerStack, esDepth); 94 95 System.out.println("Now starting the blocked thread"); 96 blocked.start(); 97 98 try { 99 examiner.join(); 100 blocked.join(); 101 } catch (InterruptedException e) { 102 e.printStackTrace(); 103 System.out.println("Unexpected exception."); 104 testFailed = true; 105 } 106 107 // Check that the stack is returned correctly for a terminated thread 108 checkNullThreadInfo(examiner); 109 checkNullThreadInfo(blocked); 110 111 if (testFailed) 112 throw new RuntimeException("TEST FAILED."); 113 114 System.out.println("Test passed."); 115 } 116 117 private static String INDENT = " "; 118 private static void printStack(Thread t, StackTraceElement[] stack) { 119 System.out.println(INDENT + t + 120 " stack: (length = " + stack.length + ")"); 121 if (t != null) { 122 for (int j = 0; j < stack.length; j++) { 123 System.out.println(INDENT + stack[j]); 124 } 125 System.out.println(); 126 } 127 } 128 129 private static void checkStack(Thread t, String[] expectedStack, 130 int depth) throws Exception { 131 ThreadInfo ti = mbean.getThreadInfo(t.getId(), Integer.MAX_VALUE); 132 StackTraceElement[] stack = ti.getStackTrace(); 133 134 if (trace) { 135 printStack(t, stack); 136 } 137 int frame = stack.length - 1; 138 for (int i = 0; i < depth; i++) { 139 if (! stack[frame].getMethodName().equals(expectedStack[i])) { 140 throw new RuntimeException("TEST FAILED: " + 141 "Expected " + expectedStack[i] + " in frame " + frame + 142 " but got " + stack[frame].getMethodName()); 143 } 144 frame--; 145 } 146 } 147 148 static class BlockedThread extends Thread { 149 private Semaphore handshake = new Semaphore(); 150 151 BlockedThread(String name) { 152 super(name); 153 } 154 boolean hasWaitersForBlocked() { 155 return (handshake.getWaiterCount() > 0); 156 } 157 158 void waitUntilBlocked() { 159 handshake.semaP(); 160 161 // give a chance for the examiner thread to really wait 162 Utils.goSleep(20); 163 } 164 165 void waitUntilLockAReleased() { 166 handshake.semaP(); 167 168 // give a chance for the examiner thread to really wait 169 Utils.goSleep(50); 170 } 171 172 private void notifyWaiter() { 173 // wait until the examiner waits on the semaphore 174 while (handshake.getWaiterCount() == 0) { 175 Utils.goSleep(20); 176 } 177 handshake.semaV(); 178 } 179 180 private void test() { 181 A(); 182 } 183 private void A() { 184 B(); 185 } 186 private void B() { 187 C(); 188 189 // notify the examiner about to block on lockB 190 notifyWaiter(); 191 192 synchronized (lockB) { 193 }; 194 } 195 private void C() { 196 D(); 197 } 198 private void D() { 199 // Notify that examiner about to enter lockA 200 notifyWaiter(); 201 202 synchronized (lockA) { 203 notified = false; 204 while (!notified) { 205 try { 206 // notify the examiner about to release lockA 207 notifyWaiter(); 208 // Wait and let examiner thread check the mbean 209 lockA.wait(); 210 } catch (InterruptedException e) { 211 e.printStackTrace(); 212 System.out.println("Unexpected exception."); 213 testFailed = true; 214 } 215 } 216 System.out.println("BlockedThread notified"); 217 } 218 } 219 220 public void run() { 221 test(); 222 } // run() 223 } // BlockedThread 224 225 static class Examiner extends Thread { 226 private static BlockedThread blockedThread; 227 private Semaphore handshake = new Semaphore(); 228 229 Examiner(String name) { 230 super(name); 231 } 232 233 public void setThread(BlockedThread thread) { 234 blockedThread = thread; 235 } 236 237 public synchronized void waitForStarted() { 238 // wait until the examiner is about to block 239 handshake.semaP(); 240 241 // wait until the examiner is waiting for blockedThread's notification 242 while (!blockedThread.hasWaitersForBlocked()) { 243 Utils.goSleep(50); 244 } 245 // give a chance for the examiner thread to really wait 246 Utils.goSleep(20); 247 } 248 249 private Thread itself; 250 private void examine1() { 251 synchronized (lockB) { 252 examine2(); 253 try { 254 System.out.println("Checking examiner's its own stack trace"); 255 Utils.checkThreadState(itself, Thread.State.RUNNABLE); 256 checkStack(itself, examinerStack, methodExamine1); 257 258 // wait until blockedThread is blocked on lockB 259 blockedThread.waitUntilBlocked(); 260 261 System.out.println("Checking stack trace for " + 262 "BlockedThread - should be blocked on lockB."); 263 Utils.checkThreadState(blockedThread, Thread.State.BLOCKED); 264 checkStack(blockedThread, blockedStack, methodB); 265 } catch (Exception e) { 266 e.printStackTrace(); 267 System.out.println("Unexpected exception."); 268 testFailed = true; 269 } 270 } 271 } 272 273 private void examine2() { 274 synchronized (lockA) { 275 // wait until main thread gets signalled of the semaphore 276 while (handshake.getWaiterCount() == 0) { 277 Utils.goSleep(20); 278 } 279 280 handshake.semaV(); // notify the main thread 281 try { 282 // Wait until BlockedThread is about to block on lockA 283 blockedThread.waitUntilBlocked(); 284 285 System.out.println("Checking examiner's its own stack trace"); 286 Utils.checkThreadState(itself, Thread.State.RUNNABLE); 287 checkStack(itself, examinerStack, esDepth); 288 289 System.out.println("Checking stack trace for " + 290 "BlockedThread - should be blocked on lockA."); 291 Utils.checkThreadState(blockedThread, Thread.State.BLOCKED); 292 checkStack(blockedThread, blockedStack, bsDepth); 293 294 } catch (Exception e) { 295 e.printStackTrace(); 296 System.out.println("Unexpected exception."); 297 testFailed = true; 298 } 299 } 300 301 // release lockA and let BlockedThread to get the lock 302 // and wait on lockA 303 blockedThread.waitUntilLockAReleased(); 304 305 synchronized (lockA) { 306 try { 307 System.out.println("Checking stack trace for " + 308 "BlockedThread - should be waiting on lockA."); 309 Utils.checkThreadState(blockedThread, Thread.State.WAITING); 310 checkStack(blockedThread, blockedStack, bsDepth); 311 312 // Let the blocked thread go 313 notified = true; 314 lockA.notify(); 315 } catch (Exception e) { 316 e.printStackTrace(); 317 System.out.println("Unexpected exception."); 318 testFailed = true; 319 } 320 } 321 // give some time for BlockedThread to proceed 322 Utils.goSleep(50); 323 } // examine2() 324 325 public void run() { 326 itself = Thread.currentThread(); 327 examine1(); 328 } // run() 329 } // Examiner 330 }