Path: blob/master/test/jdk/java/lang/StackWalker/VerifyStackTrace.java
41149 views
/*1* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223import java.lang.reflect.InvocationTargetException;24import java.security.AccessController;25import java.security.PrivilegedAction;26import java.util.EnumSet;27import java.util.concurrent.atomic.AtomicLong;28import java.lang.StackWalker.StackFrame;29import java.lang.invoke.MethodHandle;30import java.lang.invoke.MethodHandles;31import java.lang.invoke.MethodType;32import java.util.Objects;3334import static java.lang.StackWalker.Option.*;3536/**37* @test38* @bug 8140450 819790139* @summary Verify stack trace information obtained with respect to StackWalker40* options, when the stack contains lambdas, method handle invoke41* virtual calls, and reflection.42* @run main/othervm VerifyStackTrace43* @run main/othervm/java.security.policy=stackwalk.policy VerifyStackTrace44* @author danielfuchs45*/46public class VerifyStackTrace {4748static interface TestCase {49StackWalker walker();50String description();51String expected();52}53static final class TestCase1 implements TestCase {54private final StackWalker walker = StackWalker.getInstance(RETAIN_CLASS_REFERENCE);5556private final String description = "StackWalker.getInstance(" +57"StackWalker.Option.RETAIN_CLASS_REFERENCE)";5859// Note: line numbers and lambda hashes will be erased when60// comparing stack traces. However, the stack may change61// if some methods are being renamed in the code base.62// If the JDKcode base changes and the test fails because of that,63// then after validating that the actual stack trace obtained64// is indeed correct (no frames are skipped that shouldn't)65// then you can cut & paste the <-- actual --> stack printed in the66// test output in here:67private final String expected =68"1: VerifyStackTrace.lambda$test$1(VerifyStackTrace.java:209)\n" +69"2: VerifyStackTrace$Handle.execute(VerifyStackTrace.java:145)\n" +70"3: VerifyStackTrace$Handle.run(VerifyStackTrace.java:158)\n" +71"4: VerifyStackTrace.invoke(VerifyStackTrace.java:188)\n" +72"5: VerifyStackTrace$1.run(VerifyStackTrace.java:218)\n" +73"6: java.base/java.security.AccessController.doPrivileged(AccessController.java:310)\n" +74"7: VerifyStackTrace.test(VerifyStackTrace.java:227)\n" +75"8: VerifyStackTrace.main(VerifyStackTrace.java:182)\n";7677@Override public StackWalker walker() { return walker;}78@Override public String description() { return description;}79@Override public String expected() { return expected;}80}81static final class TestCase2 implements TestCase {82private final StackWalker walker = StackWalker.getInstance(83EnumSet.of(RETAIN_CLASS_REFERENCE, SHOW_REFLECT_FRAMES));8485private final String description = "nStackWalker.getInstance(" +86"StackWalker.Option.RETAIN_CLASS_REFERENCE, " +87"StackWalker.Option.SHOW_REFLECT_FRAMES)";8889// Note: line numbers and lambda hashes will be erased when90// comparing stack traces. However, the stack may change91// if some methods are being renamed in the code base.92// If the JDK code base changes and the test fails because of that,93// then after validating that the actual stack trace obtained94// is indeed correct (no frames are skipped that shouldn't)95// then you can cut & paste the <-- actual --> stack printed in the96// test output in here (don't forget the final \n):97private final String expected =98"1: VerifyStackTrace.lambda$test$1(VerifyStackTrace.java:211)\n" +99"2: VerifyStackTrace$Handle.execute(VerifyStackTrace.java:147)\n" +100"3: VerifyStackTrace$Handle.run(VerifyStackTrace.java:160)\n" +101"4: VerifyStackTrace.invoke(VerifyStackTrace.java:190)\n" +102"5: java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n" +103"6: java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n" +104"7: java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n" +105"8: java.base/java.lang.reflect.Method.invoke(Method.java:520)\n" +106"9: VerifyStackTrace$1.run(VerifyStackTrace.java:220)\n" +107"10: java.base/java.security.AccessController.doPrivileged(AccessController.java:310)\n" +108"11: VerifyStackTrace.test(VerifyStackTrace.java:229)\n" +109"12: VerifyStackTrace.main(VerifyStackTrace.java:185)\n";110111@Override public StackWalker walker() { return walker;}112@Override public String description() { return description;}113@Override public String expected() { return expected;}114}115static class TestCase3 implements TestCase {116private final StackWalker walker = StackWalker.getInstance(117EnumSet.of(RETAIN_CLASS_REFERENCE, SHOW_HIDDEN_FRAMES));118119private final String description = "StackWalker.getInstance(" +120"StackWalker.Option.RETAIN_CLASS_REFERENCE, " +121"StackWalker.Option.SHOW_HIDDEN_FRAMES)";122123// Note: line numbers and lambda hashes will be erased when124// comparing stack traces. However, the stack may change125// if some methods are being renamed in the code base.126// If the JDK code base changes and the test fails because of that,127// then after validating that the actual stack trace obtained128// is indeed correct (no frames are skipped that shouldn't)129// then you can cut & paste the <-- actual --> stack printed in the130// test output in here (don't forget the final \n):131private final String expected =132"1: VerifyStackTrace.lambda$test$1(VerifyStackTrace.java:213)\n" +133"2: VerifyStackTrace$$Lambda$1/0x00000007c0089430.run(Unknown Source)\n" +134"3: VerifyStackTrace$Handle.execute(VerifyStackTrace.java:149)\n" +135"4: java.base/java.lang.invoke.LambdaForm$DMH/0x00000007c008a830.invokeVirtual_LL_V(LambdaForm$DMH)\n" +136"5: java.base/java.lang.invoke.LambdaForm$MH/0x00000007c008a830.invoke_MT(LambdaForm$MH)\n" +137"6: VerifyStackTrace$Handle.run(VerifyStackTrace.java:162)\n" +138"7: VerifyStackTrace.invoke(VerifyStackTrace.java:192)\n" +139"8: java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n" +140"9: java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n" +141"10: java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n" +142"11: java.base/java.lang.reflect.Method.invoke(Method.java:520)\n" +143"12: VerifyStackTrace$1.run(VerifyStackTrace.java:222)\n" +144"13: java.base/java.security.AccessController.executePrivileged(AccessController.java:759)\n" +145"14: java.base/java.security.AccessController.doPrivileged(AccessController.java:310)\n" +146"15: VerifyStackTrace.test(VerifyStackTrace.java:231)\n" +147"16: VerifyStackTrace.main(VerifyStackTrace.java:188)\n";148149@Override public StackWalker walker() { return walker;}150@Override public String description() { return description;}151@Override public String expected() { return expected;}152}153154static final class TestCase4 extends TestCase3 {155private final StackWalker walker = StackWalker.getInstance(156EnumSet.allOf(StackWalker.Option.class));157158private final String description = "StackWalker.getInstance(" +159"StackWalker.Option.RETAIN_CLASS_REFERENCE, " +160"StackWalker.Option.SHOW_HIDDEN_FRAMES, " +161"StackWalker.Option.SHOW_REFLECT_FRAMES)";162163@Override public StackWalker walker() {return walker;}164@Override public String description() {return description;}165}166167public static class Handle implements Runnable {168169Runnable impl;170public Handle(Runnable run) {171this.impl = run;172}173174public void execute(Runnable run) {175run.run();176}177178public void run() {179MethodHandles.Lookup lookup = MethodHandles.lookup();180MethodHandle handle = null;181try {182handle = lookup.findVirtual(Handle.class, "execute",183MethodType.methodType(void.class, Runnable.class));184} catch(NoSuchMethodException | IllegalAccessException x) {185throw new RuntimeException(x);186}187try {188handle.invoke(this, impl);189} catch(Error | RuntimeException x) {190throw x;191} catch(Throwable t) {192throw new RuntimeException(t);193}194}195}196197static String prepare(String produced, boolean eraseSensitiveInfo) {198if (eraseSensitiveInfo) {199// Erase sensitive information before comparing:200// comparing line numbers is too fragile, so we just erase them201// out before comparing. We also erase the hash-like names of202// synthetic frames introduced by lambdas & method handles203return produced.replaceAll(":[1-9][0-9]*\\)", ":00)")204.replaceAll("/0x[0-9a-f]+\\.run", "/xxxxxxxx.run")205.replaceAll("/0x[0-9a-f]+\\.invoke", "/xxxxxxxx.invoke")206// LFs may or may not be pre-generated, making frames differ207.replaceAll("DirectMethodHandle\\$Holder", "LambdaForm\\$DMH")208.replaceAll("Invokers\\$Holder", "LambdaForm\\$MH")209.replaceAll("MH\\.invoke", "MH/xxxxxxxx.invoke")210// invoke frames may or may not have basic method type211// information encoded for diagnostic purposes212.replaceAll("xx\\.invoke([A-Za-z]*)_[A-Z_]+", "xx.invoke$1")213.replaceAll("\\$[0-9]+", "\\$??");214} else {215return produced;216}217}218219220public static void main(String[] args) {221test(new TestCase1());222test(new TestCase2());223test(new TestCase3());224test(new TestCase4());225}226227public static void invoke(Runnable run) {228run.run();229}230231static final class Recorder {232boolean found; // stop recording after main233public void recordSTE(long counter, StringBuilder s, StackFrame f) {234if (found) return;235found = VerifyStackTrace.class.equals(f.getDeclaringClass()) &&236"main".equals(f.getMethodName());237String line = String.format("%d: %s", counter, f.toStackTraceElement());238s.append(line).append('\n');239System.out.println(line);240}241}242243244static void test(TestCase test) {245System.out.println("\nTesting: " + test.description());246final AtomicLong counter = new AtomicLong();247final StringBuilder builder = new StringBuilder();248final Recorder recorder = new Recorder();249final Runnable run = () -> test.walker().forEach(250f -> recorder.recordSTE(counter.incrementAndGet(), builder, f));251final Handle handle = new Handle(run);252253// We're not using lambda on purpose here. We want the anonymous254// class on the stack.255PrivilegedAction<Object> pa = new PrivilegedAction<Object>() {256@Override257public Object run() {258try {259return VerifyStackTrace.class260.getMethod("invoke", Runnable.class)261.invoke(null, handle);262} catch (NoSuchMethodException263| IllegalAccessException264| InvocationTargetException ex) {265System.out.flush();266throw new RuntimeException(ex);267}268}269};270AccessController.doPrivileged(pa);271System.out.println("Main found: " + recorder.found);272if (!Objects.equals(prepare(test.expected(), true), prepare(builder.toString(), true))) {273System.out.flush();274try {275// sleep to make it less likely that System.out & System.err will276// interleave.277Thread.sleep(1000);278} catch (InterruptedException ex) {279}280System.err.println("\nUnexpected stack trace: "281+ "\n<!-- expected -->\n"282+ prepare(test.expected(), true)283+ "\n<-- actual -->\n"284+ prepare(builder.toString(), false));285throw new RuntimeException("Unexpected stack trace for: " + test.description());286}287}288289290}291292293