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