Debugger Socket

The debugger socket is a plugin that instantiates a customizable debugger proxy and creates a persistent bi-directional connection between the debugger and the running process.

This article discusses the API of the debug socket plugin and how to use it in your debugger implementation plugin. This is what the overall debugger architecture looks like.

818

Setting Up a Connection

The socket starts by trying to connect (via TCP) to a port determined by adding one to the debug port that is specified by the runner. If a connection is made the connection process is done and the connect event is fired.

If the port is not open, the socket assumes the proxy process is not yet running. The socket will start a node process that runs the source code provided by your debugger implementation plugin's getProxySource() function. Once the proxy process is started the socket will once again try to connect to the specified port. This must not fail. If it does fail, the end event is fired and the socket will attempt to connect, ad infinitum. When the connection succeeds the connect event is fired.

The debugger implementation is responsible for calling connect() to initiate the connection to the proxy.

socket.connect();

socket.once("connect", function(){
    console.log("Connected");
});
socket.on("data", function(chunk){
    console.log("Received", chunk);
});

🚧

Note that the v8 debugger doesn't listen to the connect event but only listens to the data event as the v8 debugger always sends data first on connect. When reconnecting the socket will emit the data event with an empty message.

Connection Cycle

There are five events that indicate a change in the connection state; connect, away, beforeBack, back and end.

The away and back events are fired when the connection is temporarily lost and then regained, respectively. There can be a considerable time between the two events and a debugger implementation must sync the debugger's state again after this happens (see also the sync() function).

The end event is fired when a connection failed which implies that any debug session is destroyed. A debugger implementation must keep track of all the debug commands it has sent and must call the callbacks with an error object in the end event.

socket.on("end", function(){
    pendingQueue.forEach(function(item) {
        item.callback(new Error("Debug Session Ended"));
    });
});

The beforeBack event fires prior to the back event and must be used by a debugger to retry those commands that have not yet been confirmed to have been received by the debugger.

socket.on("beforeBack", function(){
    pendingQueue.forEach(function(item) {
        retry(item);
    });
});

Reconnecting

Managing reconnects to an existing debug process is the primary reason for the socket and proxy. The proxy will survive a user's session; when a user disconnects (even for hours) and later returns, the debugger proxy is still running and the client can connect to it via the connection logic as described above.

Connection State

There are three connection states that are available as constants on the socket.

socket.DISCONNECTED
socket.CONNECTED
socket.CONNECTING

Test for the current state of the socket using the attached property.

socket.connected == socket.CONNECTING

Getting a Reference to the Socket

You'll get a reference to the socket in the attach() function of your debugger plugin.

var socket;
function attach(_socket, reconnect, callback) {
    socket = _socket;

In this example we store the socket reference in a variable that is local to the plugin. This allows you to reference the socket outside of the attach() function as well. You'll most likely need the reference in the detach() function.

Sending Data

Send any utf-8 data via the socket using the send() method.

socket.send("{ json: true }");