line by line stepping and stack trace
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 11 Aug 2022 01:19:05 +0000 (20:19 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 11 Aug 2022 01:19:05 +0000 (20:19 -0500)
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

index dbf52c39a03c60af56fc22f01336eb65c7735fb6..ff40cc6fdef45954317a603a6b7e2544cfe5f60c 100644 (file)
@@ -24,6 +24,7 @@ typedef struct debug_func_info_t {
 
 typedef struct debug_file_info_t {
     char *name;
+    u32 file_id;
     u32 line_count;
     i32 line_buffer_offset;
 } debug_file_info_t;
@@ -57,6 +58,7 @@ 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);
+bool debug_info_lookup_func(debug_info_t *info, u32 func_id, debug_func_info_t *out);
 
 //
 // This builder is used in conjunction with code builder to output
@@ -97,6 +99,18 @@ typedef enum debug_exec_state_t {
     debug_state_hit_breakpoint,
 } debug_exec_state_t;
 
+typedef enum debug_pause_reason_t {
+    debug_pause_entry = 1,
+    debug_pause_step = 2,
+} debug_pause_reason_t;
+
+typedef struct debug_breakpoint_t {
+    u32 id;
+    u32 instr;
+    u32 file_id;
+    u32 line;
+} debug_breakpoint_t;
+
 typedef struct debug_thread_state_t {
     u32 id;
 
@@ -106,8 +120,10 @@ typedef struct debug_thread_state_t {
     i32 run_count;
     sem_t wait_semaphore;
 
-    // :BreakpointID
-    bh_arr(u32) breakpoints;
+    bool pause_at_next_line;
+    debug_pause_reason_t pause_reason;
+
+    bh_arr(debug_breakpoint_t) breakpoints;
     u32 last_breakpoint_hit;
 } debug_thread_state_t;
 
@@ -126,6 +142,8 @@ typedef struct debug_state_t {
     bh_arr(debug_thread_state_t *) threads;
     u32 next_thread_id;
 
+    u32 next_breakpoint_id;
+
     pthread_t debug_thread;
     bool debug_thread_running;
 
index 31e3f2c8175d7f4f30ca0f6b0bce241b25a2a1a0..fa1dcf5a52bbc81cde77d2ff91e01acda8b130af 100644 (file)
@@ -32,6 +32,7 @@ void debug_host_stop(debug_state_t *debug) {
 
 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));
+    memset(new_thread, 0, sizeof(*new_thread));
 
     new_thread->state = debug_state_starting;
     new_thread->ovm_state = ovm_state;
index 98743dd6a7b1f0a6c2371c4a1e310656baf1136e..d50b2343e879c896e3f3d291c48a9e986ddef73f 100644 (file)
@@ -34,6 +34,7 @@ void debug_info_import_file_info(debug_info_t *info, u8 *data, u32 len) {
         file_info.line_buffer_offset = -1;
 
         u32 file_id = uleb128_to_uint(data, &offset);
+        file_info.file_id = file_id;
         file_info.line_count = uleb128_to_uint(data, &offset);
 
         u32 name_length = uleb128_to_uint(data, &offset);
@@ -115,3 +116,11 @@ bool debug_info_lookup_file_by_name(debug_info_t *info, char *name, debug_file_i
 
     return false;
 }
+
+bool debug_info_lookup_func(debug_info_t *info, u32 func_id, debug_func_info_t *out) {
+    if (!info || !info->has_debug_info) return false;
+
+    if (func_id > (u32) bh_arr_length(info->funcs)) return false;
+    *out = info->funcs[func_id];
+    return true;
+}
\ No newline at end of file
index 489b5e607a8c4db94c600c584da46b99150afcfa..1b84defd21cb42fb2e6c0e319bdd55151d9e4b84 100644 (file)
@@ -79,7 +79,7 @@ void debug_info_builder_step(debug_info_builder_t *builder) {
             builder->next_file_line_offset += file_info->line_count;
         }
 
-        u32 line_index = file_info->line_buffer_offset + builder->current_line;
+        i32 line_index = file_info->line_buffer_offset + builder->current_line;
         u32 target     = bh_arr_length(builder->info->instruction_reducer);
 
         if (line_index >= bh_arr_capacity(builder->info->line_to_instruction) ||
index 5e7716c0f08fb6dac9233c7139b2b06236b36b80..7de5aae27d44c242ce9d2ee1c6a5974951e2b1aa 100644 (file)
@@ -5,13 +5,21 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/un.h>
-
+#include <assert.h>
 
 
 #define CMD_NOP 0
 #define CMD_RES 1
 #define CMD_BRK 2
-#define CMD_LOC 3
+#define CMD_CLR_BRK 3
+#define CMD_LOC 4
+#define CMD_STEP 5
+#define CMD_TRACE 6
+
+#define EVT_NOP 0
+#define EVT_BRK_HIT 1
+#define EVT_PAUSE 2
+#define EVT_RESPONSE 0xffffffff
 
 struct msg_parse_ctx_t {
     char *data;
@@ -20,7 +28,7 @@ struct msg_parse_ctx_t {
 };
 
 static void send_response_header(debug_state_t *debug, unsigned int message_number) {
-    unsigned int RESPONSE_HEADER = 0xffffffff;
+    unsigned int RESPONSE_HEADER = EVT_RESPONSE;
     send(debug->client_fd, &RESPONSE_HEADER, 4, 0);
     send(debug->client_fd, &message_number, 4, 0);
 }
@@ -72,22 +80,24 @@ static char *parse_string(debug_state_t *debug, struct msg_parse_ctx_t *ctx) {
     return buf;
 }
 
+static void resume_thread(debug_thread_state_t *thread) {
+    thread->run_count = -1;
+    sem_post(&thread->wait_semaphore);
+}
+
 static void process_command(debug_state_t *debug, struct msg_parse_ctx_t *ctx) {
     u32 msg_id     = parse_int(debug, ctx);
     u32 command_id = parse_int(debug, ctx);
-    printf("[INFO ] Recv command: %d\n", command_id);
+
+    // printf("[INFO ] Recv command: %d\n", command_id);
 
     switch (command_id) {
         case CMD_NOP: {
-            // Returns: header
-
             send_response_header(debug, msg_id);
             break;
         }
 
         case CMD_RES: {
-            // Returns: nothing
-
             u32 thread_id = parse_int(debug, ctx);
 
             bool resumed_a_thread = false;
@@ -95,10 +105,7 @@ static void process_command(debug_state_t *debug, struct msg_parse_ctx_t *ctx) {
             // Release the thread(s)
             bh_arr_each(debug_thread_state_t *, thread, debug->threads) {
                 if (thread_id == 0xffffffff || (*thread)->id == thread_id) {
-                    // This logic for "how to resume a thread" should be moved
-                    // elsewhere, but now its fine here.
-                    (*thread)->run_count = -1;
-                    sem_post(&(*thread)->wait_semaphore);
+                    resume_thread(*thread);
                     resumed_a_thread = true;
                 }
             }
@@ -109,22 +116,19 @@ static void process_command(debug_state_t *debug, struct msg_parse_ctx_t *ctx) {
         }
 
         case CMD_BRK: {
-            //
-            // Returns: header success:bool  breakpoint_handle:u32
-
-            char *filename = parse_string(debug, ctx);
-            int   line     = parse_int(debug, ctx);
+            char    *filename = parse_string(debug, ctx);
+            unsigned int line = parse_int(debug, ctx);
 
             //
             // TODO: This translation logic will have to be abstracted
             debug_file_info_t file_info;
             bool file_found = debug_info_lookup_file_by_name(debug->info, filename, &file_info);
             if (!file_found) {
-                goto send_error;
+                goto brk_send_error;
             }
 
             if (line > file_info.line_count) {
-                goto send_error;
+                goto brk_send_error;
             }
 
             u32 instr;
@@ -132,23 +136,28 @@ static void process_command(debug_state_t *debug, struct msg_parse_ctx_t *ctx) {
                 line += 1;
 
                 if (line > file_info.line_count) {
-                    goto send_error;
+                    goto brk_send_error;
                 }
             }
 
             printf("[INFO ] Setting breakpoint at %s:%d (%xd)\n", filename, line, instr);
             
+            debug_breakpoint_t bp;
+            bp.id = debug->next_breakpoint_id++;
+            bp.instr = instr;
+            bp.file_id = file_info.file_id;
+            bp.line = line;
             bh_arr_each(debug_thread_state_t *, thread, debug->threads) {
-                bh_arr_push((*thread)->breakpoints, instr);
+                bh_arr_push((*thread)->breakpoints, bp);
             }
 
             send_response_header(debug, msg_id);
             send_bool(debug, true);
-            send_int(debug, 1);    // TODO: This should be a unique breakpoint ID
+            send_int(debug, bp.id);    // TODO: This should be a unique breakpoint ID
             send_int(debug, line);
             break;
 
-          send_error:
+          brk_send_error:
             send_response_header(debug, msg_id);
             send_bool(debug, false);
             send_int(debug, -1);
@@ -156,6 +165,36 @@ static void process_command(debug_state_t *debug, struct msg_parse_ctx_t *ctx) {
             break;
         }
 
+        case CMD_CLR_BRK: {
+            char *filename = parse_string(debug, ctx);
+
+            debug_file_info_t file_info;
+            bool file_found = debug_info_lookup_file_by_name(debug->info, filename, &file_info);
+            if (!file_found) {
+                goto clr_brk_send_error;
+            }
+
+            bh_arr_each(debug_thread_state_t *, thread, debug->threads) {
+                bh_arr_each(debug_breakpoint_t, bp, (*thread)->breakpoints) {
+                    if (bp->file_id == file_info.file_id) {
+                        // This is kind of hacky but it does successfully delete
+                        // a single element from the array and move the iterator.
+                        bh_arr_fastdelete((*thread)->breakpoints, bp - (*thread)->breakpoints);
+                        bp--;
+                    }
+                }
+            }
+
+            send_response_header(debug, msg_id);
+            send_bool(debug, true);
+            break;
+
+          clr_brk_send_error:
+            send_response_header(debug, msg_id);
+            send_bool(debug, false);
+            break;
+        }
+
         case CMD_LOC: {
             u32 thread_id = parse_int(debug, ctx);
 
@@ -188,6 +227,77 @@ static void process_command(debug_state_t *debug, struct msg_parse_ctx_t *ctx) {
             send_int(debug, -1);
             break;
         }
+        
+        case CMD_STEP: {
+            u32 granularity = parse_int(debug, ctx);
+            u32 thread_id = parse_int(debug, ctx);
+            
+            if (granularity == 1) {
+                bh_arr_each(debug_thread_state_t *, thread, debug->threads) {
+                    if ((*thread)->id == thread_id) {
+                        (*thread)->pause_at_next_line = true;
+                        resume_thread(*thread);
+                    }
+                }
+            }
+
+            send_response_header(debug, msg_id);
+            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);
+
+            debug_thread_state_t *thread = NULL;
+            bh_arr_each(debug_thread_state_t *, pthread, debug->threads) {
+                if ((*pthread)->id == thread_id) {
+                    thread = *pthread;
+                    break;
+                }
+            }
+
+            if (thread == NULL) {
+                send_response_header(debug, msg_id);
+                send_int(debug, 0);
+                break;
+            }
+
+            bh_arr(ovm_stack_frame_t) frames = thread->ovm_state->stack_frames;
+
+            send_response_header(debug, msg_id);
+            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));
+                send_string(debug, func_info.name);
+
+                // :CopyPaste from CMD_LOC case
+                debug_loc_info_t loc_info;
+                debug_file_info_t file_info;
+
+                u32 instr;
+                if (frame == &bh_arr_last(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));
+
+                send_string(debug, file_info.name);
+                send_int(debug, loc_info.line);
+            }
+
+            break;
+        }
     }
 }
 
@@ -271,8 +381,13 @@ void *__debug_thread_entry(void * data) {
             if ((*thread)->state == debug_state_hit_breakpoint) {
                 (*thread)->state = debug_state_paused;
 
-                u32 bp_id = (*thread)->last_breakpoint_hit; // :BreakpointID
-                u32 instr = (*thread)->breakpoints[bp_id];
+                u32 instr, bp_id = (*thread)->last_breakpoint_hit;
+                bh_arr_each(debug_breakpoint_t, bp, (*thread)->breakpoints) {
+                    if (bp->id == bp_id) {
+                        instr = bp->instr;
+                        break;
+                    }
+                }
 
                 debug_loc_info_t loc_info;
                 debug_info_lookup_location(debug->info, instr, &loc_info);
@@ -280,7 +395,7 @@ void *__debug_thread_entry(void * data) {
                 debug_file_info_t file_info;
                 debug_info_lookup_file(debug->info, loc_info.file_id, &file_info);
 
-                send_int(debug, 1);
+                send_int(debug, EVT_BRK_HIT);
                 send_int(debug, bp_id);
                 send_int(debug, (*thread)->id);
             }
@@ -288,8 +403,9 @@ void *__debug_thread_entry(void * data) {
             if ((*thread)->state == debug_state_pausing) {
                 (*thread)->state = debug_state_paused;
 
-                send_int(debug, 2);
+                send_int(debug, EVT_PAUSE);
                 send_int(debug, (*thread)->id);
+                send_int(debug, (*thread)->pause_reason);
             }
         }
         
index 37d03bd4944a5ee7d84e307fa6e5a9073a348516..63561a59a57c2c14bfaae63317351b33c4325983 100644 (file)
@@ -686,14 +686,27 @@ ovm_value_t ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t
         if (state->debug) {
             if (state->debug->run_count == 0) {
                 state->debug->state = debug_state_pausing;
+                state->debug->pause_reason = debug_pause_entry; // This is not always due to entry...
                 goto should_wait;
             }
 
-            bh_arr_each(u32, bp, state->debug->breakpoints) {
-                if (*bp == (u32) state->pc) {
-                    // :BreakpointID
+            if (state->debug->pause_at_next_line) {
+                debug_loc_info_t l1, l2;
+                debug_info_lookup_location(engine->debug->info, state->pc - 1, &l1);
+                debug_info_lookup_location(engine->debug->info, state->pc,     &l2);
+
+                if (l1.file_id != l2.file_id || l1.line != l2.line) {
+                    state->debug->pause_at_next_line = false;
+                    state->debug->pause_reason = debug_pause_step;
+                    state->debug->state = debug_state_pausing;
+                    goto should_wait;
+                }
+            }
+
+            bh_arr_each(debug_breakpoint_t, bp, state->debug->breakpoints) {
+                if (bp->instr == (u32) state->pc) {
                     state->debug->state = debug_state_hit_breakpoint;
-                    state->debug->last_breakpoint_hit = bp - state->debug->breakpoints;
+                    state->debug->last_breakpoint_hit = bp->id;
                     goto should_wait;
                 }
             }