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