/* * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * @test * @bug 8143214 * @summary Verify outputs of Thread.dumpStack() and Throwable.printStackTrace(). * This test should also been run against jdk9 successfully except of * VM option MemberNameInStackFrame. * @run main/othervm DumpStackTest * @run main/othervm -Dstackwalk.newThrowable=false DumpStackTest * @run main/othervm -Dstackwalk.newThrowable=true -XX:-MemberNameInStackFrame DumpStackTest * @run main/othervm -Dstackwalk.newThrowable=true -XX:+MemberNameInStackFrame DumpStackTest */ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.reflect.Method; import java.util.Arrays; import java.util.function.Consumer; public class DumpStackTest { public static void main(String args[]) { test(); testThread(); testLambda(); testMethodInvoke(); testMethodHandle(); } static class CallFrame { final String classname; final String methodname; CallFrame(Class c, String methodname) { this(c.getName(), methodname); } CallFrame(String classname, String methodname) { this.classname = classname; this.methodname = methodname; } String getClassName() { return classname; } String getMethodName() { return methodname; } String getFileName() { int i = classname.lastIndexOf('.'); int j = classname.lastIndexOf('$'); String name = classname.substring(i+1, j >= 0 ? j : classname.length()); return name + ".java"; } @Override public String toString() { return classname + "." + methodname + "(" + getFileName() + ")"; } } static void test() { CallFrame[] callStack = new CallFrame[] { new CallFrame(Thread.class, "getStackTrace"), new CallFrame(DumpStackTest.class, "test"), new CallFrame(DumpStackTest.class, "main"), // if invoked from jtreg new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke0"), // non-public class new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke"), new CallFrame("sun.reflect.DelegatingMethodAccessorImpl", "invoke"), new CallFrame(Method.class, "invoke"), new CallFrame(Thread.class, "run"), }; assertStackTrace(Thread.currentThread().getStackTrace(), callStack); getStackTrace(callStack); } static void getStackTrace(CallFrame[] callStack) { // this method is the top of the stack callStack[0] = new CallFrame(DumpStackTest.class, "getStackTrace"); try { throw new RuntimeException(); } catch(RuntimeException ex) { assertStackTrace(ex.getStackTrace(), callStack); } } static void testThread() { Thread t1 = new Thread() { public void run() { c(); } void c() { CallFrame[] callStack = new CallFrame[] { new CallFrame(Thread.class, "getStackTrace"), new CallFrame(this.getClass(), "c"), new CallFrame(this.getClass(), "run") }; assertStackTrace(Thread.currentThread().getStackTrace(), callStack); DumpStackTest.getStackTrace(callStack); } }; t1.start(); try { t1.join(); } catch(InterruptedException e) {} } static void testLambda() { Consumer c = (x) -> consumeLambda(); c.accept(null); } static void consumeLambda() { CallFrame[] callStack = new CallFrame[]{ new CallFrame(Thread.class, "getStackTrace"), new CallFrame(DumpStackTest.class, "consumeLambda"), new CallFrame(DumpStackTest.class, "lambda$testLambda$0"), new CallFrame(DumpStackTest.class, "testLambda"), new CallFrame(DumpStackTest.class, "main"), // if invoked from jtreg new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke0"), new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke"), new CallFrame("sun.reflect.DelegatingMethodAccessorImpl", "invoke"), new CallFrame(Method.class, "invoke"), new CallFrame(Thread.class, "run") }; assertStackTrace(Thread.currentThread().getStackTrace(), callStack); DumpStackTest.getStackTrace(callStack); } static void testMethodInvoke() { try { Method m = DumpStackTest.class.getDeclaredMethod("methodInvoke"); m.invoke(null); } catch(Exception e) { throw new RuntimeException(e); } } static void methodInvoke() { CallFrame[] callStack = new CallFrame[] { new CallFrame(Thread.class, "getStackTrace"), new CallFrame(DumpStackTest.class, "methodInvoke"), new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke0"), new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke"), new CallFrame("sun.reflect.DelegatingMethodAccessorImpl", "invoke"), new CallFrame(Method.class, "invoke"), new CallFrame(DumpStackTest.class, "testMethodInvoke"), new CallFrame(DumpStackTest.class, "main"), // if invoked from jtreg new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke0"), new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke"), new CallFrame("sun.reflect.DelegatingMethodAccessorImpl", "invoke"), new CallFrame(Method.class, "invoke"), new CallFrame(Thread.class, "run") }; assertStackTrace(Thread.currentThread().getStackTrace(), callStack); DumpStackTest.getStackTrace(callStack); } static void testMethodHandle() { MethodHandles.Lookup lookup = MethodHandles.lookup(); try { MethodHandle handle = lookup.findStatic(DumpStackTest.class, "methodHandle", MethodType.methodType(void.class)); handle.invoke(); } catch(Throwable t) { throw new RuntimeException(t); } } static void methodHandle() { CallFrame[] callStack = new CallFrame[]{ new CallFrame(Thread.class, "getStackTrace"), new CallFrame(DumpStackTest.class, "methodHandle"), new CallFrame(DumpStackTest.class, "testMethodHandle"), new CallFrame(DumpStackTest.class, "main"), // if invoked from jtreg new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke0"), new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke"), new CallFrame("sun.reflect.DelegatingMethodAccessorImpl", "invoke"), new CallFrame(Method.class, "invoke"), new CallFrame(Thread.class, "run") }; assertStackTrace(Thread.currentThread().getStackTrace(), callStack); DumpStackTest.getStackTrace(callStack); } static void assertStackTrace(StackTraceElement[] actual, CallFrame[] expected) { System.out.println("--- Actual ---"); Arrays.stream(actual).forEach(e -> System.out.println(e)); System.out.println("--- Expected ---"); Arrays.stream(expected).forEach(e -> System.out.println(e)); for (int i = 0, j = 0; i < actual.length; i++) { // filter test framework classes if (actual[i].getClassName().startsWith("com.sun.javatest.regtest")) continue; assertEquals(actual[i], expected[j++], i); } } static void assertEquals(StackTraceElement actual, CallFrame expected, int idx) { if (!actual.getClassName().equals(expected.getClassName()) || !actual.getFileName().equals(expected.getFileName()) || !actual.getMethodName().equals(expected.getMethodName())) { throw new RuntimeException("StackTraceElements mismatch at index " + idx + ". Expected [" + expected + "], but get [" + actual + "]"); } } }