Skip to content

Using Eclipse as Node Applications Debugger

raycohen edited this page Nov 30, 2011 · 45 revisions

Eclipse debugger plugin for V8 can be easily used to debug node scripts. (On OSX 10.5 the plugin requires an Eclipse 64 bit Version, started with Java SE 6). The V8 engine provides debugging support by enabling attachment of the remote debugger on a TCP port (5858 is node’s default). There are 2 debug related node options:


 node --debug[=port] NodeApp.js

or

 node --debug-brk[=port] NodeApp.js

The --debug option will just enable remote debugger connection on given port and then start the application normally. Even when debugger is connected to the running node instance later on, the script execution will not be stopped until “Suspend” command is issued by Eclipse debugger. Another way to stop the execution is to browse the source code of the JavaScript modules comprising the application and double click on the line number at the desired position in script to break at (most likely a callback). Once execution stops you can set/clear more breakpoints, but also inspect call stack and view content of all program variables.

The --debug-brk option is needed when your script is short lived (no time to attach debugger) and/or you want to observe the NodeApp.js execution from the very start. This option will force execution to break at the first line of the main script and wait for debugger to connect. The behavior upon connection is now different – the script is suspended and no breakpoints are set. Note that V8 engine debugger is not behaving very good when it steps over or steps into require() method (it will crash), so try to set up first breakpoint past the initial module loading. This will also enable you to set breakpoints in any of those modules as well.

Installation

Rather than installing the V8 Eclipse Debugging Plugin on an existing Eclipse Java or Eclipse C++ installation, you might want to install the plugin into its own Eclipse Platform. This prevents an instability of the plugin, Node.js or V8 from disrupting your Java or C++ projects. You can download a clean slate Eclipse Platform, then use the plugin update utility to install V8 Eclipse Debugging Plugin.

  • Download a clean slate Eclipse Platform for your operating system.
  • Unzip the Eclipse Platform to a directory on your hard drive.
  • Run the Eclipse Platform. You will be asked to create a workspace. Create a workspace separate from any Java or C++ workspaces you may already have created. If this is your first time using Eclipse, the default is fine.
  • Proceed to the workbench by clicking the icon with the arrow titled Workbench in the top right corner of the Eclipse window.
  • Select the menu item Help > Install New Software.... A dialog box titled Install will appear. This contains a plugin installation wizard. The first step of the wizard is titled Available Software.
  • At the top of the dialog you will see a combo box labeled Work with:. Click the button to the right of the combo box labeled Add. A dialog box titled Add Repository will appear.
  • In the textbox labeled Name: enter Google Chrome Developer Tools. In the textbox labeled Location: enter http://chromedevtools.googlecode.com/svn/update/dev/. Click OK.
  • Now select Google Chrome Developer Tools – http://chromedevtools.googlecode.com/svn/update/dev/ from the Work with: combo box. Google Chrome Developer Tools will appear in the table below. Click the checkbox next to Google Chrome Developer Tools in the table. Click the button labeled Next > at the bottom of the dialog box. The next wizard step titled Install Details is displayed.
  • Click the button labeled Next >. The next wizard step titled Review Licenses is displayed.
  • Once you are satisfied with the licenses, you can select the I accept the terms of the license agreement radio button and click the button labeled Finish. A progress dialog box titled Installing Software will be displayed. After installation is complete click the Restart Now button.
  • Eclipse will restart with the V8 Eclipse Debugging Plugin installed.

Sample Debugging Session

Here is a simple script that will be used to demonstrate debugging procedure:


// dbgtest.js

var sys=require('sys');
var count = 0;

sys.debug("Starting ...");


function timer_tick() {
  count = count+1;
  sys.debug("Tick count: " + count);
  if (count === 10) {
    count += 1000;
    sys.debug("Set break here");
 }
 setTimeout(timer_tick, 1000);
}

timer_tick();

Start the above script:


$ node --debug dbgtest.js
debugger listening on port 5858
DEBUG: Starting ...
DEBUG: Tick count: 1
DEBUG: Tick count: 2
DEBUG: Tick count: 3 
// and so on

Assuming that you have already installed Eclipse, together with the plugin mentioned above, now you need to make initial debug profile.

In the Menu, choose Window → Open Perspective → Other, then select Debug from the list and click OK, to ensure you are viewing the debug perspective.

Click the drop box button (little black triangle) next to the green bug one and select “Debug Configurations …” option.

Now start making new debug configuration by clicking the “New” button:

Note that this configuration is named Node-5858 and the port is set accordingly. While you there create one or more additional configurations such as Node-5859, Node-5860, … with respective ports, so you can later debug more than one node executable at the same time. Note also that most recent version of V8 debugger plugin has option to specify remote host as well.

If you decided to edit the source lookup path, be sure to leave the ‘Default’ entry. Break points won’t work if you remove it.

Start the debugging session by clicking “Debug” button. If everything goes well, you will get something like this:

You can suspend the execution by pressing “Pause” button, however since it will most likely stop at node.js:process.loop() which, being a native C++ function, is not very interesting – it is better to expand Node-5858 in Project Explorer, open dbgtest.js and set a break somewhere in timer callback, such as line 11, by double click on the line number.

The breakpoint will be reached within a second:

Now you can examine stack frames, browse program variables – and do all the other useful stuff. If the same script was started with:

$ node --debug-brk dbgtest.js
debugger listening on port 5858
Waiting for remote debugger connection...

then debugger screen after session is connected would look like this:

As it was mentioned before, avoid stepping over or into require() calls, so the first safe place to stop is at line 4.
If you set breakpoint there and resume execution by clicking “Play” button, the script will stop at line 6, most likely due to JIT interaction.

You probably noticed that Eclipse V8 debugger can not connect to the remote host. This simple transparent TCP proxy script will enable debugging of the remote node application running on the deployment or test server:


// Transparent TCP proxy for single connection only
// Zoran Tomicic ztomicic (at) gmail.com
// 

var sys = require('sys');
var tcp = require('net');

var encoding = "binary"
var localActive = false;  // flag to make sure that only one connection
                          // to remote is active

var verbose = false; 

if (process.argv.length < 4) {
  sys.puts
  ("Usage: node tcpproxy.js <local port> <remote host> <remote port> [-v]\n"+
   "                        -v for verbose operation\n");
  process.exit(1);
}

if (process.argv[5] === "-v")
  verbose = true;

function log(s) {
  if (verbose)
    sys.puts("tcpproxy: "+ s);
}


var localPort  = process.argv[2];
var remoteHost = process.argv[3];
var remotePort = process.argv[4];

var remote;

var local = tcp.createServer(function (socket) {

  socket.addListener("connect", function () {
    if (localActive === false) {
      localActive = true;
      socket.setEncoding(encoding);
      socket.setTimeout(0);
      log("Local connect");
    } else {
      log("Local reject");
      socket.end();
      return;
    }

    remote = tcp.createConnection(remotePort, remoteHost);
    remote.setEncoding(encoding);
    remote.setTimeout(0);

    log("Connecting to remote");
    remote.addListener("connect", function() {
      log("Remote connect");
    });

    remote.addListener("data", function(data) {
      socket.write(data, encoding);
      log("Remote: ("+data.length+ ")\n"+data);
    });

    remote.addListener("end", function () {
      log("Remote close");
      socket.end();
      remote.end();
    });

  });

  socket.addListener("data", function (data) {
    remote.write(data, encoding);
    log("Local: ("+data.length+ ")\n"+data);
  });

  socket.addListener("end", function () {
    log("Local close");
    socket.end();
    remote.end();
    localActive = false;
  });

});

log("Starting to listen at "+ localPort +" ...");
local.listen(localPort);


So, if you want to attach debugger to node instance on remote server www.example.com:


LocalMachine:$ node tcpproxy.js 5858 www.example.com 8888 &

and on the remote machine:


RemoteMachine:$ node --debug-brk=8888 MyApp.js

Now the Eclipse debugging session will be routed via localhost:5858 to www.example.com:8888. The above script needs to be started only once for particular remote host and port combination. If verbose option is set, the tcpproxy.js may act as a sniffer for V8 Debugger protocol as well.

Note:
The most recent version of Eclipse V8 debugger plugin has an option to specify remote host, so above proxy script is not required any more. It may still be useful if debugger is to reach out through corporate firewall.

Eclipse V8 debugger can also be used in conjunction with gdb/ddd in order to debug native C/C++ code. For example, if particular invocation of frequently called native function is problematic, but the other ones are fine, you would first set the breakpoint in the script code, and once you hit this one, then you would set/enable the breakpoint in native function as well.

Note: If node segfaults when you are trying to debug, recompile node with set GCC_VERSION=44 before running make during node installation (source)

Clone this wiki locally