starting to work on debug server
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Sun, 31 Jul 2022 20:27:56 +0000 (15:27 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Sun, 31 Jul 2022 20:27:56 +0000 (15:27 -0500)
include/ovm_debug.h
include/vm.h
src/debug/debug_host.c [new file with mode: 0644]
src/debug/debug_info.c
src/debug/debug_info_builder.c
src/vm/vm.c
src/wasm/engine.c
src/wasm/module.c
src/wasm/module_parsing.h

index c76763ec8b6eb3d32a2bc29550c909ca958ece2c..b58aed29e9a70b020f6a0efb7bbc37593a4c38d3 100644 (file)
@@ -2,6 +2,8 @@
 #define _OVM_DEBUG_H
 
 #include "bh.h"
+#include <semaphore.h>
+#include <stdbool.h>
 
 typedef struct debug_loc_info_t {
     u32 file_id;
@@ -20,14 +22,29 @@ typedef struct debug_func_info_t {
     u32 debug_op_offset;
 } debug_func_info_t;
 
+typedef struct debug_file_info_t {
+    char *name;
+    u32 line_count;
+    i32 line_buffer_offset;
+} debug_file_info_t;
+
 typedef struct debug_info_t {
     bh_allocator alloc;
 
+    // func index -> func info
     bh_arr(debug_func_info_t) funcs;
+
+    // reducer output -> line info
     bh_arr(debug_loc_info_t) line_info;
+
+    // instruction index -> reducer output
     bh_arr(u32) instruction_reducer;
 
-    bh_arr(char *) file_names;
+    // line index -> instruction index
+    bh_arr(u32) line_to_instruction;
+
+    // file_id -> file info
+    bh_arr(debug_file_info_t) files;
 } debug_info_t;
 
 void debug_info_init(debug_info_t *);
@@ -35,6 +52,10 @@ void debug_info_free(debug_info_t *);
 void debug_info_import_file_info(debug_info_t *, u8 *data, u32 len);
 void debug_info_import_func_info(debug_info_t *, u8 *data, u32 len);
 
+bool debug_info_lookup_location(debug_info_t *info, u32 instruction, debug_loc_info_t *out);
+bool debug_info_lookup_file(debug_info_t *info, u32 file_id, debug_file_info_t *out);
+bool debug_info_lookup_file_by_name(debug_info_t *info, char *name, debug_file_info_t *out);
+
 //
 // This builder is used in conjunction with code builder to output
 // debug information for each instruction that is generated in OVM.
@@ -47,11 +68,12 @@ typedef struct debug_info_builder_t {
 
     u32 current_file_id;
     u32 current_line;
+    u32 next_file_line_offset;
 
     bh_arr(char) symbol_scope_stack;
 
     u32 remaining_reps;
-    
+
     b32 locked : 1;
 } debug_info_builder_t;
 
@@ -62,4 +84,49 @@ void debug_info_builder_step(debug_info_builder_t *);
 void debug_info_builder_begin_func(debug_info_builder_t *, i32 func_idx);
 void debug_info_builder_end_func(debug_info_builder_t *);
 
-#endif
\ No newline at end of file
+
+typedef enum debug_exec_state_t {
+    debug_state_starting,
+    debug_state_ready,
+    debug_state_running,
+    debug_state_paused
+} debug_exec_state_t;
+
+typedef struct debug_thread_state_t {
+    u32 id;
+
+    debug_exec_state_t state;
+    struct ovm_state_t *ovm_state;
+
+    i32 run_count;
+    sem_t wait_semaphore;
+
+    bh_arr(u32) breakpoints;
+} debug_thread_state_t;
+
+//
+// This represents known state of the debugger. There should only
+// be one state per running program, as it is tied to the ovm_engine_t.
+//
+typedef struct debug_state_t {
+    bh_allocator alloc;
+
+    debug_info_t *info;
+
+    bh_arr(debug_thread_state_t *) threads;
+    u32 next_thread_id;
+
+    pthread_t debug_thread;
+    bool debug_thread_running;
+
+    u32 listen_socket_fd;
+    u32 client_fd;
+} debug_state_t;
+
+void debug_host_init(debug_state_t *debug);
+void debug_host_start(debug_state_t *debug);
+void debug_host_stop(debug_state_t *debug);
+u32  debug_host_register_thread(debug_state_t *debug, struct ovm_state_t *ovm_state);
+debug_thread_state_t *debug_host_lookup_thread(debug_state_t *debug, u32 id);
+
+#endif
index e7378680f76e9989bf3ad801ad313b6f60cbe20f..44868f87dc7cbaef6a827e12199afb03f1d6200b 100644 (file)
@@ -2,6 +2,7 @@
 #define _ONYX_VM_H
 
 #include "bh.h"
+#include "ovm_debug.h"
 #include <stdbool.h>
 #include <pthread.h>
 
@@ -88,6 +89,8 @@ struct ovm_engine_t {
 
     i64   memory_size; // This is probably going to always be 4GiB.
     void *memory;
+
+    debug_state_t *debug;
 };
 
 ovm_engine_t *ovm_engine_new(ovm_store_t *store);
@@ -118,6 +121,8 @@ struct ovm_state_t {
     // running instances of the program *could* have different
     // native functions linked.
     bh_arr(ovm_external_func_t) external_funcs;
+
+    debug_thread_state_t *debug;
 };
 
 ovm_state_t *ovm_state_new(ovm_engine_t *engine, ovm_program_t *program);
diff --git a/src/debug/debug_host.c b/src/debug/debug_host.c
new file mode 100644 (file)
index 0000000..f6f0522
--- /dev/null
@@ -0,0 +1,97 @@
+
+#include "ovm_debug.h"
+#include "vm.h"
+
+void debug_host_init(debug_state_t *debug) {
+    memset(debug, 0, sizeof(*debug));
+    debug->alloc = bh_heap_allocator();
+    
+    debug->info = NULL;
+
+    debug->threads = NULL;
+    debug->next_thread_id = 0;
+    bh_arr_new(debug->alloc, debug->threads, 4);
+
+    debug->listen_socket_fd = 0;
+    debug->client_fd = 0;
+}
+
+static void *debug_thread_entry(void *);
+
+void debug_host_start(debug_state_t *debug) {
+    if (debug->debug_thread_running) return;
+
+    pthread_create(&debug->debug_thread, NULL, debug_thread_entry, debug);
+}
+
+void debug_host_stop(debug_state_t *debug) {
+    debug->debug_thread_running = false;
+    pthread_join(debug->debug_thread, NULL);
+}
+
+u32 debug_host_register_thread(debug_state_t *debug, ovm_state_t *ovm_state) {
+    debug_thread_state_t *new_thread = bh_alloc(debug->alloc, sizeof(*new_thread));
+
+    new_thread->state = debug_state_starting;
+    new_thread->ovm_state = ovm_state;
+    new_thread->run_count = 0;                    // Start threads in stopped state.
+    sem_init(&new_thread->wait_semaphore, 0, 0);
+
+    new_thread->breakpoints = NULL;
+    bh_arr_new(debug->alloc, new_thread->breakpoints, 8);
+
+    u32 id = debug->next_thread_id++;
+    new_thread->id = id;
+
+    bh_arr_push(debug->threads, new_thread);
+    return id;
+}
+
+debug_thread_state_t *debug_host_lookup_thread(debug_state_t *debug, u32 id) {
+    bh_arr_each(debug_thread_state_t *, pthread, debug->threads) {
+        if ((*pthread)->id == id) return *pthread;
+    }
+    return NULL;
+}
+
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+static void *debug_thread_entry(void *data) {
+    debug_state_t *debug = data;
+    debug->debug_thread_running = true;
+    
+    // Set up socket listener
+    // Wait for initial connection/handshake before entering loop?
+    
+    debug->listen_socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+
+    struct sockaddr_un local_addr, remote_addr;
+    local_addr.sun_family = AF_UNIX;
+    strcpy(local_addr.sun_path, "/tmp/ovm-debug.0000"); // TODO: Make this dynamic so mulitple servers can exist at a time.
+    unlink(local_addr.sun_path);                        // TODO: Remove this line for the same reason.
+    int len = strlen(local_addr.sun_path) + sizeof(local_addr.sun_family);
+    bind(debug->listen_socket_fd, (struct sockaddr *)&local_addr, len);
+    
+    //
+    // Currently, there can only be 1 connected debugger instance at a time.
+    listen(debug->listen_socket_fd, 1);
+
+    len = sizeof(struct sockaddr_un);
+    debug->client_fd = accept(debug->listen_socket_fd, (void * restrict)&remote_addr, &len);
+
+    close(debug->listen_socket_fd);
+
+    printf("Client connected");
+
+    while (debug->debug_thread_running) {
+        // ...
+        
+    }
+
+    close(debug->client_fd);
+
+    return NULL;
+}
index 3a41586fa26cc06b520db51c70f466c71559db2f..0341563a94737275ff532c2fe4fe160018ee01f4 100644 (file)
@@ -8,7 +8,8 @@ void debug_info_init(debug_info_t *info) {
     bh_arr_new(info->alloc, info->funcs, 16);
     bh_arr_new(info->alloc, info->line_info, 1024);
     bh_arr_new(info->alloc, info->instruction_reducer, 4096);
-    bh_arr_new(info->alloc, info->file_names, 16);
+    bh_arr_new(info->alloc, info->files, 16);
+    bh_arr_new(info->alloc, info->line_to_instruction, 1024);
 }
 
 void debug_info_free(debug_info_t *info) {
@@ -16,8 +17,10 @@ void debug_info_free(debug_info_t *info) {
     bh_arr_free(info->line_info);
     bh_arr_free(info->instruction_reducer);
 
-    bh_arr_each(char *, name, info->file_names) bh_free(info->alloc, *name);
-    bh_arr_free(info->file_names);
+    bh_arr_each(debug_file_info_t, file, info->files) {
+        bh_free(info->alloc, file->name);
+    }
+    bh_arr_free(info->files);
 }
 
 void debug_info_import_file_info(debug_info_t *info, u8 *data, u32 len) {
@@ -25,14 +28,19 @@ void debug_info_import_file_info(debug_info_t *info, u8 *data, u32 len) {
 
     i32 count = uleb128_to_uint(data, &offset);
     fori (i, 0, (i32) count) {
+        debug_file_info_t file_info;
+        file_info.line_buffer_offset = -1;
+
         u32 file_id = uleb128_to_uint(data, &offset);
+        file_info.line_count = uleb128_to_uint(data, &offset);
+
         u32 name_length = uleb128_to_uint(data, &offset);
-        char *name = bh_alloc_array(info->alloc, char, name_length + 1);
-        memcpy(name, data + offset, name_length);
-        name[name_length] = 0;
+        file_info.name = bh_alloc_array(info->alloc, char, name_length + 1);
+        memcpy(file_info.name, data + offset, name_length);
+        file_info.name[name_length] = 0;
         offset += name_length;
 
-        bh_arr_set_at(info->file_names, file_id, name);
+        bh_arr_set_at(info->files, file_id, file_info);
     }
 
     assert(offset == len);
@@ -69,3 +77,29 @@ void debug_info_import_func_info(debug_info_t *info, u8 *data, u32 len) {
 
     assert(offset == len);
 }
+
+bool debug_info_lookup_location(debug_info_t *info, u32 instruction, debug_loc_info_t *out) {
+    if (instruction > (u32) bh_arr_length(info->instruction_reducer)) return false;
+    *out = info->line_info[info->instruction_reducer[instruction]];
+    return true;
+}
+
+bool debug_info_lookup_file(debug_info_t *info, u32 file_id, debug_file_info_t *out) {
+    if (file_id > (u32) bh_arr_length(info->files)) return false;
+    *out = info->files[file_id];
+    return true;
+}
+
+//
+// For now, this is going to compare the strings exactly. In the future, it might be a good
+// to do a levenschtein distance or something, so the full path isn't needed.
+bool debug_info_lookup_file_by_name(debug_info_t *info, char *name, debug_file_info_t *out) {
+    bh_arr_each(debug_file_info_t, file, info->files) {
+        if (!strcmp(file->name, name)) {
+            *out = *file;
+            return true;
+        }
+    }
+
+    return false;
+}
index 343c45207f298b6aae131199caf3ce26bf9d812c..d920af82b181f3cecaeb92bbf4d9ad46762e4d15 100644 (file)
@@ -15,19 +15,20 @@ void debug_info_builder_prepare(debug_info_builder_t *builder, u8 *data) {
     builder->reader_offset = 0;
     builder->current_file_id = 0;
     builder->current_line = 0;
+    builder->next_file_line_offset = 0;
     builder->remaining_reps = 0;
 }
 
 static void debug_info_builder_parse(debug_info_builder_t *builder) {
     u32 count = 0;
 
-    while (1) {
+    while (!builder->locked) {
         u8 instr = builder->data[builder->reader_offset++];
         switch (instr & 0b11000000) {
             case 0b00000000:
                 instr &= 0b00111111;
                 switch (instr) {
-                    case 0: builder->locked = 1; break;
+                    case 0: builder->locked = 1; break; // Early return!
                     case 1:
                         builder->current_file_id = uleb128_to_uint(builder->data, &builder->reader_offset);
                         builder->current_line    = uleb128_to_uint(builder->data, &builder->reader_offset);
@@ -61,7 +62,9 @@ static void debug_info_builder_parse(debug_info_builder_t *builder) {
 }
 
 void debug_info_builder_step(debug_info_builder_t *builder) {
-    while (builder->remaining_reps == 0) {
+    if (builder->data == NULL) return;
+
+    while (builder->remaining_reps == 0 && !builder->locked) {
         debug_info_builder_parse(builder);
 
         debug_loc_info_t info; 
@@ -69,10 +72,21 @@ void debug_info_builder_step(debug_info_builder_t *builder) {
         info.line    = builder->current_line;
         info.symbols = 0;
         bh_arr_push(builder->info->line_info, info);
+
+        debug_file_info_t *file_info = &builder->info->files[info.file_id];
+        if (file_info->line_buffer_offset == -1) {
+            file_info->line_buffer_offset = builder->next_file_line_offset;
+            builder->next_file_line_offset += file_info->line_count;
+        }
+
+        u32 line_index = file_info->line_buffer_offset + builder->current_line;
+        u32 target     = bh_arr_length(builder->info->instruction_reducer) - 1;
+        bh_arr_set_at(builder->info->line_to_instruction, line_index, target);
     }
 
     if (builder->locked) return;
 
+    assert(builder->remaining_reps);
     builder->remaining_reps -= 1;
     return;
 }
@@ -82,13 +96,19 @@ void debug_info_builder_emit_location(debug_info_builder_t *builder) {
 }
 
 void debug_info_builder_begin_func(debug_info_builder_t *builder, i32 func_idx) {
-    assert(func_idx < bh_arr_length(builder->info->funcs));
+    if (func_idx >= bh_arr_length(builder->info->funcs)) return;
+
     debug_func_info_t *func_info = &builder->info->funcs[func_idx];
 
     builder->reader_offset = func_info->debug_op_offset;
-    assert(builder->reader_offset < 20000);
+    assert(builder->data[builder->reader_offset] == 2);
+    assert(builder->data[builder->reader_offset+1] == 1);
+    builder->remaining_reps = 0;
     builder->locked = 0;
 }
 
 void debug_info_builder_end_func(debug_info_builder_t *builder) {
+    assert(!builder->locked);
+    debug_info_builder_step(builder);
+    assert(builder->locked);
 }
index 155d2e401a5677af041b5b2ef61e240bd40932a5..70adeee05e03f9d62f4e16bc45660540ac6a791a 100644 (file)
@@ -84,7 +84,7 @@ int ovm_program_register_static_ints(ovm_program_t *program, int len, int *data)
 
 void ovm_program_modify_static_int(ovm_program_t *program, int arr, int idx, int new_value) {
     if (arr >= bh_arr_length(program->static_data)) return;
-    
+
     ovm_static_integer_array_t array = program->static_data[arr];
     if (idx >= array.len) return;
 
@@ -445,6 +445,8 @@ ovm_engine_t *ovm_engine_new(ovm_store_t *store) {
     engine->memory = mmap(NULL, engine->memory_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
     pthread_mutex_init(&engine->atomic_mutex, NULL);
 
+    engine->debug = NULL;
+
     return engine;
 }
 
@@ -491,9 +493,14 @@ ovm_state_t *ovm_state_new(ovm_engine_t *engine, ovm_program_t *program) {
     state->external_funcs = NULL;
     bh_arr_new(store->heap_allocator, state->external_funcs, 8);
 
+    if (engine->debug) {
+        u32 thread_id = debug_host_register_thread(engine->debug, state);
+        state->debug = debug_host_lookup_thread(engine->debug, thread_id);
+    }
+
 #ifdef OVM_VERBOSE
     ovm_program_print_instructions(program, 0, bh_arr_length(program->code));
-#endif 
+#endif
 
     return state;
 }
@@ -653,8 +660,21 @@ ovm_value_t ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t
     bool release_mutex_at_end = false;
 
     ovm_value_t tmp_val;
-   
+
     while (state->pc < bh_arr_length(program->code)) {
+        // This will become the line change detection
+        // debug_loc_info_t loc_info;
+        // debug_info_lookup_location(engine->debug->info, state->pc, &loc_info);
+        // if (loc_info.file_id != last_file || loc_info.line != last_line) {
+        //     last_file = loc_info.file_id;
+        //     last_line = loc_info.line;
+
+        //     debug_file_info_t file_info;
+        //     debug_info_lookup_file(engine->debug->info, last_file, &file_info);
+        //     
+        //     printf("(%d, %d) %s:%d\n", last_file, last_line, file_info.name, last_line);
+        // }
+        
 #ifdef OVM_VERBOSE
         ovm_program_print_instructions(program, state->pc, 1);
 #endif
@@ -920,7 +940,7 @@ ovm_value_t ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t
                 VAL(instr.r).u64 = 0; \
                 VAL(instr.r).dtype = instr.stype; \
                 break;
-            
+
             OVM_IMM(OVM_TYPE_I8,  i8,  i)
             OVM_IMM(OVM_TYPE_I16, i16, i)
             OVM_IMM(OVM_TYPE_I32, i32, i)
@@ -1018,7 +1038,7 @@ ovm_value_t ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t
 
             case OVMI_RETURN: {
                 ovm_value_t val = VAL(instr.a);
-                ovm_stack_frame_t frame = ovm__func_teardown_stack_frame(engine, state, program); 
+                ovm_stack_frame_t frame = ovm__func_teardown_stack_frame(engine, state, program);
                 state->pc = frame.return_address;
 
                 if (bh_arr_length(state->stack_frames) == 0) {
@@ -1035,11 +1055,11 @@ ovm_value_t ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t
                 }
 
 #ifdef OVM_VERBOSE
-                printf("Returning from %s to %s: ", frame.func->name, bh_arr_last(state->stack_frames).func->name); 
+                printf("Returning from %s to %s: ", frame.func->name, bh_arr_last(state->stack_frames).func->name);
                 ovm_print_val(val);
                 printf("\n\n");
 #endif
-                
+
                 break;
             }
 
@@ -1130,7 +1150,7 @@ ovm_value_t ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t
             case OVM_TYPED_INSTR(OVMI_CVT_I64_S, OVM_TYPE_I8):  CVT(i64, i8,  OVM_TYPE_I8,  i8);
             case OVM_TYPED_INSTR(OVMI_CVT_I64_S, OVM_TYPE_I16): CVT(i64, i16, OVM_TYPE_I16, i16);
             case OVM_TYPED_INSTR(OVMI_CVT_I64_S, OVM_TYPE_I32): CVT(i64, i32, OVM_TYPE_I32, i32);
-            
+
             case OVM_TYPED_INSTR(OVMI_CVT_I64,   OVM_TYPE_F32): CVT(u64, f32, OVM_TYPE_F32, f32);
             case OVM_TYPED_INSTR(OVMI_CVT_I64_S, OVM_TYPE_F32): CVT(i64, f32, OVM_TYPE_F32, f32);
             case OVM_TYPED_INSTR(OVMI_CVT_I64,   OVM_TYPE_F64): CVT(u64, f64, OVM_TYPE_F64, f64);
@@ -1203,4 +1223,3 @@ ovm_value_t ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t
 
     return ((ovm_value_t) {0});
 }
-
index 8f2162d554f516e99640afccc887e2ccb430d736..55df04199607d1a867337df4845b9ad095ef084c 100644 (file)
@@ -3,24 +3,37 @@
 #include "vm.h"
 
 wasm_engine_t *wasm_engine_new() {
+    wasm_engine_t *engine = wasm_engine_new_with_config(NULL);
+    return engine;
+}
+
+wasm_engine_t *wasm_engine_new_with_config(wasm_config_t *config) {
     ovm_store_t *store = ovm_store_new();
 
     wasm_engine_t *engine = bh_alloc_item(store->heap_allocator, wasm_engine_t);
-    engine->config = NULL;
+    engine->config = config;
     engine->store = store;
     
     ovm_engine_t *ovm_engine = ovm_engine_new(store);
     engine->engine = ovm_engine;
-    return engine;
-}
 
-wasm_engine_t *wasm_engine_new_with_config(wasm_config_t *config) {
-    wasm_engine_t *engine = wasm_engine_new(); 
-    engine->config = config;
+    if (config && config->debug_enabled) {
+        // This should maybe be moved elsewhere?
+        debug_state_t *debug = bh_alloc_item(store->heap_allocator, debug_state_t);
+        engine->engine->debug = debug;
+
+        debug_host_init(engine->engine->debug);
+        debug_host_start(engine->engine->debug);
+    }
+
     return engine;
 }
 
 void wasm_engine_delete(wasm_engine_t *engine) {
+    if (engine->engine->debug) {
+        debug_host_stop(engine->engine->debug);
+    }
+
     ovm_store_t *store = engine->store;
     ovm_engine_delete(engine->engine);
     bh_free(store->heap_allocator, engine);
index f561afb74b226efb5f9b5c0ed529584a447471ac..8e469a674b5e9668f835ec1915285b3c65095ea5 100644 (file)
@@ -71,6 +71,11 @@ wasm_module_t *wasm_module_new(wasm_store_t *store, const wasm_byte_vec_t *binar
 
     debug_info_init(&module->debug_info);
 
+    if (store->engine->engine->debug) {
+        assert(store->engine->engine->debug->info == NULL);
+        store->engine->engine->debug->info = &module->debug_info;
+    }
+
     bool success = module_build(module, binary); 
     return module;
 }
index 1776d2ccf5466998e76b06dbabbbfe32b2e4892e..8d1e2efcc5daf32be08e2addc5ea4e852d3ab393 100644 (file)
@@ -20,10 +20,11 @@ struct build_context {
 
     int func_table_arr_idx;
     int next_external_func_idx;
+    
+    debug_info_builder_t debug_builder;
 
     // This will be set/reset for every code (function) entry.
     ovm_code_builder_t builder;
-    debug_info_builder_t debug_builder;
 };
 
 #define PEEK_BYTE(ctx)    ((ctx)->binary.data[(ctx)->offset])
@@ -504,6 +505,8 @@ static void parse_fe_instruction(build_context *ctx) {
 }
 
 static void parse_instruction(build_context *ctx) {
+    debug_info_builder_step(&ctx->debug_builder);
+
     unsigned char instr_byte = CONSUME_BYTE(ctx);
     switch (instr_byte) {
         case 0x00: break;
@@ -891,8 +894,6 @@ static void parse_instruction(build_context *ctx) {
 
         default: assert(("UNHANDLED INSTRUCTION", 0));
     }
-
-    debug_info_builder_step(&ctx->debug_builder);
 }
 
 static void parse_expression(build_context *ctx) {