• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2015, 2017, 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.EnumSet;
30 import java.util.HashSet;
31 import java.util.List;
32 import java.util.Random;
33 import java.util.Set;
34 import java.util.TreeSet;
35 
36 import jdk.test.lib.RandomFactory;
37 import org.testng.annotations.Test;
38 
39 /**
40  * @test
41  * @bug 8140450
42  * @summary Stack Walk Test (use -Dseed=X to set PRNG seed)
43  * @library /test/lib
44  * @build jdk.test.lib.RandomFactory
45  * @compile StackRecorderUtil.java
46  * @run main/othervm StackWalkTest
47  * @run main/othervm/java.security.policy=stackwalktest.policy StackWalkTest
48  * @run main/othervm StackWalkTest -random:50
49  * @run main/othervm/java.security.policy=stackwalktest.policy StackWalkTest -random:50
50  * @author danielfuchs, bchristi
51  * @key randomness
52  */
53 public class StackWalkTest {
54     private static boolean random = false;
55     private static boolean verbose = false;
56     private static int randomRuns = 50;
57 
58     private static final int MAX_RANDOM_DEPTH = 1000;
59 
60     static final Set<String> infrastructureClasses = new TreeSet<>(Arrays.asList(
61             "jdk.internal.reflect.NativeMethodAccessorImpl",
62             "jdk.internal.reflect.DelegatingMethodAccessorImpl",
63             "java.lang.reflect.Method",
64             "com.sun.javatest.regtest.MainWrapper$MainThread",
65             "com.sun.javatest.regtest.agent.MainWrapper$MainThread",
66             "java.lang.Thread"
67     ));
68     static final List<Class<?>> streamPipelines = Arrays.asList(
69         classForName("java.util.stream.AbstractPipeline"),
70         classForName("java.util.stream.TerminalOp")
71     );
classForName(String name)72     static Class<?> classForName(String name) {
73         try {
74             return Class.forName(name);
75         } catch (ClassNotFoundException e){
76             throw new RuntimeException(e);
77         }
78     }
79 
isStreamPipeline(Class<?> clazz)80     private static boolean isStreamPipeline(Class<?> clazz) {
81         for (Class<?> c : streamPipelines) {
82             if (c.isAssignableFrom(clazz)) {
83                 return true;
84             }
85         }
86         return false;
87     }
88 
89     StackRecorderUtil recorder;
90     int count = 0;
91     boolean didWalk = false;
92 
93     final int estDepth;
94     final Set<StackWalker.Option> swOptions;
95 
StackWalkTest()96     public StackWalkTest() {
97         this(EnumSet.noneOf(StackWalker.Option.class), -1);
98     }
99 
StackWalkTest(Set<StackWalker.Option> swOptions)100     public StackWalkTest(Set<StackWalker.Option> swOptions) {
101         this(swOptions, -1);
102     }
103 
StackWalkTest(int estimatedDepth)104     public StackWalkTest(int estimatedDepth) {
105         // Android-changed: Error-prone catches this upstream bug that the param is not used.
106         // this(EnumSet.noneOf(StackWalker.Option.class), -1);
107         this(EnumSet.noneOf(StackWalker.Option.class), estimatedDepth);
108     }
109 
StackWalkTest(Set<StackWalker.Option> swOptions, int estimatedDepth)110     public StackWalkTest(Set<StackWalker.Option> swOptions, int estimatedDepth) {
111         this.swOptions = swOptions;
112         this.estDepth = estimatedDepth;
113     }
114 
createStackWalker()115     private StackWalker createStackWalker() {
116         // test all StackWalker factory methods
117         if (this.estDepth < 0) {
118             if (swOptions.isEmpty()) {
119                 return StackWalker.getInstance();
120             } else {
121                 return StackWalker.getInstance(swOptions);
122             }
123         }
124         return StackWalker.getInstance(swOptions, estDepth);
125     }
consume(StackFrame sf)126     public void consume(StackFrame sf) {
127         if (count == 0 && swOptions.contains(StackWalker.Option.RETAIN_CLASS_REFERENCE)
128                 && isStreamPipeline(sf.getDeclaringClass())) {
129             return;
130         }
131         if (verbose) {
132             System.out.println("\t" + sf.getClassName() + "." + sf.getMethodName());
133         }
134         if (count >= recorder.frameCount()) {
135             // We've gone past main()...
136             if (infrastructureClasses.contains(sf.getClassName())) {
137                 // safe to ignore
138                 return;
139             }
140 
141             // Android-added: Ignore extra Android test classes.
142             String[] androidInfraPackages = new String[] {
143                 "android.app.Instrumentation",
144                 "androidx.test",
145                 "com.android.cts",
146                 "org.junit",
147                 "org.testng",
148                 "vogar.target",
149             };
150             for (String pkg : androidInfraPackages) {
151                 if (sf.getClassName().startsWith(pkg)) {
152                     return;
153                 }
154             }
155         }
156         try {
157             recorder.compareFrame(count, sf);
158         } catch (IndexOutOfBoundsException e) {
159             // Extra non-infra frame in stream
160             throw new RuntimeException("extra non-infra stack frame at count "
161                     + count + ": <" + sf + ">", e);
162         }
163         count++;
164     }
165 
166     public class Call {
walk(int total, int markAt)167         public void walk(int total, int markAt) {
168             recorder.add(Call.class, "walk", "StackWalkTest.java");
169             long swFrameCount = createStackWalker().walk(s -> s.count());
170 
171             if (verbose) {
172                 System.out.println("Call.walk() total=" + total + ", markAt=" + markAt);
173                 System.out.println("recorder frames:");
174                 for (StackRecorderUtil.TestFrame f : recorder) {
175                     System.out.println("\t" + f.declaringClass + "." + f.methodName);
176                 }
177                 System.out.println("\nStackWalker recorded " + swFrameCount + " frames");
178                 System.out.flush();
179             }
180             long recFrameCount = (long)recorder.frameCount();
181             if (swFrameCount < recFrameCount) {
182                 throw new RuntimeException("StackWalker recorded fewer frames ("+
183                         swFrameCount + ") than recorded ("+ recorder.frameCount() +
184                         ") - " + "estimatedDepth set to " + estDepth);
185             }
186             if (verbose) {
187                 System.out.println("StackWalker frames:");
188             }
189             createStackWalker().forEach(StackWalkTest.this::consume);
190             didWalk = true;
191         }
call(int total, int current, int markAt)192         public void call(int total, int current, int markAt) {
193             recorder.add(Call.class, "call", "StackWalkTest.java");
194             if (current < total) {
195                 testCall.call(total, current+1, markAt);
196             } else {
197                 walk(total, markAt);
198             }
199         }
200     }
201 
202     public class Marker extends Call {
203         @Override
call(int total, int current, int markAt)204         public void call(int total, int current, int markAt) {
205             recorder.add(Marker.class, "call", "StackWalkTest.java");
206             if (current < total) {
207                 testCall.call(total, current+1, markAt);
208             } else {
209                 walk(total, markAt);
210             }
211         }
212     }
213     private Call markerCall = new Marker();
214 
215     public class Test extends Call {
216         @Override
call(int total, int current, int markAt)217         public void call(int total, int current, int markAt) {
218             recorder.add(Test.class, "call", "StackWalkTest.java");
219             if (current < total) {
220                 int nexti = current + 1;
221                 if (nexti==markAt) {
222                     markerCall.call(total, nexti, markAt);
223                 } else {
224                     testCall.call2(total, nexti, markAt);
225                 }
226             } else {
227                 walk(total, markAt);
228             }
229         }
call2(int total, int current, int markAt)230         public void call2(int total, int current, int markAt) {
231             recorder.add(Test.class, "call2", "StackWalkTest.java");
232             if (current < total) {
233                 int nexti = current + 1;
234                 if (nexti==markAt) {
235                     markerCall.call(total, nexti, markAt);
236                 } else {
237                     test2Call.call(total, nexti, markAt);
238                 }
239             } else {
240                 walk(total, markAt);
241             }
242         }
243     }
244     private Test testCall = new Test();
245 
246     /** Inherits call() from Call */
247     public class Test2 extends Call {}
248     private Test2 test2Call = new Test2();
249 
runTest(Class callerClass, String callerMethod, int stackDepth, int markAt)250     public void runTest(Class callerClass, String callerMethod, int stackDepth,
251                         int markAt) {
252         if (didWalk) {
253             throw new IllegalStateException("StackWalkTest already used");
254         }
255         // Test may run into StackOverflow when running in -Xcomp mode on deep stack
256         assert stackDepth <= 1000;
257         assert markAt <= stackDepth : "markAt(" + markAt + ") > stackDepth("
258                 + stackDepth + ")";
259         System.out.print("runTest(" + swOptions
260                 + "), estimatedDepth=" + estDepth);
261 
262         recorder = new StackRecorderUtil(swOptions);
263         recorder.add(callerClass, callerMethod, "StackWalkTest.java");
264         recorder.add(StackWalkTest.class, "runTest", "StackWalkTest.java");
265 
266         Test test1 = new Test();
267         test1.call(stackDepth, 0, markAt);
268 
269         System.out.println(" finished");
270         if (!didWalk) {
271             throw new IllegalStateException("Test wasn't actually performed");
272         }
273     }
274 
main(String[] args)275     public static void main(String[] args) {
276         String rand = "-random";
277         String randItems = "-random:";
278         for(String arg : args) {
279             if (arg.startsWith(rand)) {
280                 random = true;
281                 try {
282                     if(arg.startsWith(randItems)) {
283                         randomRuns = Integer.valueOf(arg.substring(randItems.length()));
284                     }
285                 } catch(NumberFormatException e) {}
286             } else if("-verbose".equals(arg)) {
287                 verbose = true;
288             }
289         }
290         if (random) {
291             Random rng = RandomFactory.getRandom();
292             for (int iters = 0; iters < randomRuns; iters++) {
293                 Set<StackWalker.Option> opts = new HashSet<>();
294                 if (rng.nextBoolean()) {
295                     opts.add(RETAIN_CLASS_REFERENCE);
296                 }
297 
298                 int depth = 1 + rng.nextInt(MAX_RANDOM_DEPTH);
299 
300                 StackWalkTest swt;
301                 if (rng.nextBoolean() && depth > 1) {
302                     // Test that specifying an estimatedDepth doesn't prevent
303                     // full stack traversal
304                     swt = new StackWalkTest(opts, 1+rng.nextInt(depth-1));
305                 } else {
306                     swt = new StackWalkTest(opts);
307                 }
308 
309                 int markAt = rng.nextInt(depth+1);
310                 System.out.print(depth + "@" + markAt + " ");
311                 System.out.flush();
312                 swt.runTest(StackWalkTest.class, "main", depth, markAt);
313             }
314         } else {
315             // Long stack, default maxDepth
316             StackWalkTest swt;
317             swt = new StackWalkTest();
318             swt.runTest(StackWalkTest.class, "main", 1000, 10);
319 
320             // Long stack, matching maxDepth
321             swt = new StackWalkTest(2000);
322             swt.runTest(StackWalkTest.class, "main", 1000, 10);
323 
324             // Long stack, maximum maxDepth
325             swt = new StackWalkTest(Integer.MAX_VALUE);
326             swt.runTest(StackWalkTest.class, "main", 1000, 10);
327 
328             //
329             // Single batch
330             //
331             swt = new StackWalkTest(); // default maxDepth
332             swt.runTest(StackWalkTest.class, "main", 6, 3);
333 
334             swt = new StackWalkTest(4); // maxDepth < stack
335             swt.runTest(StackWalkTest.class, "main", 6, 3);
336 
337             swt = new StackWalkTest(2); // maxDepth < marker
338             swt.runTest(StackWalkTest.class, "main", 6, 4);
339 
340             //
341             // 2 batches
342             //
343             swt = new StackWalkTest(); // default maxDepth
344             swt.runTest(StackWalkTest.class, "main", 24, 10);
345             swt = new StackWalkTest(18); // maxDepth < stack
346             swt.runTest(StackWalkTest.class, "main", 24, 10);
347             swt = new StackWalkTest(8); // maxDepth < marker
348             swt.runTest(StackWalkTest.class, "main", 24, 10);
349 
350             //
351             // 3 batch
352             //
353             swt = new StackWalkTest(); // default maxDepth
354             swt.runTest(StackWalkTest.class, "main", 60, 20);
355             swt = new StackWalkTest(35); // maxDepth < stack
356             swt.runTest(StackWalkTest.class, "main", 60, 20);
357             swt = new StackWalkTest(8); // maxDepth < marker
358             swt.runTest(StackWalkTest.class, "main", 60, 20);
359 
360             //
361             // StackWalker.Options
362             //
363             swt = new StackWalkTest();
364             swt.runTest(StackWalkTest.class, "main", 50, 10);
365 
366             swt = new StackWalkTest(EnumSet.of(RETAIN_CLASS_REFERENCE));
367             swt.runTest(StackWalkTest.class, "main", 80, 40);
368 
369             swt = new StackWalkTest(EnumSet.of(RETAIN_CLASS_REFERENCE), 50);
370             swt.runTest(StackWalkTest.class, "main", 1000, 524);
371         }
372     }
373 }
374