Browse Source

Implement AppRequest/AppNotify debugger commands

When Duktape receives an AppRequest, it pushes all the dvalues in the
message to the value stack and calls the request callback. That callback
can process the message and optionally push its own values to the stack,
which will be sent back to the client as a reply.

It is also possible for the target to send application-specific notifys
to the client by calling duk_debugger_notify(). These will be received
by the client as an AppNotify message.
pull/596/head
Bruce Pascoe 9 years ago
parent
commit
f4580e127f
  1. 78
      src/duk_api_debug.c
  2. 23
      src/duk_api_public.h.in
  3. 47
      src/duk_debugger.c
  4. 3
      src/duk_debugger.h
  5. 1
      src/duk_heap.h
  6. 1
      src/duk_heap_alloc.c

78
src/duk_api_debug.c

@ -40,14 +40,15 @@ DUK_EXTERNAL void duk_push_context_dump(duk_context *ctx) {
#if defined(DUK_USE_DEBUGGER_SUPPORT)
DUK_EXTERNAL void duk_debugger_attach(duk_context *ctx,
duk_debug_read_function read_cb,
duk_debug_write_function write_cb,
duk_debug_peek_function peek_cb,
duk_debug_read_flush_function read_flush_cb,
duk_debug_write_flush_function write_flush_cb,
duk_debug_detached_function detached_cb,
void *udata) {
DUK_EXTERNAL void duk_debugger_attach_custom(duk_context *ctx,
duk_debug_read_function read_cb,
duk_debug_write_function write_cb,
duk_debug_peek_function peek_cb,
duk_debug_read_flush_function read_flush_cb,
duk_debug_write_flush_function write_flush_cb,
duk_debug_request_function request_cb,
duk_debug_detached_function detached_cb,
void *udata) {
duk_hthread *thr = (duk_hthread *) ctx;
duk_heap *heap;
const char *str;
@ -70,6 +71,7 @@ DUK_EXTERNAL void duk_debugger_attach(duk_context *ctx,
heap->dbg_peek_cb = peek_cb;
heap->dbg_read_flush_cb = read_flush_cb;
heap->dbg_write_flush_cb = write_flush_cb;
heap->dbg_request_cb = request_cb;
heap->dbg_detached_cb = detached_cb;
heap->dbg_udata = udata;
heap->dbg_have_next_byte = 0;
@ -140,22 +142,58 @@ DUK_EXTERNAL void duk_debugger_cooperate(duk_context *ctx) {
DUK_UNREF(processed_messages);
}
DUK_EXTERNAL duk_bool_t duk_debugger_notify(duk_context *ctx, duk_idx_t nvalues) {
duk_hthread *thr;
duk_idx_t top, idx;
duk_bool_t ret = 0;
DUK_ASSERT_CTX_VALID(ctx);
thr = (duk_hthread *) ctx;
DUK_ASSERT(thr != NULL);
DUK_ASSERT(thr->heap != NULL);
if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) {
top = duk_get_top(ctx);
if (top < nvalues) {
DUK_ERROR_API((duk_hthread *) ctx, "not enough stack values for notify");
}
duk_debug_write_notify(thr, DUK_DBG_CMD_APPNOTIFY);
for (idx = top - nvalues; idx < top; idx++) {
duk_tval *tv = DUK_GET_TVAL_POSIDX(ctx, idx);
duk_debug_write_tval(thr, tv);
}
duk_debug_write_eom(thr);
/* Return non-zero (true) if we have a good reason to believe the notify was
* delivered; if we're still attached at least a transport error was not indicated
* by the transport write callback. This is not a 100% guarantee of course.
*/
if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) {
ret = 1;
}
}
duk_pop_n(ctx, nvalues);
return ret;
}
#else /* DUK_USE_DEBUGGER_SUPPORT */
DUK_EXTERNAL void duk_debugger_attach(duk_context *ctx,
duk_debug_read_function read_cb,
duk_debug_write_function write_cb,
duk_debug_peek_function peek_cb,
duk_debug_read_flush_function read_flush_cb,
duk_debug_write_flush_function write_flush_cb,
duk_debug_detached_function detached_cb,
void *udata) {
DUK_EXTERNAL void duk_debugger_attach_custom(duk_context *ctx,
duk_debug_read_function read_cb,
duk_debug_write_function write_cb,
duk_debug_peek_function peek_cb,
duk_debug_read_flush_function read_flush_cb,
duk_debug_write_flush_function write_flush_cb,
duk_debug_request_function request_cb,
duk_debug_detached_function detached_cb,
void *udata) {
DUK_ASSERT_CTX_VALID(ctx);
DUK_UNREF(read_cb);
DUK_UNREF(write_cb);
DUK_UNREF(peek_cb);
DUK_UNREF(read_flush_cb);
DUK_UNREF(write_flush_cb);
DUK_UNREF(request_cb);
DUK_UNREF(detached_cb);
DUK_UNREF(udata);
DUK_ERROR_API((duk_hthread *) ctx, "no debugger support");
@ -172,4 +210,12 @@ DUK_EXTERNAL void duk_debugger_cooperate(duk_context *ctx) {
DUK_UNREF(ctx);
}
DUK_EXTERNAL duk_bool_t duk_debugger_notify(duk_context *ctx, duk_idx_t nvalues) {
DUK_ASSERT_CTX_VALID(ctx);
/* no debugger support, just pop values */
duk_pop_n(ctx, nvalues);
return 0;
}
#endif /* DUK_USE_DEBUGGER_SUPPORT */

23
src/duk_api_public.h.in

@ -56,6 +56,7 @@ typedef duk_size_t (*duk_debug_write_function) (void *udata, const char *buffer,
typedef duk_size_t (*duk_debug_peek_function) (void *udata);
typedef void (*duk_debug_read_flush_function) (void *udata);
typedef void (*duk_debug_write_flush_function) (void *udata);
typedef duk_idx_t (*duk_debug_request_function) (void *udata, duk_context *ctx, duk_idx_t nvalues);
typedef void (*duk_debug_detached_function) (void *udata);
struct duk_memory_functions {
@ -942,16 +943,22 @@ DUK_EXTERNAL_DECL void duk_push_context_dump(duk_context *ctx);
* Debugger (debug protocol)
*/
DUK_EXTERNAL_DECL void duk_debugger_attach(duk_context *ctx,
duk_debug_read_function read_cb,
duk_debug_write_function write_cb,
duk_debug_peek_function peek_cb,
duk_debug_read_flush_function read_flush_cb,
duk_debug_write_flush_function write_flush_cb,
duk_debug_detached_function detached_cb,
void *udata);
#define duk_debugger_attach(ctx,read_cb,write_cb,peek_cb,read_flush_cb,write_flush_cb,detached_cb,udata) \
duk_debugger_attach_custom((ctx), (read_cb), (write_cb), (peek_cb), (read_flush_cb), (write_flush_cb), \
NULL, (detached_cb), (udata))
DUK_EXTERNAL_DECL void duk_debugger_attach_custom(duk_context *ctx,
duk_debug_read_function read_cb,
duk_debug_write_function write_cb,
duk_debug_peek_function peek_cb,
duk_debug_read_flush_function read_flush_cb,
duk_debug_write_flush_function write_flush_cb,
duk_debug_request_function request_cb,
duk_debug_detached_function detached_cb,
void *udata);
DUK_EXTERNAL_DECL void duk_debugger_detach(duk_context *ctx);
DUK_EXTERNAL_DECL void duk_debugger_cooperate(duk_context *ctx);
DUK_EXTERNAL_DECL duk_bool_t duk_debugger_notify(duk_context *ctx, duk_idx_t nvalues);
/*
* Date provider related constants

47
src/duk_debugger.c

@ -1448,6 +1448,49 @@ DUK_LOCAL void duk__debug_handle_detach(duk_hthread *thr, duk_heap *heap) {
DUK__SET_CONN_BROKEN(thr, 0); /* not an error */
}
DUK_LOCAL void duk__debug_handle_apprequest(duk_hthread *thr, duk_heap *heap) {
DUK_D(DUK_DPRINT("debug command AppRequest"));
if (heap->dbg_request_cb != NULL) {
duk_context *ctx = (duk_context *) thr;
duk_idx_t nrets;
duk_idx_t nvalues = 0;
duk_idx_t old_top;
duk_idx_t top, idx;
/* Read tvals from the message and push them onto the valstack,
* then call the request callback to process the request.
*/
old_top = duk_get_top(ctx); /* save stack top */
while (duk_debug_peek_byte(thr) != DUK_DBG_MARKER_EOM) {
duk_debug_read_tval(thr); /* push to stack */
nvalues++;
}
/* Request callback should push values for reply to client onto valstack */
nrets = heap->dbg_request_cb(heap->dbg_udata, ctx, nvalues);
if (nrets > 0) {
DUK_ASSERT(duk_get_top(ctx) >= old_top + nrets);
/* Reply with tvals pushed by request callback */
duk_debug_write_byte(thr, DUK_DBG_MARKER_REPLY);
top = duk_get_top(ctx);
for (idx = top - nrets; idx < top; idx++) {
duk_debug_write_tval(thr, DUK_GET_TVAL_POSIDX(ctx, idx));
}
duk_debug_write_eom(thr);
} else {
DUK_ASSERT(duk_get_top(ctx) >= old_top + 1);
duk_debug_write_error_eom(thr, DUK_DBG_ERR_APPLICATION, duk_safe_to_string(ctx, -1));
}
duk_set_top(ctx, old_top); /* restore stack top */
} else {
DUK_D(DUK_DPRINT("no request callback, treat AppRequest as unsupported"));
duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNSUPPORTED, "AppRequest unsupported by target");
}
}
#if defined(DUK_USE_DEBUGGER_DUMPHEAP)
DUK_LOCAL void duk__debug_dump_heaphdr(duk_hthread *thr, duk_heap *heap, duk_heaphdr *hdr) {
DUK_UNREF(heap);
@ -1760,6 +1803,10 @@ DUK_LOCAL void duk__debug_process_message(duk_hthread *thr) {
duk__debug_handle_get_bytecode(thr, heap);
break;
}
case DUK_DBG_CMD_APPREQUEST: {
duk__debug_handle_apprequest(thr, heap);
break;
}
default: {
DUK_D(DUK_DPRINT("debug command unsupported: %d", (int) cmd));
duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNSUPPORTED, "unsupported command");

3
src/duk_debugger.h

@ -13,6 +13,7 @@
#define DUK_DBG_ERR_UNSUPPORTED 0x01
#define DUK_DBG_ERR_TOOMANY 0x02
#define DUK_DBG_ERR_NOTFOUND 0x03
#define DUK_DBG_ERR_APPLICATION 0x03
/* Initiated by Duktape */
#define DUK_DBG_CMD_STATUS 0x01
@ -21,6 +22,7 @@
#define DUK_DBG_CMD_LOG 0x04
#define DUK_DBG_CMD_THROW 0x05
#define DUK_DBG_CMD_DETACHING 0x06
#define DUK_DBG_CMD_APPNOTIFY 0x07
/* Initiated by debug client */
#define DUK_DBG_CMD_BASICINFO 0x10
@ -41,6 +43,7 @@
#define DUK_DBG_CMD_DETACH 0x1f
#define DUK_DBG_CMD_DUMPHEAP 0x20
#define DUK_DBG_CMD_GETBYTECODE 0x21
#define DUK_DBG_CMD_APPREQUEST 0x22
#if defined(DUK_USE_DEBUGGER_SUPPORT)
DUK_INTERNAL_DECL void duk_debug_do_detach(duk_heap *heap);

1
src/duk_heap.h

@ -438,6 +438,7 @@ struct duk_heap {
duk_debug_peek_function dbg_peek_cb;
duk_debug_read_flush_function dbg_read_flush_cb;
duk_debug_write_flush_function dbg_write_flush_cb;
duk_debug_request_function dbg_request_cb;
duk_debug_detached_function dbg_detached_cb;
void *dbg_udata;

1
src/duk_heap_alloc.c

@ -849,6 +849,7 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func,
res->dbg_peek_cb = NULL;
res->dbg_read_flush_cb = NULL;
res->dbg_write_flush_cb = NULL;
res->dbg_request_cb = NULL;
res->dbg_udata = NULL;
res->dbg_step_thread = NULL;
#endif

Loading…
Cancel
Save