1 /*
2  * Copyright (c) 2001, 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 /**
25  * @test
26  * @bug 8187143
27  * @summary JDI crash in ~BufferBlob::MethodHandles adapters
28  *
29  * @run build TestScaffold VMConnection TargetListener TargetAdapter
30  * @run compile -g NashornPopFrameTest.java
31  * @run driver NashornPopFrameTest
32  */
33 import com.sun.jdi.*;
34 import com.sun.jdi.event.*;
35 import com.sun.jdi.request.*;
36 
37 import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
38 import javax.script.*;
39 
40 import java.io.PrintStream;
41 
42 
43 // The debuggee, creates and uses a Nashorn engine to evaluate a simple script.
44 
45 // The debugger  tries to set a breakpoint in Nashorn internal DEBUGGER method.
46 // When the breakpoint is reached, it looks for stack frame whose method's
47 // declaring type name starts with jdk.nashorn.internal.scripts.Script$.
48 // (nashorn dynamically generated classes)
49 // It then pops stack frames using the ThreadReference.popFrames() call, up to
50 // and including the above stackframe.
51 // The execution of the debuggee application is resumed after the needed
52 // frames have been popped.
53 
54 class ScriptDebuggee {
55     public final static int BKPT_LINE = 74;
56     static ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine();
57     static public String failReason = null;
58 
doit()59     static void doit() throws Exception {
60         System.out.println("Debugee: started!");
61         String script =
62                 "function f() {\r\n" +
63                         " debugger;\r\n" +
64                         " debugger;\r\n" +
65                         "}\r\n" +
66                         "f();";
67         try {
68             engine.eval(script);
69         } catch (Exception ex) {
70             failReason = "ScriptDebuggee failed: Exception in engine.eval(): "
71                     + ex.toString();
72             ex.printStackTrace();
73         }
74         System.out.println("Debugee: finished!"); // BKPT_LINE
75     }
76 
main(String[] args)77     public static void main(String[] args) throws Exception {
78         doit();
79     }
80 }
81 
82 /********** test program **********/
83 
84 public class NashornPopFrameTest extends TestScaffold {
85     static PrintStream out = System.out;
86     static boolean breakpointReached = false;
87     String debuggeeFailReason = null;
88     ClassType targetClass;
89     ThreadReference mainThread;
90     BreakpointRequest bkptRequest;
91 
NashornPopFrameTest(String args[])92     NashornPopFrameTest(String args[]) {
93         super(args);
94     }
95 
main(String[] args)96     public static void main(String[] args)      throws Exception {
97         NashornPopFrameTest nashornPopFrameTest = new NashornPopFrameTest(args);
98         nashornPopFrameTest.startTests();
99     }
100 
101     /********** test core **********/
102 
runTests()103     protected void runTests() throws Exception {
104         /*
105          * Get to the top of main() to determine targetClass and mainThread
106          */
107         BreakpointEvent bpe = startToMain("ScriptDebuggee");
108         targetClass = (ClassType)bpe.location().declaringType();
109         out.println("Agent: runTests: after startToMain()");
110 
111         mainThread = bpe.thread();
112         EventRequestManager erm = vm().eventRequestManager();
113 
114         Location loc = findLocation(targetClass, ScriptDebuggee.BKPT_LINE);
115 
116         try {
117             addListener(this);
118         } catch (Exception ex){
119             ex.printStackTrace();
120             failure("Failed: Could not add listener");
121             throw new Exception("NashornPopFrameTest: failed with Exception in AddListener");
122         }
123 
124         pauseAtDebugger(vm());
125         bkptRequest = erm.createBreakpointRequest(loc);
126         bkptRequest.enable();
127 
128         vm().resume();
129 
130         try {
131             listen(vm());
132         } catch (Exception exp) {
133             exp.printStackTrace();
134             failure("Failed: Caught Exception while Listening");
135             throw new Exception("NashornPopFrameTest: failed with Exception in listen()");
136         }
137 
138         // Debugger continues to run until it receives a VMdisconnect event either because
139         // the Debuggee crashed / got exception / finished successfully.
140         while (!vmDisconnected) {
141             try {
142                 Thread.sleep(100);
143             } catch (InterruptedException ee) {
144             }
145         }
146 
147         removeListener(this);
148 
149         if (breakpointReached) {
150             if (debuggeeFailReason != null) {
151                 failure(debuggeeFailReason);
152             }
153         } else {
154             failure("Expected breakpoint in ScriptDebuggee:" +
155                     ScriptDebuggee.BKPT_LINE + " was not reached");
156         }
157         if (testFailed) {
158             throw new Exception("NashornPopFrameTest: failed");
159         }
160         out.println("NashornPopFrameTest: passed");
161     }
162 
pauseAtDebugger(VirtualMachine vm)163     private static void pauseAtDebugger(VirtualMachine vm) throws AbsentInformationException {
164         for (ReferenceType t : vm.allClasses()) pauseAtDebugger(t);
165     }
166 
167     // Set a breakpoint in Nashorn internal DEBUGGER method.
pauseAtDebugger(ReferenceType t)168     private static void pauseAtDebugger(ReferenceType t) throws AbsentInformationException {
169         if (!t.name().endsWith(".ScriptRuntime")) {
170             return;
171         }
172         for (Location l : t.allLineLocations()) {
173             if (!l.method().name().equals("DEBUGGER")) continue;
174             BreakpointRequest bkptReq = t.virtualMachine().eventRequestManager().createBreakpointRequest(l);
175             out.println("Setting breakpoint for " + l);
176             bkptReq.enable();
177             break;
178         }
179     }
180 
listen(VirtualMachine vm)181     private static void listen(VirtualMachine vm) throws Exception {
182         EventQueue eventQueue = vm.eventQueue();
183         EventSet es = eventQueue.remove();
184         if (es != null) {
185             handle(es);
186         }
187     }
188 
189     // Handle event when breakpoint is reached
handle(EventSet eventSet)190     private static void handle(EventSet eventSet) throws Exception {
191         out.println("Agent handle(): started");
192         for (Event event : eventSet) {
193             if (event instanceof BreakpointEvent) {
194                 findFrameAndPop(event);
195             }
196         }
197         eventSet.resume();
198         out.println("Agent handle(): finished");
199     }
200 
findFrameAndPop(Event event)201     private static void findFrameAndPop(Event event) throws Exception {
202         ThreadReference thread = ((BreakpointEvent) event).thread();
203         out.println("Agent: handling Breakpoint " + " at " +
204                 ((BreakpointEvent) event).location() +
205                 " in thread: " + thread);
206         StackFrame sf = findScriptFrame(thread);
207         if (sf != null) {
208             out.println("Thread Pop Frame on StackFrame = " + sf);
209             thread.popFrames(sf);
210         }
211     }
212 
213     // Find stack frame whose method's declaring type name starts with
214     // jdk.nashorn.internal.scripts.Script$ and return that frame
findScriptFrame(ThreadReference t)215     private static StackFrame findScriptFrame(ThreadReference t) throws IncompatibleThreadStateException {
216         for (int i = 0; i < t.frameCount(); i++) {
217             StackFrame sf = t.frame(i);
218             String typeName = sf.location().method().declaringType().name();
219             if (typeName.startsWith("jdk.nashorn.internal.scripts.Script$")) {
220                 out.println("Agent: in findScriptFrame: TypeName = " + typeName);
221                 return sf;
222             }
223         }
224         throw new RuntimeException("no script frame");
225     }
226 
227     static int bkptCount = 0;
228 
229     /********** event handlers **********/
230 
breakpointReached(BreakpointEvent event)231     public void breakpointReached(BreakpointEvent event) {
232         ThreadReference thread = ((BreakpointEvent) event).thread();
233         String locStr = "" + ((BreakpointEvent) event).location();
234         out.println("Agent: BreakpointEvent #" + (bkptCount++) +
235                 " at " + locStr + " in thread: " + thread);
236         if (locStr.equals("ScriptDebuggee:" + ScriptDebuggee.BKPT_LINE)) {
237             breakpointReached = true;
238             Field failReasonField = targetClass.fieldByName("failReason");
239             Value failReasonVal = targetClass.getValue(failReasonField);
240             if (failReasonVal != null) {
241                 debuggeeFailReason = ((StringReference)failReasonVal).value();
242             }
243             bkptRequest.disable();
244         }
245     }
246 
eventSetComplete(EventSet set)247     public void eventSetComplete(EventSet set) {
248         set.resume();
249     }
250 
vmDisconnected(VMDisconnectEvent event)251     public void vmDisconnected(VMDisconnectEvent event) {
252         println("Agent: Got VMDisconnectEvent");
253     }
254 
255 }
256