1 /* 2 * Copyright (c) 2003, 2004, 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 * @run build Semaphore Utils 32 * @run main ThreadStackTrace 33 */ 34 35 import java.lang.management.*; 36 37 public class ThreadStackTrace { 38 private static ThreadMXBean mbean 39 = ManagementFactory.getThreadMXBean(); 40 private static boolean notified = false; 41 private static Object lockA = new Object(); 42 private static Object lockB = new Object(); 43 private static volatile boolean testFailed = false; 44 private static String[] blockedStack = {"run", "test", "A", "B", "C", "D"}; 45 private static int bsDepth = 6; 46 private static int methodB = 4; 47 private static String[] examinerStack = {"run", "examine1", "examine2"}; 48 private static int esDepth = 3; 49 private static int methodExamine1= 2; 50 51 private static void checkNullThreadInfo(Thread t) throws Exception { 52 ThreadInfo ti = mbean.getThreadInfo(t.getId()); 53 if (ti != null) { 54 ThreadInfo info = 55 mbean.getThreadInfo(t.getId(), Integer.MAX_VALUE); 56 System.out.println(INDENT + "TEST FAILED:"); 57 if (info != null) { 58 printStack(t, info.getStackTrace()); 59 System.out.println(INDENT + "Thread state: " + info.getThreadState()); 60 } 61 throw new RuntimeException("TEST FAILED: " + 62 "getThreadInfo() is expected to return null for " + t); 63 } 64 } 65 66 private static boolean trace = false; 67 public static void main(String args[]) throws Exception { 68 if (args.length > 0 && args[0].equals("trace")) { 69 trace = true; 70 } 71 72 Examiner examiner = new Examiner("Examiner"); 73 BlockedThread blocked = new BlockedThread("BlockedThread"); 74 examiner.setThread(blocked); 75 76 checkNullThreadInfo(examiner); 77 checkNullThreadInfo(blocked); 78 79 // Start the threads and check them in Blocked and Waiting states 80 examiner.start(); 81 82 // block until examiner begins doing its real work 83 examiner.waitForStarted(); 84 85 System.out.println("Checking stack trace for the examiner thread " + 86 "is waiting to begin."); 87 88 // The Examiner should be waiting to be notified by the BlockedThread 89 Utils.checkThreadState(examiner, Thread.State.WAITING); 90 91 // Check that the stack is returned correctly for a new thread 92 checkStack(examiner, examinerStack, esDepth); 93 94 System.out.println("Now starting the blocked thread"); 95 blocked.start(); 96 97 try { 98 examiner.join(); 99 blocked.join(); 100 } catch (InterruptedException e) { 101 e.printStackTrace(); 102 System.out.println("Unexpected exception."); 103 testFailed = true; 104 } 105 106 // Check that the stack is returned correctly for a terminated thread 107 checkNullThreadInfo(examiner); 108 checkNullThreadInfo(blocked); 109 110 if (testFailed) 111 throw new RuntimeException("TEST FAILED."); 112 113 System.out.println("Test passed."); 114 } 115 116 private static String INDENT = " "; 117 private static void printStack(Thread t, StackTraceElement[] stack) { 118 System.out.println(INDENT + t + 119 " stack: (length = " + stack.length + ")"); 120 if (t != null) { 121 for (int j = 0; j < stack.length; j++) { 122 System.out.println(INDENT + stack[j]); 123 } 124 System.out.println(); 125 } 126 } 127 128 private static void checkStack(Thread t, String[] expectedStack, 129 int depth) throws Exception { 130 ThreadInfo ti = mbean.getThreadInfo(t.getId(), Integer.MAX_VALUE); 131 StackTraceElement[] stack = ti.getStackTrace(); 132 133 if (trace) { 134 printStack(t, stack); 135 } 136 int frame = stack.length - 1; 137 for (int i = 0; i < depth; i++) { 138 if (! stack[frame].getMethodName().equals(expectedStack[i])) { 139 throw new RuntimeException("TEST FAILED: " + 140 "Expected " + expectedStack[i] + " in frame " + frame + 141 " but got " + stack[frame].getMethodName()); 142 } 143 frame--; 144 } 145 } 146 147 static class BlockedThread extends Thread { 148 private Semaphore handshake = new Semaphore(); 149 150 BlockedThread(String name) { 151 super(name); 152 } 153 boolean hasWaitersForBlocked() { 154 return (handshake.getWaiterCount() > 0); 155 } 156 157 void waitUntilBlocked() { 158 handshake.semaP(); 159 160 // give a chance for the examiner thread to really wait 161 Utils.goSleep(20); 162 } 163 164 void waitUntilLockAReleased() { 165 handshake.semaP(); 166 167 // give a chance for the examiner thread to really wait 168 Utils.goSleep(50); 169 } 170 171 private void notifyWaiter() { 172 // wait until the examiner waits on the semaphore 173 while (handshake.getWaiterCount() == 0) { 174 Utils.goSleep(20); 175 } 176 handshake.semaV(); 177 } 178 179 private void test() { 180 A(); 181 } 182 private void A() { 183 B(); 184 } 185 private void B() { 186 C(); 187 188 // notify the examiner about to block on lockB 189 notifyWaiter(); 190 191 synchronized (lockB) { 192 }; 193 } 194 private void C() { 195 D(); 196 } 197 private void D() { 198 // Notify that examiner about to enter lockA 199 notifyWaiter(); 200 201 synchronized (lockA) { 202 notified = false; 203 while (!notified) { 204 try { 205 // notify the examiner about to release lockA 206 notifyWaiter(); 207 // Wait and let examiner thread check the mbean 208 lockA.wait(); 209 } catch (InterruptedException e) { 210 e.printStackTrace(); 211 System.out.println("Unexpected exception."); 212 testFailed = true; 213 } 214 } 215 System.out.println("BlockedThread notified"); 216 } 217 } 218 219 public void run() { 220 test(); 221 } // run() 222 } // BlockedThread 223 224 static class Examiner extends Thread { 225 private static BlockedThread blockedThread; 226 private Semaphore handshake = new Semaphore(); 227 228 Examiner(String name) { 229 super(name); 230 } 231 232 public void setThread(BlockedThread thread) { 233 blockedThread = thread; 234 } 235 236 public synchronized void waitForStarted() { 237 // wait until the examiner is about to block 238 handshake.semaP(); 239 240 // wait until the examiner is waiting for blockedThread's notification 241 while (!blockedThread.hasWaitersForBlocked()) { 242 Utils.goSleep(50); 243 } 244 // give a chance for the examiner thread to really wait 245 Utils.goSleep(20); 246 } 247 248 private Thread itself; 249 private void examine1() { 250 synchronized (lockB) { 251 examine2(); 252 try { 253 System.out.println("Checking examiner's its own stack trace"); 254 Utils.checkThreadState(itself, Thread.State.RUNNABLE); 255 checkStack(itself, examinerStack, methodExamine1); 256 257 // wait until blockedThread is blocked on lockB 258 blockedThread.waitUntilBlocked(); 259 260 System.out.println("Checking stack trace for " + 261 "BlockedThread - should be blocked on lockB."); 262 Utils.checkThreadState(blockedThread, Thread.State.BLOCKED); 263 checkStack(blockedThread, blockedStack, methodB); 264 } catch (Exception e) { 265 e.printStackTrace(); 266 System.out.println("Unexpected exception."); 267 testFailed = true; 268 } 269 } 270 } 271 272 private void examine2() { 273 synchronized (lockA) { 274 // wait until main thread gets signalled of the semaphore 275 while (handshake.getWaiterCount() == 0) { 276 Utils.goSleep(20); 277 } 278 279 handshake.semaV(); // notify the main thread 280 try { 281 // Wait until BlockedThread is about to block on lockA 282 blockedThread.waitUntilBlocked(); 283 284 System.out.println("Checking examiner's its own stack trace"); 285 Utils.checkThreadState(itself, Thread.State.RUNNABLE); 286 checkStack(itself, examinerStack, esDepth); 287 288 System.out.println("Checking stack trace for " + 289 "BlockedThread - should be blocked on lockA."); 290 Utils.checkThreadState(blockedThread, Thread.State.BLOCKED); 291 checkStack(blockedThread, blockedStack, bsDepth); 292 293 } catch (Exception e) { 294 e.printStackTrace(); 295 System.out.println("Unexpected exception."); 296 testFailed = true; 297 } 298 } 299 300 // release lockA and let BlockedThread to get the lock 301 // and wait on lockA 302 blockedThread.waitUntilLockAReleased(); 303 304 synchronized (lockA) { 305 try { 306 System.out.println("Checking stack trace for " + 307 "BlockedThread - should be waiting on lockA."); 308 Utils.checkThreadState(blockedThread, Thread.State.WAITING); 309 checkStack(blockedThread, blockedStack, bsDepth); 310 311 // Let the blocked thread go 312 notified = true; 313 lockA.notify(); 314 } catch (Exception e) { 315 e.printStackTrace(); 316 System.out.println("Unexpected exception."); 317 testFailed = true; 318 } 319 } 320 // give some time for BlockedThread to proceed 321 Utils.goSleep(50); 322 } // examine2() 323 324 public void run() { 325 itself = Thread.currentThread(); 326 examine1(); 327 } // run() 328 } // Examiner 329 }