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