test/jdk/java/lang/StackWalker/StackStreamTest.java
author phh
Sat, 30 Nov 2019 14:33:05 -0800
changeset 59330 5b96c12f909d
parent 47216 71c04702a3d5
permissions -rw-r--r--
8234541: C1 emits an empty message when it inlines successfully Summary: Use "inline" as the message when successfull Reviewed-by: thartmann, mdoerr Contributed-by: navy.xliu@gmail.com

/*
 * 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);
        }
    }

}