Browse Source

Example debug transport with dvalue encode/decode

This is a useful example for users who want to implement the debug client
directly on the target, in which case the debug protocol is decoded on the
target instead of a remote client.
pull/297/head
Sami Vaarala 9 years ago
parent
commit
e19cab722b
  1. 17
      examples/debug-trans-dvalue/Makefile
  2. 8
      examples/debug-trans-dvalue/README.rst
  3. 1239
      examples/debug-trans-dvalue/duk_trans_dvalue.c
  4. 113
      examples/debug-trans-dvalue/duk_trans_dvalue.h
  5. 236
      examples/debug-trans-dvalue/test.c

17
examples/debug-trans-dvalue/Makefile

@ -0,0 +1,17 @@
# Set DUKTAPE_SRC to 'src' dir of Duktape distributable.
# The default is for the dist environment.
DUKTAPE_SRC=../../src
DUKTAPE_OPTS=
DUKTAPE_OPTS+=-DDUK_OPT_ASSERTIONS
DUKTAPE_OPTS+=-DDUK_OPT_DEBUGGER_SUPPORT -DDUK_OPT_INTERRUPT_COUNTER
DUKTAPE_OPTS+=-DDUK_OPT_DEBUGGER_FWD_PRINTALERT
DUKTAPE_OPTS+=-DDUK_OPT_DEBUGGER_DUMPHEAP
#DUKTAPE_OPTS+=-DDUK_OPT_DEBUGGER_TRANSPORT_TORTURE
TRANS_OPTS=
#TRANS_OPTS+=-DDEBUG_PRINTS
test: test.c duk_trans_dvalue.c duk_trans_dvalue.h
echo $(DUKTAPE_SRC)
gcc -O0 -g -ggdb -Wall -Wextra -std=c99 -o test -I$(DUKTAPE_SRC) -I. \
$(DUKTAPE_OPTS) $(TRANS_OPTS) \
$(DUKTAPE_SRC)/duktape.c duk_trans_dvalue.c test.c -lm

8
examples/debug-trans-dvalue/README.rst

@ -0,0 +1,8 @@
===========================================================
Debug transport with local debug protocol encoding/decoding
===========================================================
This example implements a debug transport which decodes/encodes the Duktape
debug protocol locally into a more easy to use C interface, which is useful
for debug clients implemented locally on the target. The example also
demonstrates how to trial parse dvalues in C.

1239
examples/debug-trans-dvalue/duk_trans_dvalue.c

File diff suppressed because it is too large

113
examples/debug-trans-dvalue/duk_trans_dvalue.h

@ -0,0 +1,113 @@
#ifndef DUK_TRANS_DVALUE_H_INCLUDED
#define DUK_TRANS_DVALUE_H_INCLUDED
#include "duktape.h"
typedef struct duk_dvalue duk_dvalue;
typedef struct duk_trans_buffer duk_trans_buffer;
typedef struct duk_trans_dvalue_ctx duk_trans_dvalue_ctx;
typedef void (*duk_trans_dvalue_received_function)(duk_trans_dvalue_ctx *ctx, duk_dvalue *dv);
typedef void (*duk_trans_dvalue_cooperate_function)(duk_trans_dvalue_ctx *ctx, int block);
typedef void (*duk_trans_dvalue_handshake_function)(duk_trans_dvalue_ctx *ctx, const char *handshake_line);
typedef void (*duk_trans_dvalue_detached_function)(duk_trans_dvalue_ctx *ctx);
/* struct duk_dvalue 'tag' values, note that these have nothing to do with
* Duktape debug protocol inital byte. Struct fields used with the type
* are noted next to the define.
*/
#define DUK_DVALUE_EOM 1 /* no fields */
#define DUK_DVALUE_REQ 2 /* no fields */
#define DUK_DVALUE_REP 3 /* no fields */
#define DUK_DVALUE_ERR 4 /* no fields */
#define DUK_DVALUE_NFY 5 /* no fields */
#define DUK_DVALUE_INTEGER 6 /* i: 32-bit signed integer */
#define DUK_DVALUE_STRING 7 /* buf: string data, len: string length */
#define DUK_DVALUE_BUFFER 8 /* buf: buffer data, len: buffer length */
#define DUK_DVALUE_UNUSED 9 /* no fields */
#define DUK_DVALUE_UNDEFINED 10 /* no fields */
#define DUK_DVALUE_NULL 11 /* no fields */
#define DUK_DVALUE_TRUE 12 /* no fields */
#define DUK_DVALUE_FALSE 13 /* no fields */
#define DUK_DVALUE_NUMBER 14 /* d: ieee double */
#define DUK_DVALUE_OBJECT 15 /* i: class number, buf: pointer data, len: pointer length */
#define DUK_DVALUE_POINTER 16 /* buf: pointer data, len: pointer length */
#define DUK_DVALUE_LIGHTFUNC 17 /* i: lightfunc flags, buf: pointer data, len: pointer length */
#define DUK_DVALUE_HEAPPTR 18 /* buf: pointer data, len: pointer length */
struct duk_dvalue {
/* Could use a union for the value but the gain would be relatively small. */
int tag;
int i;
double d;
size_t len;
unsigned char *buf;
};
struct duk_trans_buffer {
unsigned char *base;
size_t write_offset;
size_t read_offset;
size_t alloc_size;
};
struct duk_trans_dvalue_ctx {
duk_trans_dvalue_received_function received;
duk_trans_dvalue_cooperate_function cooperate;
duk_trans_dvalue_handshake_function handshake;
duk_trans_dvalue_detached_function detached;
duk_trans_buffer send_buf; /* sending towards Duktape (duktape read callback) */
duk_trans_buffer recv_buf; /* receiving from Duktape (duktape write callback) */
int handshake_done;
int double_byteorder; /* 0=little endian, 1=big endian, 2=mixed endian */
};
/* Buffer size needed by duk_dvalue_to_string(). */
#define DUK_DVALUE_TOSTRING_BUFLEN 256
/* Dvalue handling. */
duk_dvalue *duk_dvalue_alloc(void);
void duk_dvalue_free(duk_dvalue *dv);
void duk_dvalue_to_string(duk_dvalue *dv, char *buf);
duk_dvalue *duk_dvalue_make_tag(int tag);
duk_dvalue *duk_dvalue_make_tag_int(int tag, int intval);
duk_dvalue *duk_dvalue_make_tag_double(int tag, double dblval);
duk_dvalue *duk_dvalue_make_tag_data(int tag, const char *buf, size_t len);
duk_dvalue *duk_dvalue_make_tag_int_data(int tag, int intval, const char *buf, size_t len);
/* Initializing and freeing the transport context. */
duk_trans_dvalue_ctx *duk_trans_dvalue_init(void);
void duk_trans_dvalue_free(duk_trans_dvalue_ctx *ctx);
/* Sending dvalues towards Duktape. */
void duk_trans_dvalue_send(duk_trans_dvalue_ctx *ctx, duk_dvalue *dv);
void duk_trans_dvalue_send_eom(duk_trans_dvalue_ctx *ctx);
void duk_trans_dvalue_send_req(duk_trans_dvalue_ctx *ctx);
void duk_trans_dvalue_send_rep(duk_trans_dvalue_ctx *ctx);
void duk_trans_dvalue_send_err(duk_trans_dvalue_ctx *ctx);
void duk_trans_dvalue_send_nfy(duk_trans_dvalue_ctx *ctx);
void duk_trans_dvalue_send_integer(duk_trans_dvalue_ctx *ctx, int val);
void duk_trans_dvalue_send_string(duk_trans_dvalue_ctx *ctx, const char *str);
void duk_trans_dvalue_send_lstring(duk_trans_dvalue_ctx *ctx, const char *str, size_t len);
void duk_trans_dvalue_send_buffer(duk_trans_dvalue_ctx *ctx, const char *buf, size_t len);
void duk_trans_dvalue_send_unused(duk_trans_dvalue_ctx *ctx);
void duk_trans_dvalue_send_undefined(duk_trans_dvalue_ctx *ctx);
void duk_trans_dvalue_send_null(duk_trans_dvalue_ctx *ctx);
void duk_trans_dvalue_send_true(duk_trans_dvalue_ctx *ctx);
void duk_trans_dvalue_send_false(duk_trans_dvalue_ctx *ctx);
void duk_trans_dvalue_send_number(duk_trans_dvalue_ctx *ctx, double val);
void duk_trans_dvalue_send_object(duk_trans_dvalue_ctx *ctx, int classnum, const char *ptr_data, size_t ptr_len);
void duk_trans_dvalue_send_pointer(duk_trans_dvalue_ctx *ctx, const char *ptr_data, size_t ptr_len);
void duk_trans_dvalue_send_lightfunc(duk_trans_dvalue_ctx *ctx, int lf_flags, const char *ptr_data, size_t ptr_len);
void duk_trans_dvalue_send_heapptr(duk_trans_dvalue_ctx *ctx, const char *ptr_data, size_t ptr_len);
void duk_trans_dvalue_send_req_cmd(duk_trans_dvalue_ctx *ctx, int cmd);
/* Duktape debug callbacks provided by the transport. */
duk_size_t duk_trans_dvalue_read_cb(void *udata, char *buffer, duk_size_t length);
duk_size_t duk_trans_dvalue_write_cb(void *udata, const char *buffer, duk_size_t length);
duk_size_t duk_trans_dvalue_peek_cb(void *udata);
void duk_trans_dvalue_read_flush_cb(void *udata);
void duk_trans_dvalue_write_flush_cb(void *udata);
void duk_trans_dvalue_detached_cb(void *udata);
#endif /* DUK_TRANS_DVALUE_H_INCLUDED */

236
examples/debug-trans-dvalue/test.c

@ -0,0 +1,236 @@
/*
* Example program using the dvalue debug transport.
*/
#include <stdio.h>
#include <stdlib.h>
#include "duktape.h"
#include "duk_trans_dvalue.h"
void my_cooperate(duk_trans_dvalue_ctx *ctx, int block) {
static int first_blocked = 1;
if (!block) {
/* Duktape is not blocked; you can cooperate with e.g. a user
* interface here and send dvalues to Duktape, but don't block.
*/
return;
}
/* Duktape is blocked on a read and won't continue until debug
* command(s) are sent.
*
* Normally you'd enter your own event loop here, and process
* events until something needs to be sent to Duktape. For
* example, the user might press a "Step over" button in the
* UI which would cause dvalues to be sent. You can then
* return from this callback.
*
* The code below sends some example messages for testing the
* dvalue handling of the transport.
*
* If you create dvalues manually and send them using
* duk_trans_dvalue_send(), you must free the dvalues after
* the send call returns using duk_dvalue_free().
*/
if (first_blocked) {
char *tmp;
int i;
/* First time Duktape becomes blocked, send DumpHeap which
* exercises a lot of parsing code.
*
* NOTE: Valgrind may complain about reading uninitialized
* bytes. This is caused by the DumpHeap command writing out
* verbatim duk_tval values which are intentionally not
* always fully initialized for performance reasons.
*/
first_blocked = 0;
fprintf(stderr, "Duktape is blocked, send DumpHeap\n");
fflush(stderr);
duk_trans_dvalue_send_req(ctx);
duk_trans_dvalue_send_integer(ctx, 0x20); /* DumpHeap */
duk_trans_dvalue_send_eom(ctx);
/* Also send a dummy TriggerStatus request with trailing dvalues
* ignored by Duktape; Duktape will parse the dvalues to be able to
* skip them, so that the dvalue encoding is exercised.
*/
tmp = malloc(100000); /* long buffer, >= 65536 chars */
for (i = 0; i < 100000; i++) {
tmp[i] = (char) i;
}
duk_trans_dvalue_send_req(ctx);
duk_trans_dvalue_send_integer(ctx, 0x11); /* TriggerStatus */
duk_trans_dvalue_send_string(ctx, "dummy"); /* short, <= 31 chars */
duk_trans_dvalue_send_string(ctx, "123456789012345678901234567890foobar"); /* medium, >= 32 chars */
duk_trans_dvalue_send_lstring(ctx, (const char *) tmp, 65535UL);
duk_trans_dvalue_send_lstring(ctx, (const char *) tmp, 65536UL);
duk_trans_dvalue_send_lstring(ctx, (const char *) tmp, 100000UL);
duk_trans_dvalue_send_buffer(ctx, (const char *) tmp, 255U);
duk_trans_dvalue_send_buffer(ctx, (const char *) tmp, 65535UL);
duk_trans_dvalue_send_buffer(ctx, (const char *) tmp, 65536UL);
duk_trans_dvalue_send_buffer(ctx, (const char *) tmp, 100000UL);
duk_trans_dvalue_send_unused(ctx);
duk_trans_dvalue_send_undefined(ctx);
duk_trans_dvalue_send_null(ctx);
duk_trans_dvalue_send_true(ctx);
duk_trans_dvalue_send_false(ctx);
duk_trans_dvalue_send_number(ctx, 123.456);
duk_trans_dvalue_send_object(ctx, 12 /*classnum*/, (const char *) tmp, 8); /* fake ptr len */
duk_trans_dvalue_send_pointer(ctx, (const char *) tmp, 8); /* fake ptr len */
duk_trans_dvalue_send_lightfunc(ctx, 0xdabc /*lf_flags*/, (const char *) tmp, 8); /* fake ptr len */
duk_trans_dvalue_send_heapptr(ctx, (const char *) tmp, 8); /* fake ptr len */
duk_trans_dvalue_send_eom(ctx);
}
fprintf(stderr, "Duktape is blocked, send Eval and StepInto to resume execution\n");
fflush(stderr);
/* duk_trans_dvalue_send_req_cmd() sends a REQ dvalue followed by
* an integer dvalue (command) for convenience.
*/
duk_trans_dvalue_send_req_cmd(ctx, 0x1e); /* 0x1e = Eval */
duk_trans_dvalue_send_string(ctx, "evalMe");
duk_trans_dvalue_send_eom(ctx);
duk_trans_dvalue_send_req_cmd(ctx, 0x14); /* 0x14 = StepOver */
duk_trans_dvalue_send_eom(ctx);
}
void my_received(duk_trans_dvalue_ctx *ctx, duk_dvalue *dv) {
char buf[DUK_DVALUE_TOSTRING_BUFLEN];
(void) ctx;
duk_dvalue_to_string(dv, buf);
fprintf(stderr, "Received dvalue: %s\n", buf);
fflush(stderr);
/* Here a normal debug client would wait for dvalues until an EOM
* dvalue was received (which completes a debug message). The
* debug message would then be handled, possibly causing UI changes
* and/or causing debug commands to be sent to Duktape.
*
* The callback is responsible for eventually freeing the dvalue.
* Here we free it immediately, but an actual client would probably
* gather dvalues into an array or linked list to handle when the
* debug message was complete.
*/
duk_dvalue_free(dv);
}
void my_handshake(duk_trans_dvalue_ctx *ctx, const char *line) {
(void) ctx;
/* The Duktape handshake line is given in 'line' (without LF).
* The 'line' argument can be accessed for the duration of the
* callback (read only). Don't free 'line' here, the transport
* handles that.
*/
fprintf(stderr, "Received handshake line: '%s'\n", line);
fflush(stderr);
}
void my_detached(duk_trans_dvalue_ctx *ctx) {
(void) ctx;
/* Detached call forwarded as is. */
fprintf(stderr, "Debug transport detached\n");
fflush(stderr);
}
int main(int argc, char *argv[]) {
duk_context *ctx;
duk_trans_dvalue_ctx *trans_ctx;
int exitval = 0;
(void) argc; (void) argv; /* suppress warning */
ctx = duk_create_heap_default();
if (!ctx) {
fprintf(stderr, "Failed to create Duktape heap\n");
fflush(stderr);
exitval = 1;
goto cleanup;
}
trans_ctx = duk_trans_dvalue_init();
if (!trans_ctx) {
fprintf(stderr, "Failed to create debug transport context\n");
fflush(stderr);
exitval = 1;
goto cleanup;
}
trans_ctx->cooperate = my_cooperate;
trans_ctx->received = my_received;
trans_ctx->handshake = my_handshake;
trans_ctx->detached = my_detached;
/* Attach debugger; this will fail with a fatal error here unless
* debugger support is compiled in. To fail more gracefully, call
* this under a duk_safe_call() to catch the error.
*/
duk_debugger_attach(ctx,
duk_trans_dvalue_read_cb,
duk_trans_dvalue_write_cb,
duk_trans_dvalue_peek_cb,
duk_trans_dvalue_read_flush_cb,
duk_trans_dvalue_write_flush_cb,
duk_trans_dvalue_detached_cb,
(void *) trans_ctx);
fprintf(stderr, "Debugger attached, running eval\n");
fflush(stderr);
/* Evaluate simple test code, callbacks will "step over" until end.
*
* The test code here is just for exercising the debug transport.
* The 'evalMe' variable is evaluated (using debugger command Eval)
* before every step to force different dvalues to be carried over
* the transport.
*/
duk_eval_string(ctx,
"var evalMe;\n"
"\n"
"print('Hello world!');\n"
"[ undefined, null, true, false, 123, -123, 123.1, 0, -0, 1/0, 0/0, -1/0, \n"
" 'foo', Duktape.Buffer('bar'), Duktape.Pointer('dummy'), Math.cos, \n"
"].forEach(function (val) {\n"
" print(val);\n"
" evalMe = val;\n"
"});\n"
"\n"
"var str = 'xxx'\n"
"for (i = 0; i < 10; i++) {\n"
" print(i, str);\n"
" evalMe = str;\n"
" evalMe = Duktape.Buffer(str);\n"
" str = str + str;\n"
"}\n"
);
duk_pop(ctx);
duk_debugger_detach(ctx);
cleanup:
if (trans_ctx) {
duk_trans_dvalue_free(trans_ctx);
trans_ctx = NULL;
}
if (ctx) {
duk_destroy_heap(ctx);
}
return exitval;
}
Loading…
Cancel
Save