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 }