test/jdk/com/sun/jdi/NashornPopFrameTest.java
author psomashe
Fri, 09 Feb 2018 09:56:05 -0800
changeset 48952 e999964446d7
permissions -rw-r--r--
8193150: Create a jtreg version of the test from JDK-8187143. Summary: Create a new jtreg test based on the test attached to the bug and add it to the ProblemsList. Reviewed-by: sspitsyn, cjplummer

/*
 * 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");
    }

}