Add debugging features

This commit is contained in:
2021-04-08 12:26:32 +03:00
parent 0ac140869a
commit cffbb0a94d
17 changed files with 231 additions and 65 deletions
+1
View File
@@ -8,6 +8,7 @@ VM_OBJS=$(O)/vm/main.o \
$(O)/vm/unit.o \
$(O)/vm/stack.o \
$(O)/vm/error.o \
$(O)/vm/debug.o \
$(O)/core/vector.o \
$(O)/core/hash.o
COMPILER_OBJS=$(O)/compiler/main.o \
+3 -1
View File
@@ -304,8 +304,10 @@ void emit(struct function *fn, struct context *ctx, struct node *expr) {
emit_insn(fn, OP(OP_CAR, 0));
} else if (!strcmp(n0->n_ident, "cdr") && c0 == 1) {
emit_insn(fn, OP(OP_CDR, 0));
} else if (!strcmp(n0->n_ident, "trace") && c0 == 1) {
} else if (!strcmp(n0->n_ident, "debug/trace") && c0 == 1) {
emit_insn(fn, OP(OP_DEBUG, OP_DEBUG_TRACE));
} else if (!strcmp(n0->n_ident, "debug/break")) {
emit_insn(fn, OP(OP_DEBUG, OP_DEBUG_BREAKPOINT));
} else {
size_t index;
int kind;
+1
View File
@@ -46,3 +46,4 @@
#define OP_DEBUG 0x80
#define OP_DEBUG_TRACE 0x01
#define OP_DEBUG_BREAKPOINT 0x02
+7
View File
@@ -3,4 +3,11 @@
(define a (cons 1 (cons 2 (cons 3 (cons 4 nil)))))
(define (f x)
(debug/break)
(+ x 1))
(print (f 1))
(print (list-ref a 1))
(print (list-ref a 1))
+95
View File
@@ -0,0 +1,95 @@
#include <string.h>
#include <stdio.h>
#include "error.h"
#include "debug.h"
#include "vmstate.h"
#include "vmval.h"
#include "unit.h"
int debug_breakpoint(struct vm *vm) {
struct vm_func_entry *func;
struct vm_unit *unit;
uint64_t w0;
uint32_t opcode;
char line[256];
char *p;
unit = vector_ref(&vm->units, vm->lp);
func = vector_ref(&unit->functions, vm->fp);
opcode = func->bytecode[vm->ip];
fprintf(stderr, "%02zx:%02zx:%04zx: %08x\n", vm->lp, vm->fp, vm->ip, opcode);
while (1) {
fprintf(stderr, ">> ");
if (!fgets(line, sizeof(line), stdin)) {
return 0;
}
if ((p = strchr(line, '\n')) != NULL) {
*p = 0;
}
if (!strcmp(line, "s")) {
vm->flags |= VM_SINGLESTEP;
return 0;
} else if (!strcmp(line, "c")) {
vm->flags &= ~VM_SINGLESTEP;
return 0;
} else if (!strcmp(line, "q")) {
return -ERR_ERROR_EXIT;
} else if (!strcmp(line, "regs")) {
fprintf(stderr, " lp = 0x%04zx, fp = 0x%04zx\n", vm->lp, vm->fp);
fprintf(stderr, " ip = 0x%04zx\n", vm->ip);
fprintf(stderr, " dsp = 0x%04zx\n", vm->data_stack.sp);
fprintf(stderr, " csp = 0x%04zx, cbp = 0x%04zx\n", vm->call_stack.sp, vm->bp);
} else if (!strcmp(line, "locals")) {
fprintf(stderr, "Local variables:\n");
for (size_t i = 0; i < func->local_count; ++i) {
w0 = vm->call_stack.data[vm->bp - i - 1];
fprintf(stderr, "[bp - %zu]: ", i + 1);
vm_print(stderr, w0);
fprintf(stderr, "\n");
}
fprintf(stderr, "Arguments:\n");
for (size_t i = 0; i < func->argc; ++i) {
w0 = vm->call_stack.data[vm->bp + i];
fprintf(stderr, "[bp + %zu]: ", i);
vm_print(stderr, w0);
fprintf(stderr, "\n");
}
} else if (!strcmp(line, "bt")) {
fprintf(stderr, "Call frame trace:\n");
size_t bp, lp, fp, ip, sp;
size_t count;
struct vm_func_entry *f;
struct vm_unit *u;
bp = vm->bp;
lp = vm->lp;
fp = vm->fp;
ip = vm->ip;
count = 0;
while (1) {
u = vector_ref(&vm->units, lp);
f = vector_ref(&u->functions, fp);
sp = bp + f->argc;
fprintf(stderr, "%2zu: %02zx:%02zx:%04zx: %08x\n",
count, lp, fp, ip, f->bytecode[ip]);
if (count == 5) {
break;
}
bp = vm->call_stack.data[sp + 3];
lp = vm->call_stack.data[sp + 2];
fp = vm->call_stack.data[sp + 1];
ip = vm->call_stack.data[sp + 0];
++count;
if (bp == 0) {
break;
}
}
}
}
}
+2
View File
@@ -13,6 +13,8 @@ const char *vm_strerror(int e) {
case -ERR_STACK_UNDERFLOW: return "Stack underflow";
case -ERR_STACK_OVERFLOW: return "Stack overflow";
case -ERR_NOT_FOUND: return "Unit not found";
case -ERR_INVALID_STATE: return "Invalid VM state";
case -ERR_ERROR_EXIT: return "Program exited with an error";
default:
return "Unknown error";
}
+5
View File
@@ -0,0 +1,5 @@
#pragma once
struct vm;
int debug_breakpoint(struct vm *vm);
+2
View File
@@ -11,5 +11,7 @@
#define ERR_STACK_UNDERFLOW 9
#define ERR_STACK_OVERFLOW 10
#define ERR_NOT_FOUND 11
#define ERR_INVALID_STATE 12
#define ERR_ERROR_EXIT 13
const char *vm_strerror(int e);
+3 -3
View File
@@ -4,7 +4,7 @@
#include "list.h"
#include "vector.h"
struct vm_state;
struct vm;
struct vm_ref_entry;
struct vm_unresolved_ref {
@@ -29,11 +29,11 @@ struct vm_unit_info {
struct vm_unit *unit;
};
int vm_load_unit_file(struct vm_state *vm,
int vm_load_unit_file(struct vm *vm,
struct vm_unit_info *info,
struct list_head *refs,
FILE *fp);
int vm_load_unit(struct vm_state *vm,
int vm_load_unit(struct vm *vm,
struct vm_unit_info *info,
struct list_head *refs,
const char *name);
+26 -12
View File
@@ -10,8 +10,18 @@
#define MAXARG 12
#define MAXLOC 64
#define VM_SINGLESTEP (1 << 0)
struct vm_value;
enum vm_state {
STATE_INIT = 0,
STATE_RUNNING,
STATE_BREAKPOINT,
STATE_EXITED,
STATE_ERROR
};
static inline uint64_t encode_ref(struct vm_value *ref) {
uintptr_t addr = (uintptr_t) ref;
assert(!(addr & 1));
@@ -22,29 +32,33 @@ static inline uint64_t encode_ref(struct vm_value *ref) {
}
}
struct vm_state {
struct vm {
struct stack data_stack;
struct stack call_stack;
struct vector units;
size_t bp, lp, fp, ip;
uint32_t flags;
enum vm_state state;
};
int vm_state_init(struct vm_state *vm, size_t stack_size);
void vm_state_free(struct vm_state *vm);
int vm_init(struct vm *vm, size_t stack_size);
void vm_free(struct vm *vm);
struct vm_unit *vm_add_unit(struct vm_state *vm, size_t stack_size);
struct vm_unit *vm_add_unit(struct vm *vm, size_t stack_size);
// Bytecode interpretation
int vm_eval_step(struct vm_state *vm);
int vm_eval_unit(struct vm_state *vm, size_t index);
int vm_eval_step(struct vm *vm);
int vm_eval_unit(struct vm *vm, size_t index);
// Stack operation
int vm_pop(struct vm_state *vm, uint64_t *w);
int vm_pop_integer(struct vm_state *vm, int64_t *v);
int vm_pop(struct vm *vm, uint64_t *w);
int vm_pop_integer(struct vm *vm, int64_t *v);
int vm_push_integer(struct vm_state *vm, int64_t v);
int vm_push_bool(struct vm_state *vm, int v);
int vm_push_ref(struct vm_state *vm, struct vm_value *obj);
int vm_push_integer(struct vm *vm, int64_t v);
int vm_push_bool(struct vm *vm, int v);
int vm_push_ref(struct vm *vm, struct vm_value *obj);
uint64_t vm_get_arg(struct vm_state *vm, size_t index);
uint64_t vm_get_arg(struct vm *vm, size_t index);
+2 -1
View File
@@ -2,6 +2,7 @@
#include <stddef.h>
#include <stdint.h>
#include <assert.h>
#include <stdio.h>
#include "vmstring.h"
#include "vmstate.h"
@@ -76,4 +77,4 @@ struct vm_value *vm_cons(uint64_t w0, uint64_t w1);
struct vm_value *vm_makestr(const char *str);
struct vm_value *vm_func(size_t lib_index, size_t fn_index);
void vm_print(uint64_t w);
void vm_print(FILE *dst, uint64_t w);
+4 -4
View File
@@ -12,14 +12,14 @@
#include "vmstate.h"
#include "vmval.h"
static int c_print(struct vm_state *vm) {
static int c_print(struct vm *vm) {
uint64_t w;
int res;
if ((res = vm_pop(vm, &w)) != 0) {
return res;
}
vm_print(w);
vm_print(stdout, w);
printf("\n");
vm_unref(w);
return 0;
@@ -210,7 +210,7 @@ static int vm_load_exports(struct vm_unit_info *info,
return 0;
}
int vm_load_unit_file(struct vm_state *vm,
int vm_load_unit_file(struct vm *vm,
struct vm_unit_info *info,
struct list_head *refs,
FILE *fp) {
@@ -251,7 +251,7 @@ int vm_load_unit_file(struct vm_state *vm,
return 0;
}
int vm_load_unit(struct vm_state *vm,
int vm_load_unit(struct vm *vm,
struct vm_unit_info *info,
struct list_head *refs,
const char *name) {
+4 -3
View File
@@ -31,11 +31,11 @@ static int execute_file(const char *filename) {
struct list_head unresolved;
struct vm_export_entry *export;
struct vm_ref_entry *ref_entry;
struct vm_state vm;
struct vm vm;
struct vm_unit_info *info;
struct vm_unresolved_ref *ref;
if ((res = vm_state_init(&vm, 4096)) != 0) {
if ((res = vm_init(&vm, 4096)) != 0) {
return res;
}
@@ -112,11 +112,12 @@ static int execute_file(const char *filename) {
hash_free(&unit_map);
// Start execution
vm.state = STATE_RUNNING;
if ((res = vm_eval_unit(&vm, 0)) != 0) {
return res;
}
vm_state_free(&vm);
vm_free(&vm);
return 0;
}
-1
View File
@@ -13,7 +13,6 @@ struct vm_func_entry *unit_add_function(struct vm_unit *u) {
}
void unit_free(struct vm_unit *u) {
printf("Release unit\n");
struct vm_func_entry *func;
vector_free(&u->ref_table);
+5 -5
View File
@@ -2,11 +2,11 @@
#include <errno.h>
int vm_pop(struct vm_state *vm, uint64_t *w) {
int vm_pop(struct vm *vm, uint64_t *w) {
return stack_pop(&vm->data_stack, w);
}
int vm_pop_integer(struct vm_state *vm, int64_t *v) {
int vm_pop_integer(struct vm *vm, int64_t *v) {
int res;
uint64_t w;
if ((res = stack_pop(&vm->data_stack, &w)) != 0) {
@@ -22,11 +22,11 @@ int vm_pop_integer(struct vm_state *vm, int64_t *v) {
return 0;
}
int vm_push_integer(struct vm_state *vm, int64_t v) {
int vm_push_integer(struct vm *vm, int64_t v) {
return stack_push(&vm->data_stack, v & ~FLAG_REF);
}
int vm_push_bool(struct vm_state *vm, int v) {
int vm_push_bool(struct vm *vm, int v) {
if (v) {
return stack_push(&vm->data_stack, 1);
} else {
@@ -34,6 +34,6 @@ int vm_push_bool(struct vm_state *vm, int v) {
}
}
int vm_push_ref(struct vm_state *vm, struct vm_value *obj) {
int vm_push_ref(struct vm *vm, struct vm_value *obj) {
return stack_push(&vm->data_stack, encode_ref(obj));
}
+55 -19
View File
@@ -1,3 +1,4 @@
#include "debug.h"
#include "error.h"
#include "op.h"
#include "unit.h"
@@ -16,7 +17,7 @@ static inline int64_t sximm(uint32_t in) {
}
}
static int vm_push_context(struct vm_state *vm) {
static int vm_push_context(struct vm *vm) {
int res;
if ((res = stack_push(&vm->call_stack, vm->bp)) != 0) {
return res;
@@ -33,7 +34,7 @@ static int vm_push_context(struct vm_state *vm) {
return 0;
}
static int vm_pop_context(struct vm_state *vm) {
static int vm_pop_context(struct vm *vm) {
int res;
if ((res = stack_pop(&vm->call_stack, &vm->ip)) != 0) {
return res;
@@ -50,7 +51,7 @@ static int vm_pop_context(struct vm_state *vm) {
return 0;
}
int vm_state_init(struct vm_state *vm, size_t stack_size) {
int vm_init(struct vm *vm, size_t stack_size) {
int res;
if ((res = stack_init(&vm->data_stack, stack_size)) != 0) {
@@ -62,6 +63,8 @@ int vm_state_init(struct vm_state *vm, size_t stack_size) {
vector_init(&vm->units, sizeof(struct vm_unit));
vm->state = STATE_INIT;
vm->flags = 0;
vm->lp = 0;
vm->fp = 0;
vm->ip = 0;
@@ -69,7 +72,7 @@ int vm_state_init(struct vm_state *vm, size_t stack_size) {
return 0;
}
void vm_state_free(struct vm_state *vm) {
void vm_free(struct vm *vm) {
struct vm_unit *unit;
stack_free(&vm->data_stack);
stack_free(&vm->call_stack);
@@ -80,7 +83,7 @@ void vm_state_free(struct vm_state *vm) {
vector_free(&vm->units);
}
struct vm_unit *vm_add_unit(struct vm_state *vm, size_t global_pool_size) {
struct vm_unit *vm_add_unit(struct vm *vm, size_t global_pool_size) {
struct vm_unit *unit = vector_append(&vm->units);
if (!unit) {
return unit;
@@ -96,7 +99,7 @@ struct vm_unit *vm_add_unit(struct vm_state *vm, size_t global_pool_size) {
}
// Call an entry from function table of a unit
int vm_call_unit_index(struct vm_state *vm, size_t lib_index, size_t fn_index) {
int vm_call_unit_index(struct vm *vm, size_t lib_index, size_t fn_index) {
struct vm_func_entry *func;
struct vm_unit *unit;
int res;
@@ -136,26 +139,26 @@ int vm_call_unit_index(struct vm_state *vm, size_t lib_index, size_t fn_index) {
return 0;
}
static uint64_t *vm_local_ref(struct vm_state *vm, size_t index) {
static uint64_t *vm_local_ref(struct vm *vm, size_t index) {
// TODO index sanity checks
// [bp - index - 1]
++index;
return &vm->call_stack.data[vm->bp - index];
}
static uint64_t *vm_arg_ref(struct vm_state *vm, size_t index) {
static uint64_t *vm_arg_ref(struct vm *vm, size_t index) {
// [bp + index]
return &vm->call_stack.data[vm->bp + index];
}
int vm_call_ref(struct vm_state *vm, struct vm_value *ref) {
int vm_call_ref(struct vm *vm, struct vm_value *ref) {
if (ref->type != VT_FUNC) {
return -ERR_OPERAND_TYPE;
}
return vm_call_unit_index(vm, ref->v_func.lib_index, ref->v_func.fn_index);
}
static int vm_read_ext_ref(struct vm_state *vm, struct vm_ref_entry *ref, uint64_t *w) {
static int vm_read_ext_ref(struct vm *vm, struct vm_ref_entry *ref, uint64_t *w) {
struct vm_unit *unit;
int res;
unit = vector_ref(&vm->units, ref->unit_index);
@@ -173,26 +176,29 @@ static int vm_read_ext_ref(struct vm_state *vm, struct vm_ref_entry *ref, uint64
return 0;
}
static int vm_eval_debug(struct vm_state *vm, struct vm_func_entry *func, uint32_t arg) {
static int vm_eval_debug(struct vm *vm, struct vm_func_entry *func, uint32_t arg) {
(void) func;
uint64_t w0;
int res;
switch (arg) {
case 0x01:
case OP_DEBUG_TRACE:
if ((res = stack_pop(&vm->data_stack, &w0)) != 0) {
return res;
}
printf("trace: ");
vm_print(w0);
vm_print(stdout, w0);
printf("\n");
vm_unref(w0);
return 0;
case OP_DEBUG_BREAKPOINT:
vm->state = STATE_BREAKPOINT;
return 0;
default:
return -ERR_OPCODE_UNDEFINED;
}
}
int vm_eval_step(struct vm_state *vm) {
int vm_eval_step(struct vm *vm) {
uint64_t w0, w1;
int64_t sw0, sw1;
uint64_t *wr0;
@@ -202,13 +208,16 @@ int vm_eval_step(struct vm_state *vm) {
struct vm_value *v0;
int res;
if (vm->state != STATE_RUNNING) {
return -ERR_INVALID_STATE;
}
assert(vm->lp < vm->units.size);
struct vm_unit *unit = vector_ref(&vm->units, vm->lp);
assert(vm->fp < unit->functions.size);
struct vm_func_entry *func = vector_ref(&unit->functions, vm->fp);
uint32_t opcode = func->bytecode[vm->ip++];
printf("%02zx:%02zx:%02zx: %08x\n", vm->lp, vm->fp, vm->ip, opcode);
switch (opcode >> 24) {
case OP_ADD:
if ((res = vm_pop_integer(vm, &sw0)) != 0) {
@@ -368,7 +377,7 @@ int vm_eval_step(struct vm_state *vm) {
}
r0 = vector_ref(&unit->ref_table, i0);
if (r0->flags & REF_NATIVE) {
int (*func) (struct vm_state *) = (void *) r0->ref_native;
int (*func) (struct vm *) = (void *) r0->ref_native;
if (!func) {
return -ERR_OPERAND_TYPE;
}
@@ -447,8 +456,9 @@ int vm_eval_step(struct vm_state *vm) {
}
}
int vm_eval_unit(struct vm_state *vm, size_t index) {
struct vm_unit *unit;
int vm_eval_unit(struct vm *vm, size_t index) {
struct vm_unit *unit, *curr_unit;
struct vm_func_entry *curr_func;
size_t csp;
int res;
@@ -459,11 +469,37 @@ int vm_eval_unit(struct vm_state *vm, size_t index) {
// Run until return
while (vm->call_stack.sp != csp) {
if (vm->flags & VM_SINGLESTEP) {
curr_unit = vector_ref(&vm->units, vm->lp);
curr_func = vector_ref(&curr_unit->functions, vm->fp);
// Check if it's a breakpoint instruction and skip it
if (curr_func->bytecode[vm->ip] == (uint32_t) OP(OP_DEBUG, OP_DEBUG_BREAKPOINT)) {
++vm->ip;
continue;
}
if ((res = debug_breakpoint(vm)) != 0) {
return res;
}
}
if (vm->state == STATE_BREAKPOINT) {
if ((res = debug_breakpoint(vm)) != 0) {
return res;
}
vm->state = STATE_RUNNING;
} else if (vm->state == STATE_ERROR) {
res = -ERR_ERROR_EXIT;
break;
} else if (vm->state == STATE_EXITED) {
break;
}
if ((res = vm_eval_step(vm)) != 0) {
return res;
}
}
unit->is_loaded = 1;
return 0;
return res;
}
+16 -16
View File
@@ -3,51 +3,51 @@
#include <stdlib.h>
#include <stdio.h>
static void vm_print2(uint64_t w, int cdepth);
static void vm_print2(FILE *fp, uint64_t w, int cdepth);
static void vm_print_ref(struct vm_value *value, int cdepth) {
static void vm_print_ref(FILE *fp, struct vm_value *value, int cdepth) {
uint64_t w0;
if (!value) {
printf("nil");
fprintf(fp, "nil");
return;
}
switch (value->type) {
case VT_STRING:
printf("\"%s\"", vm_cstr(&value->v_string));
fprintf(fp, "\"%s\"", vm_cstr(&value->v_string));
break;
case VT_CONS:
if (!cdepth) {
printf("(");
}
vm_print2(value->v_cons.fat_ar, 0);
vm_print2(fp, value->v_cons.fat_ar, 0);
w0 = value->v_cons.fat_dr;
if (!null_q(w0)) {
printf(" ");
fprintf(fp, " ");
if (cons_q(w0)) {
vm_print2(w0, cdepth + 1);
vm_print2(fp, w0, cdepth + 1);
} else {
printf(". ");
vm_print2(w0, cdepth + 1);
fprintf(fp, ". ");
vm_print2(fp, w0, cdepth + 1);
}
}
if (!cdepth) {
printf(")");
fprintf(fp, ")");
}
break;
case VT_FUNC:
printf("<function %zu:%zu>", value->v_func.lib_index, value->v_func.fn_index);
fprintf(fp, "<function %zu:%zu>", value->v_func.lib_index, value->v_func.fn_index);
break;
}
}
static void vm_print2(uint64_t w, int cdepth) {
static void vm_print2(FILE *fp, uint64_t w, int cdepth) {
if (w & FLAG_REF) {
vm_print_ref((void *) (w << 1), cdepth);
vm_print_ref(fp, (void *) (w << 1), cdepth);
} else {
if (w & (1ULL << 62)) {
w |= 1ULL << 63;
}
printf("%ld", (int64_t) w);
fprintf(fp, "%ld", (int64_t) w);
}
}
@@ -131,6 +131,6 @@ struct vm_value *vm_func(size_t lib_index, size_t fn_index) {
return v;
}
void vm_print(uint64_t w) {
vm_print2(w, 0);
void vm_print(FILE *fp, uint64_t w) {
vm_print2(fp, w, 0);
}