# HG changeset patch # User Michael Van De Vanter # Date 1446683620 28800 # Node ID 1c3deda60a9e875e1481021d25518dd9a1f1d379 # Parent c3e397ce5941dd42e38e3e85dea5590bb233bc42 Truffle/Debugging: the REPL debugger now remembers breakpoint requests it receives from the command line client when there have been no executions yet (which means breakpoints cannot be set in the engine/debugger). When the first execution event arrives (with a reference to the Debugger), any "pending" breakpoints are created with the Debugger. This involved a bit of refactoring on the Breakpoint class hierarchy. diff -r c3e397ce5941 -r 1c3deda60a9e truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Breakpoint.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Breakpoint.java Tue Oct 27 17:44:27 2015 -0700 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Breakpoint.java Wed Nov 04 16:33:40 2015 -0800 @@ -33,13 +33,10 @@ import com.oracle.truffle.api.source.Source; /** - * Breakpoint in a {@link com.oracle.truffle.api.vm.PolyglotEngine} with - * {@link com.oracle.truffle.api.debug debugging turned on}. You can ask - * {@link Debugger#setLineBreakpoint(int, com.oracle.truffle.api.source.LineLocation, boolean)} or - * {@link Debugger#setTagBreakpoint(int, com.oracle.truffle.api.instrument.SyntaxTag, boolean)} to - * create an instance of {@link Breakpoint}. + * Breakpoint in an executing {@link com.oracle.truffle.api.vm.PolyglotEngine}. + * + * @see Debugger */ -@SuppressWarnings("javadoc") public abstract class Breakpoint { /** @@ -111,7 +108,7 @@ private State state; - Breakpoint(State state, int ignoreCount, boolean isOneShot) { + protected Breakpoint(State state, int ignoreCount, boolean isOneShot) { this.state = state; this.isOneShot = isOneShot; this.ignoreCount = ignoreCount; @@ -200,7 +197,7 @@ assert state == s; } - final void setState(State state) { + protected final void setState(State state) { this.state = state; } diff -r c3e397ce5941 -r 1c3deda60a9e truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java Tue Oct 27 17:44:27 2015 -0700 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java Wed Nov 04 16:33:40 2015 -0800 @@ -152,7 +152,7 @@ * @throws IOException if the breakpoint can not be set. */ @TruffleBoundary - public Breakpoint setLineBreakpoint(int ignoreCount, LineLocation lineLocation, boolean oneShot) throws IOException { + public LineBreakpoint setLineBreakpoint(int ignoreCount, LineLocation lineLocation, boolean oneShot) throws IOException { return lineBreaks.create(ignoreCount, lineLocation, oneShot); } @@ -165,7 +165,7 @@ * @throws IOException if the breakpoint already set */ @TruffleBoundary - public Breakpoint setTagBreakpoint(int ignoreCount, SyntaxTag tag, boolean oneShot) throws IOException { + public TagBreakpoint setTagBreakpoint(int ignoreCount, SyntaxTag tag, boolean oneShot) throws IOException { return tagBreaks.create(ignoreCount, tag, oneShot); } diff -r c3e397ce5941 -r 1c3deda60a9e truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpoint.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpoint.java Tue Oct 27 17:44:27 2015 -0700 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpoint.java Wed Nov 04 16:33:40 2015 -0800 @@ -31,16 +31,25 @@ * * @see Debugger */ -abstract class LineBreakpoint extends Breakpoint { +public abstract class LineBreakpoint extends Breakpoint { + + private final LineLocation lineLocation; - LineBreakpoint(State state, int ignoreCount, boolean isOneShot) { + protected LineBreakpoint(State state, LineLocation lineLocation, int ignoreCount, boolean isOneShot) { super(state, ignoreCount, isOneShot); + this.lineLocation = lineLocation; } /** * Gets the {@linkplain LineLocation source line location} that specifies where this breakpoint * will trigger. */ - public abstract LineLocation getLineLocation(); + public final LineLocation getLineLocation() { + return lineLocation; + } + @Override + public String getLocationDescription() { + return "Line: " + lineLocation.getShortDescription(); + } } diff -r c3e397ce5941 -r 1c3deda60a9e truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpointFactory.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpointFactory.java Tue Oct 27 17:44:27 2015 -0700 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpointFactory.java Wed Nov 04 16:33:40 2015 -0800 @@ -272,8 +272,6 @@ private static final String SHOULD_NOT_HAPPEN = "LineBreakpointImpl: should not happen"; - private final LineLocation lineLocation; - // Cached assumption that the global status of line breakpoint activity has not changed. private Assumption breakpointsActiveAssumption; @@ -290,9 +288,7 @@ private List instruments = new ArrayList<>(); public LineBreakpointImpl(int ignoreCount, LineLocation lineLocation, boolean oneShot) { - super(ENABLED_UNRESOLVED, ignoreCount, oneShot); - this.lineLocation = lineLocation; - + super(ENABLED_UNRESOLVED, lineLocation, ignoreCount, oneShot); this.breakpointsActiveAssumption = LineBreakpointFactory.this.breakpointsActiveUnchanged.getAssumption(); this.isEnabled = true; this.enabledUnchangedAssumption = Truffle.getRuntime().createAssumption(BREAKPOINT_NAME + " enabled state unchanged"); @@ -471,16 +467,6 @@ warningLog.addWarning(String.format("Exception in %s: %s", getShortDescription(), ex.getMessage())); } - @Override - public String getLocationDescription() { - return "Line: " + lineLocation.getShortDescription(); - } - - @Override - public LineLocation getLineLocation() { - return lineLocation; - } - private final class UnconditionalLineBreakInstrumentListener extends DefaultStandardInstrumentListener { @Override diff -r c3e397ce5941 -r 1c3deda60a9e truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpoint.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpoint.java Tue Oct 27 17:44:27 2015 -0700 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpoint.java Wed Nov 04 16:33:40 2015 -0800 @@ -31,15 +31,24 @@ * * @see Debugger */ -abstract class TagBreakpoint extends Breakpoint { +public abstract class TagBreakpoint extends Breakpoint { + + private final SyntaxTag tag; - TagBreakpoint(State state, int ignoreCount, boolean isOneShot) { + protected TagBreakpoint(State state, SyntaxTag tag, int ignoreCount, boolean isOneShot) { super(state, ignoreCount, isOneShot); + this.tag = tag; } /** * Gets the tag that specifies where this breakpoint will trigger. */ - public abstract SyntaxTag getTag(); + public final SyntaxTag getTag() { + return tag; + } + @Override + public String getLocationDescription() { + return "Tag " + tag.name(); + } } diff -r c3e397ce5941 -r 1c3deda60a9e truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java Tue Oct 27 17:44:27 2015 -0700 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java Wed Nov 04 16:33:40 2015 -0800 @@ -241,8 +241,6 @@ private static final String SHOULD_NOT_HAPPEN = "TagBreakpointImpl: should not happen"; - private final SyntaxTag tag; - // Cached assumption that the global status of tag breakpoint activity has not changed. private Assumption breakpointsActiveAssumption; @@ -259,8 +257,7 @@ private List instruments = new ArrayList<>(); private TagBreakpointImpl(int ignoreCount, SyntaxTag tag, boolean oneShot) { - super(ENABLED, ignoreCount, oneShot); - this.tag = tag; + super(ENABLED, tag, ignoreCount, oneShot); this.breakpointsActiveAssumption = TagBreakpointFactory.this.breakpointsActiveUnchanged.getAssumption(); this.isEnabled = true; this.enabledUnchangedAssumption = Truffle.getRuntime().createAssumption(BREAKPOINT_NAME + " enabled state unchanged"); @@ -359,7 +356,7 @@ @TruffleBoundary private String getShortDescription() { - return BREAKPOINT_NAME + "@" + tag.name(); + return BREAKPOINT_NAME + "@" + getTag().name(); } private void changeState(State after) { @@ -433,16 +430,6 @@ warningLog.addWarning(String.format("Exception in %s: %s", getShortDescription(), ex.getMessage())); } - @Override - public String getLocationDescription() { - return "Tag " + tag.name(); - } - - @Override - public SyntaxTag getTag() { - return tag; - } - private final class UnconditionalTagBreakInstrumentListener extends DefaultStandardInstrumentListener { @Override @@ -450,7 +437,5 @@ TagBreakpointImpl.this.nodeEnter(node, vFrame); } } - } - } diff -r c3e397ce5941 -r 1c3deda60a9e truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLHandler.java --- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLHandler.java Tue Oct 27 17:44:27 2015 -0700 +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLHandler.java Wed Nov 04 16:33:40 2015 -0800 @@ -26,6 +26,7 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import com.oracle.truffle.api.debug.Breakpoint; @@ -353,15 +354,14 @@ @Override public REPLMessage[] receive(REPLMessage request, REPLServer replServer) { final REPLMessage reply = createReply(); - int deleteCount = 0; - for (Breakpoint breakpoint : replServer.getBreakpoints()) { - breakpoint.dispose(); - deleteCount++; - } - if (deleteCount == 0) { + final Collection breakpoints = replServer.getBreakpoints(); + if (breakpoints.isEmpty()) { return finishReplyFailed(reply, "no breakpoints to delete"); } - return finishReplySucceeded(reply, Integer.toString(deleteCount) + " breakpoints deleted"); + for (Breakpoint breakpoint : breakpoints) { + breakpoint.dispose(); + } + return finishReplySucceeded(reply, Integer.toString(breakpoints.size()) + " breakpoints deleted"); } }; diff -r c3e397ce5941 -r 1c3deda60a9e truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServer.java --- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServer.java Tue Oct 27 17:44:27 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,372 +0,0 @@ -/* - * Copyright (c) 2015, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ -package com.oracle.truffle.tools.debug.shell.server; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.WeakHashMap; - -import com.oracle.truffle.api.debug.Breakpoint; -import com.oracle.truffle.api.debug.Debugger; -import com.oracle.truffle.api.debug.ExecutionEvent; -import com.oracle.truffle.api.debug.SuspendedEvent; -import com.oracle.truffle.api.frame.FrameInstance; -import com.oracle.truffle.api.frame.MaterializedFrame; -import com.oracle.truffle.api.instrument.StandardSyntaxTag; -import com.oracle.truffle.api.instrument.Visualizer; -import com.oracle.truffle.api.instrument.impl.DefaultVisualizer; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.source.LineLocation; -import com.oracle.truffle.api.source.Source; -import com.oracle.truffle.api.source.SourceSection; -import com.oracle.truffle.api.vm.EventConsumer; -import com.oracle.truffle.api.vm.PolyglotEngine; -import com.oracle.truffle.api.vm.PolyglotEngine.Language; -import com.oracle.truffle.api.vm.PolyglotEngine.Value; -import com.oracle.truffle.tools.debug.shell.REPLMessage; -import com.oracle.truffle.tools.debug.shell.client.SimpleREPLClient; - -/** - * The server side of a simple message-based protocol for a possibly remote language - * Read-Eval-Print-Loop. - */ -public final class REPLServer { - - // Language-agnostic - private final PolyglotEngine engine; - private Debugger db; - private Context currentServerContext; - private SimpleREPLClient replClient = null; - private String statusPrefix; - private final Map handlerMap = new HashMap<>(); - - // TODO (mlvdv) Language-specific - private final PolyglotEngine.Language language; - private final Visualizer visualizer; - - // Breakpoints registered with the debugger - private int breakpointCounter; - private Map breakpoints = new WeakHashMap<>(); - - /** - * Create a single-language server. - */ - public REPLServer(String mimeType, Visualizer visualizer) { - this.visualizer = visualizer == null ? new DefaultVisualizer() : visualizer; - EventConsumer onHalted = new EventConsumer(SuspendedEvent.class) { - @Override - protected void on(SuspendedEvent ev) { - REPLServer.this.haltedAt(ev); - } - }; - EventConsumer onExec = new EventConsumer(ExecutionEvent.class) { - @Override - protected void on(ExecutionEvent event) { - db = event.getDebugger(); - event.prepareStepInto(); - } - }; - engine = PolyglotEngine.buildNew().onEvent(onHalted).onEvent(onExec).build(); - this.language = engine.getLanguages().get(mimeType); - if (language == null) { - throw new RuntimeException("Implementation not found for \"" + mimeType + "\""); - } - statusPrefix = languageName(language); - } - - public void add(REPLHandler handler) { - handlerMap.put(handler.getOp(), handler); - } - - /** - * Starts up a server; status returned in a message. - */ - public void start() { - - addHandlers(); - this.replClient = new SimpleREPLClient(this); - this.currentServerContext = new Context(null, null); - replClient.start(); - } - - protected void addHandlers() { - add(REPLHandler.BACKTRACE_HANDLER); - add(REPLHandler.BREAK_AT_LINE_HANDLER); - add(REPLHandler.BREAK_AT_LINE_ONCE_HANDLER); - add(REPLHandler.BREAK_AT_THROW_HANDLER); - add(REPLHandler.BREAK_AT_THROW_ONCE_HANDLER); - add(REPLHandler.BREAKPOINT_INFO_HANDLER); - add(REPLHandler.CALL_HANDLER); - add(REPLHandler.CLEAR_BREAK_HANDLER); - add(REPLHandler.CONTINUE_HANDLER); - add(REPLHandler.DELETE_HANDLER); - add(REPLHandler.DISABLE_BREAK_HANDLER); - add(REPLHandler.ENABLE_BREAK_HANDLER); - add(REPLHandler.EVAL_HANDLER); - add(REPLHandler.FILE_HANDLER); - add(REPLHandler.FRAME_HANDLER); - add(REPLHandler.INFO_HANDLER); - add(REPLHandler.KILL_HANDLER); - add(REPLHandler.LOAD_HANDLER); - add(REPLHandler.QUIT_HANDLER); - add(REPLHandler.SET_BREAK_CONDITION_HANDLER); - add(REPLHandler.STEP_INTO_HANDLER); - add(REPLHandler.STEP_OUT_HANDLER); - add(REPLHandler.STEP_OVER_HANDLER); - add(REPLHandler.TRUFFLE_HANDLER); - add(REPLHandler.TRUFFLE_NODE_HANDLER); - add(REPLHandler.UNSET_BREAK_CONDITION_HANDLER); - } - - void haltedAt(SuspendedEvent event) { - // Create and push a new debug context where execution is halted - currentServerContext = new Context(currentServerContext, event); - - // Message the client that execution is halted and is in a new debugging context - final REPLMessage message = new REPLMessage(); - message.put(REPLMessage.OP, REPLMessage.STOPPED); - final SourceSection src = event.getNode().getSourceSection(); - final Source source = src.getSource(); - message.put(REPLMessage.SOURCE_NAME, source.getName()); - message.put(REPLMessage.FILE_PATH, source.getPath()); - message.put(REPLMessage.LINE_NUMBER, Integer.toString(src.getStartLine())); - message.put(REPLMessage.STATUS, REPLMessage.SUCCEEDED); - message.put(REPLMessage.DEBUG_LEVEL, Integer.toString(currentServerContext.getLevel())); - List warnings = event.getRecentWarnings(); - if (!warnings.isEmpty()) { - final StringBuilder sb = new StringBuilder(); - for (String warning : warnings) { - sb.append(warning + "\n"); - } - message.put(REPLMessage.WARNINGS, sb.toString()); - } - try { - // Cheat with synchrony: call client directly about entering a nested debugging - // context. - replClient.halted(message); - } finally { - // Returns when "continue" or "kill" is called in the new debugging context - - // Pop the debug context, and return so that the old context will continue - currentServerContext = currentServerContext.predecessor; - } - } - - /** - * Execution context of a halted program, possibly nested. - */ - public final class Context { - - private final Context predecessor; - private final int level; - private final SuspendedEvent event; - - Context(Context predecessor, SuspendedEvent event) { - this.level = predecessor == null ? 0 : predecessor.getLevel() + 1; - this.predecessor = predecessor; - this.event = event; - } - - /** - * The nesting depth of this context in the current session. - */ - int getLevel() { - return level; - } - - /** - * The AST node where execution is halted in this context. - */ - Node getNodeAtHalt() { - return event.getNode(); - } - - /** - * Evaluates given code snippet in the context of currently suspended execution. - * - * @param code the snippet to evaluate - * @param frame null in case the evaluation should happen in top most frame, - * non-null value - * @return result of the evaluation - * @throws IOException if something goes wrong - */ - Object eval(String code, FrameInstance frame) throws IOException { - if (event == null) { - throw new IOException("top level \"eval\" not yet supported"); - } - return event.eval(code, frame); - } - - /** - * The frame where execution is halted in this context. - */ - MaterializedFrame getFrameAtHalt() { - return event.getFrame(); - } - - /** - * Dispatches a REPL request to the appropriate handler. - */ - REPLMessage[] receive(REPLMessage request) { - final String command = request.get(REPLMessage.OP); - final REPLHandler handler = handlerMap.get(command); - - if (handler == null) { - final REPLMessage message = new REPLMessage(); - message.put(REPLMessage.OP, command); - message.put(REPLMessage.STATUS, REPLMessage.FAILED); - message.put(REPLMessage.DISPLAY_MSG, statusPrefix + " op \"" + command + "\" not supported"); - final REPLMessage[] reply = new REPLMessage[]{message}; - return reply; - } - return handler.receive(request, REPLServer.this); - } - - /** - * Provides access to the execution stack. - * - * @return immutable list of stack elements - */ - List getStack() { - List frames = new ArrayList<>(); - int frameCount = 1; - for (FrameInstance frameInstance : event.getStack()) { - if (frameCount == 1) { - frames.add(new FrameDebugDescription(frameCount, event.getNode(), frameInstance)); - } else { - frames.add(new FrameDebugDescription(frameCount, frameInstance.getCallNode(), frameInstance)); - } - frameCount++; - } - return Collections.unmodifiableList(frames); - } - - void prepareStepOut() { - event.prepareStepOut(); - } - - void prepareStepInto(int repeat) { - event.prepareStepInto(repeat); - } - - void prepareStepOver(int repeat) { - event.prepareStepOver(repeat); - } - - void prepareContinue() { - event.prepareContinue(); - } - } - - /** - * Ask the server to handle a request. Return a non-empty array of messages to simulate remote - * operation where the protocol has possibly multiple messages being returned asynchronously in - * response to each request. - */ - public REPLMessage[] receive(REPLMessage request) { - if (currentServerContext == null) { - final REPLMessage message = new REPLMessage(); - message.put(REPLMessage.STATUS, REPLMessage.FAILED); - message.put(REPLMessage.DISPLAY_MSG, "server not started"); - final REPLMessage[] reply = new REPLMessage[]{message}; - return reply; - } - return currentServerContext.receive(request); - } - - Context getCurrentContext() { - return currentServerContext; - } - - Visualizer getVisualizer() { - return visualizer; - } - - // TODO (mlvdv) language-specific - Language getLanguage() { - return language; - } - - // TODO (mlvdv) language-specific - public String getLanguageName() { - return languageName(this.language); - } - - private static String languageName(Language lang) { - return lang.getName() + "(" + lang.getVersion() + ")"; - } - - void eval(Source source) throws IOException { - engine.eval(source); - } - - Breakpoint setLineBreakpoint(int ignoreCount, LineLocation lineLocation, boolean oneShot) throws IOException { - final Breakpoint breakpoint = db.setLineBreakpoint(ignoreCount, lineLocation, oneShot); - registerBreakpoint(breakpoint); - return breakpoint; - } - - Breakpoint setTagBreakpoint(int ignoreCount, StandardSyntaxTag tag, boolean oneShot) throws IOException { - final Breakpoint breakpoint = db.setTagBreakpoint(ignoreCount, tag, oneShot); - registerBreakpoint(breakpoint); - return breakpoint; - } - - private synchronized void registerBreakpoint(Breakpoint breakpoint) { - breakpoints.put(breakpoint, breakpointCounter++); - } - - synchronized Breakpoint findBreakpoint(int id) { - for (Map.Entry entrySet : breakpoints.entrySet()) { - if (id == entrySet.getValue()) { - return entrySet.getKey(); - } - } - return null; - } - - Collection getBreakpoints() { - return db.getBreakpoints(); - } - - synchronized int getBreakpointID(Breakpoint breakpoint) { - final Integer id = breakpoints.get(breakpoint); - return id == null ? -1 : id; - } - - void call(String name) throws IOException { - Value symbol = engine.findGlobalSymbol(name); - if (symbol == null) { - throw new IOException("symboleval f \"" + name + "\" not found"); - } - symbol.invoke(null); - } - -}