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