added symbol info and message packing
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 12 Aug 2022 01:31:19 +0000 (20:31 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 12 Aug 2022 01:31:19 +0000 (20:31 -0500)
include/bh.h
include/ovm_debug.h
src/debug/debug_host.c
src/debug/debug_info.c
src/debug/debug_info_builder.c
src/debug/debug_thread.c
src/vm/vm.c
src/wasm/module_parsing.h

index 3cf52f981152242a6041754370ead682d5b27223..ef09cf6f4bf950419c59759c3220c16dc97a3452 100644 (file)
@@ -541,6 +541,7 @@ typedef struct bh_buffer {
 
 BH_DEF void bh_buffer_init(bh_buffer* buffer, bh_allocator alloc, i32 length);
 BH_DEF void bh_buffer_free(bh_buffer* buffer);
+BH_DEF void bh_buffer_clear(bh_buffer *buffer);
 BH_DEF void bh_buffer_grow(bh_buffer* buffer, i32 length);
 BH_DEF void bh_buffer_append(bh_buffer* buffer, const void * data, i32 length);
 BH_DEF void bh_buffer_concat(bh_buffer* buffer, bh_buffer other);
@@ -2306,6 +2307,10 @@ BH_DEF void bh_buffer_free(bh_buffer* buffer) {
     buffer->capacity = 0;
 }
 
+BH_DEF void bh_buffer_clear(bh_buffer *buffer) {
+    buffer->length = 0;
+}
+
 BH_DEF void bh_buffer_grow(bh_buffer* buffer, i32 length) {
     if (buffer == NULL) return;
 
index 2037b3b2e7eeaa44532436abf8eed3375d80f09b..68ca5971e7a02147388e7d405c586e99ef1aa3d4 100644 (file)
@@ -8,7 +8,7 @@
 typedef struct debug_loc_info_t {
     u32 file_id;
     u32 line;
-    u32 symbols;
+    u32 symbol_scope;
 } debug_loc_info_t;
 
 typedef struct debug_func_info_t {
@@ -29,6 +29,25 @@ typedef struct debug_file_info_t {
     i32 line_buffer_offset;
 } debug_file_info_t;
 
+typedef enum debug_sym_loc_kind_t {
+    debug_sym_loc_register = 1,
+    debug_sym_loc_stack    = 2,
+    debug_sym_loc_global   = 3
+} debug_sym_loc_kind_t;
+
+typedef struct debug_sym_info_t {
+    char *name;
+    u32 sym_id;
+    debug_sym_loc_kind_t loc_kind;
+    u32 loc;
+    u32 type;
+} debug_sym_info_t;
+
+typedef struct debug_sym_scope_t {
+    bh_arr(u32) symbols;
+    i32 parent; // -1 for root
+} debug_sym_scope_t;
+
 typedef struct debug_info_t {
     bh_allocator alloc;
 
@@ -48,12 +67,19 @@ typedef struct debug_info_t {
 
     // file_id -> file info
     bh_arr(debug_file_info_t) files;
+
+    // sym_id -> sym info
+    bh_arr(debug_sym_info_t) symbols;
+
+    // scope id  -> symbol scope
+    bh_arr(debug_sym_scope_t) symbol_scopes;
 } debug_info_t;
 
 void debug_info_init(debug_info_t *);
 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);
+void debug_info_import_sym_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);
@@ -74,7 +100,7 @@ typedef struct debug_info_builder_t {
     u32 current_line;
     u32 next_file_line_offset;
 
-    bh_arr(char) symbol_scope_stack;
+    i32 current_scope;
 
     u32 remaining_reps;
 
@@ -127,6 +153,8 @@ typedef struct debug_thread_state_t {
 
     bh_arr(debug_breakpoint_t) breakpoints;
     u32 last_breakpoint_hit;
+
+    u32 state_change_write_fd;
 } debug_thread_state_t;
 
 //
@@ -151,6 +179,10 @@ typedef struct debug_state_t {
 
     u32 listen_socket_fd;
     u32 client_fd;
+
+    bh_buffer send_buffer;
+    
+    u32 state_change_pipes[2];
 } debug_state_t;
 
 void debug_host_init(debug_state_t *debug);
index fa1dcf5a52bbc81cde77d2ff91e01acda8b130af..b306afd73d6cbc59674fce2ffd434ce04a986acc 100644 (file)
@@ -17,6 +17,10 @@ void debug_host_init(debug_state_t *debug) {
 
     debug->listen_socket_fd = 0;
     debug->client_fd = 0;
+
+    if (pipe(debug->state_change_pipes) != 0) {
+        printf("[ERROR] Failed to create thread notification pipes.\n");
+    }
 }
 
 void debug_host_start(debug_state_t *debug) {
@@ -45,6 +49,8 @@ u32 debug_host_register_thread(debug_state_t *debug, ovm_state_t *ovm_state) {
     u32 id = debug->next_thread_id++;
     new_thread->id = id;
 
+    new_thread->state_change_write_fd = debug->state_change_pipes[1];
+
     bh_arr_push(debug->threads, new_thread);
     return id;
 }
index d50b2343e879c896e3f3d291c48a9e986ddef73f..7db20908683742337f70617cc50d113234d8adf7 100644 (file)
@@ -11,6 +11,8 @@ void debug_info_init(debug_info_t *info) {
     bh_arr_new(info->alloc, info->instruction_reducer, 4096);
     bh_arr_new(info->alloc, info->files, 16);
     bh_arr_new(info->alloc, info->line_to_instruction, 1024);
+    bh_arr_new(info->alloc, info->symbols, 128);
+    bh_arr_new(info->alloc, info->symbol_scopes, 128);
 }
 
 void debug_info_free(debug_info_t *info) {
@@ -82,6 +84,35 @@ void debug_info_import_func_info(debug_info_t *info, u8 *data, u32 len) {
     assert(offset == len);
 }
 
+void debug_info_import_sym_info(debug_info_t *info, u8 *data, u32 len) {
+    u32 offset = 0;
+    info->has_debug_info = true;
+
+    i32 count = uleb128_to_uint(data, &offset);
+    fori (i, 0, count) {
+        debug_sym_info_t sym_info;
+        sym_info.sym_id = uleb128_to_uint(data, &offset);
+
+        u32 name_length = uleb128_to_uint(data, &offset);
+        if (name_length == 0) {
+            sym_info.name = NULL;
+        } else {
+            sym_info.name = bh_alloc_array(info->alloc, char, name_length + 1);
+            memcpy(sym_info.name, data + offset, name_length);
+            sym_info.name[name_length] = 0;
+            offset += name_length;
+        }
+        
+        sym_info.loc_kind = uleb128_to_uint(data, &offset);
+        sym_info.loc = uleb128_to_uint(data, &offset);
+        sym_info.type = uleb128_to_uint(data, &offset);
+
+        bh_arr_set_at(info->symbols, sym_info.sym_id, sym_info);
+    }
+
+    assert(offset == len);
+}
+
 bool debug_info_lookup_location(debug_info_t *info, u32 instruction, debug_loc_info_t *out) {
     if (!info || !info->has_debug_info) return false;
 
index 1b84defd21cb42fb2e6c0e319bdd55151d9e4b84..13c33dfa235218c8a6f536f471787231d75a6e6e 100644 (file)
@@ -7,7 +7,6 @@ void debug_info_builder_init(debug_info_builder_t *builder, debug_info_t *info)
     memset(builder, 0, sizeof(*builder));
 
     builder->info = info;
-    bh_arr_new(bh_heap_allocator(), builder->symbol_scope_stack, 8);
 }
 
 void debug_info_builder_prepare(debug_info_builder_t *builder, u8 *data) {
@@ -15,10 +14,39 @@ 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->current_scope = -1;
     builder->next_file_line_offset = 0;
     builder->remaining_reps = 0;
 }
 
+static i32 debug_info_builder_push_scope(debug_info_builder_t *builder) {
+    debug_sym_scope_t scope;
+    scope.symbols = NULL;
+    bh_arr_new(builder->info->alloc, scope.symbols, 4);    
+    scope.parent = builder->current_scope;
+
+    i32 scope_id = bh_arr_length(builder->info->symbol_scopes);
+    builder->current_scope = scope_id;
+
+    bh_arr_push(builder->info->symbol_scopes, scope);
+    return scope_id;
+}
+
+static i32 debug_info_builder_pop_scope(debug_info_builder_t *builder) {
+    if (builder->current_scope == -1) return -1;
+
+    debug_sym_scope_t *scope = &builder->info->symbol_scopes[builder->current_scope];
+    builder->current_scope = scope->parent;
+    return builder->current_scope;
+}
+
+static void debug_info_builder_add_symbol(debug_info_builder_t *builder, u32 sym_id) {
+    if (builder->current_scope == -1) return;
+
+    debug_sym_scope_t *scope = &builder->info->symbol_scopes[builder->current_scope];
+    bh_arr_push(scope->symbols, sym_id);
+}
+
 static void debug_info_builder_parse(debug_info_builder_t *builder) {
     u32 count = 0;
 
@@ -33,10 +61,19 @@ static void debug_info_builder_parse(debug_info_builder_t *builder) {
                         builder->current_file_id = uleb128_to_uint(builder->data, &builder->reader_offset);
                         builder->current_line    = uleb128_to_uint(builder->data, &builder->reader_offset);
                         break;
-                    case 2: break;
-                    case 3: break;
+
+                    case 2:
+                        debug_info_builder_push_scope(builder);
+                        break;
+                        
+                    case 3:
+                        debug_info_builder_pop_scope(builder);
+                        break;
+
                     case 4:
-                        uleb128_to_uint(builder->data, &builder->reader_offset);
+                        debug_info_builder_add_symbol(
+                            builder,
+                            uleb128_to_uint(builder->data, &builder->reader_offset));
                         break;
                 }
                 break;
@@ -68,9 +105,9 @@ void debug_info_builder_step(debug_info_builder_t *builder) {
         debug_info_builder_parse(builder);
 
         debug_loc_info_t info; 
-        info.file_id = builder->current_file_id;
-        info.line    = builder->current_line;
-        info.symbols = 0;
+        info.file_id      = builder->current_file_id;
+        info.line         = builder->current_line;
+        info.symbol_scope = builder->current_scope;
         bh_arr_push(builder->info->line_info, info);
 
         debug_file_info_t *file_info = &builder->info->files[info.file_id];
index bffe9a91d0260db9257d74edbe339a86b14d7ce7..6abc5443b1a480e5d4a7ed27c2677f33df0f6d86 100644 (file)
@@ -6,6 +6,7 @@
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <assert.h>
+#include <poll.h>
 
 
 #define CMD_NOP 0
 #define CMD_CLR_BRK 3
 #define CMD_STEP 5
 #define CMD_TRACE 6
+#define CMD_THREADS 7
+#define CMD_VARS 8
 
 #define EVT_NOP 0
 #define EVT_BRK_HIT 1
 #define EVT_PAUSE 2
+#define EVT_NEW_THREAD 3 // Not Implemented
 #define EVT_RESPONSE 0xffffffff
 
 struct msg_parse_ctx_t {
@@ -28,34 +32,32 @@ struct msg_parse_ctx_t {
 
 static void send_response_header(debug_state_t *debug, unsigned int message_number) {
     unsigned int RESPONSE_HEADER = EVT_RESPONSE;
-    send(debug->client_fd, &RESPONSE_HEADER, 4, 0);
-    send(debug->client_fd, &message_number, 4, 0);
+    bh_buffer_write_u32(&debug->send_buffer, EVT_RESPONSE);
+    bh_buffer_write_u32(&debug->send_buffer, message_number);
 }
 
 static void send_string(debug_state_t *debug, const char *str) {
-    unsigned int len;
     if (!str) {
-        len = 0;
-        send(debug->client_fd, &len, 4, 0);
+        bh_buffer_write_u32(&debug->send_buffer, 0);
     } else {
-        len = strlen(str);
-        send(debug->client_fd, &len, 4, 0);
-        send(debug->client_fd, str, len, 0);
+        unsigned int len = strlen(str);
+        bh_buffer_write_u32(&debug->send_buffer, len);
+        bh_buffer_append(&debug->send_buffer, str, len);
     }
 }
 
 static void send_bytes(debug_state_t *debug, const char *bytes, unsigned int len) {
-    send(debug->client_fd, &len, 4, 0);
-    send(debug->client_fd, bytes, len, 0);
+    bh_buffer_write_u32(&debug->send_buffer, len);
+    bh_buffer_append(&debug->send_buffer, bytes, len);
 }
 
 static void send_int(debug_state_t *debug, unsigned int x) {
-    send(debug->client_fd, &x, 4, 0);
+    bh_buffer_write_u32(&debug->send_buffer, x);
 }
 
 static void send_bool(debug_state_t *debug, bool x) {
     bool v = x ? 1 : 0;
-    send(debug->client_fd, &v, 1, 0);
+    bh_buffer_write_byte(&debug->send_buffer, v);
 }
 
 static int parse_int(debug_state_t *debug, struct msg_parse_ctx_t *ctx) {
@@ -90,6 +92,27 @@ static void resume_thread(debug_thread_state_t *thread) {
     sem_post(&thread->wait_semaphore);
 }
 
+static void get_stack_frame_location(debug_state_t *debug,
+    debug_func_info_t *func_info, debug_file_info_t *file_info, debug_loc_info_t *loc_info,
+    debug_thread_state_t *thread, ovm_stack_frame_t *frame) {
+
+    ovm_func_t *func = frame->func;
+
+    assert(debug_info_lookup_func(debug->info, func->id, func_info));
+
+    u32 instr;
+    if (frame == &bh_arr_last(thread->ovm_state->stack_frames)) {
+        instr = thread->ovm_state->pc;
+    } else {
+        instr = (frame + 1)->return_address;
+    }
+
+    while (!debug_info_lookup_location(debug->info, instr, loc_info))
+        instr++;
+
+    assert(debug_info_lookup_file(debug->info, loc_info->file_id, file_info));
+}
+
 static void process_command(debug_state_t *debug, struct msg_parse_ctx_t *ctx) {
 #define ON_THREAD(tid) \
     bh_arr_each(debug_thread_state_t *, thread, debug->threads) \
@@ -252,8 +275,6 @@ static void process_command(debug_state_t *debug, struct msg_parse_ctx_t *ctx) {
             break;
         }
 
-        // The trace command should be probably eclipse
-        // the loc command at some point?
         case CMD_TRACE: {
             unsigned int thread_id = parse_int(debug, ctx);
 
@@ -277,31 +298,80 @@ static void process_command(debug_state_t *debug, struct msg_parse_ctx_t *ctx) {
             send_int(debug, bh_arr_length(frames));
 
             bh_arr_rev_each(ovm_stack_frame_t, frame, frames) {
-                ovm_func_t *func = frame->func;
-
                 debug_func_info_t func_info;
-                assert(debug_info_lookup_func(debug->info, func->id, &func_info));
+                debug_file_info_t file_info;
+                debug_loc_info_t  loc_info;
+
+                get_stack_frame_location(debug, &func_info, &file_info, &loc_info, thread, frame);
+
                 send_string(debug, func_info.name);
+                send_string(debug, file_info.name);
+                send_int(debug, loc_info.line);
+            }
 
-                debug_loc_info_t loc_info;
-                debug_file_info_t file_info;
+            break;
+        }
 
-                u32 instr;
-                if (frame == &bh_arr_last(frames)) {
-                    instr = thread->ovm_state->pc;
-                } else {
-                    instr = (frame + 1)->return_address;
+        case CMD_THREADS: {
+            bh_arr(debug_thread_state_t *) threads = debug->threads;
+
+            send_response_header(debug, msg_id);
+            send_int(debug, bh_arr_length(threads));
+
+            char buf[128];
+            bh_arr_each(debug_thread_state_t *, thread, threads) {
+                send_int(debug, (*thread)->id);
+
+                snprintf(buf, 128, "thread #%d", (*thread)->id);
+                send_string(debug, buf);
+            }
+
+            break;
+        }
+
+        case CMD_VARS: {
+            i32 stack_frame = parse_int(debug, ctx);
+            u32 thread_id   = parse_int(debug, ctx);
+
+            ON_THREAD(thread_id) {
+                bh_arr(ovm_stack_frame_t) frames = (*thread)->ovm_state->stack_frames;
+                if (stack_frame >= bh_arr_length(frames)) {
+                    goto vars_error;
                 }
 
-                while (!debug_info_lookup_location(debug->info, instr, &loc_info))
-                    instr++;
+                ovm_stack_frame_t *frame = &frames[bh_arr_length(frames) - 1 - stack_frame];
 
-                assert(debug_info_lookup_file(debug->info, loc_info.file_id, &file_info));
+                debug_func_info_t func_info;
+                debug_file_info_t file_info;
+                debug_loc_info_t  loc_info;
 
-                send_string(debug, file_info.name);
-                send_int(debug, loc_info.line);
+                get_stack_frame_location(debug, &func_info, &file_info, &loc_info, *thread, frame);
+
+                send_response_header(debug, msg_id);
+
+                i32 symbol_scope = loc_info.symbol_scope;
+                while (symbol_scope != -1) {
+                    debug_sym_scope_t sym_scope = debug->info->symbol_scopes[symbol_scope];
+
+                    bh_arr_each(u32, sym_id, sym_scope.symbols) {
+                        debug_sym_info_t *sym = &debug->info->symbols[*sym_id];
+
+                        send_int(debug, 0);
+                        send_string(debug, sym->name);
+                        send_string(debug, "???");
+                        send_string(debug, "unknown type");
+                    }
+
+                    symbol_scope = sym_scope.parent;
+                }
+                
+                send_int(debug, 1);
+                return;
             }
 
+          vars_error:
+            send_response_header(debug, msg_id);
+            send_int(debug, 1);
             break;
         }
     }
@@ -349,11 +419,22 @@ void *__debug_thread_entry(void * data) {
     // Disable blocking reads and write in the client socket
     // Alternatively, a MSG_DONTWAIT could be used below
     fcntl(debug->client_fd, F_SETFL, O_NONBLOCK);
+    fcntl(debug->state_change_pipes[0], F_SETFL, O_NONBLOCK);
 
     printf("[INFO ] Client connected\n");
 
+    bh_buffer_init(&debug->send_buffer, bh_heap_allocator(), 1024);
+
+    struct pollfd poll_fds[2];
+    poll_fds[0].fd = debug->state_change_pipes[0];
+    poll_fds[0].events = POLLIN;
+    poll_fds[1].fd = debug->client_fd;
+    poll_fds[1].events = POLLIN;
+
     char command[4096];
     while (debug->debug_thread_running) {
+        poll(poll_fds, 2, 1000);
+
         //
         // Try to read commands from the client.
         // If an error was returned, bail out of this thread.
@@ -382,12 +463,15 @@ void *__debug_thread_entry(void * data) {
 
         //
         // Check the state of the running program and report any changes.
-        
+
+        char buf;
+        int dummy = read(debug->state_change_pipes[0], &buf, 1);
+
         bh_arr_each(debug_thread_state_t *, thread, debug->threads) {
             if ((*thread)->state == debug_state_hit_breakpoint) {
                 (*thread)->state = debug_state_paused;
 
-                u32 instr, bp_id = (*thread)->last_breakpoint_hit;
+                i32 instr = -1, bp_id = (*thread)->last_breakpoint_hit;
                 bh_arr_each(debug_breakpoint_t, bp, (*thread)->breakpoints) {
                     if (bp->id == bp_id) {
                         instr = bp->instr;
@@ -395,6 +479,8 @@ void *__debug_thread_entry(void * data) {
                     }
                 }
 
+                if (instr == -1) continue;
+
                 debug_loc_info_t loc_info;
                 debug_info_lookup_location(debug->info, instr, &loc_info);
 
@@ -414,7 +500,9 @@ void *__debug_thread_entry(void * data) {
                 send_int(debug, (*thread)->pause_reason);
             }
         }
-        
+
+        send(debug->client_fd, debug->send_buffer.data, debug->send_buffer.length, 0);
+        bh_buffer_clear(&debug->send_buffer);
 
         bh_arena_clear(&debug->tmp_arena);
     }
index 150644a2f8ddc4a91db2c7f96cc6179c8e8f3b0a..6567caa574177d8a8aad83d6b9ef0ddc734e519c 100644 (file)
@@ -717,12 +717,12 @@ ovm_value_t ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t
             goto shouldnt_wait;
 
           should_wait:
+            assert(write(state->debug->state_change_write_fd, "1", 1));
             sem_wait(&state->debug->wait_semaphore);
             state->debug->state = debug_state_running;
 
           shouldnt_wait:
             if (state->debug->run_count > 0) state->debug->run_count--;
-            (void) 0;
         }
 
         //
index 29d01d2799acd65fba7b5db145ec7672d3c5e981..75b5fcd8ae2621e9c53cab18d7eb81c11b49a6ba 100644 (file)
@@ -91,6 +91,10 @@ static void parse_custom_section(build_context *ctx) {
         if (!strcmp(name, "ovm_debug_ops")) {
             debug_info_builder_prepare(&ctx->debug_builder, cs.data);
         }
+
+        if (!strcmp(name, "ovm_debug_syms")) {
+            debug_info_import_sym_info(ctx->debug_builder.info, cs.data, cs.size);
+        }
     }
 
     ctx->offset = end_of_section;