jdk/test/java/lang/StackWalker/StackWalkTest.java
changeset 34371 cc29db9f73d8
parent 34369 b6df4cc80001
parent 34370 70d1245398ed
child 34372 ccdd9223ab7a
equal deleted inserted replaced
34369:b6df4cc80001 34371:cc29db9f73d8
     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 
       
    24 import static java.lang.StackWalker.Option.*;
       
    25 import java.lang.StackWalker.StackFrame;
       
    26 import java.util.Arrays;
       
    27 import java.util.EnumSet;
       
    28 import java.util.HashSet;
       
    29 import java.util.List;
       
    30 import java.util.Random;
       
    31 import java.util.Set;
       
    32 import java.util.TreeSet;
       
    33 
       
    34 import 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  */
       
    54 public 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 }