StackWalkTest.java revision 14176:8606d027b2c2
155682Smarkm/* 2233294Sstas * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3233294Sstas * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4233294Sstas * 555682Smarkm * This code is free software; you can redistribute it and/or modify it 6233294Sstas * under the terms of the GNU General Public License version 2 only, as 7233294Sstas * published by the Free Software Foundation. 8233294Sstas * 955682Smarkm * This code is distributed in the hope that it will be useful, but WITHOUT 10233294Sstas * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11233294Sstas * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1255682Smarkm * version 2 for more details (a copy is included in the LICENSE file that 13233294Sstas * accompanied this code). 14233294Sstas * 15233294Sstas * You should have received a copy of the GNU General Public License version 1655682Smarkm * 2 along with this work; if not, write to the Free Software Foundation, 17233294Sstas * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18233294Sstas * 19233294Sstas * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2055682Smarkm * or visit www.oracle.com if you need additional information or have any 21233294Sstas * questions. 22233294Sstas */ 23233294Sstas 24233294Sstasimport static java.lang.StackWalker.Option.*; 25233294Sstasimport java.lang.StackWalker.StackFrame; 26233294Sstasimport java.util.Arrays; 27233294Sstasimport java.util.EnumSet; 28233294Sstasimport java.util.HashSet; 29233294Sstasimport java.util.List; 30233294Sstasimport java.util.Random; 31233294Sstasimport java.util.Set; 3255682Smarkmimport java.util.TreeSet; 3355682Smarkm 3455682Smarkmimport jdk.testlibrary.RandomFactory; 3555682Smarkm 3672445Sassar/** 3772445Sassar * @test 38178825Sdfr * @bug 8140450 3955682Smarkm * @summary Stack Walk Test (use -Dseed=X to set PRNG seed) 40233294Sstas * @library /lib/testlibrary 41233294Sstas * @build jdk.testlibrary.* 4255682Smarkm * @compile StackRecorderUtil.java 4355682Smarkm * @run main/othervm StackWalkTest 4455682Smarkm * @run main/othervm/java.security.policy=stackwalktest.policy StackWalkTest 4572445Sassar * @run main/othervm StackWalkTest -random:50 4655682Smarkm * @run main/othervm/java.security.policy=stackwalktest.policy StackWalkTest -random:50 4755682Smarkm * @run main/othervm -XX:-MemberNameInStackFrame -Dstackwalk.newThrowable=false StackWalkTest -random:50 4855682Smarkm * @run main/othervm -XX:-MemberNameInStackFrame -Dstackwalk.newThrowable=true StackWalkTest -random:50 49233294Sstas * @run main/othervm -XX:+MemberNameInStackFrame -Dstackwalk.newThrowable=false StackWalkTest -random:50 50233294Sstas * @run main/othervm -XX:+MemberNameInStackFrame -Dstackwalk.newThrowable=true StackWalkTest -random:50 51233294Sstas * @author danielfuchs, bchristi 5255682Smarkm * @key randomness 5355682Smarkm */ 5455682Smarkmpublic class StackWalkTest { 5555682Smarkm private static boolean random = false; 5655682Smarkm private static boolean verbose = false; 57233294Sstas private static int randomRuns = 50; 58233294Sstas 5955682Smarkm private static final int MAX_RANDOM_DEPTH = 1000; 6072445Sassar 61233294Sstas static final Set<String> infrastructureClasses = new TreeSet<>(Arrays.asList( 6272445Sassar "jdk.internal.reflect.NativeMethodAccessorImpl", 6372445Sassar "jdk.internal.reflect.DelegatingMethodAccessorImpl", 6472445Sassar "java.lang.reflect.Method", 65178825Sdfr "com.sun.javatest.regtest.MainWrapper$MainThread", 66178825Sdfr "com.sun.javatest.regtest.agent.MainWrapper$MainThread", 6772445Sassar "java.lang.Thread" 68233294Sstas )); 69233294Sstas static final List<Class<?>> streamPipelines = Arrays.asList( 7055682Smarkm classForName("java.util.stream.AbstractPipeline"), 71233294Sstas classForName("java.util.stream.TerminalOp") 7272445Sassar ); 73233294Sstas static Class<?> classForName(String name) { 74233294Sstas try { 7555682Smarkm return Class.forName(name); 7655682Smarkm } catch (ClassNotFoundException e){ 7755682Smarkm throw new RuntimeException(e); 7855682Smarkm } 7955682Smarkm } 8055682Smarkm 8155682Smarkm private static boolean isStreamPipeline(Class<?> clazz) { 8255682Smarkm for (Class<?> c : streamPipelines) { 8355682Smarkm if (c.isAssignableFrom(clazz)) { 8455682Smarkm return true; 8555682Smarkm } 8655682Smarkm } 8755682Smarkm return false; 8855682Smarkm } 8955682Smarkm 9055682Smarkm StackRecorderUtil recorder; 9155682Smarkm int count = 0; 92178825Sdfr boolean didWalk = false; 93178825Sdfr 94233294Sstas final int estDepth; 95178825Sdfr final Set<StackWalker.Option> swOptions; 9655682Smarkm 97233294Sstas public StackWalkTest() { 9855682Smarkm this(EnumSet.noneOf(StackWalker.Option.class), -1); 9978527Sassar } 10055682Smarkm 10172445Sassar public StackWalkTest(Set<StackWalker.Option> swOptions) { 10272445Sassar this(swOptions, -1); 10372445Sassar } 10455682Smarkm 105233294Sstas public StackWalkTest(int estimatedDepth) { 106178825Sdfr this(EnumSet.noneOf(StackWalker.Option.class), -1); 107233294Sstas } 108233294Sstas 10955682Smarkm public StackWalkTest(Set<StackWalker.Option> swOptions, int estimatedDepth) { 11055682Smarkm this.swOptions = swOptions; 11155682Smarkm this.estDepth = estimatedDepth; 11255682Smarkm } 11355682Smarkm 11455682Smarkm private StackWalker createStackWalker() { 11555682Smarkm // test all StackWalker factory methods 11655682Smarkm if (this.estDepth < 0) { 11755682Smarkm if (swOptions.isEmpty()) { 118178825Sdfr return StackWalker.getInstance(); 119178825Sdfr } else { 12055682Smarkm return StackWalker.getInstance(swOptions); 121178825Sdfr } 122178825Sdfr } 123178825Sdfr return StackWalker.getInstance(swOptions, estDepth); 124178825Sdfr } 125178825Sdfr public void consume(StackFrame sf) { 126233294Sstas if (count == 0 && swOptions.contains(StackWalker.Option.RETAIN_CLASS_REFERENCE) 127178825Sdfr && isStreamPipeline(sf.getDeclaringClass())) { 128178825Sdfr return; 129178825Sdfr } 130233294Sstas if (verbose) { 131178825Sdfr System.out.println("\t" + sf.getClassName() + "." + sf.getMethodName()); 132178825Sdfr } 133233294Sstas if (count >= recorder.frameCount()) { 134178825Sdfr // We've gone past main()... 135233294Sstas if (infrastructureClasses.contains(sf.getClassName())) { 136178825Sdfr // safe to ignore 137178825Sdfr return; 138178825Sdfr } 139178825Sdfr } 140178825Sdfr try { 141178825Sdfr recorder.compareFrame(count, sf); 142178825Sdfr } catch (IndexOutOfBoundsException e) { 14355682Smarkm // Extra non-infra frame in stream 14455682Smarkm throw new RuntimeException("extra non-infra stack frame at count " 14555682Smarkm + count + ": <" + sf + ">", e); 14655682Smarkm } 14755682Smarkm count++; 14855682Smarkm } 14955682Smarkm 15055682Smarkm public class Call { 15172445Sassar public void walk(int total, int markAt) { 15272445Sassar recorder.add(Call.class, "walk", "StackWalkTest.java"); 153178825Sdfr long swFrameCount = createStackWalker().walk(s -> s.count()); 154233294Sstas 155178825Sdfr if (verbose) { 156178825Sdfr System.out.println("Call.walk() total=" + total + ", markAt=" + markAt); 157178825Sdfr System.out.println("recorder frames:"); 158178825Sdfr for (StackRecorderUtil.TestFrame f : recorder) { 159178825Sdfr System.out.println("\t" + f.declaringClass + "." + f.methodName); 160178825Sdfr } 161178825Sdfr System.out.println("\nStackWalker recorded " + swFrameCount + " frames"); 162178825Sdfr System.out.flush(); 163233294Sstas } 164233294Sstas long recFrameCount = (long)recorder.frameCount(); 165233294Sstas if (swFrameCount < recFrameCount) { 166233294Sstas throw new RuntimeException("StackWalker recorded fewer frames ("+ 167233294Sstas swFrameCount + ") than recorded ("+ recorder.frameCount() + 168233294Sstas ") - " + "estimatedDepth set to " + estDepth); 169233294Sstas } 170233294Sstas if (verbose) { 171233294Sstas System.out.println("StackWalker frames:"); 172233294Sstas } 173233294Sstas createStackWalker().forEach(StackWalkTest.this::consume); 174233294Sstas didWalk = true; 175233294Sstas } 176233294Sstas public void call(int total, int current, int markAt) { 17778527Sassar recorder.add(Call.class, "call", "StackWalkTest.java"); 17878527Sassar if (current < total) { 17978527Sassar testCall.call(total, current+1, markAt); 18078527Sassar } else { 181233294Sstas walk(total, markAt); 182233294Sstas } 183233294Sstas } 184233294Sstas } 185233294Sstas 186233294Sstas public class Marker extends Call { 187233294Sstas @Override 18872445Sassar public void call(int total, int current, int markAt) { 189233294Sstas recorder.add(Marker.class, "call", "StackWalkTest.java"); 19055682Smarkm if (current < total) { 191233294Sstas testCall.call(total, current+1, markAt); 192233294Sstas } else { 19355682Smarkm walk(total, markAt); 194233294Sstas } 195233294Sstas } 196233294Sstas } 197233294Sstas private Call markerCall = new Marker(); 198233294Sstas 199233294Sstas public class Test extends Call { 20055682Smarkm @Override 20155682Smarkm 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 // Test may run into StackOverflow when running in -Xcomp mode on deep stack 240 assert stackDepth <= 1000; 241 assert markAt <= stackDepth : "markAt(" + markAt + ") > stackDepth(" 242 + stackDepth + ")"; 243 System.out.print("runTest(" + swOptions 244 + "), estimatedDepth=" + estDepth); 245 246 recorder = new StackRecorderUtil(swOptions); 247 recorder.add(callerClass, callerMethod, "StackWalkTest.java"); 248 recorder.add(StackWalkTest.class, "runTest", "StackWalkTest.java"); 249 250 Test test1 = new Test(); 251 test1.call(stackDepth, 0, markAt); 252 253 System.out.println(" finished"); 254 if (!didWalk) { 255 throw new IllegalStateException("Test wasn't actually performed"); 256 } 257 } 258 259 public static void main(String[] args) { 260 String rand = "-random"; 261 String randItems = "-random:"; 262 for(String arg : args) { 263 if (arg.startsWith(rand)) { 264 random = true; 265 try { 266 if(arg.startsWith(randItems)) { 267 randomRuns = Integer.valueOf(arg.substring(randItems.length())); 268 } 269 } catch(NumberFormatException e) {} 270 } else if("-verbose".equals(arg)) { 271 verbose = true; 272 } 273 } 274 if (random) { 275 Random rng = RandomFactory.getRandom(); 276 for (int iters = 0; iters < randomRuns; iters++) { 277 Set<StackWalker.Option> opts = new HashSet<>(); 278 if (rng.nextBoolean()) { 279 opts.add(RETAIN_CLASS_REFERENCE); 280 } 281 282 int depth = 1 + rng.nextInt(MAX_RANDOM_DEPTH); 283 284 StackWalkTest swt; 285 if (rng.nextBoolean() && depth > 1) { 286 // Test that specifying an estimatedDepth doesn't prevent 287 // full stack traversal 288 swt = new StackWalkTest(opts, 1+rng.nextInt(depth-1)); 289 } else { 290 swt = new StackWalkTest(opts); 291 } 292 293 int markAt = rng.nextInt(depth+1); 294 System.out.print(depth + "@" + markAt + " "); 295 System.out.flush(); 296 swt.runTest(StackWalkTest.class, "main", depth, markAt); 297 } 298 } else { 299 // Long stack, default maxDepth 300 StackWalkTest swt; 301 swt = new StackWalkTest(); 302 swt.runTest(StackWalkTest.class, "main", 1000, 10); 303 304 // Long stack, matching maxDepth 305 swt = new StackWalkTest(2000); 306 swt.runTest(StackWalkTest.class, "main", 1000, 10); 307 308 // Long stack, maximum maxDepth 309 swt = new StackWalkTest(Integer.MAX_VALUE); 310 swt.runTest(StackWalkTest.class, "main", 1000, 10); 311 312 // 313 // Single batch 314 // 315 swt = new StackWalkTest(); // default maxDepth 316 swt.runTest(StackWalkTest.class, "main", 6, 3); 317 318 swt = new StackWalkTest(4); // maxDepth < stack 319 swt.runTest(StackWalkTest.class, "main", 6, 3); 320 321 swt = new StackWalkTest(2); // maxDepth < marker 322 swt.runTest(StackWalkTest.class, "main", 6, 4); 323 324 // 325 // 2 batches 326 // 327 swt = new StackWalkTest(); // default maxDepth 328 swt.runTest(StackWalkTest.class, "main", 24, 10); 329 swt = new StackWalkTest(18); // maxDepth < stack 330 swt.runTest(StackWalkTest.class, "main", 24, 10); 331 swt = new StackWalkTest(8); // maxDepth < marker 332 swt.runTest(StackWalkTest.class, "main", 24, 10); 333 334 // 335 // 3 batch 336 // 337 swt = new StackWalkTest(); // default maxDepth 338 swt.runTest(StackWalkTest.class, "main", 60, 20); 339 swt = new StackWalkTest(35); // maxDepth < stack 340 swt.runTest(StackWalkTest.class, "main", 60, 20); 341 swt = new StackWalkTest(8); // maxDepth < marker 342 swt.runTest(StackWalkTest.class, "main", 60, 20); 343 344 // 345 // StackWalker.Options 346 // 347 swt = new StackWalkTest(); 348 swt.runTest(StackWalkTest.class, "main", 50, 10); 349 350 swt = new StackWalkTest(EnumSet.of(RETAIN_CLASS_REFERENCE)); 351 swt.runTest(StackWalkTest.class, "main", 80, 40); 352 353 swt = new StackWalkTest(EnumSet.of(RETAIN_CLASS_REFERENCE), 50); 354 swt.runTest(StackWalkTest.class, "main", 1000, 524); 355 } 356 } 357} 358