--- /dev/null 2015-11-09 17:15:07.000000000 -0800 +++ new/jdk/test/java/lang/StackWalker/VerifyStackTrace.java 2015-11-09 17:15:07.000000000 -0800 @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.lang.reflect.InvocationTargetException; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.EnumSet; +import java.util.concurrent.atomic.AtomicLong; +import java.lang.StackWalker.StackFrame; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.Objects; + +import static java.lang.StackWalker.Option.*; + +/** + * @test + * @bug 8140450 + * @summary Verify stack trace information obtained with respect to StackWalker + * options, when the stack contains lambdas, method handle invoke + * virtual calls, and reflection. + * @run main VerifyStackTrace + * @run main/othervm/java.security.policy=stackwalk.policy VerifyStackTrace + * @author danielfuchs + */ +public class VerifyStackTrace { + + static interface TestCase { + StackWalker walker(); + String description(); + String expected(); + } + static final class TestCase1 implements TestCase { + private final StackWalker walker = StackWalker.create(RETAIN_CLASS_REFERENCE); + + private final String description = "StackWalker.create(" + + "StackWalker.Option.RETAIN_CLASS_REFERENCE)"; + + // Note: line numbers and lambda hashes will be erased when + // comparing stack traces. However, the stack may change + // if some methods are being renamed in the code base. + // If the JDKcode base changes and the test fails because of that, + // then after validating that the actual stack trace obtained + // is indeed correct (no frames are skipped that shouldn't) + // the you can cut & paste the <-- actual --> stack printed in the + // test output in here: + private final String expected = + "1: VerifyStackTrace.lambda$test$1(VerifyStackTrace.java:209)\n" + + "2: VerifyStackTrace$Handle.execute(VerifyStackTrace.java:145)\n" + + "3: VerifyStackTrace$Handle.run(VerifyStackTrace.java:158)\n" + + "4: VerifyStackTrace.invoke(VerifyStackTrace.java:188)\n" + + "5: VerifyStackTrace$1.run(VerifyStackTrace.java:218)\n" + + "6: java.security.AccessController.doPrivileged(Native Method)\n" + + "7: VerifyStackTrace.test(VerifyStackTrace.java:227)\n" + + "8: VerifyStackTrace.main(VerifyStackTrace.java:182)\n"; + + @Override public StackWalker walker() { return walker;} + @Override public String description() { return description;} + @Override public String expected() { return expected;} + } + static final class TestCase2 implements TestCase { + private final StackWalker walker = StackWalker.create( + EnumSet.of(RETAIN_CLASS_REFERENCE, SHOW_REFLECT_FRAMES)); + + private final String description = "nStackWalker.create(" + + "StackWalker.Option.RETAIN_CLASS_REFERENCE, " + + "StackWalker.Option.SHOW_REFLECT_FRAMES)"; + + // Note: line numbers and lambda hashes will be erased when + // comparing stack traces. However, the stack may change + // if some methods are being renamed in the code base. + // If the JDK code base changes and the test fails because of that, + // then after validating that the actual stack trace obtained + // is indeed correct (no frames are skipped that shouldn't) + // the you can cut & paste the <-- actual --> stack printed in the + // test output in here (don't forget the final \n): + private final String expected = + "1: VerifyStackTrace.lambda$test$1(VerifyStackTrace.java:211)\n" + + "2: VerifyStackTrace$Handle.execute(VerifyStackTrace.java:147)\n" + + "3: VerifyStackTrace$Handle.run(VerifyStackTrace.java:160)\n" + + "4: VerifyStackTrace.invoke(VerifyStackTrace.java:190)\n" + + "5: sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n" + + "6: sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n" + + "7: sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n" + + "8: java.lang.reflect.Method.invoke(Method.java:520)\n" + + "9: VerifyStackTrace$1.run(VerifyStackTrace.java:220)\n" + + "10: java.security.AccessController.doPrivileged(Native Method)\n" + + "11: VerifyStackTrace.test(VerifyStackTrace.java:229)\n" + + "12: VerifyStackTrace.main(VerifyStackTrace.java:185)\n"; + + @Override public StackWalker walker() { return walker;} + @Override public String description() { return description;} + @Override public String expected() { return expected;} + } + static class TestCase3 implements TestCase { + private final StackWalker walker = StackWalker.create( + EnumSet.of(RETAIN_CLASS_REFERENCE, SHOW_HIDDEN_FRAMES)); + + private final String description = "StackWalker.create(" + + "StackWalker.Option.RETAIN_CLASS_REFERENCE, " + + "StackWalker.Option.SHOW_HIDDEN_FRAMES)"; + + // Note: line numbers and lambda hashes will be erased when + // comparing stack traces. However, the stack may change + // if some methods are being renamed in the code base. + // If the JDK code base changes and the test fails because of that, + // then after validating that the actual stack trace obtained + // is indeed correct (no frames are skipped that shouldn't) + // the you can cut & paste the <-- actual --> stack printed in the + // test output in here (don't forget the final \n): + private final String expected = + "1: VerifyStackTrace.lambda$test$1(VerifyStackTrace.java:213)\n" + + "2: VerifyStackTrace$$Lambda$1/662441761.run()\n" + + "3: VerifyStackTrace$Handle.execute(VerifyStackTrace.java:149)\n" + + "4: java.lang.invoke.LambdaForm$DMH/2008017533.invokeVirtual_LL_V(LambdaForm$DMH)\n" + + "5: java.lang.invoke.LambdaForm$MH/1395089624.invoke_MT(LambdaForm$MH)\n" + + "6: VerifyStackTrace$Handle.run(VerifyStackTrace.java:162)\n" + + "7: VerifyStackTrace.invoke(VerifyStackTrace.java:192)\n" + + "8: sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n" + + "9: sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n" + + "10: sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n" + + "11: java.lang.reflect.Method.invoke(Method.java:520)\n" + + "12: VerifyStackTrace$1.run(VerifyStackTrace.java:222)\n" + + "13: java.security.AccessController.doPrivileged(Native Method)\n" + + "14: VerifyStackTrace.test(VerifyStackTrace.java:231)\n" + + "15: VerifyStackTrace.main(VerifyStackTrace.java:188)\n"; + + @Override public StackWalker walker() { return walker;} + @Override public String description() { return description;} + @Override public String expected() { return expected;} + } + + static final class TestCase4 extends TestCase3 { + private final StackWalker walker = StackWalker.create( + EnumSet.allOf(StackWalker.Option.class)); + + private final String description = "StackWalker.create(" + + "StackWalker.Option.RETAIN_CLASS_REFERENCE, " + + "StackWalker.Option.SHOW_HIDDEN_FRAMES, " + + "StackWalker.Option.SHOW_REFLECT_FRAMES)"; + + @Override public StackWalker walker() {return walker;} + @Override public String description() {return description;} + } + + public static class Handle implements Runnable { + + Runnable impl; + public Handle(Runnable run) { + this.impl = run; + } + + public void execute(Runnable run) { + run.run(); + } + + public void run() { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle handle = null; + try { + handle = lookup.findVirtual(Handle.class, "execute", + MethodType.methodType(void.class, Runnable.class)); + } catch(NoSuchMethodException | IllegalAccessException x) { + throw new RuntimeException(x); + } + try { + handle.invoke(this, impl); + } catch(Error | RuntimeException x) { + throw x; + } catch(Throwable t) { + throw new RuntimeException(t); + } + } + } + + static String prepare(String produced, boolean eraseSensitiveInfo) { + if (eraseSensitiveInfo) { + // Erase sensitive information before comparing: + // comparing line numbers is too fragile, so we just erase them + // out before comparing. We also erase the hash-like names of + // synthetic frames introduced by lambdas & method handles + return produced.replaceAll(":[1-9][0-9]*\\)", ":00)") + .replaceAll("/[0-9]+\\.run", "/xxxxxxxx.run") + .replaceAll("/[0-9]+\\.invoke", "/xxxxxxxx.invoke") + .replaceAll("\\$[0-9]+", "\\$??"); + } else { + return produced; + } + } + + + public static void main(String[] args) { + test(new TestCase1()); + test(new TestCase2()); + test(new TestCase3()); + test(new TestCase4()); + } + + public static void invoke(Runnable run) { + run.run(); + } + + static final class Recorder { + boolean found; // stop recording after main + public void recordSTE(long counter, StringBuilder s, StackFrame f) { + if (found) return; + found = VerifyStackTrace.class.equals(f.getDeclaringClass()) && + "main".equals(f.getMethodName()); + String line = String.format("%d: %s", counter, f.toStackTraceElement()); + s.append(line).append('\n'); + System.out.println(line); + } + } + + + static void test(TestCase test) { + System.out.println("\nTesting: " + test.description()); + final AtomicLong counter = new AtomicLong(); + final StringBuilder builder = new StringBuilder(); + final Recorder recorder = new Recorder(); + final Runnable run = () -> test.walker().forEach( + f -> recorder.recordSTE(counter.incrementAndGet(), builder, f)); + final Handle handle = new Handle(run); + + // We're not using lambda on purpose here. We want the anonymous + // class on the stack. + PrivilegedAction pa = new PrivilegedAction() { + @Override + public Object run() { + try { + return VerifyStackTrace.class + .getMethod("invoke", Runnable.class) + .invoke(null, handle); + } catch (NoSuchMethodException + | IllegalAccessException + | InvocationTargetException ex) { + System.out.flush(); + throw new RuntimeException(ex); + } + } + }; + AccessController.doPrivileged(pa); + System.out.println("Main found: " + recorder.found); + if (!Objects.equals(prepare(test.expected(), true), prepare(builder.toString(), true))) { + System.out.flush(); + try { + // sleep to make it less likely that System.out & System.err will + // interleave. + Thread.sleep(1000); + } catch (InterruptedException ex) { + } + System.err.println("\nUnexpected stack trace: " + + "\n\n" + + prepare(test.expected(), true) + + "\n<-- actual -->\n" + + prepare(builder.toString(), false)); + throw new RuntimeException("Unexpected stack trace for: " + test.description()); + } + } + + +}