1 /* 2 * Copyright (c) 2015, 2018, 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 java.lang.reflect.InvocationTargetException; 25 import java.security.AccessController; 26 import java.security.PrivilegedAction; 27 import java.util.EnumSet; 28 import java.util.concurrent.atomic.AtomicLong; 29 import java.lang.StackWalker.StackFrame; 30 import java.lang.invoke.MethodHandle; 31 import java.lang.invoke.MethodHandles; 32 import java.lang.invoke.MethodType; 33 import java.util.Objects; 34 35 import static java.lang.StackWalker.Option.*; 36 37 /** 38 * @test 39 * @bug 8140450 8197901 40 * @summary Verify stack trace information obtained with respect to StackWalker 41 * options, when the stack contains lambdas, method handle invoke 42 * virtual calls, and reflection. 43 * @run main/othervm VerifyStackTrace 44 * @run main/othervm/java.security.policy=stackwalk.policy VerifyStackTrace 45 * @author danielfuchs 46 */ 47 public class VerifyStackTrace { 48 49 static interface TestCase { walker()50 StackWalker walker(); description()51 String description(); expected()52 String expected(); 53 } 54 static final class TestCase1 implements TestCase { 55 private final StackWalker walker = StackWalker.getInstance(RETAIN_CLASS_REFERENCE); 56 57 private final String description = "StackWalker.getInstance(" + 58 "StackWalker.Option.RETAIN_CLASS_REFERENCE)"; 59 60 // Note: line numbers and lambda hashes will be erased when 61 // comparing stack traces. However, the stack may change 62 // if some methods are being renamed in the code base. 63 // If the JDKcode base changes and the test fails because of that, 64 // then after validating that the actual stack trace obtained 65 // is indeed correct (no frames are skipped that shouldn't) 66 // then you can cut & paste the <-- actual --> stack printed in the 67 // test output in here: 68 private final String expected = 69 "1: VerifyStackTrace.lambda$test$1(VerifyStackTrace.java:209)\n" + 70 "2: VerifyStackTrace$Handle.execute(VerifyStackTrace.java:145)\n" + 71 "3: VerifyStackTrace$Handle.run(VerifyStackTrace.java:158)\n" + 72 "4: VerifyStackTrace.invoke(VerifyStackTrace.java:188)\n" + 73 "5: VerifyStackTrace$1.run(VerifyStackTrace.java:218)\n" + 74 "6: java.base/java.security.AccessController.doPrivileged(AccessController.java:310)\n" + 75 "7: VerifyStackTrace.test(VerifyStackTrace.java:227)\n" + 76 "8: VerifyStackTrace.main(VerifyStackTrace.java:182)\n"; 77 walker()78 @Override public StackWalker walker() { return walker;} description()79 @Override public String description() { return description;} expected()80 @Override public String expected() { return expected;} 81 } 82 static final class TestCase2 implements TestCase { 83 private final StackWalker walker = StackWalker.getInstance( 84 EnumSet.of(RETAIN_CLASS_REFERENCE, SHOW_REFLECT_FRAMES)); 85 86 private final String description = "nStackWalker.getInstance(" + 87 "StackWalker.Option.RETAIN_CLASS_REFERENCE, " + 88 "StackWalker.Option.SHOW_REFLECT_FRAMES)"; 89 90 // Note: line numbers and lambda hashes will be erased when 91 // comparing stack traces. However, the stack may change 92 // if some methods are being renamed in the code base. 93 // If the JDK code base changes and the test fails because of that, 94 // then after validating that the actual stack trace obtained 95 // is indeed correct (no frames are skipped that shouldn't) 96 // then you can cut & paste the <-- actual --> stack printed in the 97 // test output in here (don't forget the final \n): 98 private final String expected = 99 "1: VerifyStackTrace.lambda$test$1(VerifyStackTrace.java:211)\n" + 100 "2: VerifyStackTrace$Handle.execute(VerifyStackTrace.java:147)\n" + 101 "3: VerifyStackTrace$Handle.run(VerifyStackTrace.java:160)\n" + 102 "4: VerifyStackTrace.invoke(VerifyStackTrace.java:190)\n" + 103 "5: java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n" + 104 "6: java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n" + 105 "7: java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n" + 106 "8: java.base/java.lang.reflect.Method.invoke(Method.java:520)\n" + 107 "9: VerifyStackTrace$1.run(VerifyStackTrace.java:220)\n" + 108 "10: java.base/java.security.AccessController.doPrivileged(AccessController.java:310)\n" + 109 "11: VerifyStackTrace.test(VerifyStackTrace.java:229)\n" + 110 "12: VerifyStackTrace.main(VerifyStackTrace.java:185)\n"; 111 walker()112 @Override public StackWalker walker() { return walker;} description()113 @Override public String description() { return description;} expected()114 @Override public String expected() { return expected;} 115 } 116 static class TestCase3 implements TestCase { 117 private final StackWalker walker = StackWalker.getInstance( 118 EnumSet.of(RETAIN_CLASS_REFERENCE, SHOW_HIDDEN_FRAMES)); 119 120 private final String description = "StackWalker.getInstance(" + 121 "StackWalker.Option.RETAIN_CLASS_REFERENCE, " + 122 "StackWalker.Option.SHOW_HIDDEN_FRAMES)"; 123 124 // Note: line numbers and lambda hashes will be erased when 125 // comparing stack traces. However, the stack may change 126 // if some methods are being renamed in the code base. 127 // If the JDK code base changes and the test fails because of that, 128 // then after validating that the actual stack trace obtained 129 // is indeed correct (no frames are skipped that shouldn't) 130 // then you can cut & paste the <-- actual --> stack printed in the 131 // test output in here (don't forget the final \n): 132 private final String expected = 133 "1: VerifyStackTrace.lambda$test$1(VerifyStackTrace.java:213)\n" + 134 "2: VerifyStackTrace$$Lambda$1/0x00000007c0089430.run(Unknown Source)\n" + 135 "3: VerifyStackTrace$Handle.execute(VerifyStackTrace.java:149)\n" + 136 "4: java.base/java.lang.invoke.LambdaForm$DMH/0x00000007c008a830.invokeVirtual_LL_V(LambdaForm$DMH)\n" + 137 "5: java.base/java.lang.invoke.LambdaForm$MH/0x00000007c008a830.invoke_MT(LambdaForm$MH)\n" + 138 "6: VerifyStackTrace$Handle.run(VerifyStackTrace.java:162)\n" + 139 "7: VerifyStackTrace.invoke(VerifyStackTrace.java:192)\n" + 140 "8: java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n" + 141 "9: java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n" + 142 "10: java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n" + 143 "11: java.base/java.lang.reflect.Method.invoke(Method.java:520)\n" + 144 "12: VerifyStackTrace$1.run(VerifyStackTrace.java:222)\n" + 145 "13: java.base/java.security.AccessController.executePrivileged(AccessController.java:759)\n" + 146 "14: java.base/java.security.AccessController.doPrivileged(AccessController.java:310)\n" + 147 "15: VerifyStackTrace.test(VerifyStackTrace.java:231)\n" + 148 "16: VerifyStackTrace.main(VerifyStackTrace.java:188)\n"; 149 walker()150 @Override public StackWalker walker() { return walker;} description()151 @Override public String description() { return description;} expected()152 @Override public String expected() { return expected;} 153 } 154 155 static final class TestCase4 extends TestCase3 { 156 private final StackWalker walker = StackWalker.getInstance( 157 EnumSet.allOf(StackWalker.Option.class)); 158 159 private final String description = "StackWalker.getInstance(" + 160 "StackWalker.Option.RETAIN_CLASS_REFERENCE, " + 161 "StackWalker.Option.SHOW_HIDDEN_FRAMES, " + 162 "StackWalker.Option.SHOW_REFLECT_FRAMES)"; 163 walker()164 @Override public StackWalker walker() {return walker;} description()165 @Override public String description() {return description;} 166 } 167 168 public static class Handle implements Runnable { 169 170 Runnable impl; Handle(Runnable run)171 public Handle(Runnable run) { 172 this.impl = run; 173 } 174 execute(Runnable run)175 public void execute(Runnable run) { 176 run.run(); 177 } 178 run()179 public void run() { 180 MethodHandles.Lookup lookup = MethodHandles.lookup(); 181 MethodHandle handle = null; 182 try { 183 handle = lookup.findVirtual(Handle.class, "execute", 184 MethodType.methodType(void.class, Runnable.class)); 185 } catch(NoSuchMethodException | IllegalAccessException x) { 186 throw new RuntimeException(x); 187 } 188 try { 189 handle.invoke(this, impl); 190 } catch(Error | RuntimeException x) { 191 throw x; 192 } catch(Throwable t) { 193 throw new RuntimeException(t); 194 } 195 } 196 } 197 prepare(String produced, boolean eraseSensitiveInfo)198 static String prepare(String produced, boolean eraseSensitiveInfo) { 199 if (eraseSensitiveInfo) { 200 // Erase sensitive information before comparing: 201 // comparing line numbers is too fragile, so we just erase them 202 // out before comparing. We also erase the hash-like names of 203 // synthetic frames introduced by lambdas & method handles 204 return produced.replaceAll(":[1-9][0-9]*\\)", ":00)") 205 .replaceAll("/0x[0-9a-f]+\\.run", "/xxxxxxxx.run") 206 .replaceAll("/0x[0-9a-f]+\\.invoke", "/xxxxxxxx.invoke") 207 // LFs may or may not be pre-generated, making frames differ 208 .replaceAll("DirectMethodHandle\\$Holder", "LambdaForm\\$DMH") 209 .replaceAll("Invokers\\$Holder", "LambdaForm\\$MH") 210 .replaceAll("MH\\.invoke", "MH/xxxxxxxx.invoke") 211 // invoke frames may or may not have basic method type 212 // information encoded for diagnostic purposes 213 .replaceAll("xx\\.invoke([A-Za-z]*)_[A-Z_]+", "xx.invoke$1") 214 .replaceAll("\\$[0-9]+", "\\$??"); 215 } else { 216 return produced; 217 } 218 } 219 220 main(String[] args)221 public static void main(String[] args) { 222 test(new TestCase1()); 223 test(new TestCase2()); 224 test(new TestCase3()); 225 test(new TestCase4()); 226 } 227 invoke(Runnable run)228 public static void invoke(Runnable run) { 229 run.run(); 230 } 231 232 static final class Recorder { 233 boolean found; // stop recording after main recordSTE(long counter, StringBuilder s, StackFrame f)234 public void recordSTE(long counter, StringBuilder s, StackFrame f) { 235 if (found) return; 236 found = VerifyStackTrace.class.equals(f.getDeclaringClass()) && 237 "main".equals(f.getMethodName()); 238 String line = String.format("%d: %s", counter, f.toStackTraceElement()); 239 s.append(line).append('\n'); 240 System.out.println(line); 241 } 242 } 243 244 test(TestCase test)245 static void test(TestCase test) { 246 System.out.println("\nTesting: " + test.description()); 247 final AtomicLong counter = new AtomicLong(); 248 final StringBuilder builder = new StringBuilder(); 249 final Recorder recorder = new Recorder(); 250 final Runnable run = () -> test.walker().forEach( 251 f -> recorder.recordSTE(counter.incrementAndGet(), builder, f)); 252 final Handle handle = new Handle(run); 253 254 // We're not using lambda on purpose here. We want the anonymous 255 // class on the stack. 256 PrivilegedAction<Object> pa = new PrivilegedAction<Object>() { 257 @Override 258 public Object run() { 259 try { 260 return VerifyStackTrace.class 261 .getMethod("invoke", Runnable.class) 262 .invoke(null, handle); 263 } catch (NoSuchMethodException 264 | IllegalAccessException 265 | InvocationTargetException ex) { 266 System.out.flush(); 267 throw new RuntimeException(ex); 268 } 269 } 270 }; 271 AccessController.doPrivileged(pa); 272 System.out.println("Main found: " + recorder.found); 273 if (!Objects.equals(prepare(test.expected(), true), prepare(builder.toString(), true))) { 274 System.out.flush(); 275 try { 276 // sleep to make it less likely that System.out & System.err will 277 // interleave. 278 Thread.sleep(1000); 279 } catch (InterruptedException ex) { 280 } 281 System.err.println("\nUnexpected stack trace: " 282 + "\n<!-- expected -->\n" 283 + prepare(test.expected(), true) 284 + "\n<-- actual -->\n" 285 + prepare(builder.toString(), false)); 286 throw new RuntimeException("Unexpected stack trace for: " + test.description()); 287 } 288 } 289 290 291 } 292