1 /*
2 * Debugging related API calls
3 */
4
5 #include "duk_internal.h"
6
duk_push_context_dump(duk_context * ctx)7 DUK_EXTERNAL void duk_push_context_dump(duk_context *ctx) {
8 duk_idx_t idx;
9 duk_idx_t top;
10
11 DUK_ASSERT_CTX_VALID(ctx);
12
13 /* We don't duk_require_stack() here now, but rely on the caller having
14 * enough space.
15 */
16
17 top = duk_get_top(ctx);
18 duk_push_array(ctx);
19 for (idx = 0; idx < top; idx++) {
20 duk_dup(ctx, idx);
21 duk_put_prop_index(ctx, -2, idx);
22 }
23
24 /* XXX: conversion errors should not propagate outwards.
25 * Perhaps values need to be coerced individually?
26 */
27 duk_bi_json_stringify_helper(ctx,
28 duk_get_top_index(ctx), /*idx_value*/
29 DUK_INVALID_INDEX, /*idx_replacer*/
30 DUK_INVALID_INDEX, /*idx_space*/
31 DUK_JSON_FLAG_EXT_CUSTOM |
32 DUK_JSON_FLAG_ASCII_ONLY |
33 DUK_JSON_FLAG_AVOID_KEY_QUOTES /*flags*/);
34
35 duk_push_sprintf(ctx, "ctx: top=%ld, stack=%s", (long) top, (const char *) duk_safe_to_string(ctx, -1));
36 duk_replace(ctx, -3); /* [ ... arr jsonx(arr) res ] -> [ ... res jsonx(arr) ] */
37 duk_pop(ctx);
38 DUK_ASSERT(duk_is_string(ctx, -1));
39 }
40
41 #if defined(DUK_USE_DEBUGGER_SUPPORT)
42
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)43 DUK_EXTERNAL void duk_debugger_attach_custom(duk_context *ctx,
44 duk_debug_read_function read_cb,
45 duk_debug_write_function write_cb,
46 duk_debug_peek_function peek_cb,
47 duk_debug_read_flush_function read_flush_cb,
48 duk_debug_write_flush_function write_flush_cb,
49 duk_debug_request_function request_cb,
50 duk_debug_detached_function detached_cb,
51 void *udata) {
52 duk_hthread *thr = (duk_hthread *) ctx;
53 duk_heap *heap;
54 const char *str;
55 duk_size_t len;
56
57 /* XXX: should there be an error or an automatic detach if
58 * already attached?
59 */
60
61 DUK_D(DUK_DPRINT("application called duk_debugger_attach()"));
62
63 DUK_ASSERT_CTX_VALID(ctx);
64 DUK_ASSERT(read_cb != NULL);
65 DUK_ASSERT(write_cb != NULL);
66 /* Other callbacks are optional. */
67
68 heap = thr->heap;
69 heap->dbg_read_cb = read_cb;
70 heap->dbg_write_cb = write_cb;
71 heap->dbg_peek_cb = peek_cb;
72 heap->dbg_read_flush_cb = read_flush_cb;
73 heap->dbg_write_flush_cb = write_flush_cb;
74 heap->dbg_request_cb = request_cb;
75 heap->dbg_detached_cb = detached_cb;
76 heap->dbg_udata = udata;
77 heap->dbg_have_next_byte = 0;
78
79 /* Start in paused state. */
80 heap->dbg_processing = 0;
81 heap->dbg_paused = 1;
82 heap->dbg_state_dirty = 1;
83 heap->dbg_force_restart = 0;
84 heap->dbg_step_type = 0;
85 heap->dbg_step_thread = NULL;
86 heap->dbg_step_csindex = 0;
87 heap->dbg_step_startline = 0;
88 heap->dbg_exec_counter = 0;
89 heap->dbg_last_counter = 0;
90 heap->dbg_last_time = 0.0;
91
92 /* Send version identification and flush right afterwards. Note that
93 * we must write raw, unframed bytes here.
94 */
95 duk_push_sprintf(ctx, "%ld %ld %s %s\n",
96 (long) DUK_DEBUG_PROTOCOL_VERSION,
97 (long) DUK_VERSION,
98 (const char *) DUK_GIT_DESCRIBE,
99 (const char *) DUK_USE_TARGET_INFO);
100 str = duk_get_lstring(ctx, -1, &len);
101 DUK_ASSERT(str != NULL);
102 duk_debug_write_bytes(thr, (const duk_uint8_t *) str, len);
103 duk_debug_write_flush(thr);
104 duk_pop(ctx);
105 }
106
duk_debugger_detach(duk_context * ctx)107 DUK_EXTERNAL void duk_debugger_detach(duk_context *ctx) {
108 duk_hthread *thr;
109
110 DUK_D(DUK_DPRINT("application called duk_debugger_detach()"));
111
112 DUK_ASSERT_CTX_VALID(ctx);
113 thr = (duk_hthread *) ctx;
114 DUK_ASSERT(thr != NULL);
115 DUK_ASSERT(thr->heap != NULL);
116
117 /* Can be called multiple times with no harm. */
118 duk_debug_do_detach(thr->heap);
119 }
120
duk_debugger_cooperate(duk_context * ctx)121 DUK_EXTERNAL void duk_debugger_cooperate(duk_context *ctx) {
122 duk_hthread *thr;
123 duk_bool_t processed_messages;
124
125 DUK_ASSERT_CTX_VALID(ctx);
126 thr = (duk_hthread *) ctx;
127 DUK_ASSERT(thr != NULL);
128 DUK_ASSERT(thr->heap != NULL);
129
130 if (!DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) {
131 return;
132 }
133 if (thr->callstack_top > 0 || thr->heap->dbg_processing) {
134 /* Calling duk_debugger_cooperate() while Duktape is being
135 * called into is not supported. This is not a 100% check
136 * but prevents any damage in most cases.
137 */
138 return;
139 }
140
141 processed_messages = duk_debug_process_messages(thr, 1 /*no_block*/);
142 DUK_UNREF(processed_messages);
143 }
144
duk_debugger_notify(duk_context * ctx,duk_idx_t nvalues)145 DUK_EXTERNAL duk_bool_t duk_debugger_notify(duk_context *ctx, duk_idx_t nvalues) {
146 duk_hthread *thr;
147 duk_idx_t top;
148 duk_idx_t idx;
149 duk_bool_t ret = 0;
150
151 DUK_ASSERT_CTX_VALID(ctx);
152 thr = (duk_hthread *) ctx;
153 DUK_ASSERT(thr != NULL);
154 DUK_ASSERT(thr->heap != NULL);
155
156 DUK_D(DUK_DPRINT("application called duk_debugger_notify() with nvalues=%ld", (long) nvalues));
157
158 top = duk_get_top(ctx);
159 if (top < nvalues) {
160 DUK_ERROR_API(thr, "not enough stack values for notify");
161 return ret; /* unreachable */
162 }
163 if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) {
164 duk_debug_write_notify(thr, DUK_DBG_CMD_APPNOTIFY);
165 for (idx = top - nvalues; idx < top; idx++) {
166 duk_tval *tv = DUK_GET_TVAL_POSIDX(ctx, idx);
167 duk_debug_write_tval(thr, tv);
168 }
169 duk_debug_write_eom(thr);
170
171 /* Return non-zero (true) if we have a good reason to believe
172 * the notify was delivered; if we're still attached at least
173 * a transport error was not indicated by the transport write
174 * callback. This is not a 100% guarantee of course.
175 */
176 if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) {
177 ret = 1;
178 }
179 }
180 duk_pop_n(ctx, nvalues);
181 return ret;
182 }
183
duk_debugger_pause(duk_context * ctx)184 DUK_EXTERNAL void duk_debugger_pause(duk_context *ctx) {
185 duk_hthread *thr;
186
187 DUK_ASSERT_CTX_VALID(ctx);
188 thr = (duk_hthread *) ctx;
189 DUK_ASSERT(thr != NULL);
190 DUK_ASSERT(thr->heap != NULL);
191
192 DUK_D(DUK_DPRINT("application called duk_debugger_pause()"));
193
194 /* Treat like a debugger statement: ignore when not attached. */
195 if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) {
196 DUK_HEAP_SET_PAUSED(thr->heap);
197
198 /* Pause on the next opcode executed. This is always safe to do even
199 * inside the debugger message loop: the interrupt counter will be reset
200 * to its proper value when the message loop exits.
201 */
202 thr->interrupt_init = 1;
203 thr->interrupt_counter = 0;
204 }
205 }
206
207 #else /* DUK_USE_DEBUGGER_SUPPORT */
208
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)209 DUK_EXTERNAL void duk_debugger_attach_custom(duk_context *ctx,
210 duk_debug_read_function read_cb,
211 duk_debug_write_function write_cb,
212 duk_debug_peek_function peek_cb,
213 duk_debug_read_flush_function read_flush_cb,
214 duk_debug_write_flush_function write_flush_cb,
215 duk_debug_request_function request_cb,
216 duk_debug_detached_function detached_cb,
217 void *udata) {
218 DUK_ASSERT_CTX_VALID(ctx);
219 DUK_UNREF(read_cb);
220 DUK_UNREF(write_cb);
221 DUK_UNREF(peek_cb);
222 DUK_UNREF(read_flush_cb);
223 DUK_UNREF(write_flush_cb);
224 DUK_UNREF(request_cb);
225 DUK_UNREF(detached_cb);
226 DUK_UNREF(udata);
227 DUK_ERROR_API((duk_hthread *) ctx, "no debugger support");
228 }
229
duk_debugger_detach(duk_context * ctx)230 DUK_EXTERNAL void duk_debugger_detach(duk_context *ctx) {
231 DUK_ASSERT_CTX_VALID(ctx);
232 DUK_ERROR_API((duk_hthread *) ctx, "no debugger support");
233 }
234
duk_debugger_cooperate(duk_context * ctx)235 DUK_EXTERNAL void duk_debugger_cooperate(duk_context *ctx) {
236 /* nop */
237 DUK_ASSERT_CTX_VALID(ctx);
238 DUK_UNREF(ctx);
239 }
240
duk_debugger_notify(duk_context * ctx,duk_idx_t nvalues)241 DUK_EXTERNAL duk_bool_t duk_debugger_notify(duk_context *ctx, duk_idx_t nvalues) {
242 duk_idx_t top;
243
244 DUK_ASSERT_CTX_VALID(ctx);
245
246 top = duk_get_top(ctx);
247 if (top < nvalues) {
248 DUK_ERROR_API((duk_hthread *) ctx, "not enough stack values for notify");
249 return 0; /* unreachable */
250 }
251
252 /* No debugger support, just pop values. */
253 duk_pop_n(ctx, nvalues);
254 return 0;
255 }
256
duk_debugger_pause(duk_context * ctx)257 DUK_EXTERNAL void duk_debugger_pause(duk_context *ctx) {
258 /* Treat like debugger statement: nop */
259 DUK_ASSERT_CTX_VALID(ctx);
260 DUK_UNREF(ctx);
261 }
262
263 #endif /* DUK_USE_DEBUGGER_SUPPORT */
264