1/*
2 * Copyright (c) 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 8143214
27 * @summary Verify outputs of Thread.dumpStack() and Throwable.printStackTrace().
28 *          This test should also been run against jdk9 successfully except of
29 *          VM option MemberNameInStackFrame.
30 * @run main/othervm DumpStackTest
31 */
32
33import java.lang.invoke.MethodHandle;
34import java.lang.invoke.MethodHandles;
35import java.lang.invoke.MethodType;
36import java.lang.reflect.Method;
37import java.util.Arrays;
38import java.util.function.Consumer;
39
40public class DumpStackTest {
41
42    public static void main(String args[]) {
43        test();
44        testThread();
45        testLambda();
46        testMethodInvoke();
47        testMethodHandle();
48    }
49
50    static class CallFrame {
51        final String classname;
52        final String methodname;
53        CallFrame(Class<?> c, String methodname) {
54            this(c.getName(), methodname);
55        }
56        CallFrame(String classname, String methodname) {
57            this.classname = classname;
58            this.methodname = methodname;
59        }
60
61        String getClassName() {
62            return classname;
63        }
64        String getMethodName() {
65            return methodname;
66        }
67        String getFileName() {
68            int i = classname.lastIndexOf('.');
69            int j = classname.lastIndexOf('$');
70            String name = classname.substring(i+1, j >= 0 ? j : classname.length());
71            return name + ".java";
72        }
73        @Override
74        public String toString() {
75            return classname + "." + methodname + "(" + getFileName() + ")";
76        }
77    }
78
79    static void test() {
80        CallFrame[] callStack = new CallFrame[] {
81                new CallFrame(Thread.class, "getStackTrace"),
82                new CallFrame(DumpStackTest.class, "test"),
83                new CallFrame(DumpStackTest.class, "main"),
84                // if invoked from jtreg
85                new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke0"), // non-public class
86                new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke"),
87                new CallFrame("jdk.internal.reflect.DelegatingMethodAccessorImpl", "invoke"),
88                new CallFrame(Method.class, "invoke"),
89                new CallFrame(Thread.class, "run"),
90        };
91
92        assertStackTrace(Thread.currentThread().getStackTrace(), callStack);
93        getStackTrace(callStack);
94    }
95
96    static void getStackTrace(CallFrame[] callStack) {
97        // this method is the top of the stack
98        callStack[0] = new CallFrame(DumpStackTest.class, "getStackTrace");
99
100        try {
101            throw new RuntimeException();
102        } catch(RuntimeException ex) {
103            assertStackTrace(ex.getStackTrace(), callStack);
104        }
105    }
106    static void testThread() {
107        Thread t1 = new Thread() {
108            public void run() {
109                c();
110            }
111
112            void c() {
113                CallFrame[] callStack = new CallFrame[] {
114                        new CallFrame(Thread.class, "getStackTrace"),
115                        new CallFrame(this.getClass(), "c"),
116                        new CallFrame(this.getClass(), "run")
117                };
118                assertStackTrace(Thread.currentThread().getStackTrace(), callStack);
119                DumpStackTest.getStackTrace(callStack);
120            }
121        };
122        t1.start();
123        try {
124            t1.join();
125        } catch(InterruptedException e) {}
126    }
127
128    static void testLambda() {
129        Consumer<Void> c = (x) -> consumeLambda();
130        c.accept(null);
131    }
132
133    static void consumeLambda() {
134        CallFrame[] callStack = new CallFrame[]{
135                new CallFrame(Thread.class, "getStackTrace"),
136                new CallFrame(DumpStackTest.class, "consumeLambda"),
137                new CallFrame(DumpStackTest.class, "lambda$testLambda$0"),
138                new CallFrame(DumpStackTest.class, "testLambda"),
139                new CallFrame(DumpStackTest.class, "main"),
140                // if invoked from jtreg
141                new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke0"),
142                new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke"),
143                new CallFrame("jdk.internal.reflect.DelegatingMethodAccessorImpl", "invoke"),
144                new CallFrame(Method.class, "invoke"),
145                new CallFrame(Thread.class, "run")
146        };
147        assertStackTrace(Thread.currentThread().getStackTrace(), callStack);
148        DumpStackTest.getStackTrace(callStack);
149    }
150
151    static void testMethodInvoke() {
152        try {
153            Method m = DumpStackTest.class.getDeclaredMethod("methodInvoke");
154            m.invoke(null);
155        } catch(Exception e) {
156            throw new RuntimeException(e);
157        }
158    }
159
160    static void methodInvoke() {
161        CallFrame[] callStack = new CallFrame[] {
162                new CallFrame(Thread.class, "getStackTrace"),
163                new CallFrame(DumpStackTest.class, "methodInvoke"),
164                new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke0"),
165                new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke"),
166                new CallFrame("jdk.internal.reflect.DelegatingMethodAccessorImpl", "invoke"),
167                new CallFrame(Method.class, "invoke"),
168                new CallFrame(DumpStackTest.class, "testMethodInvoke"),
169                new CallFrame(DumpStackTest.class, "main"),
170                // if invoked from jtreg
171                new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke0"),
172                new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke"),
173                new CallFrame("jdk.internal.reflect.DelegatingMethodAccessorImpl", "invoke"),
174                new CallFrame(Method.class, "invoke"),
175                new CallFrame(Thread.class, "run")
176        };
177        assertStackTrace(Thread.currentThread().getStackTrace(), callStack);
178        DumpStackTest.getStackTrace(callStack);
179    }
180
181    static void testMethodHandle() {
182        MethodHandles.Lookup lookup = MethodHandles.lookup();
183        try {
184            MethodHandle handle = lookup.findStatic(DumpStackTest.class, "methodHandle",
185                                                    MethodType.methodType(void.class));
186            handle.invoke();
187        } catch(Throwable t) {
188            throw new RuntimeException(t);
189        }
190    }
191
192    static void methodHandle() {
193        CallFrame[] callStack = new CallFrame[]{
194                new CallFrame(Thread.class, "getStackTrace"),
195                new CallFrame(DumpStackTest.class, "methodHandle"),
196                new CallFrame(DumpStackTest.class, "testMethodHandle"),
197                new CallFrame(DumpStackTest.class, "main"),
198                // if invoked from jtreg
199                new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke0"),
200                new CallFrame("jdk.internal.reflect.NativeMethodAccessorImpl", "invoke"),
201                new CallFrame("jdk.internal.reflect.DelegatingMethodAccessorImpl", "invoke"),
202                new CallFrame(Method.class, "invoke"),
203                new CallFrame(Thread.class, "run")
204        };
205        assertStackTrace(Thread.currentThread().getStackTrace(), callStack);
206        DumpStackTest.getStackTrace(callStack);
207    }
208
209    static void assertStackTrace(StackTraceElement[] actual, CallFrame[] expected) {
210        System.out.println("--- Actual ---");
211        Arrays.stream(actual).forEach(e -> System.out.println(e));
212        System.out.println("--- Expected ---");
213        Arrays.stream(expected).forEach(e -> System.out.println(e));
214
215        for (int i = 0, j = 0; i < actual.length; i++) {
216            // filter test framework classes
217            if (actual[i].getClassName().startsWith("com.sun.javatest.regtest"))
218                continue;
219            assertEquals(actual[i], expected[j++], i);
220        }
221
222    }
223    static void assertEquals(StackTraceElement actual, CallFrame expected, int idx) {
224        if (!actual.getClassName().equals(expected.getClassName()) ||
225                !actual.getFileName().equals(expected.getFileName()) ||
226                !actual.getMethodName().equals(expected.getMethodName())) {
227            throw new RuntimeException("StackTraceElements mismatch at index " + idx +
228                ". Expected [" + expected + "], but get [" + actual + "]");
229        }
230    }
231}
232