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