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