From: Lucas Bajolet Date: Wed, 2 Apr 2014 21:08:14 +0000 (-0400) Subject: debugger: Added Websocket compatible implementation of the debugger. X-Git-Tag: v0.6.6~125^2~2 X-Git-Url: http://nitlanguage.org debugger: Added Websocket compatible implementation of the debugger. Signed-off-by: Lucas Bajolet --- diff --git a/src/nitdbg_websocket_server.nit b/src/nitdbg_websocket_server.nit new file mode 100644 index 0000000..d8c23ca --- /dev/null +++ b/src/nitdbg_websocket_server.nit @@ -0,0 +1,38 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2014 Lucas Bajolet +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Network debugger for a nit program (server part) +module nitdbg_websocket_server + +import websocket_debugger +import debugger_commons + +redef class InterpretCommons + + redef fun launch + do + super + if toolcontext.opt_debug_port.value < 0 or toolcontext.opt_debug_port.value > 65535 then + toolcontext.option_context.usage + return + end + + modelbuilder.run_debugger_network_mode(mainmodule.as(not null),arguments.as(not null),toolcontext.opt_debug_port.value) + end + +end + +(new InterpretCommons).launch diff --git a/src/websocket_debugger.nit b/src/websocket_debugger.nit new file mode 100644 index 0000000..f3c7cc0 --- /dev/null +++ b/src/websocket_debugger.nit @@ -0,0 +1,209 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2014 Lucas Bajolet +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Network debugger for a nit program, based on the original debugger +# Replaces access to stdin/stdout to send data on the network to the client concerned +module websocket_debugger + +import websocket +intrude import debugger + +redef class ToolContext + + var opt_debug_port: OptionInt = new OptionInt("Sets the debug port (Defaults to 22125) - Must be contained between 0 and 65535", 22125, "--port") + + redef init + do + super + self.option_context.add_option(self.opt_debug_port) + end + +end + +redef class ModelBuilder + fun run_debugger_network_mode(mainmodule: MModule, arguments: Array[String], port: Int) + do + var time0 = get_time + self.toolcontext.info("*** START INTERPRETING ***", 1) + + var interpreter = new NetworkDebugger(self, mainmodule, arguments, port) + + init_naive_interpreter(interpreter, mainmodule) + + var time1 = get_time + self.toolcontext.info("*** END INTERPRETING: {time1-time0} ***", 2) + + interpreter.ns.stop_server + end +end + +# Extends the debugger by adding new network capabilities for remote debugging +class NetworkDebugger + super Debugger + + # Represents the connexion with a particular client (Actually the only one accepted at the moment) + private var ns: WebSocket + + # Initializes the debugger, waits for a client to connect + # Then starts debugging as usual + init(modelbuilder: ModelBuilder, mainmodule: MModule, arguments: Array[String], port: Int) + do + print "Listening on port {port}" + + ns = new WebSocket(port, 1) + + ns.accept + + print "Client connected" + + stdin.connection = ns + + if stdout isa Stdout then + (stdout.as(Stdout)).connection = ns + else + ns.stop_server + abort + end + + super + end + + # Checks on every call if the client has sent a command before continuing debugging + redef fun stmt(n) + do + if stdin.poll_in then + var command = gets + if command == "stop" then + n.debug("Stopped by client") + while process_debug_command(gets) do end + else + process_debug_command(command) + end + end + + super(n) + end + +end + +redef class ANode + + # Breaks automatically when encountering an error + # Permits the injunction of commands before crashing + # Disconnects from the client before crashing + redef private fun fatal(v: NaiveInterpreter, message: String) + do + if v isa Debugger then + print "An error was encountered, the program will stop now." + self.debug(message) + while v.process_debug_command(gets) do end + end + + if v isa NetworkDebugger then + stdin.connection.stop_server + end + + super + end +end + +# Replaces Stdin to read on the network +redef class Stdin + + # Represents the connection with a client + var connection: nullable WebSocket = null + + # Used to store data that has been read from the connection + var buf: Buffer = new FlatBuffer + var buf_pos: Int = 0 + + # Checks if data is available for reading + redef fun poll_in + do + return connection.can_read(0) + end + + # Reads the whole content of the buffer + # Blocking if the buffer is empty + redef fun read_all + do + var loc_buf = new FlatBuffer + if connection.can_read(0) then buf.append(connection.read) + for i in [buf_pos .. buf.length-1] do loc_buf.add(buf.chars[i]) + buf.clear + buf_pos = 0 + return loc_buf.to_s + end + + # Reads a single char on the incoming buffer + # If the buffer is empty, the call is blocking + redef fun read_char + do + if connection.can_read(0) then buf.append(connection.read) + if buf_pos >= buf.length then + buf.clear + buf_pos = 0 + #Blocking call + buf.append(connection.read) + end + buf_pos += 1 + return buf.chars[buf_pos-1].ascii + end + + # Reads a line on the network if available + # Stops at the first encounter of a \n character + # If the buffer is empty, the read_line call is blocking + redef fun read_line + do + var line_buf = new FlatBuffer + if connection.can_read(0) then + buf.append(connection.read) + end + var has_found_eol: Bool = false + loop + if buf_pos >= buf.length then + buf.clear + buf_pos = 0 + # Blocking call + buf.append(connection.read) + end + buf_pos += 1 + if buf.chars[buf_pos-1] == '\n' then break + line_buf.add(buf.chars[buf_pos-1]) + end + return line_buf.to_s + end +end + +# Replaces Stdout to write on the network +redef class Stdout + + # Connection with the client object + var connection: nullable WebSocket = null + + # Writes a string on the network if available, else + # it is written in the standard output (Terminal) + redef fun write(s) + do + if connection != null then + connection.write(s) + else + super + end + end + +end +