Browse Source

Debugger doc: error codes, apprequest cleanup

pull/600/head
Sami Vaarala 9 years ago
parent
commit
ea368b0f31
  1. 114
      doc/debugger.rst

114
doc/debugger.rst

@ -21,12 +21,13 @@ Duktape provides the following basic debugging features:
* Get/put variable at any callstack level
* A mechanism for application-defined requests (AppRequest) and notifications
(AppNotify)
* Forwarding of print(), alert(), and logger writes
* Full heap dump (debugger web UI converts to JSON)
* Bindings for application-defined commands (AppRequest and AppNotify)
Duktape debugging architecture is based on the following major pieces:
* A standard **debug protocol**, implemented directly by Duktape.
@ -79,10 +80,15 @@ To integrate debugger support into your target, you need to:
etc. (A detach can also occur if explicitly requested by the debug client
or if Duktape detects a debug stream error.)
* **If you have an eventloop**: optionally call ``duk_debugger_cooperate()``
* **If you have an event loop**: optionally call ``duk_debugger_cooperate()``
once in a while when no call to Duktape is in progress. This allows debug
commands to be executed outside of any Duktape calls.
Duktape comes with a bundled debug client which supports a plain TCP transport.
There are also several third party debug clients which may be adapted to talk
to your target: they share the same debug protocol so only the transport will
need adaptation.
You can also write your own debug client by implementing the client side of
the debug protocol. The debug client is intended to adapt to the target
debug protocol version, so your debug client may need changes from time to
@ -92,8 +98,8 @@ with the same semantic versioning principles as the Duktape API.
You can implement the binary debug protocol directly in your debug client,
but an easier option is to use the JSON mapping of the debug protocol which
is much more user friendly. Duktape includes a proxy server which converts
between the JSON mapping and the binary debug protocol (which actually runs
on the target).
between the JSON mapping and the binary debug protocol which actually runs
on the target.
Example debug client and server
-------------------------------
@ -142,7 +148,8 @@ A standard debug transport
This is up to user code, because the most appropriate transport varies a great
deal: Wi-Fi, serial port, etc. However, TCP is probably a good default
transport if there are no specific reasons not to use it.
transport if there are no specific reasons not to use it. The bundled
example debugger web UI and JSON debug proxy use TCP as a transport.
A standard debugger UI
::::::::::::::::::::::
@ -1828,9 +1835,9 @@ If the target hasn't registered a request callback, Duktape responds::
ERR 2 "AppRequest unsupported by target" EOM
The target might also respond with error, e.g.::
The application request callback may also indicate an error, e.g.::
ERR 4 "unrecognized AppRequest message"
ERR 4 "missing argument for SetFrameRate"
This is a custom request message whose meaning and semantics depend on the
application.
@ -1852,7 +1859,7 @@ which merely serves to marshal them back and forth through a defined API.
AppNotify messages may be sent by pushing the contents of the message to the
stack and calling ``duk_debugger_notify()`` passing the number of values
pushed. Each value pushed will be sent as a dvalue in the message. So if you
pushed. Each value pushed will be sent as a dvalue in the message. So if you
push two strings, "foo" and "bar", the client will see ``NFY 7 "foo" "bar" EOM``.
AppRequest is used to make requests to the target which are not directly
@ -1871,40 +1878,50 @@ AppRequest is received, the request callback is invoked with the contents of
the message on the value stack, and may push its own values to be sent in
reply.
This is a do-nothing request callback::
This is a minimal do-nothing request callback::
duk_idx_t duk_cb_debug_request(duk_context *ctx, void *udata, duk_idx_t nvalues) {
/* nop */
/* Number of return values is returned: here empty reply. */
return 0;
}
The above callback simply responds with ``REP EOM`` (an empty reply) to all
requests. This is admittedly not very useful and a real implementation should
process the values it receives on the stack, push its own values to send in
reply, and return a non-negative integer indicating how many values it pushed.
The above dummy callback simply responds with ``REP EOM`` (an empty reply) to
all requests.
Here is a slightly more useful implementation::
A more useful callback should process the values it receives on the value
stack, push its own values to send in reply, and return a non-negative integer
indicating how many values it pushed. Here is a slightly more useful
implementation::
duk_idx_t duk_cb_debug_request(duk_context *ctx, void *udata, duk_idx_t nvalues) {
/* Must use negative stack indices to access dvalues */
const char *cmd_name = duk_get_string(ctx, -nvalues + 0);
if (strcmp(cmd_name, "VersionInfo") == 0) {
const char *cmd_name = NULL;
/* Callback must be very careful NEVER to access values below
* 'nvalues' topmost value stack elements.
*/
if (nvalues >= 1) {
/* Must access values relative to stack top. */
cmd_name = duk_get_string(ctx, -nvalues + 0);
}
if (cmd_name == NULL) {
/* Return -1 to send an ERR reply. The value on top of the stack
* should be a string which will be used for the error text sent
* to the debug client.
*/
duk_push_string(ctx, "missing application specific command name");
return -1;
} else if (strcmp(cmd_name, "VersionInfo") == 0) {
/* Return a positive integer to send a REP containing values pushed
* to the stack. The return value indicates how many dvalues you
* to the stack. The return value indicates how many dvalues you
* are including in the response.
*/
duk_push_string(ctx, "My Awesome Program");
duk_push_int(ctx, 81200); /* ver. 8.12.0 */
return 2; /* 2 dvalues */
} else {
/* Return -1 to send an ERR reply. The value on top of the stack
* will be string coerced and used for the error text.
*/
duk_push_string(ctx, "unrecognized AppRequest command name");
duk_push_sprintf(ctx, "unrecognized application specific command name: %s",
cmd_name);
return -1;
}
}
@ -1914,12 +1931,13 @@ unsupported command, eliciting an ERR reply from Duktape saying so. A target
is always free to send AppNotify messages.
As a precaution, the target should try to avoid sending structured values such
as JS objects in notify messages as their heap pointers may become stale by the
time the client receives them. This is especially true for notifications sent
while the target is running. It's better to stick to primitives which have
unique dvalue representations, e.g. numbers, booleans, and strings. If a
structured value does need to be sent, it can simply be e.g. JSON/JX encoded
and sent as a string instead.
as JS objects in notify messages as their heap pointers may become stale by
the time the client receives and inspects them. This is especially true for
notifications sent while the target is running. It's better to stick to
primitives which have unique dvalue representations, e.g. numbers, booleans,
and strings. If a structured value does need to be sent, it can simply be
e.g. JSON/JX encoded and sent as a string instead (carefully avoiding uncaught
errors).
Important notes on the request callback
---------------------------------------
@ -1928,6 +1946,10 @@ The request callback is provided with a ``duk_context`` pointer with which it
can access the value stack and is assumed to be trusted. There are certain
things it MUST NOT do. Specifically:
* It MUST NOT assume that ``nvalues`` has any specific value. In particular
it might be zero so that there are no arguments to the callback (not even a
string used, by convention, to identify an application specific command).
* It MUST NOT attempt to access or pop any values from the top of the stack
beyond the ``nvalues`` it is given and the values it pushes itself.
@ -1963,8 +1985,8 @@ AppRequest/AppNotify command format
As a general convention, it is recommended for the first field in an AppRequest
or AppNotify message after the command number be a string identifying the
command, e.g. "VersionInfo" or "RebootDevice". This makes it simpler for
different clients and targets to interoperate. Unrecognized command names can
command, e.g. "VersionInfo" or "RebootDevice". This makes it simpler for
different clients and targets to interoperate. Unrecognized command names can
simply be ignored, whereas, e.g. integer commands may be interpreted
differently depending on the debug client and target in use.
@ -3209,3 +3231,23 @@ It might be cleaner to provide either:
* Allow user code to proactively call into Duktape to indicate the transport
is broken (beyond calling ``duk_debugger_detach()``).
Extend ERR message with a programmatic string error code
--------------------------------------------------------
Current error messages have the form::
ERR <error number> <message> EOM
The number space is awkward to manage modularly, and doesn't work well for
application specific errors which would be useful for e.g. AppRequest
messages. Extend the error format to::
ERR <error number> <error string code> <message> EOM
String codes could follow an all caps convention like ``"NOT_FOUND"``::
ERR 3 "NOT_FOUND" "breakpoint not found" EOM
String error codes are easy to extend without conflicts like one has with
a numbered sequence.

Loading…
Cancel
Save