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 }