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 
  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 
  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 
  92     NashornPopFrameTest(String args[]) {
  93         super(args);
  94     }
  95 
  96     public static void main(String[] args)      throws Exception {
  97         NashornPopFrameTest nashornPopFrameTest = new NashornPopFrameTest(args);
  98         nashornPopFrameTest.startTests();
  99     }
 100 
 101     /********** test core **********/
 102 
 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 
 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.
 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 
 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
 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 
 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
 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 
 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 
 247     public void eventSetComplete(EventSet set) {
 248         set.resume();
 249     }
 250 
 251     public void vmDisconnected(VMDisconnectEvent event) {
 252         println("Agent: Got VMDisconnectEvent");
 253     }
 254 
 255 }