File: duk_api_debug.c

package info (click to toggle)
duktape 2.7.0-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 21,160 kB
  • sloc: ansic: 215,359; python: 5,961; javascript: 4,555; makefile: 477; cpp: 205
file content (261 lines) | stat: -rw-r--r-- 8,111 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
/*
 *  Debugging related API calls
 */

#include "duk_internal.h"

#if defined(DUK_USE_JSON_SUPPORT)
DUK_EXTERNAL void duk_push_context_dump(duk_hthread *thr) {
	duk_idx_t idx;
	duk_idx_t top;

	DUK_ASSERT_API_ENTRY(thr);

	/* We don't duk_require_stack() here now, but rely on the caller having
	 * enough space.
	 */

	top = duk_get_top(thr);
	duk_push_bare_array(thr);
	for (idx = 0; idx < top; idx++) {
		duk_dup(thr, idx);
		duk_put_prop_index(thr, -2, (duk_uarridx_t) idx);
	}

	/* XXX: conversion errors should not propagate outwards.
	 * Perhaps values need to be coerced individually?
	 */
	duk_bi_json_stringify_helper(thr,
	                             duk_get_top_index(thr), /*idx_value*/
	                             DUK_INVALID_INDEX, /*idx_replacer*/
	                             DUK_INVALID_INDEX, /*idx_space*/
	                             DUK_JSON_FLAG_EXT_CUSTOM | DUK_JSON_FLAG_ASCII_ONLY |
	                                 DUK_JSON_FLAG_AVOID_KEY_QUOTES /*flags*/);

	duk_push_sprintf(thr, "ctx: top=%ld, stack=%s", (long) top, (const char *) duk_safe_to_string(thr, -1));
	duk_replace(thr, -3); /* [ ... arr jsonx(arr) res ] -> [ ... res jsonx(arr) ] */
	duk_pop(thr);
	DUK_ASSERT(duk_is_string(thr, -1));
}
#else /* DUK_USE_JSON_SUPPORT */
DUK_EXTERNAL void duk_push_context_dump(duk_hthread *thr) {
	DUK_ASSERT_API_ENTRY(thr);
	DUK_ERROR_UNSUPPORTED(thr);
	DUK_WO_NORETURN(return;);
}
#endif /* DUK_USE_JSON_SUPPORT */

#if defined(DUK_USE_DEBUGGER_SUPPORT)

DUK_EXTERNAL void duk_debugger_attach(duk_hthread *thr,
                                      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_heap *heap;
	const char *str;
	duk_size_t len;

	/* XXX: should there be an error or an automatic detach if
	 * already attached?
	 */

	DUK_D(DUK_DPRINT("application called duk_debugger_attach()"));

	DUK_ASSERT_API_ENTRY(thr);
	DUK_ASSERT(read_cb != NULL);
	DUK_ASSERT(write_cb != NULL);
	/* Other callbacks are optional. */

	heap = thr->heap;
	heap->dbg_read_cb = read_cb;
	heap->dbg_write_cb = write_cb;
	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;

	/* Start in paused state. */
	heap->dbg_processing = 0;
	heap->dbg_state_dirty = 0;
	heap->dbg_force_restart = 0;
	heap->dbg_pause_flags = 0;
	heap->dbg_pause_act = NULL;
	heap->dbg_pause_startline = 0;
	heap->dbg_exec_counter = 0;
	heap->dbg_last_counter = 0;
	heap->dbg_last_time = 0.0;
	duk_debug_set_paused(heap); /* XXX: overlap with fields above */

	/* Send version identification and flush right afterwards.  Note that
	 * we must write raw, unframed bytes here.
	 */
	duk_push_sprintf(thr,
	                 "%ld %ld %s %s\n",
	                 (long) DUK_DEBUG_PROTOCOL_VERSION,
	                 (long) DUK_VERSION,
	                 (const char *) DUK_GIT_DESCRIBE,
	                 (const char *) DUK_USE_TARGET_INFO);
	str = duk_get_lstring(thr, -1, &len);
	DUK_ASSERT(str != NULL);
	duk_debug_write_bytes(thr, (const duk_uint8_t *) str, len);
	duk_debug_write_flush(thr);
	duk_pop(thr);
}

DUK_EXTERNAL void duk_debugger_detach(duk_hthread *thr) {
	DUK_D(DUK_DPRINT("application called duk_debugger_detach()"));

	DUK_ASSERT_API_ENTRY(thr);
	DUK_ASSERT(thr->heap != NULL);

	/* Can be called multiple times with no harm. */
	duk_debug_do_detach(thr->heap);
}

DUK_EXTERNAL void duk_debugger_cooperate(duk_hthread *thr) {
	duk_bool_t processed_messages;

	DUK_ASSERT_API_ENTRY(thr);
	DUK_ASSERT(thr->heap != NULL);

	if (!duk_debug_is_attached(thr->heap)) {
		return;
	}
	if (thr->callstack_curr != NULL || thr->heap->dbg_processing) {
		/* Calling duk_debugger_cooperate() while Duktape is being
		 * called into is not supported.  This is not a 100% check
		 * but prevents any damage in most cases.
		 */
		return;
	}

	processed_messages = duk_debug_process_messages(thr, 1 /*no_block*/);
	DUK_UNREF(processed_messages);
}

DUK_EXTERNAL duk_bool_t duk_debugger_notify(duk_hthread *thr, duk_idx_t nvalues) {
	duk_idx_t top;
	duk_idx_t idx;
	duk_bool_t ret = 0;

	DUK_ASSERT_API_ENTRY(thr);
	DUK_ASSERT(thr->heap != NULL);

	DUK_D(DUK_DPRINT("application called duk_debugger_notify() with nvalues=%ld", (long) nvalues));

	top = duk_get_top(thr);
	if (top < nvalues) {
		DUK_ERROR_RANGE(thr, "not enough stack values for notify");
		DUK_WO_NORETURN(return 0;);
	}
	if (duk_debug_is_attached(thr->heap)) {
		duk_debug_write_notify(thr, DUK_DBG_CMD_APPNOTIFY);
		for (idx = top - nvalues; idx < top; idx++) {
			duk_tval *tv = DUK_GET_TVAL_POSIDX(thr, 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_debug_is_attached(thr->heap)) {
			ret = 1;
		}
	}
	duk_pop_n(thr, nvalues);
	return ret;
}

DUK_EXTERNAL void duk_debugger_pause(duk_hthread *thr) {
	DUK_ASSERT_API_ENTRY(thr);
	DUK_ASSERT(thr->heap != NULL);

	DUK_D(DUK_DPRINT("application called duk_debugger_pause()"));

	/* Treat like a debugger statement: ignore when not attached. */
	if (duk_debug_is_attached(thr->heap)) {
		if (duk_debug_is_paused(thr->heap)) {
			DUK_D(DUK_DPRINT("duk_debugger_pause() called when already paused; ignoring"));
		} else {
			duk_debug_set_paused(thr->heap);

			/* Pause on the next opcode executed.  This is always safe to do even
			 * inside the debugger message loop: the interrupt counter will be reset
			 * to its proper value when the message loop exits.
			 */
			thr->interrupt_init = 1;
			thr->interrupt_counter = 0;
		}
	}
}

#else /* DUK_USE_DEBUGGER_SUPPORT */

DUK_EXTERNAL void duk_debugger_attach(duk_hthread *thr,
                                      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_API_ENTRY(thr);
	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_TYPE(thr, "no debugger support");
	DUK_WO_NORETURN(return;);
}

DUK_EXTERNAL void duk_debugger_detach(duk_hthread *thr) {
	DUK_ASSERT_API_ENTRY(thr);
	DUK_ERROR_TYPE(thr, "no debugger support");
	DUK_WO_NORETURN(return;);
}

DUK_EXTERNAL void duk_debugger_cooperate(duk_hthread *thr) {
	/* nop */
	DUK_ASSERT_API_ENTRY(thr);
	DUK_UNREF(thr);
}

DUK_EXTERNAL duk_bool_t duk_debugger_notify(duk_hthread *thr, duk_idx_t nvalues) {
	duk_idx_t top;

	DUK_ASSERT_API_ENTRY(thr);

	top = duk_get_top(thr);
	if (top < nvalues) {
		DUK_ERROR_RANGE_INVALID_COUNT(thr);
		DUK_WO_NORETURN(return 0;);
	}

	/* No debugger support, just pop values. */
	duk_pop_n(thr, nvalues);
	return 0;
}

DUK_EXTERNAL void duk_debugger_pause(duk_hthread *thr) {
	/* Treat like debugger statement: nop */
	DUK_ASSERT_API_ENTRY(thr);
	DUK_UNREF(thr);
}

#endif /* DUK_USE_DEBUGGER_SUPPORT */