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