fixed `stopOnEntry` logic to be more correct; fixed multi-threaded breakpoints
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 14 Dec 2022 16:59:46 +0000 (10:59 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 14 Dec 2022 16:59:46 +0000 (10:59 -0600)
compiler/src/polymorph.h
interpreter/include/ovm_debug.h
interpreter/src/debug/debug_host.c
interpreter/src/debug/debug_info.c
interpreter/src/debug/debug_thread.c
interpreter/src/vm/vm.c
misc/vscode/out/ovmDebug.js
misc/vscode/ovmDebug.ts
shared/lib/linux_x86_64/lib/libovmwasm.so

index 0c0583b3533e920863df8594815cf2216d59ba6b..8fee9c845c04de40a1fefb3c3bac5bd00d086cfe 100644 (file)
@@ -481,9 +481,10 @@ static AstTyped* try_lookup_based_on_partial_function_type(AstFunction *pp, AstF
     AstTyped *result = (AstTyped *) polymorphic_proc_lookup(pp, PPLM_By_Function_Type, ft->partial_function_type, pp->token);
 
     // If the result is not ready (NULL, yield flag, no type, or `type_auto_return` as return type), wait.
-    if (result
-        && result->type == NULL
-        || (result->type->kind == Type_Kind_Function && result->type->Function.return_type == &type_auto_return)) {
+    if (result && (
+            result->type == NULL
+        || (result->type->kind == Type_Kind_Function && result->type->Function.return_type == &type_auto_return)))
+    {
         doing_nested_polymorph_lookup = 1;
         result = NULL;
     }
index a2c08670d47ba67d8809c76079aeb967e270f93f..2b8a70a469abd190538b948d94430a1ee8a7ac60 100644 (file)
@@ -172,6 +172,7 @@ bool debug_info_lookup_location(debug_info_t *info, u32 instruction, debug_loc_i
 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);
+i32  debug_info_lookup_instr_by_file_line(debug_info_t *info, char *filename, u32 line);
 
 //
 // This builder is used in conjunction with code builder to output
@@ -231,6 +232,13 @@ typedef struct debug_thread_state_t {
     debug_exec_state_t state;
     struct ovm_state_t *ovm_state;
 
+    // This flag signals if the thread has done
+    // ANY execution. When a thread first starts
+    // the thread signals a `paused` event, with
+    // an `entry` reason. When the thread pauses
+    // later on, it should be a `step` reason.
+    b32 started;
+
     i32 run_count;
     sem_t wait_semaphore;
 
@@ -239,7 +247,6 @@ typedef struct debug_thread_state_t {
     i32 extra_frames_since_last_pause;
     debug_pause_reason_t pause_reason;
 
-    bh_arr(debug_breakpoint_t) breakpoints;
     u32 last_breakpoint_hit;
 
     u32 state_change_write_fd;
@@ -262,6 +269,7 @@ typedef struct debug_state_t {
     u32 next_thread_id;
 
     u32 next_breakpoint_id;
+    bh_arr(debug_breakpoint_t) breakpoints;
 
     pthread_t debug_thread;
     bool debug_thread_running;
index 8ae0ce1928e3383b5684fb6803a38c7dcc8c2d62..82b0c877a08104c98e1043a23b7b72203ac25be1 100644 (file)
@@ -44,9 +44,6 @@ u32 debug_host_register_thread(debug_state_t *debug, ovm_state_t *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;
 
index 6f1000375b86cb8d8847e12278051a4a5fe03bf9..e9fc60d94d5011466fe77725712330b7df977872 100644 (file)
@@ -211,6 +211,31 @@ bool debug_info_lookup_file(debug_info_t *info, u32 file_id, debug_file_info_t *
     return true;
 }
 
+i32 debug_info_lookup_instr_by_file_line(debug_info_t *info, char *filename, u32 line) {
+    if (!info || !info->has_debug_info) return 0;
+
+    debug_file_info_t file_info;
+    bool file_found = debug_info_lookup_file_by_name(info, filename, &file_info);
+    if (!file_found) {
+        return -1;
+    }
+
+    if (line > file_info.line_count) {
+        return -1;
+    }
+
+    u32 instr;
+    while ((instr = info->line_to_instruction[file_info.line_buffer_offset + line]) == 0) {
+        line += 1;
+
+        if (line > file_info.line_count) {
+            return -1;
+        }
+    }
+
+    return instr;
+}
+
 //
 // 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.
index b87843b9a629a89415ae3a9b4058b7a5fff70af7..d613fdd9e9dacea71895553d675d092d5997315c 100644 (file)
@@ -163,41 +163,24 @@ static void process_command(debug_state_t *debug, struct msg_parse_ctx_t *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 brk_send_error;
-            }
-
-            if (line > file_info.line_count) {
-                goto brk_send_error;
-            }
-
-            u32 instr;
-            while ((instr = debug->info->line_to_instruction[file_info.line_buffer_offset + line]) == 0) {
-                line += 1;
-
-                if (line > file_info.line_count) {
-                    goto brk_send_error;
-                }
-            }
+            i32 instr = debug_info_lookup_instr_by_file_line(debug->info, filename, line);
+            if (instr < 0) goto brk_send_error;
 
             printf("[INFO ] Setting breakpoint at %s:%d (%x)\n", filename, line, instr);
+
+            debug_file_info_t file_info;
+            debug_info_lookup_file_by_name(debug->info, filename, &file_info);
             
             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, bp);
-            }
+            bh_arr_push(debug->breakpoints, bp);
 
             send_response_header(debug, msg_id);
             send_bool(debug, true);
-            send_int(debug, bp.id);    // TODO: This should be a unique breakpoint ID
+            send_int(debug, bp.id);
             send_int(debug, line);
             break;
 
@@ -220,14 +203,12 @@ static void process_command(debug_state_t *debug, struct msg_parse_ctx_t *ctx) {
                 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--;
-                    }
+            bh_arr_each(debug_breakpoint_t, bp, debug->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(debug->breakpoints, bp - debug->breakpoints);
+                    bp--;
                 }
             }
 
@@ -554,7 +535,7 @@ void *__debug_thread_entry(void * data) {
                 (*thread)->state = debug_state_paused;
 
                 i32 instr = -1, bp_id = (*thread)->last_breakpoint_hit;
-                bh_arr_each(debug_breakpoint_t, bp, (*thread)->breakpoints) {
+                bh_arr_each(debug_breakpoint_t, bp, debug->breakpoints) {
                     if (bp->id == bp_id) {
                         instr = bp->instr;
                         break;
index 9080e0f449185977471b3443f906e46aa0ec7e6a..ca036621b474b08f8754e64204620cb58a03a600 100644 (file)
@@ -696,7 +696,14 @@ 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...
+
+                if (state->debug->started) {
+                    state->debug->pause_reason = debug_pause_step;
+                } else {
+                    state->debug->pause_reason = debug_pause_entry;
+                    state->debug->started = 1;
+                }
+
                 goto should_wait;
             }
 
@@ -716,7 +723,8 @@ ovm_value_t ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t
                 }
             }
 
-            bh_arr_each(debug_breakpoint_t, bp, state->debug->breakpoints) {
+            ovm_assert(engine->debug);
+            bh_arr_each(debug_breakpoint_t, bp, engine->debug->breakpoints) {
                 if (bp->instr == (u32) state->pc) {
                     state->debug->state = debug_state_hit_breakpoint;
                     state->debug->last_breakpoint_hit = bp->id;
index 58653c235ab17956cf0be72377f997fe086b2698..f6edc2cf2203a398604d5333c1f3985e96a48d44 100644 (file)
@@ -31,6 +31,13 @@ class OVMDebugSession extends debugadapter_1.LoggingDebugSession {
             this.sendEvent(new debugadapter_1.StoppedEvent("breakpoint", ev.threadId));
         });
         this.debugger.on("paused", (ev) => {
+            if (ev.reason == "entry") {
+                this.sendEvent(new debugadapter_1.ThreadEvent("started", ev.thread_id));
+                if (!this.stopOnEntry) {
+                    this.debugger.resume(ev.thread_id);
+                    return;
+                }
+            }
             this.sendEvent(new debugadapter_1.StoppedEvent(ev.reason, ev.threadId));
         });
         this.debugger.on("terminated", () => {
@@ -219,16 +226,14 @@ class OVMDebugSession extends debugadapter_1.LoggingDebugSession {
         });
     }
     attachRequest(response, args, request) {
+        var _a;
         return __awaiter(this, void 0, void 0, function* () {
             yield this._configurationDone.wait(1000);
             yield this.debugger.connect(args.socketPath);
             this._clientConnected = true;
             this._clientConnectedNotifier.notify();
+            this.stopOnEntry = (_a = args.stopOnEntry) !== null && _a !== void 0 ? _a : false;
             this.sendResponse(response);
-            this.sendEvent(new debugadapter_1.ThreadEvent("started", 1));
-            if (!args.stopOnEntry) {
-                this.debugger.resume();
-            }
         });
     }
     pauseRequest(response, args, request) {
index 35ce3d5383a44f6469d62a250e298909a065ebec..00c8c2529f62030d381590f35383076150fcf337 100644 (file)
@@ -52,6 +52,8 @@ export class OVMDebugSession extends LoggingDebugSession {
        private _variableReferences = new Handles<IVariableReference>();
        private _frameReferences = new Handles<IFrameReference>();
 
+       private stopOnEntry: boolean;
+
     public constructor() {
         super("ovm-debug-log.txt");
 
@@ -68,6 +70,15 @@ export class OVMDebugSession extends LoggingDebugSession {
                });
 
                this.debugger.on("paused", (ev) => {
+                       if (ev.reason == "entry") {
+                               this.sendEvent(new ThreadEvent("started", ev.thread_id));
+
+                               if (!this.stopOnEntry) {
+                                       this.debugger.resume(ev.thread_id);
+                                       return;
+                               }
+                       }
+
                        this.sendEvent(new StoppedEvent(ev.reason, ev.threadId));
                });
 
@@ -315,12 +326,10 @@ export class OVMDebugSession extends LoggingDebugSession {
                this._clientConnected = true;
                this._clientConnectedNotifier.notify();
 
-               this.sendResponse(response);
-               this.sendEvent(new ThreadEvent("started", 1));
+               this.stopOnEntry = args.stopOnEntry ?? false;
 
-               if (!args.stopOnEntry) {
-                       this.debugger.resume();
-               }
+               this.sendResponse(response);
+               // this.sendEvent(new ThreadEvent("started", 1));
     }
 
     protected pauseRequest(response: DebugProtocol.PauseResponse, args: DebugProtocol.PauseArguments, request?: DebugProtocol.Request): void {
index 38a91eaf2da0183452f0f5a6f01cf9cb557dc58c..1ff06037cf6cef1f9c595afde954e9d412213fc5 100755 (executable)
Binary files a/shared/lib/linux_x86_64/lib/libovmwasm.so and b/shared/lib/linux_x86_64/lib/libovmwasm.so differ