1/*
2 * Copyright (c) 2015, 2016, 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
24import static java.lang.StackWalker.Option.*;
25import java.lang.StackWalker.StackFrame;
26import java.util.Arrays;
27import java.util.List;
28import java.util.Optional;
29import java.util.logging.Logger;
30import java.util.stream.Collectors;
31
32/**
33 * @test
34 * @bug 8140450
35 * @summary Stack Stream Test
36 * @modules java.logging
37 * @run main/othervm StackStreamTest
38 */
39public class StackStreamTest {
40    public static void main(String[] argv) throws Exception {
41        new StackStreamTest().test();
42    }
43
44    private static Logger logger = Logger.getLogger("stackstream");
45    public StackStreamTest() {
46    }
47
48    public void test() {
49        A.a();
50    }
51    static class A {
52        public static void a() {
53            B.b();
54        }
55    }
56    static class B {
57        public static void b() {
58            C.c();
59        }
60    }
61    static class C {
62        public static void c() {
63            D.d();
64        }
65    }
66    static class D {
67        public static void d() {
68            E.e();
69        }
70    }
71    static class E {
72        public static void e() {
73            F.f();
74        }
75    }
76    static class F {
77        public static void f() {
78            logger.severe("log message");
79            G.g();
80            new K().k();
81        }
82    }
83
84    private static boolean isTestClass(StackFrame f) {
85        // Filter jtreg frames from the end of the stack
86        return f.getClassName().startsWith("StackStreamTest");
87    }
88
89    static class G {
90        static StackWalker STE_WALKER = StackWalker.getInstance();
91        static StackWalker DEFAULT_WALKER = StackWalker.getInstance();
92
93        private static final List<String> GOLDEN_CLASS_NAMES =
94                Arrays.asList("StackStreamTest$G",
95                              "StackStreamTest$F",
96                              "StackStreamTest$E",
97                              "StackStreamTest$D",
98                              "StackStreamTest$C",
99                              "StackStreamTest$B",
100                              "StackStreamTest$A",
101                              "StackStreamTest",
102                              "StackStreamTest");
103        private static final List<String> GOLDEN_METHOD_NAMES =
104            Arrays.asList("g", "f", "e", "d", "c", "b", "a", "test", "main");
105
106
107        public static void g() {
108
109            System.out.println("Thread dump");
110            Thread.dumpStack();
111
112            caller();
113            firstFrame();
114
115            // Check class names
116            System.out.println("check class names");
117            List<String> sfs = DEFAULT_WALKER.walk(s -> {
118                return s.filter(StackStreamTest::isTestClass)
119                        .map(StackFrame::getClassName)
120                        .collect(Collectors.toList());
121            });
122            equalsOrThrow("class names", sfs, GOLDEN_CLASS_NAMES);
123
124            // Check method names
125            System.out.println("methodNames()");
126            sfs = DEFAULT_WALKER.walk(s -> {
127                return s.filter(StackStreamTest::isTestClass)
128                        .map(StackFrame::getMethodName)
129                        .collect(Collectors.toList());}
130            );
131            equalsOrThrow("method names", sfs, GOLDEN_METHOD_NAMES);
132
133            Exception exc = new Exception("G.g stack");
134            exc.printStackTrace();
135
136            System.out.println("Stream of StackTraceElement");
137            StackWalker.getInstance()
138                .walk(s ->
139                {
140                    s.map(StackFrame::toStackTraceElement)
141                            .forEach(ste -> System.out.println("STE: " + ste));
142                    return null;
143                });
144
145            // Do we need this?
146            System.out.println("Collect StackTraceElement");
147            List<StackTraceElement> stacktrace = STE_WALKER.walk(s ->
148            {
149                // Filter out jtreg frames
150                return s.filter(StackStreamTest::isTestClass)
151                        .collect(Collectors.mapping(StackFrame::toStackTraceElement, Collectors.toList()));
152            });
153            int i=0;
154            for (StackTraceElement s : stacktrace) {
155                System.out.format("  %d: %s%n", i++, s);
156            }
157
158            // Check STEs for correctness
159            checkStackTraceElements(GOLDEN_CLASS_NAMES, GOLDEN_METHOD_NAMES, stacktrace);
160        }
161
162        static void checkStackTraceElements(List<String> classNames,
163                                            List<String> methodNames,
164                                            List<StackTraceElement> stes) {
165            if (classNames.size() != methodNames.size() ) {
166                throw new RuntimeException("Test error: classNames and methodNames should be same size");
167            }
168            if (classNames.size() != stes.size()) {
169                dumpSTEInfo(classNames, methodNames, stes);
170                throw new RuntimeException("wrong number of elements in stes");
171            }
172            for (int i = 0; i < classNames.size() ; i++) {
173                if (!classNames.get(i).equals(stes.get(i).getClassName()) ||
174                    !methodNames.get(i).equals(stes.get(i).getMethodName())) {
175                    dumpSTEInfo(classNames, methodNames, stes);
176                    throw new RuntimeException("class & method names don't match");
177                }
178            }
179        }
180
181        static void dumpSTEInfo(List<String> classNames, List<String> methodNames,
182                                List<StackTraceElement> stes) {
183            System.out.println("Observed class, method names:");
184            for (StackTraceElement ste : stes) {
185                System.out.println("  " + ste.getClassName() + ", " + ste.getMethodName());
186            }
187            System.out.println("Expected class, method names:");
188            for (int i = 0; i < classNames.size(); i++) {
189                System.out.println("  " + classNames.get(i) + ", " + methodNames.get(i));
190            }
191        }
192
193        static void firstFrame() {
194            System.out.println("first frame()");
195            StackWalker sw = StackWalker.getInstance(RETAIN_CLASS_REFERENCE);
196            sw.forEach(e -> {
197                System.out.println(e.getClassName() + "," + e.getMethodName());
198            });
199            System.out.println("\n");
200            Optional<StackFrame> frame = sw.walk(s ->
201            {
202                 return s.filter(e -> {
203                            System.err.println(e.getClassName() + " == " +
204                                               e.getClassName().equals("StackStreamTest"));
205                            return e.getClassName().equals("StackStreamTest");
206                        }).findFirst();
207            });
208            Class<?> c = frame.get().getDeclaringClass();
209            System.out.println("\nfirst frame: " + c);
210            if (c != StackStreamTest.class) {
211                throw new RuntimeException("Unexpected first caller class " + c);
212            }
213        }
214    }
215
216    private static <T> void equalsOrThrow(String label, List<T> list, List<T> expected) {
217        System.out.println("List:    " + list);
218        System.out.println("Expectd: " + list);
219        if (!list.equals(expected)) {
220            System.err.println("Observed " + label);
221            for (T s1 : list) {
222                System.out.println("  " + s1);
223            }
224            System.err.println("Expected " + label);
225            for (T s2 : expected) {
226                System.out.println("  " + s2);
227            }
228            throw new RuntimeException("Error with " + label);
229        }
230    }
231
232
233    static class K {
234        void k() {
235            k1();
236        }
237        void k1() {
238            k2();
239        }
240        void k2() {
241            k3();
242        }
243        void k3() {
244            k4();
245        }
246        void k4() {
247            k5();
248        }
249        void k5() {
250            k6();
251        }
252        void k6() {
253            k7();
254        }
255        void k7() {
256            k8();
257        }
258        void k8() {
259            k9();
260        }
261        void k9() {
262            k10();
263        }
264        void k10() {
265            k20();
266        }
267        void k20() {
268            new Caller().test();
269        }
270
271        class Caller {
272            void test() {
273                Class<?> c = StackWalker.getInstance(RETAIN_CLASS_REFERENCE).getCallerClass();
274                System.out.println("\nTesting K class : " + c);
275                Thread.dumpStack();
276                if (c != K.class) {
277                    throw new RuntimeException("Unexpected caller class "+ c);
278                }
279            }
280        }
281    }
282
283    static void caller() {
284        Class<?> c = StackWalker.getInstance(RETAIN_CLASS_REFERENCE).getCallerClass();
285        System.out.println("\ncaller class : " + c);
286        if (c != G.class) {
287            throw new RuntimeException("Unexpected caller class "+ c);
288        }
289    }
290
291}
292