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 * @author danielfuchs, bchristi 48 * @key randomness 49 */ 50 public class StackWalkTest { 51 // TODO: test rest of StackWalker options (SHOW_*_FRAMES) 52 // TODO: record unexpected classes instead of just dying 53 // TODO: add -verbose flag 54 // TODO: add real checking to Lambda inner-walk 55 56 private final static boolean verbose = false; 57 private final static int randomIters = 200; 58 private final static int DEFAULT_MAX_STACK_FRAMES = 1024; //StackWalker.MAX_STACK_FRAMES 59 private final static int MAX_RANDOM_DEPTH = 2000; 60 private final static boolean CREATE_STACK_TRACE_ELEMENTS = true; 61 62 private static boolean random = false; 63 64 static final Set<String> infrastructureClasses = new TreeSet<>(Arrays.asList( 65 "sun.reflect.NativeMethodAccessorImpl", 66 "sun.reflect.DelegatingMethodAccessorImpl", 67 "java.lang.reflect.Method", 68 "com.sun.javatest.regtest.MainWrapper$MainThread", 69 "com.sun.javatest.regtest.agent.MainWrapper$MainThread", 70 "java.lang.Thread" 71 )); 72 static final List<Class<?>> streamPipelines = Arrays.asList( 73 classForName("java.util.stream.AbstractPipeline"), 74 classForName("java.util.stream.TerminalOp") 75 ); 76 static Class<?> classForName(String name) { 77 try { 78 return Class.forName(name); 79 } catch (ClassNotFoundException e){ 80 throw new RuntimeException(e); 81 } 82 } 83 84 private static boolean isStreamPipeline(Class<?> clazz) { 85 for (Class<?> c : streamPipelines) { 86 if (c.isAssignableFrom(clazz)) { 87 return true; 88 } 89 } 90 return false; 91 } 92 93 StackRecorderUtil recorder; 94 int count = 0; 95 boolean didWalk = false; 96 97 final int maxDepth; 98 final Set<StackWalker.Option> swOptions; 99 100 public StackWalkTest() { 101 this(DEFAULT_MAX_STACK_FRAMES, null); 102 } 103 104 public StackWalkTest(Set<StackWalker.Option> swOptions) { 105 this(DEFAULT_MAX_STACK_FRAMES, swOptions); 106 } 107 108 public StackWalkTest(int maxDepth) { 109 this(maxDepth, null); 110 } 111 112 public StackWalkTest(int maxDepth, Set<StackWalker.Option> swOptions) { 113 this.maxDepth = maxDepth; 114 this.swOptions = swOptions != null 115 ? swOptions 116 : EnumSet.noneOf(StackWalker.Option.class); 117 } 118 119 private StackWalker createStackWalker() { 120 // test the other ctors, if applicable 121 if (this.maxDepth == DEFAULT_MAX_STACK_FRAMES) { 122 if (swOptions.isEmpty()) { 123 return StackWalker.create(); 124 } else { 125 return StackWalker.create(swOptions); 126 } 127 } 128 return new StackWalker(maxDepth, swOptions); 129 } 130 public void consume(StackFrame sf) { 131 if (count == 0 && swOptions.contains(StackWalker.Option.RETAIN_CLASS_REFERENCE) 132 && isStreamPipeline(sf.getDeclaringClass())) { 133 return; 134 } 135 if (verbose) { 136 System.out.println("\t" + sf.getClassName() + "." + sf.getMethodName()); 137 } 138 if (count >= recorder.frameCount()) { 139 // We've gone past main()... 140 if (infrastructureClasses.contains(sf.getClassName())) { 141 // safe to ignore 142 return; 143 } 144 } 145 try { 146 recorder.compareFrame(count, sf); 147 } catch (IndexOutOfBoundsException e) { 148 // Extra non-infra frame in stream 149 throw new RuntimeException("extra non-infra stack frame at count " 150 + count + ": <" + sf + ">", e); 151 } 152 count++; 153 154 // Maybe we should had some kind of checking inside that lambda 155 // too. For the moment we should be satisfied if it doesn't throw 156 // any exception and doesn't make the outer walk fail... 157 158 // Use SW w/ limited stack depth, as this can take a long time for larger stack depths - 159 // for each frame, it walks to whole stack again... 160 StackWalker sw = StackWalker.create(swOptions); 161 sw.walk(s -> { s.limit(128).forEach(f -> { 162 // TODO need to check the correctness of StackFrame & StackTraceElement here 163 f.getMethodName(); 164 f.getClassName(); 165 f.getFileName(); 166 f.getLineNumber(); 167 if (CREATE_STACK_TRACE_ELEMENTS) { 168 StackTraceElement st = f.toStackTraceElement(); 169 st.getClassName(); 170 st.getMethodName(); 171 st.getFileName(); 172 st.getLineNumber(); 173 } 174 }); return null;}); 175 } 176 177 public class Call { 178 public void walk(int total, int markAt) { 179 recorder.add(Call.class, "walk", "StackWalkTest.java"); 180 long swFrameCount = createStackWalker().walk(s -> s.count()); 181 182 if (verbose) { 183 System.out.println("Call.walk() total=" + total + ", markAt=" + 184 markAt + ", maxDepth="+maxDepth); 185 System.out.println("recorder frames:"); 186 for (StackRecorderUtil.TestFrame f : recorder) { 187 System.out.println("\t" + f.declaringClass + "." + f.methodName); 188 } 189 System.out.println("\nStackWalker recorded " + swFrameCount + " frames"); 190 System.out.flush(); 191 } 192 if (swFrameCount > maxDepth) { 193 throw new RuntimeException("StackWalker maxDepth is " +maxDepth 194 + ", but stream has " + swFrameCount + " frames"); 195 } 196 long recFrameCount = (long)recorder.frameCount(); 197 if (recFrameCount <= maxDepth && swFrameCount < recFrameCount) { 198 throw new RuntimeException("StackWalker recorded fewer frames (" 199 + (swFrameCount) + ") than expected (" 200 + recorder.frameCount() + "v)"); 201 } 202 if (verbose) { 203 System.out.println("StackWalker frames:"); 204 } 205 createStackWalker().forEach(StackWalkTest.this::consume); 206 didWalk = true; 207 } 208 public void call(int total, int current, int markAt) { 209 recorder.add(Call.class, "call", "StackWalkTest.java"); 210 if (current < total) { 211 testCall.call(total, current+1, markAt); 212 } else { 213 walk(total, markAt); 214 } 215 } 216 } 217 218 public class Marker extends Call { 219 @Override 220 public void call(int total, int current, int markAt) { 221 recorder.add(Marker.class, "call", "StackWalkTest.java"); 222 if (current < total) { 223 testCall.call(total, current+1, markAt); 224 } else { 225 walk(total, markAt); 226 } 227 } 228 } 229 private Call markerCall = new Marker(); 230 231 public class Test extends Call { 232 @Override 233 public void call(int total, int current, int markAt) { 234 recorder.add(Test.class, "call", "StackWalkTest.java"); 235 if (current < total) { 236 int nexti = current + 1; 237 if (nexti==markAt) { 238 markerCall.call(total, nexti, markAt); 239 } else { 240 testCall.call2(total, nexti, markAt); 241 } 242 } else { 243 walk(total, markAt); 244 } 245 } 246 public void call2(int total, int current, int markAt) { 247 recorder.add(Test.class, "call2", "StackWalkTest.java"); 248 if (current < total) { 249 int nexti = current + 1; 250 if (nexti==markAt) { 251 markerCall.call(total, nexti, markAt); 252 } else { 253 test2Call.call(total, nexti, markAt); 254 } 255 } else { 256 walk(total, markAt); 257 } 258 } 259 } 260 private Test testCall = new Test(); 261 262 /** Inherits call() from Call */ 263 public class Test2 extends Call {} 264 private Test2 test2Call = new Test2(); 265 266 public void runTest(Class callerClass, String callerMethod, int stackDepth, 267 int markAt) { 268 if (didWalk) { 269 throw new IllegalStateException("StackWalkTest already used"); 270 } 271 assert markAt <= stackDepth : "markAt(" + markAt + ") > stackDepth(" 272 + stackDepth + ")"; 273 System.out.print("runTest(" + swOptions 274 + "), maxDepth=" + maxDepth); 275 276 recorder = new StackRecorderUtil(swOptions); 277 recorder.add(callerClass, callerMethod, "StackWalkTest.java"); 278 recorder.add(StackWalkTest.class, "runTest", "StackWalkTest.java"); 279 280 Test test1 = new Test(); 281 test1.call(stackDepth, 0, markAt); 282 283 System.out.println(" finished"); 284 if (!didWalk) { 285 throw new IllegalStateException("Test wasn't actually performed"); 286 } 287 } 288 289 public static void main(String[] args) { 290 for(String arg : args) { 291 if ("-random".equals(arg)) { 292 random = true; 293 } 294 } 295 if (random) { 296 Random rng = RandomFactory.getRandom(); 297 for (int iters = 0; iters < randomIters; iters++) { 298 Set<StackWalker.Option> opts = new HashSet<>(); 299 if (rng.nextBoolean()) { 300 opts.add(RETAIN_CLASS_REFERENCE); 301 } 302 303 int depth = 1 + rng.nextInt(MAX_RANDOM_DEPTH); 304 305 StackWalkTest swt; 306 if (rng.nextBoolean() && depth > 1) { 307 swt = new StackWalkTest(1+rng.nextInt(depth-1), opts); 308 } else { 309 swt = new StackWalkTest(opts); 310 } 311 312 int markAt = rng.nextInt(depth+1); 313 System.out.print(depth + "@" + markAt + " "); 314 System.out.flush(); 315 swt.runTest(StackWalkTest.class, "main", depth, markAt); 316 } 317 } else { 318 // Long stack, default maxDepth 319 StackWalkTest swt; 320 swt = new StackWalkTest(); 321 swt.runTest(StackWalkTest.class, "main", 2000, 10); 322 323 // Long stack, matching maxDepth 324 swt = new StackWalkTest(2000); 325 swt.runTest(StackWalkTest.class, "main", 2000, 10); 326 327 // Long stack, maximum maxDepth 328 swt = new StackWalkTest(Integer.MAX_VALUE); 329 swt.runTest(StackWalkTest.class, "main", 2000, 10); 330 331 // 332 // Single batch 333 // 334 swt = new StackWalkTest(); // default maxDepth 335 swt.runTest(StackWalkTest.class, "main", 6, 3); 336 337 swt = new StackWalkTest(4); // maxDepth < stack 338 swt.runTest(StackWalkTest.class, "main", 6, 3); 339 340 swt = new StackWalkTest(2); // maxDepth < marker 341 swt.runTest(StackWalkTest.class, "main", 6, 4); 342 343 // 344 // 2 batches 345 // 346 swt = new StackWalkTest(); // default maxDepth 347 swt.runTest(StackWalkTest.class, "main", 24, 10); 348 swt = new StackWalkTest(18); // maxDepth < stack 349 swt.runTest(StackWalkTest.class, "main", 24, 10); 350 swt = new StackWalkTest(8); // maxDepth < marker 351 swt.runTest(StackWalkTest.class, "main", 24, 10); 352 353 // 354 // 3 batch 355 // 356 swt = new StackWalkTest(); // default maxDepth 357 swt.runTest(StackWalkTest.class, "main", 60, 20); 358 swt = new StackWalkTest(35); // maxDepth < stack 359 swt.runTest(StackWalkTest.class, "main", 60, 20); 360 swt = new StackWalkTest(8); // maxDepth < marker 361 swt.runTest(StackWalkTest.class, "main", 60, 20); 362 363 // 364 // StackWalker.Options 365 // 366 swt = new StackWalkTest(); 367 swt.runTest(StackWalkTest.class, "main", 50, 10); 368 369 swt = new StackWalkTest(EnumSet.of(RETAIN_CLASS_REFERENCE)); 370 swt.runTest(StackWalkTest.class, "main", 80, 40); 371 372 swt = new StackWalkTest(Integer.MAX_VALUE, EnumSet.of(RETAIN_CLASS_REFERENCE)); 373 swt.runTest(StackWalkTest.class, "main", 2000, 1048); 374 } 375 } 376 }