ThreadStackTrace.java revision 0:37a05a11f281
1112918Sjeff/*
2112918Sjeff * Copyright 2003-2004 Sun Microsystems, Inc.  All Rights Reserved.
3112918Sjeff * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4112918Sjeff *
5112918Sjeff * This code is free software; you can redistribute it and/or modify it
6112918Sjeff * under the terms of the GNU General Public License version 2 only, as
7176763Sdavidxu * published by the Free Software Foundation.
8112918Sjeff *
9126000Smtm * This code is distributed in the hope that it will be useful, but WITHOUT
10126000Smtm * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11112918Sjeff * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12112918Sjeff * version 2 for more details (a copy is included in the LICENSE file that
13113729Sjdp * accompanied this code).
14112918Sjeff *
15144518Sdavidxu * You should have received a copy of the GNU General Public License version
16112918Sjeff * 2 along with this work; if not, write to the Free Software Foundation,
17112918Sjeff * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18112918Sjeff *
19144921Sdavidxu * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
20112918Sjeff * CA 95054 USA or visit www.sun.com if you need additional information or
21144518Sdavidxu * have any questions.
22112918Sjeff */
23177494Sdavidxu
24144518Sdavidxu/*
25218414Sjkim * @test
26154130Sdavidxu * @bug     4530538
27112918Sjeff * @summary Basic unit test of ThreadInfo.getStackTrace() and
28112918Sjeff *          ThreadInfo.getThreadState()
29144518Sdavidxu * @author  Mandy Chung
30112918Sjeff *
31144518Sdavidxu * @run build Semaphore
32112918Sjeff * @run main ThreadStackTrace
33112918Sjeff */
34112918Sjeff
35144518Sdavidxuimport java.lang.management.*;
36112918Sjeff
37112918Sjeffpublic class ThreadStackTrace {
38144518Sdavidxu    private static ThreadMXBean mbean
39112918Sjeff        = ManagementFactory.getThreadMXBean();
40157112Sdavidxu    private static boolean notified = false;
41112918Sjeff    private static Object lockA = new Object();
42112918Sjeff    private static Object lockB = new Object();
43112918Sjeff    private static volatile boolean testFailed = false;
44112918Sjeff    private static String[] blockedStack = {"run", "test", "A", "B", "C", "D"};
45112918Sjeff    private static int bsDepth = 6;
46112918Sjeff    private static int methodB = 4;
47112918Sjeff    private static String[] examinerStack = {"run", "examine1", "examine2"};
48144518Sdavidxu    private static int esDepth = 3;
49216641Sdavidxu    private static int methodExamine1= 2;
50112918Sjeff
51112918Sjeff    private static void goSleep(long ms) {
52112918Sjeff        try {
53144518Sdavidxu            Thread.sleep(ms);
54112918Sjeff        } catch (InterruptedException e) {
55144518Sdavidxu            e.printStackTrace();
56144518Sdavidxu            System.out.println("Unexpected exception.");
57144518Sdavidxu            testFailed = true;
58112918Sjeff        }
59    }
60
61    private static void checkNullThreadInfo(Thread t) throws Exception {
62        ThreadInfo ti = mbean.getThreadInfo(t.getId());
63        if (ti != null) {
64            ThreadInfo info =
65                mbean.getThreadInfo(t.getId(), Integer.MAX_VALUE);
66            System.out.println(INDENT + "TEST FAILED:");
67            if (info != null) {
68                printStack(t, info.getStackTrace());
69                System.out.println(INDENT + "Thread state: " + info.getThreadState());
70            }
71            throw new RuntimeException("TEST FAILED: " +
72                "getThreadInfo() is expected to return null for " + t);
73        }
74    }
75
76    private static boolean trace = false;
77    public static void main(String args[]) throws Exception {
78        if (args.length > 0 && args[0].equals("trace")) {
79            trace = true;
80        }
81
82        Examiner examiner = new Examiner("Examiner");
83        BlockedThread blocked = new BlockedThread("BlockedThread");
84        examiner.setThread(blocked);
85
86        checkNullThreadInfo(examiner);
87        checkNullThreadInfo(blocked);
88
89        // Start the threads and check them in  Blocked and Waiting states
90        examiner.start();
91
92        // block until examiner begins doing its real work
93        examiner.waitForStarted();
94
95        System.out.println("Checking stack trace for the examiner thread " +
96                           "is waiting to begin.");
97
98        // The Examiner should be waiting to be notified by the BlockedThread
99        checkThreadState(examiner, Thread.State.WAITING);
100
101        // Check that the stack is returned correctly for a new thread
102        checkStack(examiner, examinerStack, esDepth);
103
104        System.out.println("Now starting the blocked thread");
105        blocked.start();
106
107        try {
108            examiner.join();
109            blocked.join();
110        } catch (InterruptedException e) {
111            e.printStackTrace();
112            System.out.println("Unexpected exception.");
113            testFailed = true;
114        }
115
116        // Check that the stack is returned correctly for a terminated thread
117        checkNullThreadInfo(examiner);
118        checkNullThreadInfo(blocked);
119
120        if (testFailed)
121            throw new RuntimeException("TEST FAILED.");
122
123        System.out.println("Test passed.");
124    }
125
126    private static String INDENT = "    ";
127    private static void printStack(Thread t, StackTraceElement[] stack) {
128        System.out.println(INDENT +  t +
129                           " stack: (length = " + stack.length + ")");
130        if (t != null) {
131            for (int j = 0; j < stack.length; j++) {
132                System.out.println(INDENT + stack[j]);
133            }
134            System.out.println();
135        }
136    }
137
138    private static void checkThreadState(Thread thread, Thread.State s)
139        throws Exception {
140
141        ThreadInfo ti = mbean.getThreadInfo(thread.getId());
142        if (ti.getThreadState() != s) {
143            ThreadInfo info =
144                mbean.getThreadInfo(thread.getId(), Integer.MAX_VALUE);
145            System.out.println(INDENT + "TEST FAILED:");
146            printStack(thread, info.getStackTrace());
147            System.out.println(INDENT + "Thread state: " + info.getThreadState());
148
149            throw new RuntimeException("TEST FAILED: " +
150                "Thread state for " + thread + " returns " + ti.getThreadState() +
151                ".  Expected to be " + s);
152        }
153    }
154
155    private static void checkThreadState(Thread thread,
156                                         Thread.State s1, Thread.State s2)
157        throws Exception {
158
159        ThreadInfo ti = mbean.getThreadInfo(thread.getId());
160        if (ti.getThreadState() != s1 && ti.getThreadState() != s2) {
161            throw new RuntimeException("TEST FAILED: " +
162                "Thread state for " + thread + " returns " + ti.getThreadState() +
163                ".  Expected to be " + s1 + " or " + s2);
164        }
165    }
166
167    private static void checkStack(Thread t, String[] expectedStack,
168                                   int depth) throws Exception {
169        ThreadInfo ti = mbean.getThreadInfo(t.getId(), Integer.MAX_VALUE);
170        StackTraceElement[] stack = ti.getStackTrace();
171
172        if (trace) {
173            printStack(t, stack);
174        }
175        int frame = stack.length - 1;
176        for (int i = 0; i < depth; i++) {
177            if (! stack[frame].getMethodName().equals(expectedStack[i])) {
178                throw new RuntimeException("TEST FAILED: " +
179                    "Expected " + expectedStack[i] + " in frame " + frame +
180                    " but got " + stack[frame].getMethodName());
181            }
182            frame--;
183        }
184    }
185
186    static class BlockedThread extends Thread {
187        private Semaphore handshake = new Semaphore();
188
189        BlockedThread(String name) {
190            super(name);
191        }
192        boolean hasWaitersForBlocked() {
193            return (handshake.getWaiterCount() > 0);
194        }
195
196        void waitUntilBlocked() {
197            handshake.semaP();
198
199            // give a chance for the examiner thread to really wait
200            goSleep(20);
201        }
202
203        void waitUntilLockAReleased() {
204            handshake.semaP();
205
206            // give a chance for the examiner thread to really wait
207            goSleep(50);
208        }
209
210        private void notifyWaiter() {
211            // wait until the examiner waits on the semaphore
212            while (handshake.getWaiterCount() == 0) {
213                goSleep(20);
214            }
215            handshake.semaV();
216        }
217
218        private void test() {
219            A();
220        }
221        private void A() {
222            B();
223        }
224        private void B() {
225            C();
226
227            // notify the examiner about to block on lockB
228            notifyWaiter();
229
230            synchronized (lockB) {
231            };
232        }
233        private void C() {
234            D();
235        }
236        private void D() {
237            // Notify that examiner about to enter lockA
238            notifyWaiter();
239
240            synchronized (lockA) {
241                notified = false;
242                while (!notified) {
243                    try {
244                        // notify the examiner about to release lockA
245                        notifyWaiter();
246                        // Wait and let examiner thread check the mbean
247                        lockA.wait();
248                    } catch (InterruptedException e) {
249                        e.printStackTrace();
250                        System.out.println("Unexpected exception.");
251                        testFailed = true;
252                    }
253                }
254                System.out.println("BlockedThread notified");
255            }
256        }
257
258        public void run() {
259            test();
260        } // run()
261    } // BlockedThread
262
263    static class Examiner extends Thread {
264        private static BlockedThread blockedThread;
265        private Semaphore handshake = new Semaphore();
266
267        Examiner(String name) {
268            super(name);
269        }
270
271        public void setThread(BlockedThread thread) {
272            blockedThread = thread;
273        }
274
275        public synchronized void waitForStarted() {
276            // wait until the examiner is about to block
277            handshake.semaP();
278
279            // wait until the examiner is waiting for blockedThread's notification
280            while (!blockedThread.hasWaitersForBlocked()) {
281                goSleep(50);
282            }
283            // give a chance for the examiner thread to really wait
284            goSleep(20);
285        }
286
287        private Thread itself;
288        private void examine1() {
289            synchronized (lockB) {
290                examine2();
291                try {
292                    System.out.println("Checking examiner's its own stack trace");
293                    checkThreadState(itself, Thread.State.RUNNABLE);
294                    checkStack(itself, examinerStack, methodExamine1);
295
296                    // wait until blockedThread is blocked on lockB
297                    blockedThread.waitUntilBlocked();
298
299                    System.out.println("Checking stack trace for " +
300                        "BlockedThread - should be blocked on lockB.");
301                    checkThreadState(blockedThread, Thread.State.BLOCKED);
302                    checkStack(blockedThread, blockedStack, methodB);
303                } catch (Exception e) {
304                    e.printStackTrace();
305                    System.out.println("Unexpected exception.");
306                    testFailed = true;
307                }
308            }
309        }
310
311        private void examine2() {
312            synchronized (lockA) {
313                // wait until main thread gets signalled of the semaphore
314                while (handshake.getWaiterCount() == 0) {
315                    goSleep(20);
316                }
317
318                handshake.semaV();  // notify the main thread
319                try {
320                    // Wait until BlockedThread is about to block on lockA
321                    blockedThread.waitUntilBlocked();
322
323                    System.out.println("Checking examiner's its own stack trace");
324                    checkThreadState(itself, Thread.State.RUNNABLE);
325                    checkStack(itself, examinerStack, esDepth);
326
327                    System.out.println("Checking stack trace for " +
328                        "BlockedThread - should be blocked on lockA.");
329                    checkThreadState(blockedThread, Thread.State.BLOCKED);
330                    checkStack(blockedThread, blockedStack, bsDepth);
331
332                } catch (Exception e) {
333                    e.printStackTrace();
334                    System.out.println("Unexpected exception.");
335                    testFailed = true;
336                }
337            }
338
339            // release lockA and let BlockedThread to get the lock
340            // and wait on lockA
341            blockedThread.waitUntilLockAReleased();
342
343            synchronized (lockA) {
344                try {
345                    System.out.println("Checking stack trace for " +
346                        "BlockedThread - should be waiting on lockA.");
347                    checkThreadState(blockedThread, Thread.State.WAITING);
348                    checkStack(blockedThread, blockedStack, bsDepth);
349
350                    // Let the blocked thread go
351                    notified = true;
352                    lockA.notify();
353                } catch (Exception e) {
354                    e.printStackTrace();
355                    System.out.println("Unexpected exception.");
356                    testFailed = true;
357                }
358            }
359            // give some time for BlockedThread to proceed
360            goSleep(50);
361        } // examine2()
362
363        public void run() {
364            itself = Thread.currentThread();
365            examine1();
366        } // run()
367    } // Examiner
368}
369