mirror of
				https://github.com/janet-lang/janet
				synced 2025-10-30 23:23:07 +00:00 
			
		
		
		
	Add debug/step to single step a fiber.
Very useful for implementing debuggers.
This commit is contained in:
		| @@ -69,7 +69,8 @@ | |||||||
|   (def sourcemap (dasm 'sourcemap)) |   (def sourcemap (dasm 'sourcemap)) | ||||||
|   (var last-loc [-2 -2]) |   (var last-loc [-2 -2]) | ||||||
|   (print "\n  function:   " (dasm 'name) " [" (in dasm 'source "") "]") |   (print "\n  function:   " (dasm 'name) " [" (in dasm 'source "") "]") | ||||||
|   (printf "  constants:  %.4Q\n" (dasm 'constants)) |   (when-let [constants (dasm 'constants)] | ||||||
|  |     (printf "  constants:  %.4Q\n" constants)) | ||||||
|   (printf "  slots:      %.4Q\n\n" (frame :slots)) |   (printf "  slots:      %.4Q\n\n" (frame :slots)) | ||||||
|   (def padding (string/repeat " " 20)) |   (def padding (string/repeat " " 20)) | ||||||
|   (loop [i :range [0 (length bytecode)] |   (loop [i :range [0 (length bytecode)] | ||||||
|   | |||||||
| @@ -100,9 +100,9 @@ static JanetSlot do_debug(JanetFopts opts, JanetSlot *args) { | |||||||
|     int32_t len = janet_v_count(args); |     int32_t len = janet_v_count(args); | ||||||
|     JanetSlot t = janetc_gettarget(opts); |     JanetSlot t = janetc_gettarget(opts); | ||||||
|     janetc_emit_ssu(opts.compiler, JOP_SIGNAL, t, |     janetc_emit_ssu(opts.compiler, JOP_SIGNAL, t, | ||||||
|             (len == 1) ? args[0] : janetc_cslot(janet_wrap_nil()), |                     (len == 1) ? args[0] : janetc_cslot(janet_wrap_nil()), | ||||||
|             JANET_SIGNAL_DEBUG, |                     JANET_SIGNAL_DEBUG, | ||||||
|             1); |                     1); | ||||||
|     return t; |     return t; | ||||||
| } | } | ||||||
| static JanetSlot do_in(JanetFopts opts, JanetSlot *args) { | static JanetSlot do_in(JanetFopts opts, JanetSlot *args) { | ||||||
|   | |||||||
| @@ -313,6 +313,14 @@ static Janet cfun_debug_argstack(int32_t argc, Janet *argv) { | |||||||
|     return janet_wrap_array(array); |     return janet_wrap_array(array); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static Janet cfun_debug_step(int32_t argc, Janet *argv) { | ||||||
|  |     janet_arity(argc, 1, 2); | ||||||
|  |     JanetFiber *fiber = janet_getfiber(argv, 0); | ||||||
|  |     Janet out = janet_wrap_nil(); | ||||||
|  |     janet_step(fiber, argc == 1 ? janet_wrap_nil() : argv[1], &out); | ||||||
|  |     return out; | ||||||
|  | } | ||||||
|  |  | ||||||
| static const JanetReg debug_cfuns[] = { | static const JanetReg debug_cfuns[] = { | ||||||
|     { |     { | ||||||
|         "debug/break", cfun_debug_break, |         "debug/break", cfun_debug_break, | ||||||
| @@ -381,6 +389,13 @@ static const JanetReg debug_cfuns[] = { | |||||||
|              "the fiber handling the error can see which fiber raised the signal. This function should " |              "the fiber handling the error can see which fiber raised the signal. This function should " | ||||||
|              "be used mostly for debugging purposes.") |              "be used mostly for debugging purposes.") | ||||||
|     }, |     }, | ||||||
|  |     { | ||||||
|  |         "debug/step", cfun_debug_step, | ||||||
|  |         JDOC("(debug/step fiber &opt x)\n\n" | ||||||
|  |              "Run a fiber for one virtual instruction of the Janet machine. Can optionally " | ||||||
|  |              "pass in a value that will be passed as the resuming value. Returns the signal value, " | ||||||
|  |              "which will usually be nil, as breakpoints raise nil signals.") | ||||||
|  |     }, | ||||||
|     {NULL, NULL, NULL} |     {NULL, NULL, NULL} | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1004,6 +1004,61 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in, JanetFiberStatus status) | |||||||
|     VM_END() |     VM_END() | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Execute a single instruction in the fiber. Does this by inspecting | ||||||
|  |  * the fiber, setting a breakpoint at the next instruction, executing, and | ||||||
|  |  * reseting breakpoints to how they were prior. Yes, it's a bit hacky. | ||||||
|  |  */ | ||||||
|  | JanetSignal janet_step(JanetFiber *fiber, Janet in, Janet *out) { | ||||||
|  |     Janet *stack = fiber->data + fiber->frame; | ||||||
|  |     uint32_t *pc = janet_stack_frame(stack)->pc; | ||||||
|  |  | ||||||
|  |     /* Check current opcode (sans debug flag). This tells us where the next or next two candidate | ||||||
|  |      * instructions will be. Usually it's the next instruction in memory, | ||||||
|  |      * but for branching instructions it is also the target of the branch. */ | ||||||
|  |     uint32_t *nexta = NULL, *nextb = NULL, olda, oldb; | ||||||
|  |  | ||||||
|  |     /* Set temporary breakpoints */ | ||||||
|  |     switch (*pc & 0x7F) { | ||||||
|  |         default: | ||||||
|  |             nexta = pc + 1; | ||||||
|  |             break; | ||||||
|  |         /* These we just ignore for now. Supporting them means | ||||||
|  |          * we could step into and out of functions (including JOP_CALL). */ | ||||||
|  |         case JOP_RETURN_NIL: | ||||||
|  |         case JOP_RETURN: | ||||||
|  |         case JOP_ERROR: | ||||||
|  |         case JOP_TAILCALL: | ||||||
|  |             break; | ||||||
|  |         case JOP_JUMP: | ||||||
|  |             nexta = pc + DS; | ||||||
|  |             break; | ||||||
|  |         case JOP_JUMP_IF: | ||||||
|  |         case JOP_JUMP_IF_NOT: | ||||||
|  |             nexta = pc + 1; | ||||||
|  |             nextb = pc + ES; | ||||||
|  |             break; | ||||||
|  |     } | ||||||
|  |     if (nexta) { | ||||||
|  |         olda = *nexta; | ||||||
|  |         *nexta |= 0x80; | ||||||
|  |     } | ||||||
|  |     if (nextb) { | ||||||
|  |         oldb = *nextb; | ||||||
|  |         *nextb |= 0x80; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* Go */ | ||||||
|  |     JanetSignal signal = run_vm(fiber, in, janet_fiber_status(fiber)); | ||||||
|  |  | ||||||
|  |     /* Restore */ | ||||||
|  |     if (nexta) *nexta = olda; | ||||||
|  |     if (nextb) *nextb = oldb; | ||||||
|  |  | ||||||
|  |     *out = *janet_vm_return_reg; | ||||||
|  |     return signal; | ||||||
|  | } | ||||||
|  |  | ||||||
| Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv) { | Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv) { | ||||||
|     /* Check entry conditions */ |     /* Check entry conditions */ | ||||||
|     if (!janet_vm_fiber) |     if (!janet_vm_fiber) | ||||||
|   | |||||||
| @@ -1298,6 +1298,7 @@ JANET_API int janet_init(void); | |||||||
| JANET_API void janet_deinit(void); | JANET_API void janet_deinit(void); | ||||||
| JANET_API JanetSignal janet_continue(JanetFiber *fiber, Janet in, Janet *out); | JANET_API JanetSignal janet_continue(JanetFiber *fiber, Janet in, Janet *out); | ||||||
| JANET_API JanetSignal janet_pcall(JanetFunction *fun, int32_t argn, const Janet *argv, Janet *out, JanetFiber **f); | JANET_API JanetSignal janet_pcall(JanetFunction *fun, int32_t argn, const Janet *argv, Janet *out, JanetFiber **f); | ||||||
|  | JANET_API JanetSignal janet_step(JanetFiber *fiber, Janet in, Janet *out); | ||||||
| JANET_API Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv); | JANET_API Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv); | ||||||
| JANET_API Janet janet_mcall(const char *name, int32_t argc, Janet *argv); | JANET_API Janet janet_mcall(const char *name, int32_t argc, Janet *argv); | ||||||
| JANET_API void janet_stacktrace(JanetFiber *fiber, Janet err); | JANET_API void janet_stacktrace(JanetFiber *fiber, Janet err); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Calvin Rose
					Calvin Rose