Skip to content

Latest commit

 

History

History

debugger

Duktape debug client and JSON debug proxy

Overview

Highly experimental!

Debugger web UI which connects to the Duktape command line tool or any other target supporting the example TCP transport (examples/debug-trans-socket).

Also provides a JSON debug proxy with a JSON mapping for the Duktape debug protocol.

For detailed documentation of the debugger internals, see debugger.rst.

Using the debugger web UI

Some prerequisites:

  • You'll need Node.js v0.10.x or newer. Older Node.js versions don't support the required packages.

Compile Duktape command line tool with debugger support (for further options see doc/feature-options.rst):

  • DUK_OPT_DEBUGGER_SUPPORT
  • DUK_OPT_INTERRUPT_COUNTER
  • DUK_CMDLINE_DEBUGGER_SUPPORT

The source distributable contains a Makefile to build a "duk" command with debugger support:

$ cd <duktape dist directory>
$ make -f Makefile.dukdebug

The Duktape Git repo "duk" target has debugger support enabled by default:

$ make clean duk

Start Duktape command line tool so that it waits for a debugger connection:

# For now we need to be in the directory containing the source files
# executed so that the 'fileName' properties of functions will match
# that on the debug client.

# Using source distributable
$ cd <duktape dist directory>
$ ./duk --debugger mandel.js

# Using Duktape Git repo
$ cd <duktape checkout>/tests/ecmascript/
$ ../../duk --debugger test-dev-mandel2-func.js

Start the web UI:

# Must be in 'debugger' directory.

$ cd debugger/
$ make  # runs 'node duk_debug.js'

Once the required packages are installed, the NodeJS debug client will be up and running. Open the following in your browser and start debugging:

The debug client automatically attaches to the debug target on startup. If you start the debug target later, you'll need to click "Attach" in the web UI.

Using the JSON debug proxy

A JSON debug proxy is also provided by duk_debug.js:

# Same prerequisites as above
$ make runproxy

Start Duktape command line (or whatever your target is):

$ cd <duktape checkout>/tests/ecmascript/
$ ../../duk --debugger test-dev-mandel2-func.js

You can then connect to localhost:9093 and interact with the proxy. Here's an example session using telnet and manually typed in commands The --> (send) and <-- (receiver) markers have been added for readability and are not part of the stream:

$ telnet localhost 9093
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
<-- {"notify":"_Connected","args":["1 10199 v1.1.0-275-gbd4d610-dirty duk command built from Duktape repo"]}
<-- {"notify":"Status","command":1,"args":[1,"test-dev-mandel2-func.js","global",58,0]}
--> {"request":"BasicInfo"}
<-- {"reply":true,"args":[10199,"v1.1.0-275-gbd4d610-dirty","duk command built from Duktape repo",1]}
--> {"request":"Eval", "args":[ "print(Math.PI)" ]}
<-- {"notify":"Print","command":2,"args":["3.141592653589793\n"]}
<-- {"reply":true,"args":[0,{"type":"undefined"}]}
--> {"request":"Resume"}
<-- {"reply":true,"args":[]}
<-- {"notify":"Status","command":1,"args":[0,"test-dev-mandel2-func.js","global",58,0]}
<-- {"notify":"Status","command":1,"args":[0,"test-dev-mandel2-func.js","global",58,0]}
<-- {"notify":"Print","command":2,"args":["................................................................................\n"]}
<-- {"notify":"Print","command":2,"args":["................................................................................\n"]}
<-- {"notify":"Print","command":2,"args":["................................................................................\n"]}
[...]
<-- {"notify":"_Disconnecting"}

A telnet connection allows you to experiment with debug commands by simply copy-pasting debug commands to the telnet session. This is useful even if you decide to implement the binary protocol directly.

The debug target used by the proxy can be configured with duk_debug.js command line options.

Source search path

The NodeJS debug client needs to be able to find source code files matching code running on the target ("duk" command line). The filenames used on the target and on the debug client must match exactly, because e.g. breakpoints are targeted based on the 'fileName' property of Function objects.

The search path can be set using the --source-dirs option given to duk_debug.js, with the default search paths including only ../tests/ecmascript/.

The default search path means that if a function on the target has fileName foo/bar.js it would be loaded from (relative to the duk_debug.js working directory, debugger/):

../tests/ecmascript/foo/bar.js

Similarly, if the filesystem contained:

../tests/ecmascript/baz/quux.js

the web UI dropdown would show baz/quux.js. If you selected that file and added a breakpoint, the breakpoint fileName sent to the debug target would be baz/quux.js.

Note

There's much to improve in the search path. For instance, it'd be nice to add a certain path to search but exclude files based on paths and patterns, etc.

Architecture

+-------------------+
| Web browser       |  [debug UI]
+-------------------+
      |
      | http (port 9092)
      | socket.io
      v
+-------------------+
| duk_debug.js      |  [debug client]
+-------------------+
      |          /\
      |          ||
      +----------||---- [example tcp transport] (port 9091)
      |          ||     (application provides concrete transport)
      |          ||
      |          ||---- [debug protocol stream]
      |          ||     (between debug client and Duktape)
      |          ||
+ - - | - - - - -|| - - +
:     v          ||     :
:  +-------------||-+   :  [target]
:  | application || |   :
:  +-------------||-+   :
:     ^          ||     :
:     |          ||     :   [debug API]
:     +----------||-------- debug transport callbacks
:     |          ||     :   (read, write, peek, read/write flush)
:     |          ||     :   implemented by application
:     |          \/     :
:  +----------------+   :
:  | Duktape        |   :
:  +----------------+   :
+ - - - - - - - - - - - +

The debug transport is application specific:

  • Duktape command line ("duk") and this debug client use an example TCP transport as a concrete example.
  • It is entirely up to the application to come up with the most suitable transport for its environment. Different mechanisms will be needed for Wi-Fi, serial, etc.

The debug protocol running inside the transport is transport independent:

  • The debug protocol is documented in doc/debugger.rst.
  • This debug client provides further concrete examples and clarifications on how the protocol can be used.

Using a custom transport

Quite possibly your target device cannot use the example TCP transport and you need to implement your own transport. You'll need to implement your custom transport both for the target device and for the debug client.

Target device

Implement the debug transport callbacks needed by duk_debugger_attach().

See doc/debugger.rst for details and examples/debug-trans-socket for example running code for a TCP transport.

Debug client alternative 1: duk_debug.js + custom TCP proxy

If you don't want to change duk_debug.js you can implement a TCP proxy which accepts a TCP connection from duk_debug.js and then uses your custom transport to talk to the target:

+--------------+   TCP   +-------+   custom   +--------+
| duk_debug.js | ------> | proxy | ---------> | target |
+--------------+         +-------+            +--------+

This is a straightforward option and a proxy can be used with other debug clients too (perhaps custom scripts talking to the target etc).

You could also use netcat and implement your proxy so that it talks to duk_debug.js using stdin/stdout.

Debug client alternative 2: duk_debug.js + custom NodeJS stream

To make duk_debug.js use a custom transport you need to:

  • Implement your own transport as NodeJS stream. You can add it directly to duk_debug.js but it's probably easiest to use a separate module so that the diff to duk_debug.js stays minimal.
  • Change duk_debug.js to use the custom transport instead of a TCP stream. Search for "CUSTOMTRANSPORT" in duk_debug.js.

See:

Debug client alternative 3: custom debug client

You can also implement your own debug client and debug UI with support for your custom transport.

You'll also need to implement the client part of the Duktape debugger protocol. See doc/debugger.rst for the specification and duk_debug.js for example running code which should illustrate the protocol in more detail.

The JSON debug proxy allows you to implement a debug client without needing to implement the Duktape binary debug protocol. The JSON protocol provides a roughly 1:1 mapping to the binary protocol but with an easier syntax.