diff truffle/com.oracle.truffle.sl.tools/src/com/oracle/truffle/sl/tools/debug/SLREPLServer.java @ 22003:5bc7f7b867ab

Making debugger always on for each TruffleVM execution. Introducing EventConsumer to process such debugger events. Requesting each RootNode to be associated with a TruffleLanguage, so debugger can find out proper context for each Node where executions gets suspended.
author Jaroslav Tulach <jaroslav.tulach@oracle.com>
date Sat, 18 Jul 2015 18:03:36 +0200
parents c07e64ecb528
children 503529c65456
line wrap: on
line diff
--- a/truffle/com.oracle.truffle.sl.tools/src/com/oracle/truffle/sl/tools/debug/SLREPLServer.java	Thu Jul 16 19:11:31 2015 +0200
+++ b/truffle/com.oracle.truffle.sl.tools/src/com/oracle/truffle/sl/tools/debug/SLREPLServer.java	Sat Jul 18 18:03:36 2015 +0200
@@ -40,16 +40,19 @@
  */
 package com.oracle.truffle.sl.tools.debug;
 
+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 java.util.*;
 
-import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.instrument.*;
-import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.api.vm.*;
 import com.oracle.truffle.api.vm.TruffleVM.Language;
 import com.oracle.truffle.sl.*;
-import com.oracle.truffle.tools.debug.engine.*;
+import com.oracle.truffle.sl.nodes.instrument.SLDefaultVisualizer;
 import com.oracle.truffle.tools.debug.shell.*;
 import com.oracle.truffle.tools.debug.shell.client.*;
 import com.oracle.truffle.tools.debug.shell.server.*;
@@ -58,12 +61,11 @@
  * Instantiation of the "server" side of the "REPL*" debugger for the Simple language.
  * <p>
  * The SL parser is not equipped to parse program fragments, so any debugging functions that depend
- * on this are not supported, for example {@link DebugEngine#eval(Source, Node, MaterializedFrame)}
- * and {@link Breakpoint#setCondition(String)}.
+ * on this are not supported, for example "eval" and breakpoint conditions.
  *
  * @see SimpleREPLClient
  */
-public final class SLREPLServer implements REPLServer {
+public final class SLREPLServer extends REPLServer {
 
     // TODO (mlvdv) remove when there's a better way to express this dependency
     @SuppressWarnings("unused") private static final Class<SLLanguage> DYNAMIC_DEPENDENCY = com.oracle.truffle.sl.SLLanguage.class;
@@ -83,7 +85,8 @@
     }
 
     private final Language language;
-    private final DebugEngine slDebugEngine;
+    private final TruffleVM vm;
+    private Debugger db;
     private final String statusPrefix;
     private final Map<String, REPLHandler> handlerMap = new HashMap<>();
     private SLServerContext currentServerContext;
@@ -118,13 +121,25 @@
         add(REPLHandler.TRUFFLE_HANDLER);
         add(REPLHandler.TRUFFLE_NODE_HANDLER);
 
-        TruffleVM vm = TruffleVM.newVM().build();
-        this.language = vm.getLanguages().get("application/x-sl");
+        EventConsumer<SuspendedEvent> onHalted = new EventConsumer<SuspendedEvent>(SuspendedEvent.class) {
+            @Override
+            protected void on(SuspendedEvent ev) {
+                SLREPLServer.this.haltedAt(ev);
+            }
+        };
+        EventConsumer<ExecutionEvent> onExec = new EventConsumer<ExecutionEvent>(ExecutionEvent.class) {
+            @Override
+            protected void on(ExecutionEvent event) {
+                event.prepareStepInto();
+                db = event.getDebugger();
+            }
+        };
+
+        TruffleVM newVM = TruffleVM.newVM().onEvent(onHalted).onEvent(onExec).build();
+        this.language = newVM.getLanguages().get("application/x-sl");
         assert language != null;
 
-        final SLREPLDebugClient slDebugClient = new SLREPLDebugClient(language);
-        this.slDebugEngine = DebugEngine.create(slDebugClient, language);
-
+        this.vm = newVM;
         this.statusPrefix = language.getShortName() + " REPL:";
     }
 
@@ -132,9 +147,10 @@
         this.replClient = replClient;
     }
 
+    @Override
     public REPLMessage start() {
 
-        this.currentServerContext = new SLServerContext(null, null, null);
+        this.currentServerContext = new SLServerContext(null, null);
 
         // SL doesn't load modules (like other languages), so we just return a success
         final REPLMessage reply = new REPLMessage();
@@ -143,6 +159,7 @@
         return reply;
     }
 
+    @Override
     public REPLMessage[] receive(REPLMessage request) {
         if (currentServerContext == null) {
             final REPLMessage message = new REPLMessage();
@@ -161,8 +178,8 @@
 
         private final SLServerContext predecessor;
 
-        public SLServerContext(SLServerContext predecessor, Node astNode, MaterializedFrame mFrame) {
-            super(predecessor == null ? 0 : predecessor.getLevel() + 1, astNode, mFrame);
+        public SLServerContext(SLServerContext predecessor, SuspendedEvent event) {
+            super(predecessor == null ? 0 : predecessor.getLevel() + 1, event);
             this.predecessor = predecessor;
         }
 
@@ -188,66 +205,68 @@
         }
 
         @Override
-        public DebugEngine getDebugEngine() {
-            return slDebugEngine;
+        public Visualizer getVisualizer() {
+            return new SLDefaultVisualizer();
+        }
+
+        @Override
+        public TruffleVM vm() {
+            return vm;
+        }
+
+        @Override
+        protected Debugger db() {
+            return db;
+        }
+
+        @Override
+        public void registerBreakpoint(Breakpoint breakpoint) {
+            SLREPLServer.this.registerBreakpoint(breakpoint);
+        }
+
+        @Override
+        public Breakpoint findBreakpoint(int id) {
+            return SLREPLServer.this.findBreakpoint(id);
+        }
+
+        @Override
+        public int getBreakpointID(Breakpoint breakpoint) {
+            return SLREPLServer.this.getBreakpointID(breakpoint);
         }
 
     }
 
-    /**
-     * Specialize the standard SL debug context by notifying the REPL client when execution is
-     * halted, e.g. at a breakpoint.
-     * <p>
-     * Before notification, the server creates a new context at the halted location, in which
-     * subsequent evaluations take place until such time as the client says to "continue".
-     * <p>
-     * This implementation "cheats" the intended asynchronous architecture by calling back directly
-     * to the client with the notification.
-     */
-    private final class SLREPLDebugClient implements DebugClient {
-
-        private final Language language;
-
-        SLREPLDebugClient(Language language) {
-            this.language = language;
-        }
-
-        public void haltedAt(Node node, MaterializedFrame mFrame, List<String> warnings) {
-            // Create and push a new debug context where execution is halted
-            currentServerContext = new SLServerContext(currentServerContext, node, mFrame);
+    void haltedAt(SuspendedEvent event) {
+        // Create and push a new debug context where execution is halted
+        currentServerContext = new SLServerContext(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 = node.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()));
-            if (!warnings.isEmpty()) {
-                final StringBuilder sb = new StringBuilder();
-                for (String warning : warnings) {
-                    sb.append(warning + "\n");
-                }
-                message.put(REPLMessage.WARNINGS, sb.toString());
+        // 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<String> warnings = event.getRecentWarnings();
+        if (!warnings.isEmpty()) {
+            final StringBuilder sb = new StringBuilder();
+            for (String warning : warnings) {
+                sb.append(warning + "\n");
             }
-            try {
-                // Cheat with synchrony: call client directly about entering a nested debugging
-                // context.
-                replClient.halted(message);
-            } finally {
-                // Returns when "continue" is called in the new debugging context
+            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" is called in the new debugging context
 
-                // Pop the debug context, and return so that the old context will continue
-                currentServerContext = currentServerContext.predecessor;
-            }
-        }
-
-        public Language getLanguage() {
-            return language;
+            // Pop the debug context, and return so that the old context will continue
+            currentServerContext = currentServerContext.predecessor;
         }
     }
-
 }