mirror of https://github.com/svaarala/duktape.git
Browse Source
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
5 changed files with 1613 additions and 0 deletions
@ -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 |
@ -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. |
File diff suppressed because it is too large
@ -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 */ |
@ -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…
Reference in new issue