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 /* 25 * @test 26 * @bug 8140450 8173898 27 * @summary Basic test for the StackWalker::walk method 28 * @run testng Basic 29 */ 30 31 import java.lang.StackWalker.StackFrame; 32 import java.util.List; 33 import java.util.Objects; 34 import java.util.stream.Collectors; 35 import java.util.stream.Stream; 36 import static java.lang.StackWalker.Option.*; 37 38 import org.testng.annotations.DataProvider; 39 import org.testng.annotations.Test; 40 import static org.testng.Assert.*; 41 42 public class Basic { 43 private static boolean verbose = false; 44 45 @DataProvider(name = "stackDepths") 46 public static Object[][] stackDepths() { 47 return new Object[][] { 48 { new int[] { 12 }, new int[] { 4, 8, 12} }, 49 { new int[] { 18 }, new int[] { 8, 16, 20} }, 50 { new int[] { 32 }, new int[] { 16, 32, 64} }, 51 }; 52 } 53 54 /** 55 * For a stack of a given depth, it creates a StackWalker with an estimate. 56 * Test walking different number of frames 57 */ 58 @Test(dataProvider = "stackDepths") 59 public static void test(int[] depth, int[] estimates) { 60 Basic test = new Basic(depth[0]); 61 for (int estimate : estimates) { 62 test.walk(estimate); 63 } 64 } 65 66 @Test 67 public static void testWalkFromConstructor() throws Exception { 68 System.out.println("testWalkFromConstructor:"); 69 List<String> found = ((ConstructorNewInstance)ConstructorNewInstance.class.getMethod("create") 70 .invoke(null)).collectedFrames(); 71 assertEquals(List.of(ConstructorNewInstance.class.getName()+"::<init>", 72 ConstructorNewInstance.class.getName()+"::create", 73 Basic.class.getName()+"::testWalkFromConstructor"), 74 found); 75 } 76 77 private final int depth; 78 Basic(int depth) { 79 this.depth = depth; 80 } 81 82 /* 83 * Setup a stack builder with the expected stack depth 84 * Walk the stack and count the frames. 85 */ 86 void walk(int estimate) { 87 int limit = Math.min(depth, 16); 88 List<StackFrame> frames = new StackBuilder(depth, limit).build(); 89 System.out.format("depth=%d estimate=%d expected=%d walked=%d%n", 90 depth, estimate, limit, frames.size()); 91 assertEquals(limit, frames.size()); 92 } 93 94 static class ConstructorNewInstance { 95 static final StackWalker walker = 96 StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE); 97 List<String> testFramesOrReflectionFrames; 98 public ConstructorNewInstance() { 99 testFramesOrReflectionFrames = walker.walk(this::parse); 100 } 101 public List<String> collectedFrames() { 102 return testFramesOrReflectionFrames; 103 } 104 public boolean accept(StackFrame f) { 105 // Frames whose class names don't contain "." 106 // are our own test frames. These are the ones 107 // we expect. 108 // Frames whose class names contain ".reflect." 109 // are reflection frames. None should be present, 110 // since they are supposed to be filtered by 111 // by StackWalker. If we find any, we want to fail. 112 if (!f.getClassName().contains(".") 113 || f.getClassName().contains(".reflect.")) { 114 System.out.println(" " + f); 115 return true; 116 } 117 // Filter out all other frames (in particular 118 // those from the test framework) in order to 119 // have predictable results. 120 return false; 121 } 122 public String frame(StackFrame f) { 123 return f.getClassName() + "::" + f.getMethodName(); 124 } 125 List<String> parse(Stream<StackFrame> s) { 126 return s.filter(this::accept) 127 .map(this::frame) 128 .collect(Collectors.toList()); 129 } 130 public static ConstructorNewInstance create() throws Exception { 131 return ConstructorNewInstance.class.getConstructor().newInstance(); 132 } 133 } 134 135 class StackBuilder { 136 private final int stackDepth; 137 private final int limit; 138 private int depth = 0; 139 private List<StackFrame> result; 140 StackBuilder(int stackDepth, int limit) { 141 this.stackDepth = stackDepth; // build method; 142 this.limit = limit; 143 } 144 List<StackFrame> build() { 145 trace("build"); 146 m1(); 147 return result; 148 } 149 void m1() { 150 trace("m1"); 151 m2(); 152 } 153 void m2() { 154 trace("m2"); 155 m3(); 156 } 157 void m3() { 158 trace("m3"); 159 m4(); 160 } 161 void m4() { 162 trace("m4"); 163 int remaining = stackDepth-depth-1; 164 if (remaining >= 4) { 165 m1(); 166 } else { 167 filler(remaining); 168 } 169 } 170 void filler(int i) { 171 trace("filler"); 172 if (i == 0) 173 walk(); 174 else 175 filler(--i); 176 } 177 178 void walk() { 179 StackWalker walker = StackWalker.getInstance(RETAIN_CLASS_REFERENCE); 180 result = walker.walk(s -> s.limit(limit).collect(Collectors.toList())); 181 } 182 void trace(String methodname) { 183 ++depth; 184 if (verbose) 185 System.out.format("%2d: %s%n", depth, methodname); 186 } 187 } 188 189 }