]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | ========================================= |
2 | Duktape debug client and JSON debug proxy | |
3 | ========================================= | |
4 | ||
5 | Overview | |
6 | ======== | |
7 | ||
8 | Debugger web UI which connects to the Duktape command line tool or any other | |
11fdf7f2 TL |
9 | target supporting the example TCP transport (``examples/debug-trans-socket``) |
10 | on Unix and Windows. | |
7c673cae FG |
11 | |
12 | Also provides a JSON debug proxy with a JSON mapping for the Duktape debug | |
13 | protocol. | |
14 | ||
15 | For detailed documentation of the debugger internals, see `debugger.rst`__. | |
16 | ||
17 | __ https://github.com/svaarala/duktape/blob/master/doc/debugger.rst | |
18 | ||
19 | Using the debugger web UI | |
20 | ========================= | |
21 | ||
22 | Some prerequisites: | |
23 | ||
24 | * You'll need Node.js v0.10.x or newer. Older Node.js versions don't support | |
25 | the required packages. | |
26 | ||
27 | Compile Duktape command line tool with debugger support (for further options | |
11fdf7f2 | 28 | see http://wiki.duktape.org/FeatureOptions.html): |
7c673cae FG |
29 | |
30 | * ``DUK_OPT_DEBUGGER_SUPPORT`` | |
31 | ||
32 | * ``DUK_OPT_INTERRUPT_COUNTER`` | |
33 | ||
34 | * ``DUK_CMDLINE_DEBUGGER_SUPPORT`` | |
35 | ||
36 | The source distributable contains a Makefile to build a "duk" command with | |
37 | debugger support:: | |
38 | ||
39 | $ cd <duktape dist directory> | |
40 | $ make -f Makefile.dukdebug | |
41 | ||
42 | The Duktape Git repo "duk" target has debugger support enabled by default:: | |
43 | ||
44 | $ make clean duk | |
45 | ||
46 | Start Duktape command line tool so that it waits for a debugger connection:: | |
47 | ||
48 | # For now we need to be in the directory containing the source files | |
49 | # executed so that the 'fileName' properties of functions will match | |
50 | # that on the debug client. | |
51 | ||
52 | # Using source distributable | |
53 | $ cd <duktape dist directory> | |
54 | $ ./duk --debugger mandel.js | |
55 | ||
56 | # Using Duktape Git repo | |
57 | $ cd <duktape checkout>/tests/ecmascript/ | |
58 | $ ../../duk --debugger test-dev-mandel2-func.js | |
59 | ||
60 | Start the web UI:: | |
61 | ||
62 | # Must be in 'debugger' directory. | |
63 | ||
64 | $ cd debugger/ | |
65 | $ make # runs 'node duk_debug.js' | |
66 | ||
67 | Once the required packages are installed, the NodeJS debug client will be | |
68 | up and running. Open the following in your browser and start debugging: | |
69 | ||
70 | * http://localhost:9092/ | |
71 | ||
72 | The debug client automatically attaches to the debug target on startup. | |
73 | If you start the debug target later, you'll need to click "Attach" in the | |
74 | web UI. | |
75 | ||
76 | Using the JSON debug proxy | |
77 | ========================== | |
78 | ||
11fdf7f2 TL |
79 | There are two JSON debug proxy implementations: one implemented in DukLuv |
80 | and another in Node.js. | |
7c673cae | 81 | |
11fdf7f2 TL |
82 | DukLuv JSON proxy |
83 | ----------------- | |
84 | ||
85 | DukLuv (https://github.com/creationix/dukluv) is a small and portable event | |
86 | loop based on LibUV and Duktape with MIT license (like Duktape). As such it's | |
87 | easy to embed in a custom debug client: you just include the DukLuv executable | |
88 | and the JSON proxy source file in your debug client. | |
89 | ||
90 | Install DukLuv: | |
91 | ||
92 | * Ensure ``cmake`` is installed | |
93 | ||
94 | * ``git clone https://github.com/creationix/dukluv.git`` | |
95 | ||
96 | * ``git submodule init; git submodule update`` | |
97 | ||
98 | * ``make`` | |
99 | ||
100 | * Binary should appear in: | |
101 | ||
102 | - ``./build/dukluv`` on Linux | |
103 | ||
104 | - ``.\build\Debug\dukluv.exe`` on Windows | |
105 | ||
106 | Run the proxy:: | |
107 | ||
108 | # Using Makefile; autogenerates duk_debug_meta.json | |
109 | # (You may need to edit DUKLUV in Makefile to point to your DukLuv) | |
110 | $ make runproxydukluv | |
111 | ||
112 | # Manually: see "dukluv duk_debug_proxy.js --help" for help | |
113 | $ .../path/to/dukluv duk_debug_proxy.js | |
114 | ||
115 | Start Duktape command line (or whatever your target is):: | |
116 | ||
117 | $ cd <duktape checkout>/tests/ecmascript/ | |
118 | $ ../../duk --debugger test-dev-mandel2-func.js | |
119 | ||
120 | Now connect to the proxy using e.g. telnet:: | |
121 | ||
122 | $ telnet localhost 9093 | |
123 | ||
124 | The proxy will then connect to the target and you can start issuing commands:: | |
125 | ||
126 | $ telnet localhost 9093 | |
127 | Trying 127.0.0.1... | |
128 | Connected to localhost. | |
129 | Escape character is '^]'. | |
130 | {"notify":"_TargetConnecting","args":["127.0.0.1",9091]} | |
131 | {"notify":"_TargetConnected","args":["1 10499 v1.4.0-140-gc9a6c7c duk command built from Duktape repo"]} | |
132 | {"notify":"Status","command":1,"args":[1,"test-dev-mandel2-func.js","global",58,0]} | |
133 | {"request":"BasicInfo"} | |
134 | {"reply":true,"args":[10499,"v1.4.0-140-gc9a6c7c","duk command built from Duktape repo",1]} | |
135 | {"request":"Eval","args":["print('Hello world!'); 123;"]} | |
136 | {"notify":"Print","command":2,"args":["Hello world!\n"]} | |
137 | {"reply":true,"args":[0,{"type":"number","data":"405ec00000000000"}]} | |
138 | [...] | |
139 | ||
140 | The proxy log provides dumps both JSON and dvalue binary traffic which is | |
141 | quite useful in development:: | |
142 | ||
143 | $ make runproxydukluv | |
144 | Running Dukluv based debug proxy | |
145 | "dukluv" duk_debug_proxy.js --log-level 2 --metadata duk_debug_meta.json | |
146 | 2016-02-17T13:59:42.308Z INF Proxy: Read proxy metadata from duk_debug_meta.json | |
147 | 2016-02-17T13:59:42.325Z INF Proxy: Listening for incoming JSON debug connection on 0.0.0.0:9093, target is 127.0.0.1:9091 | |
148 | 2016-02-17T13:59:47.994Z INF Proxy: JSON proxy client connected | |
149 | 2016-02-17T13:59:47.994Z INF Proxy: Connecting to debug target at 127.0.0.1:9091 | |
150 | 2016-02-17T13:59:47.994Z INF Proxy: PROXY --> CLIENT: {"notify":"_TargetConnecting","args":["127.0.0.1",9091]} | |
151 | 2016-02-17T13:59:47.994Z INF Proxy: Connected to debug target at 127.0.0.1:9091 | |
152 | 2016-02-17T13:59:48.003Z INF Proxy: PROXY --> CLIENT: {"notify":"_TargetConnected","args":["1 10499 v1.4.0-140-gc9a6c7c duk command built from Duktape repo"]} | |
153 | 2016-02-17T13:59:48.003Z INF Proxy: Target handshake: {"line":"1 10499 v1.4.0-140-gc9a6c7c duk command built from Duktape repo","protocolVersion":1,"text":"10499 v1.4.0-140-gc9a6c7c duk command built from Duktape repo","dukVersion":"1","dukGitDescribe":"10499","targetString":"v1.4.0-140-gc9a6c7c"} | |
154 | 2016-02-17T13:59:48.151Z INF Proxy: PROXY <-- TARGET: |04| | |
155 | 2016-02-17T13:59:48.152Z INF Proxy: PROXY <-- TARGET: |81| | |
156 | 2016-02-17T13:59:48.152Z INF Proxy: PROXY <-- TARGET: |81| | |
157 | 2016-02-17T13:59:48.160Z INF Proxy: PROXY <-- TARGET: |78746573742d6465762d6d616e64656c322d66756e632e6a73| | |
158 | 2016-02-17T13:59:48.161Z INF Proxy: PROXY <-- TARGET: |66676c6f62616c| | |
159 | 2016-02-17T13:59:48.165Z INF Proxy: PROXY <-- TARGET: |ba| | |
160 | 2016-02-17T13:59:48.165Z INF Proxy: PROXY <-- TARGET: |80| | |
161 | 2016-02-17T13:59:48.165Z INF Proxy: PROXY <-- TARGET: |00| | |
162 | 2016-02-17T13:59:48.165Z INF Proxy: PROXY --> CLIENT: {"notify":"Status","command":1,"args":[1,"test-dev-mandel2-func.js","global",58,0]} | |
163 | 2016-02-17T13:59:51.289Z INF Proxy: PROXY <-- CLIENT: {"request":"BasicInfo"} | |
164 | 2016-02-17T13:59:51.289Z INF Proxy: PROXY --> TARGET: |01| | |
165 | 2016-02-17T13:59:51.289Z INF Proxy: PROXY --> TARGET: |90| | |
166 | 2016-02-17T13:59:51.289Z INF Proxy: PROXY --> TARGET: |00| | |
167 | 2016-02-17T13:59:51.291Z INF Proxy: PROXY <-- TARGET: |02| | |
168 | 2016-02-17T13:59:51.291Z INF Proxy: PROXY <-- TARGET: |e903| | |
169 | 2016-02-17T13:59:51.292Z INF Proxy: PROXY <-- TARGET: |7376312e342e302d3134302d6763396136633763| | |
170 | 2016-02-17T13:59:51.293Z INF Proxy: PROXY <-- TARGET: |12002364756b20636f6d6d616e64206275696c742066726f6d2044756b74617065207265706f| | |
171 | 2016-02-17T13:59:51.293Z INF Proxy: PROXY <-- TARGET: |81| | |
172 | 2016-02-17T13:59:51.293Z INF Proxy: PROXY <-- TARGET: |00| | |
173 | 2016-02-17T13:59:51.293Z INF Proxy: PROXY --> CLIENT: {"reply":true,"args":[10499,"v1.4.0-140-gc9a6c7c","duk command built from Duktape repo",1]} | |
174 | 2016-02-17T14:00:06.105Z INF Proxy: PROXY <-- CLIENT: {"request":"Eval","args":["print('Hello world!'); 123;"]} | |
175 | 2016-02-17T14:00:06.105Z INF Proxy: PROXY --> TARGET: |01| | |
176 | 2016-02-17T14:00:06.105Z INF Proxy: PROXY --> TARGET: |9e| | |
177 | 2016-02-17T14:00:06.105Z INF Proxy: PROXY --> TARGET: |7b7072696e74282748656c6c6f20776f726c642127293b203132333b| | |
178 | 2016-02-17T14:00:06.105Z INF Proxy: PROXY --> TARGET: |00| | |
179 | 2016-02-17T14:00:06.167Z INF Proxy: PROXY <-- TARGET: |04| | |
180 | 2016-02-17T14:00:06.167Z INF Proxy: PROXY <-- TARGET: |82| | |
181 | 2016-02-17T14:00:06.167Z INF Proxy: PROXY <-- TARGET: |6d48656c6c6f20776f726c64210a| | |
182 | 2016-02-17T14:00:06.168Z INF Proxy: PROXY <-- TARGET: |00| | |
183 | 2016-02-17T14:00:06.168Z INF Proxy: PROXY --> CLIENT: {"notify":"Print","command":2,"args":["Hello world!\n"]} | |
184 | 2016-02-17T14:00:06.171Z INF Proxy: PROXY <-- TARGET: |02| | |
185 | 2016-02-17T14:00:06.171Z INF Proxy: PROXY <-- TARGET: |80| | |
186 | 2016-02-17T14:00:06.173Z INF Proxy: PROXY <-- TARGET: |1a405ec00000000000| | |
187 | 2016-02-17T14:00:06.173Z INF Proxy: PROXY <-- TARGET: |00| | |
188 | 2016-02-17T14:00:06.174Z INF Proxy: PROXY --> CLIENT: {"reply":true,"args":[0,{"type":"number","data":"405ec00000000000"}]} | |
189 | [...] | |
190 | ||
191 | Node.js JSON proxy | |
192 | ------------------ | |
193 | ||
194 | A Node.js-based JSON debug proxy is also provided by ``duk_debug.js``:: | |
195 | ||
196 | # Same prerequisites as for running the debug client | |
197 | $ make runproxynodejs | |
7c673cae FG |
198 | |
199 | Start Duktape command line (or whatever your target is):: | |
200 | ||
201 | $ cd <duktape checkout>/tests/ecmascript/ | |
202 | $ ../../duk --debugger test-dev-mandel2-func.js | |
203 | ||
204 | You can then connect to localhost:9093 and interact with the proxy. | |
205 | Here's an example session using telnet and manually typed in commands | |
206 | The ``-->`` (send) and ``<--`` (receiver) markers have been added for | |
207 | readability and are not part of the stream:: | |
208 | ||
209 | $ telnet localhost 9093 | |
210 | Trying 127.0.0.1... | |
211 | Connected to localhost. | |
212 | Escape character is '^]'. | |
11fdf7f2 | 213 | <-- {"notify":"_TargetConnected","args":["1 10199 v1.1.0-275-gbd4d610-dirty duk command built from Duktape repo"]} |
7c673cae FG |
214 | <-- {"notify":"Status","command":1,"args":[1,"test-dev-mandel2-func.js","global",58,0]} |
215 | --> {"request":"BasicInfo"} | |
216 | <-- {"reply":true,"args":[10199,"v1.1.0-275-gbd4d610-dirty","duk command built from Duktape repo",1]} | |
217 | --> {"request":"Eval", "args":[ "print(Math.PI)" ]} | |
218 | <-- {"notify":"Print","command":2,"args":["3.141592653589793\n"]} | |
219 | <-- {"reply":true,"args":[0,{"type":"undefined"}]} | |
220 | --> {"request":"Resume"} | |
221 | <-- {"reply":true,"args":[]} | |
222 | <-- {"notify":"Status","command":1,"args":[0,"test-dev-mandel2-func.js","global",58,0]} | |
223 | <-- {"notify":"Status","command":1,"args":[0,"test-dev-mandel2-func.js","global",58,0]} | |
224 | <-- {"notify":"Print","command":2,"args":["................................................................................\n"]} | |
225 | <-- {"notify":"Print","command":2,"args":["................................................................................\n"]} | |
226 | <-- {"notify":"Print","command":2,"args":["................................................................................\n"]} | |
227 | [...] | |
228 | <-- {"notify":"_Disconnecting"} | |
229 | ||
230 | A telnet connection allows you to experiment with debug commands by simply | |
231 | copy-pasting debug commands to the telnet session. This is useful even if | |
232 | you decide to implement the binary protocol directly. | |
233 | ||
234 | The debug target used by the proxy can be configured with ``duk_debug.js`` | |
235 | command line options. | |
236 | ||
237 | Source search path | |
238 | ================== | |
239 | ||
240 | The NodeJS debug client needs to be able to find source code files matching | |
241 | code running on the target ("duk" command line). **The filenames used on the | |
242 | target and on the debug client must match exactly**, because e.g. breakpoints | |
243 | are targeted based on the 'fileName' property of Function objects. | |
244 | ||
245 | The search path can be set using the ``--source-dirs`` option given to | |
246 | ``duk_debug.js``, with the default search paths including only | |
247 | ``../tests/ecmascript/``. | |
248 | ||
249 | The default search path means that if a function on the target has fileName | |
250 | ``foo/bar.js`` it would be loaded from (relative to the duk_debug.js working | |
251 | directory, ``debugger/``):: | |
252 | ||
253 | ../tests/ecmascript/foo/bar.js | |
254 | ||
255 | Similarly, if the filesystem contained:: | |
256 | ||
257 | ../tests/ecmascript/baz/quux.js | |
258 | ||
259 | the web UI dropdown would show ``baz/quux.js``. If you selected that file | |
260 | and added a breakpoint, the breakpoint fileName sent to the debug target | |
261 | would be ``baz/quux.js``. | |
262 | ||
263 | .. note:: There's much to improve in the search path. For instance, it'd | |
264 | be nice to add a certain path to search but exclude files based | |
265 | on paths and patterns, etc. | |
266 | ||
267 | Architecture | |
268 | ============ | |
269 | ||
270 | :: | |
271 | ||
272 | +-------------------+ | |
273 | | Web browser | [debug UI] | |
274 | +-------------------+ | |
275 | | | |
276 | | http (port 9092) | |
277 | | socket.io | |
278 | v | |
279 | +-------------------+ | |
280 | | duk_debug.js | [debug client] | |
281 | +-------------------+ | |
282 | | /\ | |
283 | | || | |
284 | +----------||---- [example tcp transport] (port 9091) | |
285 | | || (application provides concrete transport) | |
286 | | || | |
287 | | ||---- [debug protocol stream] | |
288 | | || (between debug client and Duktape) | |
289 | | || | |
290 | + - - | - - - - -|| - - + | |
291 | : v || : | |
292 | : +-------------||-+ : [target] | |
293 | : | application || | : | |
294 | : +-------------||-+ : | |
295 | : ^ || : | |
296 | : | || : [debug API] | |
297 | : +----------||-------- debug transport callbacks | |
298 | : | || : (read, write, peek, read/write flush) | |
299 | : | || : implemented by application | |
300 | : | \/ : | |
301 | : +----------------+ : | |
302 | : | Duktape | : | |
303 | : +----------------+ : | |
304 | + - - - - - - - - - - - + | |
305 | ||
306 | The debug transport is application specific: | |
307 | ||
308 | * Duktape command line ("duk") and this debug client use an **example** TCP | |
309 | transport as a concrete example. | |
310 | ||
311 | * It is entirely up to the application to come up with the most suitable | |
312 | transport for its environment. Different mechanisms will be needed for | |
313 | Wi-Fi, serial, etc. | |
314 | ||
315 | The debug protocol running inside the transport is transport independent: | |
316 | ||
317 | * The debug protocol is documented in ``doc/debugger.rst``. | |
318 | ||
319 | * This debug client provides further concrete examples and clarifications | |
320 | on how the protocol can be used. | |
321 | ||
322 | Using a custom transport | |
323 | ======================== | |
324 | ||
325 | Quite possibly your target device cannot use the example TCP transport and | |
326 | you need to implement your own transport. You'll need to implement your | |
327 | custom transport both for the target device and for the debug client. | |
328 | ||
329 | Target device | |
330 | ------------- | |
331 | ||
332 | Implement the debug transport callbacks needed by ``duk_debugger_attach()``. | |
333 | ||
334 | See ``doc/debugger.rst`` for details and ``examples/debug-trans-socket`` | |
335 | for example running code for a TCP transport. | |
336 | ||
337 | Debug client alternative 1: duk_debug.js + custom TCP proxy | |
338 | ----------------------------------------------------------- | |
339 | ||
340 | If you don't want to change ``duk_debug.js`` you can implement a TCP proxy | |
341 | which accepts a TCP connection from ``duk_debug.js`` and then uses your | |
342 | custom transport to talk to the target:: | |
343 | ||
344 | +--------------+ TCP +-------+ custom +--------+ | |
345 | | duk_debug.js | ------> | proxy | ---------> | target | | |
346 | +--------------+ +-------+ +--------+ | |
347 | ||
348 | This is a straightforward option and a proxy can be used with other debug | |
349 | clients too (perhaps custom scripts talking to the target etc). | |
350 | ||
351 | You could also use netcat and implement your proxy so that it talks to | |
352 | ``duk_debug.js`` using stdin/stdout. | |
353 | ||
354 | Debug client alternative 2: duk_debug.js + custom NodeJS stream | |
355 | --------------------------------------------------------------- | |
356 | ||
357 | To make ``duk_debug.js`` use a custom transport you need to: | |
358 | ||
359 | * Implement your own transport as NodeJS stream. You can add it directly to | |
360 | ``duk_debug.js`` but it's probably easiest to use a separate module so that | |
361 | the diff to ``duk_debug.js`` stays minimal. | |
362 | ||
363 | * Change ``duk_debug.js`` to use the custom transport instead of a TCP | |
364 | stream. Search for "CUSTOMTRANSPORT" in ``duk_debug.js``. | |
365 | ||
366 | See: | |
367 | ||
368 | * http://nodejs.org/api/stream.html | |
369 | ||
370 | * https://github.com/substack/stream-handbook | |
371 | ||
372 | Debug client alternative 3: custom debug client | |
373 | ----------------------------------------------- | |
374 | ||
375 | You can also implement your own debug client and debug UI with support for | |
376 | your custom transport. | |
377 | ||
378 | You'll also need to implement the client part of the Duktape debugger | |
379 | protocol. See ``doc/debugger.rst`` for the specification and ``duk_debug.js`` | |
380 | for example running code which should illustrate the protocol in more detail. | |
381 | ||
382 | The JSON debug proxy allows you to implement a debug client without needing | |
383 | to implement the Duktape binary debug protocol. The JSON protocol provides | |
384 | a roughly 1:1 mapping to the binary protocol but with an easier syntax. |