| /* |
| * Copyright (c) 2001, 2018, 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. |
| */ |
| |
| /** |
| * @test |
| * @bug 8187143 |
| * @summary JDI crash in ~BufferBlob::MethodHandles adapters |
| * |
| * @run build TestScaffold VMConnection TargetListener TargetAdapter |
| * @run compile -g NashornPopFrameTest.java |
| * @run driver NashornPopFrameTest |
| */ |
| import com.sun.jdi.*; |
| import com.sun.jdi.event.*; |
| import com.sun.jdi.request.*; |
| |
| import jdk.nashorn.api.scripting.NashornScriptEngineFactory; |
| import javax.script.*; |
| |
| import java.io.PrintStream; |
| |
| |
| // The debuggee, creates and uses a Nashorn engine to evaluate a simple script. |
| |
| // The debugger tries to set a breakpoint in Nashorn internal DEBUGGER method. |
| // When the breakpoint is reached, it looks for stack frame whose method's |
| // declaring type name starts with jdk.nashorn.internal.scripts.Script$. |
| // (nashorn dynamically generated classes) |
| // It then pops stack frames using the ThreadReference.popFrames() call, up to |
| // and including the above stackframe. |
| // The execution of the debuggee application is resumed after the needed |
| // frames have been popped. |
| |
| class ScriptDebuggee { |
| public final static int BKPT_LINE = 74; |
| static ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine(); |
| static public String failReason = null; |
| |
| static void doit() throws Exception { |
| System.out.println("Debugee: started!"); |
| String script = |
| "function f() {\r\n" + |
| " debugger;\r\n" + |
| " debugger;\r\n" + |
| "}\r\n" + |
| "f();"; |
| try { |
| engine.eval(script); |
| } catch (Exception ex) { |
| failReason = "ScriptDebuggee failed: Exception in engine.eval(): " |
| + ex.toString(); |
| ex.printStackTrace(); |
| } |
| System.out.println("Debugee: finished!"); // BKPT_LINE |
| } |
| |
| public static void main(String[] args) throws Exception { |
| doit(); |
| } |
| } |
| |
| /********** test program **********/ |
| |
| public class NashornPopFrameTest extends TestScaffold { |
| static PrintStream out = System.out; |
| static boolean breakpointReached = false; |
| String debuggeeFailReason = null; |
| ClassType targetClass; |
| ThreadReference mainThread; |
| BreakpointRequest bkptRequest; |
| |
| NashornPopFrameTest(String args[]) { |
| super(args); |
| } |
| |
| public static void main(String[] args) throws Exception { |
| NashornPopFrameTest nashornPopFrameTest = new NashornPopFrameTest(args); |
| nashornPopFrameTest.startTests(); |
| } |
| |
| /********** test core **********/ |
| |
| protected void runTests() throws Exception { |
| /* |
| * Get to the top of main() to determine targetClass and mainThread |
| */ |
| BreakpointEvent bpe = startToMain("ScriptDebuggee"); |
| targetClass = (ClassType)bpe.location().declaringType(); |
| out.println("Agent: runTests: after startToMain()"); |
| |
| mainThread = bpe.thread(); |
| EventRequestManager erm = vm().eventRequestManager(); |
| |
| Location loc = findLocation(targetClass, ScriptDebuggee.BKPT_LINE); |
| |
| try { |
| addListener(this); |
| } catch (Exception ex){ |
| ex.printStackTrace(); |
| failure("Failed: Could not add listener"); |
| throw new Exception("NashornPopFrameTest: failed with Exception in AddListener"); |
| } |
| |
| pauseAtDebugger(vm()); |
| bkptRequest = erm.createBreakpointRequest(loc); |
| bkptRequest.enable(); |
| |
| vm().resume(); |
| |
| try { |
| listen(vm()); |
| } catch (Exception exp) { |
| exp.printStackTrace(); |
| failure("Failed: Caught Exception while Listening"); |
| throw new Exception("NashornPopFrameTest: failed with Exception in listen()"); |
| } |
| |
| // Debugger continues to run until it receives a VMdisconnect event either because |
| // the Debuggee crashed / got exception / finished successfully. |
| while (!vmDisconnected) { |
| try { |
| Thread.sleep(100); |
| } catch (InterruptedException ee) { |
| } |
| } |
| |
| removeListener(this); |
| |
| if (breakpointReached) { |
| if (debuggeeFailReason != null) { |
| failure(debuggeeFailReason); |
| } |
| } else { |
| failure("Expected breakpoint in ScriptDebuggee:" + |
| ScriptDebuggee.BKPT_LINE + " was not reached"); |
| } |
| if (testFailed) { |
| throw new Exception("NashornPopFrameTest: failed"); |
| } |
| out.println("NashornPopFrameTest: passed"); |
| } |
| |
| private static void pauseAtDebugger(VirtualMachine vm) throws AbsentInformationException { |
| for (ReferenceType t : vm.allClasses()) pauseAtDebugger(t); |
| } |
| |
| // Set a breakpoint in Nashorn internal DEBUGGER method. |
| private static void pauseAtDebugger(ReferenceType t) throws AbsentInformationException { |
| if (!t.name().endsWith(".ScriptRuntime")) { |
| return; |
| } |
| for (Location l : t.allLineLocations()) { |
| if (!l.method().name().equals("DEBUGGER")) continue; |
| BreakpointRequest bkptReq = t.virtualMachine().eventRequestManager().createBreakpointRequest(l); |
| out.println("Setting breakpoint for " + l); |
| bkptReq.enable(); |
| break; |
| } |
| } |
| |
| private static void listen(VirtualMachine vm) throws Exception { |
| EventQueue eventQueue = vm.eventQueue(); |
| EventSet es = eventQueue.remove(); |
| if (es != null) { |
| handle(es); |
| } |
| } |
| |
| // Handle event when breakpoint is reached |
| private static void handle(EventSet eventSet) throws Exception { |
| out.println("Agent handle(): started"); |
| for (Event event : eventSet) { |
| if (event instanceof BreakpointEvent) { |
| findFrameAndPop(event); |
| } |
| } |
| eventSet.resume(); |
| out.println("Agent handle(): finished"); |
| } |
| |
| private static void findFrameAndPop(Event event) throws Exception { |
| ThreadReference thread = ((BreakpointEvent) event).thread(); |
| out.println("Agent: handling Breakpoint " + " at " + |
| ((BreakpointEvent) event).location() + |
| " in thread: " + thread); |
| StackFrame sf = findScriptFrame(thread); |
| if (sf != null) { |
| out.println("Thread Pop Frame on StackFrame = " + sf); |
| thread.popFrames(sf); |
| } |
| } |
| |
| // Find stack frame whose method's declaring type name starts with |
| // jdk.nashorn.internal.scripts.Script$ and return that frame |
| private static StackFrame findScriptFrame(ThreadReference t) throws IncompatibleThreadStateException { |
| for (int i = 0; i < t.frameCount(); i++) { |
| StackFrame sf = t.frame(i); |
| String typeName = sf.location().method().declaringType().name(); |
| if (typeName.startsWith("jdk.nashorn.internal.scripts.Script$")) { |
| out.println("Agent: in findScriptFrame: TypeName = " + typeName); |
| return sf; |
| } |
| } |
| throw new RuntimeException("no script frame"); |
| } |
| |
| static int bkptCount = 0; |
| |
| /********** event handlers **********/ |
| |
| public void breakpointReached(BreakpointEvent event) { |
| ThreadReference thread = ((BreakpointEvent) event).thread(); |
| String locStr = "" + ((BreakpointEvent) event).location(); |
| out.println("Agent: BreakpointEvent #" + (bkptCount++) + |
| " at " + locStr + " in thread: " + thread); |
| if (locStr.equals("ScriptDebuggee:" + ScriptDebuggee.BKPT_LINE)) { |
| breakpointReached = true; |
| Field failReasonField = targetClass.fieldByName("failReason"); |
| Value failReasonVal = targetClass.getValue(failReasonField); |
| if (failReasonVal != null) { |
| debuggeeFailReason = ((StringReference)failReasonVal).value(); |
| } |
| bkptRequest.disable(); |
| } |
| } |
| |
| public void eventSetComplete(EventSet set) { |
| set.resume(); |
| } |
| |
| public void vmDisconnected(VMDisconnectEvent event) { |
| println("Agent: Got VMDisconnectEvent"); |
| } |
| |
| } |