StackWalkTest.java revision 14176:8606d027b2c2
155682Smarkm/*
2233294Sstas * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
3233294Sstas * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4233294Sstas *
555682Smarkm * This code is free software; you can redistribute it and/or modify it
6233294Sstas * under the terms of the GNU General Public License version 2 only, as
7233294Sstas * published by the Free Software Foundation.
8233294Sstas *
955682Smarkm * This code is distributed in the hope that it will be useful, but WITHOUT
10233294Sstas * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11233294Sstas * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1255682Smarkm * version 2 for more details (a copy is included in the LICENSE file that
13233294Sstas * accompanied this code).
14233294Sstas *
15233294Sstas * You should have received a copy of the GNU General Public License version
1655682Smarkm * 2 along with this work; if not, write to the Free Software Foundation,
17233294Sstas * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18233294Sstas *
19233294Sstas * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2055682Smarkm * or visit www.oracle.com if you need additional information or have any
21233294Sstas * questions.
22233294Sstas */
23233294Sstas
24233294Sstasimport static java.lang.StackWalker.Option.*;
25233294Sstasimport java.lang.StackWalker.StackFrame;
26233294Sstasimport java.util.Arrays;
27233294Sstasimport java.util.EnumSet;
28233294Sstasimport java.util.HashSet;
29233294Sstasimport java.util.List;
30233294Sstasimport java.util.Random;
31233294Sstasimport java.util.Set;
3255682Smarkmimport java.util.TreeSet;
3355682Smarkm
3455682Smarkmimport jdk.testlibrary.RandomFactory;
3555682Smarkm
3672445Sassar/**
3772445Sassar * @test
38178825Sdfr * @bug 8140450
3955682Smarkm * @summary Stack Walk Test (use -Dseed=X to set PRNG seed)
40233294Sstas * @library /lib/testlibrary
41233294Sstas * @build jdk.testlibrary.*
4255682Smarkm * @compile StackRecorderUtil.java
4355682Smarkm * @run main/othervm StackWalkTest
4455682Smarkm * @run main/othervm/java.security.policy=stackwalktest.policy StackWalkTest
4572445Sassar * @run main/othervm StackWalkTest -random:50
4655682Smarkm * @run main/othervm/java.security.policy=stackwalktest.policy StackWalkTest -random:50
4755682Smarkm * @run main/othervm -XX:-MemberNameInStackFrame -Dstackwalk.newThrowable=false StackWalkTest -random:50
4855682Smarkm * @run main/othervm -XX:-MemberNameInStackFrame -Dstackwalk.newThrowable=true  StackWalkTest -random:50
49233294Sstas * @run main/othervm -XX:+MemberNameInStackFrame -Dstackwalk.newThrowable=false StackWalkTest -random:50
50233294Sstas * @run main/othervm -XX:+MemberNameInStackFrame -Dstackwalk.newThrowable=true  StackWalkTest -random:50
51233294Sstas * @author danielfuchs, bchristi
5255682Smarkm * @key randomness
5355682Smarkm */
5455682Smarkmpublic class StackWalkTest {
5555682Smarkm    private static boolean random = false;
5655682Smarkm    private static boolean verbose = false;
57233294Sstas    private static int randomRuns = 50;
58233294Sstas
5955682Smarkm    private static final int MAX_RANDOM_DEPTH = 1000;
6072445Sassar
61233294Sstas    static final Set<String> infrastructureClasses = new TreeSet<>(Arrays.asList(
6272445Sassar            "jdk.internal.reflect.NativeMethodAccessorImpl",
6372445Sassar            "jdk.internal.reflect.DelegatingMethodAccessorImpl",
6472445Sassar            "java.lang.reflect.Method",
65178825Sdfr            "com.sun.javatest.regtest.MainWrapper$MainThread",
66178825Sdfr            "com.sun.javatest.regtest.agent.MainWrapper$MainThread",
6772445Sassar            "java.lang.Thread"
68233294Sstas    ));
69233294Sstas    static final List<Class<?>> streamPipelines = Arrays.asList(
7055682Smarkm        classForName("java.util.stream.AbstractPipeline"),
71233294Sstas        classForName("java.util.stream.TerminalOp")
7272445Sassar    );
73233294Sstas    static Class<?> classForName(String name) {
74233294Sstas        try {
7555682Smarkm            return Class.forName(name);
7655682Smarkm        } catch (ClassNotFoundException e){
7755682Smarkm            throw new RuntimeException(e);
7855682Smarkm        }
7955682Smarkm    }
8055682Smarkm
8155682Smarkm    private static boolean isStreamPipeline(Class<?> clazz) {
8255682Smarkm        for (Class<?> c : streamPipelines) {
8355682Smarkm            if (c.isAssignableFrom(clazz)) {
8455682Smarkm                return true;
8555682Smarkm            }
8655682Smarkm        }
8755682Smarkm        return false;
8855682Smarkm    }
8955682Smarkm
9055682Smarkm    StackRecorderUtil recorder;
9155682Smarkm    int count = 0;
92178825Sdfr    boolean didWalk = false;
93178825Sdfr
94233294Sstas    final int estDepth;
95178825Sdfr    final Set<StackWalker.Option> swOptions;
9655682Smarkm
97233294Sstas    public StackWalkTest() {
9855682Smarkm        this(EnumSet.noneOf(StackWalker.Option.class), -1);
9978527Sassar    }
10055682Smarkm
10172445Sassar    public StackWalkTest(Set<StackWalker.Option> swOptions) {
10272445Sassar        this(swOptions, -1);
10372445Sassar    }
10455682Smarkm
105233294Sstas    public StackWalkTest(int estimatedDepth) {
106178825Sdfr        this(EnumSet.noneOf(StackWalker.Option.class), -1);
107233294Sstas    }
108233294Sstas
10955682Smarkm    public StackWalkTest(Set<StackWalker.Option> swOptions, int estimatedDepth) {
11055682Smarkm        this.swOptions = swOptions;
11155682Smarkm        this.estDepth = estimatedDepth;
11255682Smarkm    }
11355682Smarkm
11455682Smarkm    private StackWalker createStackWalker() {
11555682Smarkm        // test all StackWalker factory methods
11655682Smarkm        if (this.estDepth < 0) {
11755682Smarkm            if (swOptions.isEmpty()) {
118178825Sdfr                return StackWalker.getInstance();
119178825Sdfr            } else {
12055682Smarkm                return StackWalker.getInstance(swOptions);
121178825Sdfr            }
122178825Sdfr        }
123178825Sdfr        return StackWalker.getInstance(swOptions, estDepth);
124178825Sdfr    }
125178825Sdfr    public void consume(StackFrame sf) {
126233294Sstas        if (count == 0 && swOptions.contains(StackWalker.Option.RETAIN_CLASS_REFERENCE)
127178825Sdfr                && isStreamPipeline(sf.getDeclaringClass())) {
128178825Sdfr            return;
129178825Sdfr        }
130233294Sstas        if (verbose) {
131178825Sdfr            System.out.println("\t" + sf.getClassName() + "." + sf.getMethodName());
132178825Sdfr        }
133233294Sstas        if (count >= recorder.frameCount()) {
134178825Sdfr            // We've gone past main()...
135233294Sstas            if (infrastructureClasses.contains(sf.getClassName())) {
136178825Sdfr                // safe to ignore
137178825Sdfr                return;
138178825Sdfr            }
139178825Sdfr        }
140178825Sdfr        try {
141178825Sdfr            recorder.compareFrame(count, sf);
142178825Sdfr        } catch (IndexOutOfBoundsException e) {
14355682Smarkm            // Extra non-infra frame in stream
14455682Smarkm            throw new RuntimeException("extra non-infra stack frame at count "
14555682Smarkm                    + count + ": <" + sf + ">", e);
14655682Smarkm        }
14755682Smarkm        count++;
14855682Smarkm    }
14955682Smarkm
15055682Smarkm    public class Call {
15172445Sassar        public void walk(int total, int markAt) {
15272445Sassar            recorder.add(Call.class, "walk", "StackWalkTest.java");
153178825Sdfr            long swFrameCount = createStackWalker().walk(s -> s.count());
154233294Sstas
155178825Sdfr            if (verbose) {
156178825Sdfr                System.out.println("Call.walk() total=" + total + ", markAt=" + markAt);
157178825Sdfr                System.out.println("recorder frames:");
158178825Sdfr                for (StackRecorderUtil.TestFrame f : recorder) {
159178825Sdfr                    System.out.println("\t" + f.declaringClass + "." + f.methodName);
160178825Sdfr                }
161178825Sdfr                System.out.println("\nStackWalker recorded " + swFrameCount + " frames");
162178825Sdfr                System.out.flush();
163233294Sstas            }
164233294Sstas            long recFrameCount = (long)recorder.frameCount();
165233294Sstas            if (swFrameCount < recFrameCount) {
166233294Sstas                throw new RuntimeException("StackWalker recorded fewer frames ("+
167233294Sstas                        swFrameCount + ") than recorded ("+ recorder.frameCount() +
168233294Sstas                        ") - " + "estimatedDepth set to " + estDepth);
169233294Sstas            }
170233294Sstas            if (verbose) {
171233294Sstas                System.out.println("StackWalker frames:");
172233294Sstas            }
173233294Sstas            createStackWalker().forEach(StackWalkTest.this::consume);
174233294Sstas            didWalk = true;
175233294Sstas        }
176233294Sstas        public void call(int total, int current, int markAt) {
17778527Sassar            recorder.add(Call.class, "call", "StackWalkTest.java");
17878527Sassar            if (current < total) {
17978527Sassar                testCall.call(total, current+1, markAt);
18078527Sassar            } else {
181233294Sstas                walk(total, markAt);
182233294Sstas            }
183233294Sstas        }
184233294Sstas    }
185233294Sstas
186233294Sstas    public class Marker extends Call {
187233294Sstas        @Override
18872445Sassar        public void call(int total, int current, int markAt) {
189233294Sstas            recorder.add(Marker.class, "call", "StackWalkTest.java");
19055682Smarkm            if (current < total) {
191233294Sstas                testCall.call(total, current+1, markAt);
192233294Sstas            } else {
19355682Smarkm                walk(total, markAt);
194233294Sstas            }
195233294Sstas        }
196233294Sstas    }
197233294Sstas    private Call markerCall = new Marker();
198233294Sstas
199233294Sstas    public class Test extends Call {
20055682Smarkm        @Override
20155682Smarkm        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        // Test may run into StackOverflow when running in -Xcomp mode on deep stack
240        assert stackDepth <= 1000;
241        assert markAt <= stackDepth : "markAt(" + markAt + ") > stackDepth("
242                + stackDepth + ")";
243        System.out.print("runTest(" + swOptions
244                + "), estimatedDepth=" + estDepth);
245
246        recorder = new StackRecorderUtil(swOptions);
247        recorder.add(callerClass, callerMethod, "StackWalkTest.java");
248        recorder.add(StackWalkTest.class, "runTest", "StackWalkTest.java");
249
250        Test test1 = new Test();
251        test1.call(stackDepth, 0, markAt);
252
253        System.out.println(" finished");
254        if (!didWalk) {
255            throw new IllegalStateException("Test wasn't actually performed");
256        }
257    }
258
259    public static void main(String[] args) {
260        String rand = "-random";
261        String randItems = "-random:";
262        for(String arg : args) {
263            if (arg.startsWith(rand)) {
264                random = true;
265                try {
266                    if(arg.startsWith(randItems)) {
267                        randomRuns = Integer.valueOf(arg.substring(randItems.length()));
268                    }
269                } catch(NumberFormatException e) {}
270            } else if("-verbose".equals(arg)) {
271                verbose = true;
272            }
273        }
274        if (random) {
275            Random rng = RandomFactory.getRandom();
276            for (int iters = 0; iters < randomRuns; iters++) {
277                Set<StackWalker.Option> opts = new HashSet<>();
278                if (rng.nextBoolean()) {
279                    opts.add(RETAIN_CLASS_REFERENCE);
280                }
281
282                int depth = 1 + rng.nextInt(MAX_RANDOM_DEPTH);
283
284                StackWalkTest swt;
285                if (rng.nextBoolean() && depth > 1) {
286                    // Test that specifying an estimatedDepth doesn't prevent
287                    // full stack traversal
288                    swt = new StackWalkTest(opts, 1+rng.nextInt(depth-1));
289                } else {
290                    swt = new StackWalkTest(opts);
291                }
292
293                int markAt = rng.nextInt(depth+1);
294                System.out.print(depth + "@" + markAt + " ");
295                System.out.flush();
296                swt.runTest(StackWalkTest.class, "main", depth, markAt);
297            }
298        } else {
299            // Long stack, default maxDepth
300            StackWalkTest swt;
301            swt = new StackWalkTest();
302            swt.runTest(StackWalkTest.class, "main", 1000, 10);
303
304            // Long stack, matching maxDepth
305            swt = new StackWalkTest(2000);
306            swt.runTest(StackWalkTest.class, "main", 1000, 10);
307
308            // Long stack, maximum maxDepth
309            swt = new StackWalkTest(Integer.MAX_VALUE);
310            swt.runTest(StackWalkTest.class, "main", 1000, 10);
311
312            //
313            // Single batch
314            //
315            swt = new StackWalkTest(); // default maxDepth
316            swt.runTest(StackWalkTest.class, "main", 6, 3);
317
318            swt = new StackWalkTest(4); // maxDepth < stack
319            swt.runTest(StackWalkTest.class, "main", 6, 3);
320
321            swt = new StackWalkTest(2); // maxDepth < marker
322            swt.runTest(StackWalkTest.class, "main", 6, 4);
323
324            //
325            // 2 batches
326            //
327            swt = new StackWalkTest(); // default maxDepth
328            swt.runTest(StackWalkTest.class, "main", 24, 10);
329            swt = new StackWalkTest(18); // maxDepth < stack
330            swt.runTest(StackWalkTest.class, "main", 24, 10);
331            swt = new StackWalkTest(8); // maxDepth < marker
332            swt.runTest(StackWalkTest.class, "main", 24, 10);
333
334            //
335            // 3 batch
336            //
337            swt = new StackWalkTest(); // default maxDepth
338            swt.runTest(StackWalkTest.class, "main", 60, 20);
339            swt = new StackWalkTest(35); // maxDepth < stack
340            swt.runTest(StackWalkTest.class, "main", 60, 20);
341            swt = new StackWalkTest(8); // maxDepth < marker
342            swt.runTest(StackWalkTest.class, "main", 60, 20);
343
344            //
345            // StackWalker.Options
346            //
347            swt = new StackWalkTest();
348            swt.runTest(StackWalkTest.class, "main", 50, 10);
349
350            swt = new StackWalkTest(EnumSet.of(RETAIN_CLASS_REFERENCE));
351            swt.runTest(StackWalkTest.class, "main", 80, 40);
352
353            swt = new StackWalkTest(EnumSet.of(RETAIN_CLASS_REFERENCE), 50);
354            swt.runTest(StackWalkTest.class, "main", 1000, 524);
355        }
356    }
357}
358