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