/* * Copyright (c) 2015, 2016, 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. */ import static java.lang.StackWalker.Option.*; import java.lang.StackWalker.StackFrame; import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.logging.Logger; import java.util.stream.Collectors; /** * @test * @bug 8140450 * @summary Stack Stream Test * @modules java.logging * @run main/othervm StackStreamTest */ public class StackStreamTest { public static void main(String[] argv) throws Exception { new StackStreamTest().test(); } private static Logger logger = Logger.getLogger("stackstream"); public StackStreamTest() { } public void test() { A.a(); } static class A { public static void a() { B.b(); } } static class B { public static void b() { C.c(); } } static class C { public static void c() { D.d(); } } static class D { public static void d() { E.e(); } } static class E { public static void e() { F.f(); } } static class F { public static void f() { logger.severe("log message"); G.g(); new K().k(); } } private static boolean isTestClass(StackFrame f) { // Filter jtreg frames from the end of the stack return f.getClassName().startsWith("StackStreamTest"); } static class G { static StackWalker STE_WALKER = StackWalker.getInstance(); static StackWalker DEFAULT_WALKER = StackWalker.getInstance(); private static final List<String> GOLDEN_CLASS_NAMES = Arrays.asList("StackStreamTest$G", "StackStreamTest$F", "StackStreamTest$E", "StackStreamTest$D", "StackStreamTest$C", "StackStreamTest$B", "StackStreamTest$A", "StackStreamTest", "StackStreamTest"); private static final List<String> GOLDEN_METHOD_NAMES = Arrays.asList("g", "f", "e", "d", "c", "b", "a", "test", "main"); public static void g() { System.out.println("Thread dump"); Thread.dumpStack(); caller(); firstFrame(); // Check class names System.out.println("check class names"); List<String> sfs = DEFAULT_WALKER.walk(s -> { return s.filter(StackStreamTest::isTestClass) .map(StackFrame::getClassName) .collect(Collectors.toList()); }); equalsOrThrow("class names", sfs, GOLDEN_CLASS_NAMES); // Check method names System.out.println("methodNames()"); sfs = DEFAULT_WALKER.walk(s -> { return s.filter(StackStreamTest::isTestClass) .map(StackFrame::getMethodName) .collect(Collectors.toList());} ); equalsOrThrow("method names", sfs, GOLDEN_METHOD_NAMES); Exception exc = new Exception("G.g stack"); exc.printStackTrace(); System.out.println("Stream of StackTraceElement"); StackWalker.getInstance() .walk(s -> { s.map(StackFrame::toStackTraceElement) .forEach(ste -> System.out.println("STE: " + ste)); return null; }); // Do we need this? System.out.println("Collect StackTraceElement"); List<StackTraceElement> stacktrace = STE_WALKER.walk(s -> { // Filter out jtreg frames return s.filter(StackStreamTest::isTestClass) .collect(Collectors.mapping(StackFrame::toStackTraceElement, Collectors.toList())); }); int i=0; for (StackTraceElement s : stacktrace) { System.out.format(" %d: %s%n", i++, s); } // Check STEs for correctness checkStackTraceElements(GOLDEN_CLASS_NAMES, GOLDEN_METHOD_NAMES, stacktrace); } static void checkStackTraceElements(List<String> classNames, List<String> methodNames, List<StackTraceElement> stes) { if (classNames.size() != methodNames.size() ) { throw new RuntimeException("Test error: classNames and methodNames should be same size"); } if (classNames.size() != stes.size()) { dumpSTEInfo(classNames, methodNames, stes); throw new RuntimeException("wrong number of elements in stes"); } for (int i = 0; i < classNames.size() ; i++) { if (!classNames.get(i).equals(stes.get(i).getClassName()) || !methodNames.get(i).equals(stes.get(i).getMethodName())) { dumpSTEInfo(classNames, methodNames, stes); throw new RuntimeException("class & method names don't match"); } } } static void dumpSTEInfo(List<String> classNames, List<String> methodNames, List<StackTraceElement> stes) { System.out.println("Observed class, method names:"); for (StackTraceElement ste : stes) { System.out.println(" " + ste.getClassName() + ", " + ste.getMethodName()); } System.out.println("Expected class, method names:"); for (int i = 0; i < classNames.size(); i++) { System.out.println(" " + classNames.get(i) + ", " + methodNames.get(i)); } } static void firstFrame() { System.out.println("first frame()"); StackWalker sw = StackWalker.getInstance(RETAIN_CLASS_REFERENCE); sw.forEach(e -> { System.out.println(e.getClassName() + "," + e.getMethodName()); }); System.out.println("\n"); Optional<StackFrame> frame = sw.walk(s -> { return s.filter(e -> { System.err.println(e.getClassName() + " == " + e.getClassName().equals("StackStreamTest")); return e.getClassName().equals("StackStreamTest"); }).findFirst(); }); Class<?> c = frame.get().getDeclaringClass(); System.out.println("\nfirst frame: " + c); if (c != StackStreamTest.class) { throw new RuntimeException("Unexpected first caller class " + c); } } } private static <T> void equalsOrThrow(String label, List<T> list, List<T> expected) { System.out.println("List: " + list); System.out.println("Expectd: " + list); if (!list.equals(expected)) { System.err.println("Observed " + label); for (T s1 : list) { System.out.println(" " + s1); } System.err.println("Expected " + label); for (T s2 : expected) { System.out.println(" " + s2); } throw new RuntimeException("Error with " + label); } } static class K { void k() { k1(); } void k1() { k2(); } void k2() { k3(); } void k3() { k4(); } void k4() { k5(); } void k5() { k6(); } void k6() { k7(); } void k7() { k8(); } void k8() { k9(); } void k9() { k10(); } void k10() { k20(); } void k20() { new Caller().test(); } class Caller { void test() { Class<?> c = StackWalker.getInstance(RETAIN_CLASS_REFERENCE).getCallerClass(); System.out.println("\nTesting K class : " + c); Thread.dumpStack(); if (c != K.class) { throw new RuntimeException("Unexpected caller class "+ c); } } } } static void caller() { Class<?> c = StackWalker.getInstance(RETAIN_CLASS_REFERENCE).getCallerClass(); System.out.println("\ncaller class : " + c); if (c != G.class) { throw new RuntimeException("Unexpected caller class "+ c); } } }