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