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 }