VerifyStackTrace.java revision 13901:b2a69d66dc65
1213237Sgonzo/* 2213237Sgonzo * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3213237Sgonzo * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4213237Sgonzo * 5213237Sgonzo * This code is free software; you can redistribute it and/or modify it 6213237Sgonzo * under the terms of the GNU General Public License version 2 only, as 7213237Sgonzo * published by the Free Software Foundation. 8213237Sgonzo * 9213237Sgonzo * This code is distributed in the hope that it will be useful, but WITHOUT 10213237Sgonzo * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11213237Sgonzo * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12213237Sgonzo * version 2 for more details (a copy is included in the LICENSE file that 13213237Sgonzo * accompanied this code). 14213237Sgonzo * 15213237Sgonzo * You should have received a copy of the GNU General Public License version 16213237Sgonzo * 2 along with this work; if not, write to the Free Software Foundation, 17213237Sgonzo * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18213237Sgonzo * 19213237Sgonzo * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20213237Sgonzo * or visit www.oracle.com if you need additional information or have any 21213237Sgonzo * questions. 22213237Sgonzo */ 23213237Sgonzo 24213237Sgonzoimport java.lang.reflect.InvocationTargetException; 25213237Sgonzoimport java.security.AccessController; 26213237Sgonzoimport java.security.PrivilegedAction; 27213237Sgonzoimport java.util.EnumSet; 28213237Sgonzoimport java.util.concurrent.atomic.AtomicLong; 29213237Sgonzoimport java.lang.StackWalker.StackFrame; 30213237Sgonzoimport java.lang.invoke.MethodHandle; 31213237Sgonzoimport java.lang.invoke.MethodHandles; 32213237Sgonzoimport java.lang.invoke.MethodType; 33213237Sgonzoimport java.util.Objects; 34213237Sgonzo 35213237Sgonzoimport static java.lang.StackWalker.Option.*; 36213237Sgonzo 37213237Sgonzo/** 38213237Sgonzo * @test 39213237Sgonzo * @bug 8140450 40213237Sgonzo * @summary Verify stack trace information obtained with respect to StackWalker 41213237Sgonzo * options, when the stack contains lambdas, method handle invoke 42213237Sgonzo * virtual calls, and reflection. 43213237Sgonzo * @run main/othervm -XX:-MemberNameInStackFrame VerifyStackTrace 44213237Sgonzo * @run main/othervm -XX:+MemberNameInStackFrame VerifyStackTrace 45213237Sgonzo * @run main/othervm/java.security.policy=stackwalk.policy VerifyStackTrace 46213237Sgonzo * @author danielfuchs 47213237Sgonzo */ 48213237Sgonzopublic class VerifyStackTrace { 49213237Sgonzo 50213237Sgonzo static interface TestCase { 51213237Sgonzo StackWalker walker(); 52213237Sgonzo String description(); 53213237Sgonzo String expected(); 54213237Sgonzo } 55213237Sgonzo static final class TestCase1 implements TestCase { 56213237Sgonzo private final StackWalker walker = StackWalker.getInstance(RETAIN_CLASS_REFERENCE); 57213237Sgonzo 58213237Sgonzo private final String description = "StackWalker.getInstance(" + 59213237Sgonzo "StackWalker.Option.RETAIN_CLASS_REFERENCE)"; 60213237Sgonzo 61213237Sgonzo // Note: line numbers and lambda hashes will be erased when 62213237Sgonzo // comparing stack traces. However, the stack may change 63298738Smmel // if some methods are being renamed in the code base. 64298738Smmel // If the JDKcode base changes and the test fails because of that, 65298738Smmel // then after validating that the actual stack trace obtained 66298738Smmel // is indeed correct (no frames are skipped that shouldn't) 67298738Smmel // then you can cut & paste the <-- actual --> stack printed in the 68298738Smmel // test output in here: 69298738Smmel private final String expected = 70298738Smmel "1: VerifyStackTrace.lambda$test$1(VerifyStackTrace.java:209)\n" + 71298738Smmel "2: VerifyStackTrace$Handle.execute(VerifyStackTrace.java:145)\n" + 72298738Smmel "3: VerifyStackTrace$Handle.run(VerifyStackTrace.java:158)\n" + 73324755Sian "4: VerifyStackTrace.invoke(VerifyStackTrace.java:188)\n" + 74324755Sian "5: VerifyStackTrace$1.run(VerifyStackTrace.java:218)\n" + 75298738Smmel "6: java.security.AccessController.doPrivileged(java.base/Native Method)\n" + 76298738Smmel "7: VerifyStackTrace.test(VerifyStackTrace.java:227)\n" + 77298738Smmel "8: VerifyStackTrace.main(VerifyStackTrace.java:182)\n"; 78298738Smmel 79298738Smmel @Override public StackWalker walker() { return walker;} 80298738Smmel @Override public String description() { return description;} 81298738Smmel @Override public String expected() { return expected;} 82298738Smmel } 83298738Smmel static final class TestCase2 implements TestCase { 84298738Smmel private final StackWalker walker = StackWalker.getInstance( 85213237Sgonzo EnumSet.of(RETAIN_CLASS_REFERENCE, SHOW_REFLECT_FRAMES)); 86213237Sgonzo 87213237Sgonzo private final String description = "nStackWalker.getInstance(" + 88213237Sgonzo "StackWalker.Option.RETAIN_CLASS_REFERENCE, " + 89213237Sgonzo "StackWalker.Option.SHOW_REFLECT_FRAMES)"; 90213237Sgonzo 91213237Sgonzo // Note: line numbers and lambda hashes will be erased when 92213237Sgonzo // comparing stack traces. However, the stack may change 93213237Sgonzo // if some methods are being renamed in the code base. 94213237Sgonzo // If the JDK code base changes and the test fails because of that, 95213237Sgonzo // then after validating that the actual stack trace obtained 96213237Sgonzo // is indeed correct (no frames are skipped that shouldn't) 97213237Sgonzo // then you can cut & paste the <-- actual --> stack printed in the 98213237Sgonzo // test output in here (don't forget the final \n): 99213237Sgonzo private final String expected = 100324755Sian "1: VerifyStackTrace.lambda$test$1(VerifyStackTrace.java:211)\n" + 101324755Sian "2: VerifyStackTrace$Handle.execute(VerifyStackTrace.java:147)\n" + 102324755Sian "3: VerifyStackTrace$Handle.run(VerifyStackTrace.java:160)\n" + 103324755Sian "4: VerifyStackTrace.invoke(VerifyStackTrace.java:190)\n" + 104324755Sian "5: sun.reflect.NativeMethodAccessorImpl.invoke0(java.base/Native Method)\n" + 105324755Sian "6: sun.reflect.NativeMethodAccessorImpl.invoke(java.base/NativeMethodAccessorImpl.java:62)\n" + 106324755Sian "7: sun.reflect.DelegatingMethodAccessorImpl.invoke(java.base/DelegatingMethodAccessorImpl.java:43)\n" + 107324755Sian "8: java.lang.reflect.Method.invoke(java.base/Method.java:520)\n" + 108324755Sian "9: VerifyStackTrace$1.run(VerifyStackTrace.java:220)\n" + 109324755Sian "10: java.security.AccessController.doPrivileged(java.base/Native Method)\n" + 110324755Sian "11: VerifyStackTrace.test(VerifyStackTrace.java:229)\n" + 111324755Sian "12: VerifyStackTrace.main(VerifyStackTrace.java:185)\n"; 112324755Sian 113324755Sian @Override public StackWalker walker() { return walker;} 114324755Sian @Override public String description() { return description;} 115324755Sian @Override public String expected() { return expected;} 116324755Sian } 117324755Sian static class TestCase3 implements TestCase { 118324755Sian private final StackWalker walker = StackWalker.getInstance( 119324755Sian EnumSet.of(RETAIN_CLASS_REFERENCE, SHOW_HIDDEN_FRAMES)); 120324755Sian 121324755Sian private final String description = "StackWalker.getInstance(" + 122324755Sian "StackWalker.Option.RETAIN_CLASS_REFERENCE, " + 123324755Sian "StackWalker.Option.SHOW_HIDDEN_FRAMES)"; 124324755Sian 125324755Sian // Note: line numbers and lambda hashes will be erased when 126324755Sian // comparing stack traces. However, the stack may change 127324755Sian // if some methods are being renamed in the code base. 128324755Sian // If the JDK code base changes and the test fails because of that, 129324755Sian // then after validating that the actual stack trace obtained 130324755Sian // is indeed correct (no frames are skipped that shouldn't) 131324755Sian // then you can cut & paste the <-- actual --> stack printed in the 132324755Sian // test output in here (don't forget the final \n): 133324755Sian private final String expected = 134324755Sian "1: VerifyStackTrace.lambda$test$1(VerifyStackTrace.java:213)\n" + 135324755Sian "2: VerifyStackTrace$$Lambda$1/662441761.run(Unknown Source)\n" + 136324755Sian "3: VerifyStackTrace$Handle.execute(VerifyStackTrace.java:149)\n" + 137324755Sian "4: java.lang.invoke.LambdaForm$DMH/2008017533.invokeVirtual_LL_V(java.base/LambdaForm$DMH)\n" + 138324755Sian "5: java.lang.invoke.LambdaForm$MH/1395089624.invoke_MT(java.base/LambdaForm$MH)\n" + 139324755Sian "6: VerifyStackTrace$Handle.run(VerifyStackTrace.java:162)\n" + 140324755Sian "7: VerifyStackTrace.invoke(VerifyStackTrace.java:192)\n" + 141324755Sian "8: sun.reflect.NativeMethodAccessorImpl.invoke0(java.base/Native Method)\n" + 142324755Sian "9: sun.reflect.NativeMethodAccessorImpl.invoke(java.base/NativeMethodAccessorImpl.java:62)\n" + 143324755Sian "10: sun.reflect.DelegatingMethodAccessorImpl.invoke(java.base/DelegatingMethodAccessorImpl.java:43)\n" + 144324755Sian "11: java.lang.reflect.Method.invoke(java.base/Method.java:520)\n" + 145324755Sian "12: VerifyStackTrace$1.run(VerifyStackTrace.java:222)\n" + 146324755Sian "13: java.security.AccessController.doPrivileged(java.base/Native Method)\n" + 147324755Sian "14: VerifyStackTrace.test(VerifyStackTrace.java:231)\n" + 148324755Sian "15: VerifyStackTrace.main(VerifyStackTrace.java:188)\n"; 149324755Sian 150324755Sian @Override public StackWalker walker() { return walker;} 151324755Sian @Override public String description() { return description;} 152324755Sian @Override public String expected() { return expected;} 153324755Sian } 154324755Sian 155324755Sian static final class TestCase4 extends TestCase3 { 156324755Sian private final StackWalker walker = StackWalker.getInstance( 157324755Sian EnumSet.allOf(StackWalker.Option.class)); 158324755Sian 159324755Sian private final String description = "StackWalker.getInstance(" + 160324755Sian "StackWalker.Option.RETAIN_CLASS_REFERENCE, " + 161324755Sian "StackWalker.Option.SHOW_HIDDEN_FRAMES, " + 162324755Sian "StackWalker.Option.SHOW_REFLECT_FRAMES)"; 163324755Sian 164324755Sian @Override public StackWalker walker() {return walker;} 165213237Sgonzo @Override public String description() {return description;} 166213237Sgonzo } 167213237Sgonzo 168213237Sgonzo public static class Handle implements Runnable { 169213237Sgonzo 170213237Sgonzo Runnable impl; 171213237Sgonzo public Handle(Runnable run) { 172213237Sgonzo this.impl = run; 173279761Sloos } 174324755Sian 175324755Sian public void execute(Runnable run) { 176213237Sgonzo run.run(); 177213237Sgonzo } 178 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 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("-internal/", "/").replaceAll("-ea/", "/") 206 .replaceAll("java.base@[0-9]+/", "java.base/") 207 .replaceAll("/[0-9]+\\.run", "/xxxxxxxx.run") 208 .replaceAll("/[0-9]+\\.invoke", "/xxxxxxxx.invoke") 209 .replaceAll("\\$[0-9]+", "\\$??"); 210 } else { 211 return produced; 212 } 213 } 214 215 216 public static void main(String[] args) { 217 test(new TestCase1()); 218 test(new TestCase2()); 219 test(new TestCase3()); 220 test(new TestCase4()); 221 } 222 223 public static void invoke(Runnable run) { 224 run.run(); 225 } 226 227 static final class Recorder { 228 boolean found; // stop recording after main 229 public void recordSTE(long counter, StringBuilder s, StackFrame f) { 230 if (found) return; 231 found = VerifyStackTrace.class.equals(f.getDeclaringClass()) && 232 "main".equals(f.getMethodName()); 233 String line = String.format("%d: %s", counter, f.toStackTraceElement()); 234 s.append(line).append('\n'); 235 System.out.println(line); 236 } 237 } 238 239 240 static void test(TestCase test) { 241 System.out.println("\nTesting: " + test.description()); 242 final AtomicLong counter = new AtomicLong(); 243 final StringBuilder builder = new StringBuilder(); 244 final Recorder recorder = new Recorder(); 245 final Runnable run = () -> test.walker().forEach( 246 f -> recorder.recordSTE(counter.incrementAndGet(), builder, f)); 247 final Handle handle = new Handle(run); 248 249 // We're not using lambda on purpose here. We want the anonymous 250 // class on the stack. 251 PrivilegedAction<Object> pa = new PrivilegedAction<Object>() { 252 @Override 253 public Object run() { 254 try { 255 return VerifyStackTrace.class 256 .getMethod("invoke", Runnable.class) 257 .invoke(null, handle); 258 } catch (NoSuchMethodException 259 | IllegalAccessException 260 | InvocationTargetException ex) { 261 System.out.flush(); 262 throw new RuntimeException(ex); 263 } 264 } 265 }; 266 AccessController.doPrivileged(pa); 267 System.out.println("Main found: " + recorder.found); 268 if (!Objects.equals(prepare(test.expected(), true), prepare(builder.toString(), true))) { 269 System.out.flush(); 270 try { 271 // sleep to make it less likely that System.out & System.err will 272 // interleave. 273 Thread.sleep(1000); 274 } catch (InterruptedException ex) { 275 } 276 System.err.println("\nUnexpected stack trace: " 277 + "\n<!-- expected -->\n" 278 + prepare(test.expected(), true) 279 + "\n<-- actual -->\n" 280 + prepare(builder.toString(), false)); 281 throw new RuntimeException("Unexpected stack trace for: " + test.description()); 282 } 283 } 284 285 286} 287