| /* | |
| * Copyright (c) 2015, 2023, 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 java.lang.StackWalker.StackFrame; | |
| import java.util.Arrays; | |
| import java.util.List; | |
| import java.util.Optional; | |
| import java.util.Set; | |
| import java.util.logging.Logger; | |
| import java.util.stream.Collectors; | |
| import static java.lang.StackWalker.Option.*; | |
| /** | |
| * @test | |
| * @bug 8140450 8210375 | |
| * @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); | |
| System.out.println("Collect classes"); | |
| List<Class<?>> classes = StackWalker.getInstance(Set.of(DROP_METHOD_INFO, RETAIN_CLASS_REFERENCE)) | |
| .walk(s -> { | |
| return s.map(StackFrame::getDeclaringClass).collect(Collectors.toList()); | |
| }); | |
| for (i=0; i < GOLDEN_CLASS_NAMES.size(); i++) { | |
| Class<?> c = classes.get(i); | |
| if (!GOLDEN_CLASS_NAMES.get(i).equals(c.getName())) { | |
| throw new RuntimeException("unexpected class at " + i + " " + c.getName() + | |
| " expected " + GOLDEN_CLASS_NAMES.get(i)); | |
| } | |
| } | |
| } | |
| 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("Expected: " + expected); | |
| 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); | |
| } | |
| } | |
| } |