comparison graal/com.oracle.truffle.sl.tools/src/com/oracle/truffle/sl/tools/debug/SLREPLServer.java @ 21568:3b8bbf51d320

Truffle/Debugging: add the Truffle DebugEngine and supporting code, as well as add a crude command-line debugging tool used mainly to test the DebugEngine. Migrate the small tols out of project com.oracle.truffle.api into the new project com.oracle.truffle.tools.
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Tue, 26 May 2015 16:38:13 -0700
parents
children 894f82515e38
comparison
equal deleted inserted replaced
21470:1bbef57f9a38 21568:3b8bbf51d320
1 /*
2 * Copyright (c) 2014, 2015, 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package com.oracle.truffle.sl.tools.debug;
26
27 import java.io.*;
28 import java.util.*;
29
30 import com.oracle.truffle.api.*;
31 import com.oracle.truffle.api.frame.*;
32 import com.oracle.truffle.api.instrument.*;
33 import com.oracle.truffle.api.nodes.*;
34 import com.oracle.truffle.api.source.*;
35 import com.oracle.truffle.tools.debug.engine.*;
36 import com.oracle.truffle.tools.debug.shell.*;
37 import com.oracle.truffle.tools.debug.shell.client.*;
38 import com.oracle.truffle.tools.debug.shell.server.*;
39 import com.oracle.truffle.sl.factory.*;
40 import com.oracle.truffle.sl.runtime.*;
41
42 /**
43 * Instantiation of the "server" side of the "REPL*" debugger for the Simple language.
44 * <p>
45 * The SL parser is not equipped to parse program fragments, so any debugging functions that depend
46 * on this are not supported, for example {@link DebugEngine#eval(Source, Node, MaterializedFrame)}
47 * and {@link Breakpoint#setCondition(String)}.
48 *
49 * @see SimpleREPLClient
50 */
51 public final class SLREPLServer implements REPLServer {
52 public static void main(String[] args) {
53 // Cheating for the prototype: start from SL, rather than from the client.
54 final SLREPLServer server = new SLREPLServer();
55 final SimpleREPLClient client = new SimpleREPLClient(server.slContext, server);
56
57 // Cheating for the prototype: allow server access to client for recursive debugging
58 server.setClient(client);
59
60 try {
61 client.start();
62 } catch (QuitException ex) {
63 }
64 }
65
66 private final SLContext slContext;
67 private final DebugEngine slDebugEngine;
68 private final String statusPrefix;
69 private final Map<String, REPLHandler> handlerMap = new HashMap<>();
70 private SLServerContext currentServerContext;
71 private SimpleREPLClient replClient = null;
72
73 private void add(REPLHandler fileHandler) {
74 handlerMap.put(fileHandler.getOp(), fileHandler);
75 }
76
77 public SLREPLServer() {
78 add(REPLHandler.BACKTRACE_HANDLER);
79 add(REPLHandler.BREAK_AT_LINE_HANDLER);
80 add(REPLHandler.BREAK_AT_LINE_ONCE_HANDLER);
81 add(REPLHandler.BREAK_AT_THROW_HANDLER);
82 add(REPLHandler.BREAK_AT_THROW_ONCE_HANDLER);
83 add(REPLHandler.BREAKPOINT_INFO_HANDLER);
84 add(REPLHandler.CLEAR_BREAK_HANDLER);
85 add(REPLHandler.CONTINUE_HANDLER);
86 add(REPLHandler.DELETE_HANDLER);
87 add(REPLHandler.DISABLE_BREAK_HANDLER);
88 add(REPLHandler.ENABLE_BREAK_HANDLER);
89 add(REPLHandler.FILE_HANDLER);
90 add(REPLHandler.FRAME_HANDLER);
91 add(SLREPLHandler.INFO_HANDLER);
92 add(REPLHandler.KILL_HANDLER);
93 add(SLREPLHandler.LOAD_RUN_SOURCE_HANDLER);
94 add(SLREPLHandler.LOAD_STEP_SOURCE_HANDLER);
95 add(REPLHandler.QUIT_HANDLER);
96 add(REPLHandler.STEP_INTO_HANDLER);
97 add(REPLHandler.STEP_OUT_HANDLER);
98 add(REPLHandler.STEP_OVER_HANDLER);
99 add(REPLHandler.TRUFFLE_HANDLER);
100 add(REPLHandler.TRUFFLE_NODE_HANDLER);
101
102 // Set up an SL context
103 this.slContext = SLContextFactory.create(null, new PrintWriter(System.out));
104
105 final SLSourceExecutionProvider slSourceExecution = new SLSourceExecutionProvider(slContext);
106 final SLREPLDebugClient slDebugClient = new SLREPLDebugClient(this.slContext);
107 this.slDebugEngine = DebugEngine.create(slDebugClient, slSourceExecution);
108 this.statusPrefix = slContext.getLanguageShortName() + " REPL:";
109 }
110
111 private void setClient(SimpleREPLClient replClient) {
112 this.replClient = replClient;
113 }
114
115 public REPLMessage start() {
116
117 this.currentServerContext = new SLServerContext(null, null, null);
118
119 // SL doesn't load modules (like other languages), so we just return a success
120 final REPLMessage reply = new REPLMessage();
121 reply.put(REPLMessage.STATUS, REPLMessage.SUCCEEDED);
122 reply.put(REPLMessage.DISPLAY_MSG, slContext.getLanguageShortName() + " started");
123 return reply;
124 }
125
126 public REPLMessage[] receive(REPLMessage request) {
127 if (currentServerContext == null) {
128 final REPLMessage message = new REPLMessage();
129 message.put(REPLMessage.STATUS, REPLMessage.FAILED);
130 message.put(REPLMessage.DISPLAY_MSG, "server not started");
131 final REPLMessage[] reply = new REPLMessage[]{message};
132 return reply;
133 }
134 return currentServerContext.receive(request);
135 }
136
137 /**
138 * Execution context of a halted SL program.
139 */
140 public final class SLServerContext extends REPLServerContext {
141
142 private final SLServerContext predecessor;
143
144 public SLServerContext(SLServerContext predecessor, Node astNode, MaterializedFrame mFrame) {
145 super(predecessor == null ? 0 : predecessor.getLevel() + 1, astNode, mFrame);
146 this.predecessor = predecessor;
147 }
148
149 @Override
150 public REPLMessage[] receive(REPLMessage request) {
151 final String command = request.get(REPLMessage.OP);
152 final REPLHandler handler = handlerMap.get(command);
153
154 if (handler == null) {
155 final REPLMessage message = new REPLMessage();
156 message.put(REPLMessage.OP, command);
157 message.put(REPLMessage.STATUS, REPLMessage.FAILED);
158 message.put(REPLMessage.DISPLAY_MSG, statusPrefix + " op \"" + command + "\" not supported");
159 final REPLMessage[] reply = new REPLMessage[]{message};
160 return reply;
161 }
162 return handler.receive(request, currentServerContext);
163 }
164
165 @Override
166 public SLContext getLanguageContext() {
167 return slContext;
168 }
169
170 @Override
171 public DebugEngine getDebugEngine() {
172 return slDebugEngine;
173 }
174
175 }
176
177 /**
178 * Specialize the standard SL debug context by notifying the REPL client when execution is
179 * halted, e.g. at a breakpoint.
180 * <p>
181 * Before notification, the server creates a new context at the halted location, in which
182 * subsequent evaluations take place until such time as the client says to "continue".
183 * <p>
184 * This implementation "cheats" the intended asynchronous architecture by calling back directly
185 * to the client with the notification.
186 */
187 private final class SLREPLDebugClient implements DebugClient {
188
189 private final SLContext slContext;
190
191 SLREPLDebugClient(SLContext slContext) {
192 this.slContext = slContext;
193 }
194
195 public void haltedAt(Node node, MaterializedFrame mFrame, List<String> warnings) {
196 // Create and push a new debug context where execution is halted
197 currentServerContext = new SLServerContext(currentServerContext, node, mFrame);
198
199 // Message the client that execution is halted and is in a new debugging context
200 final REPLMessage message = new REPLMessage();
201 message.put(REPLMessage.OP, REPLMessage.STOPPED);
202 final SourceSection src = node.getSourceSection();
203 final Source source = src.getSource();
204 message.put(REPLMessage.SOURCE_NAME, source.getName());
205 message.put(REPLMessage.FILE_PATH, source.getPath());
206 message.put(REPLMessage.LINE_NUMBER, Integer.toString(src.getStartLine()));
207 message.put(REPLMessage.STATUS, REPLMessage.SUCCEEDED);
208 message.put(REPLMessage.DEBUG_LEVEL, Integer.toString(currentServerContext.getLevel()));
209 if (!warnings.isEmpty()) {
210 final StringBuilder sb = new StringBuilder();
211 for (String warning : warnings) {
212 sb.append(warning + "\n");
213 }
214 message.put(REPLMessage.WARNINGS, sb.toString());
215 }
216 try {
217 // Cheat with synchrony: call client directly about entering a nested debugging
218 // context.
219 replClient.halted(message);
220 } finally {
221 // Returns when "continue" is called in the new debugging context
222
223 // Pop the debug context, and return so that the old context will continue
224 currentServerContext = currentServerContext.predecessor;
225 }
226 }
227
228 public ExecutionContext getExecutionContext() {
229 return slContext;
230 }
231 }
232
233 }