added disassembly view debug command
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 3 Jan 2023 17:29:26 +0000 (11:29 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 3 Jan 2023 17:29:26 +0000 (11:29 -0600)
interpreter/src/debug/debug_thread.c
interpreter/src/vm/disasm.c
misc/vscode/out/ovmDebug.js
misc/vscode/ovmDebug.ts
shared/lib/linux_x86_64/lib/libovmwasm.so

index c01f74fe444e71187eea70398aeffcbdaa5e9c7f..bd3994a56e9e135f91b86ec2d2f50f03c112db52 100644 (file)
@@ -96,14 +96,9 @@ 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) {
-
+static u32 get_stack_frame_instruction_pointer(debug_state_t *debug, 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;
@@ -111,6 +106,19 @@ static void get_stack_frame_location(debug_state_t *debug,
         instr = (frame + 1)->return_address;
     }
 
+    return instr;
+}
+
+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;
+
+    u32 instr = get_stack_frame_instruction_pointer(debug, thread, frame);
+
+    assert(debug_info_lookup_func(debug->info, func->id, func_info));
+
     while (!debug_info_lookup_location(debug->info, instr, loc_info))
         instr++;
 
@@ -304,6 +312,7 @@ static DEBUG_COMMAND_HANDLER(debug_command_trace) {
         send_string(debug, func_info.name);
         send_string(debug, file_info.name);
         send_int(debug, loc_info.line);
+        send_int(debug, get_stack_frame_instruction_pointer(debug, thread, frame));
     }
 }
 
@@ -468,6 +477,31 @@ static DEBUG_COMMAND_HANDLER(debug_command_memory_write) {
 static DEBUG_COMMAND_HANDLER(debug_command_disassmble) {
     u32 addr = parse_int(debug, ctx);
     u32 count = parse_int(debug, ctx);
+
+    send_response_header(debug, msg_id);
+
+    bh_buffer instr_buf;
+    bh_buffer_init(&instr_buf, debug->tmp_alloc, 1024);
+
+    // This is kind of a hack, but currently there is no
+    // easy way to access the program text for the current
+    // program without going through a thread. So I just go
+    // through the first thread.
+    ovm_program_t *prog = debug->threads[0]->ovm_state->program;
+
+    while (addr < bh_arr_length(prog->code) && count--) {
+        send_int(debug, 0);
+
+        ovm_disassemble(prog, addr, &instr_buf);
+
+        send_bytes(debug, instr_buf.data, instr_buf.length);
+        bh_buffer_clear(&instr_buf);
+
+        addr += 1;
+    }
+
+    send_int(debug, 1);
+    bh_buffer_free(&instr_buf);
 }
 
 static debug_command_handler_t command_handlers[] = {
index 25d3028b8e3719d5dcbd01b40157f2794414f8e2..c23cc192b7662aeabf6d08b7af40b2bd2f6751c2 100644 (file)
@@ -17,6 +17,14 @@ enum instr_format_kind_t {
     instr_format_store,
 
     instr_format_idx_arr,
+
+    instr_format_br,
+    instr_format_br_cond,
+    instr_format_bri,
+    instr_format_bri_cond,
+
+    instr_format_call,
+    instr_format_calli,
 };
 
 typedef struct instr_format_t {
@@ -131,20 +139,57 @@ void ovm_disassemble(ovm_program_t *program, u32 instr_addr, bh_buffer *instr_te
         case OVM_TYPE_V128: bh_buffer_write_string(instr_text, "v128."); break;
     }
 
-    instr_format_t *format = &instr_formats[OVM_INSTR_INSTR(instr)];
+    instr_format_t *format = &instr_formats[OVM_INSTR_INSTR(*instr)];
 
-    bh_buffer_write(instr_text, format->instr);
+    bh_buffer_write_string(instr_text, format->instr);
 
-    char *formatted = NULL;
+    u32 formatted = 0;
     switch (format->kind) {
         case instr_format_rab: formatted = snprintf(buf, 255, "%%%d, %%%d, %%%d", instr->r, instr->a, instr->b); break;
         case instr_format_ra:  formatted = snprintf(buf, 255, "%%%d, %%%d", instr->r, instr->a); break;
         case instr_format_a:   formatted = snprintf(buf, 255, "%%%d", instr->a); break;
+
+        case instr_format_imm:
+            switch (OVM_INSTR_TYPE(*instr)) {
+                case OVM_TYPE_I8:   formatted = snprintf(buf, 255, "%%%d, %hhd", instr->r, instr->i); break;
+                case OVM_TYPE_I16:  formatted = snprintf(buf, 255, "%%%d, %hd", instr->r, instr->i); break;
+                case OVM_TYPE_I32:  formatted = snprintf(buf, 255, "%%%d, %d", instr->r, instr->i); break;
+                case OVM_TYPE_I64:  formatted = snprintf(buf, 255, "%%%d, %ld", instr->r, instr->l); break;
+                case OVM_TYPE_F32:  formatted = snprintf(buf, 255, "%%%d, %f", instr->r, instr->f); break;
+                case OVM_TYPE_F64:  formatted = snprintf(buf, 255, "%%%d, %lf", instr->r, instr->d); break;
+            }
+            break;
+
+        case instr_format_load:  formatted = snprintf(buf, 255, "%%%d, [%%%d + %d]", instr->r, instr->a, instr->b); break;
+        case instr_format_store: formatted = snprintf(buf, 255, "[%%%d + %d], %%%d", instr->r, instr->b, instr->a); break;
+
+        case instr_format_idx_arr: formatted = snprintf(buf, 255, "%%%d, __global_arr_%d[%%%d]", instr->r, instr->a, instr->b); break;
+
+        case instr_format_br:       formatted = snprintf(buf, 255, "%d", instr_addr + instr->a); break;
+        case instr_format_br_cond:  formatted = snprintf(buf, 255, "%d, %%%d", instr_addr + instr->a, instr->b); break;
+        case instr_format_bri:      formatted = snprintf(buf, 255, "ip + %%%d", instr->a); break;
+        case instr_format_bri_cond: formatted = snprintf(buf, 255, "ip + %%%d, %%%d", instr->a, instr->b); break;
+
+        case instr_format_call:
+            if (instr->r >= 0) {
+                formatted = snprintf(buf, 255, "%%%d, %d  <%s>", instr->r, instr->a, program->funcs[instr->a].name);
+            } else {
+                formatted = snprintf(buf, 255, "%d  <%s>", instr->a, program->funcs[instr->a].name);
+            }
+            break;
+
+        case instr_format_calli:
+            if (instr->r >= 0) {
+                formatted = snprintf(buf, 255, "%%%d, %%%d", instr->r, instr->a);
+            } else {
+                formatted = snprintf(buf, 255, "%%%d", instr->a);
+            }
+            break;
     }
 
-    if (formatted) {
+    if (formatted > 0) {
         bh_buffer_write_byte(instr_text, ' ');
-        bh_buffer_write_string(instr_text, formatted);
+        bh_buffer_append(instr_text, buf, formatted);
     }
 }
 
index f3812dd9092a515caf6c103c01b65377f079d1be..efe9d3ec25baa420d9d7af1a12e23bb3c36892e5 100644 (file)
@@ -158,7 +158,7 @@ class OVMDebugSession extends debugadapter_1.LoggingDebugSession {
                         frameIndex: i
                     });
                     let stack_frame = new debugadapter_1.StackFrame(frameRef, f.funcname, source, f.line);
-                    stack_frame.instructionPointerReference = "1234";
+                    stack_frame.instructionPointerReference = f.instructionPointer.toString();
                     return stack_frame;
                 })
             };
@@ -269,16 +269,20 @@ class OVMDebugSession extends debugadapter_1.LoggingDebugSession {
         this.sendResponse(response);
     }
     disassembleRequest(response, args, request) {
-        console.log(args);
-        response.body = {
-            instructions: [
-                {
-                    address: "0",
-                    instruction: "i32.add %3, %1, %2",
-                }
-            ]
-        };
-        this.sendResponse(response);
+        return __awaiter(this, void 0, void 0, function* () {
+            console.log(args);
+            let addr = parseInt(args.memoryReference) + args.offset;
+            let instrs = yield this.debugger.disassemble(addr, args.instructionCount);
+            response.body = {
+                instructions: instrs.map((i, index) => {
+                    return {
+                        address: (index + addr).toString(),
+                        instruction: i.instr
+                    };
+                })
+            };
+            this.sendResponse(response);
+        });
     }
     readMemoryRequest(response, args, request) {
         return __awaiter(this, void 0, void 0, function* () {
@@ -320,6 +324,7 @@ var OVMCommand;
     OVMCommand[OVMCommand["VARS"] = 8] = "VARS";
     OVMCommand[OVMCommand["MEM_R"] = 9] = "MEM_R";
     OVMCommand[OVMCommand["MEM_W"] = 10] = "MEM_W";
+    OVMCommand[OVMCommand["DISASM"] = 11] = "DISASM";
 })(OVMCommand || (OVMCommand = {}));
 var OVMEvent;
 (function (OVMEvent) {
@@ -504,6 +509,20 @@ class OVMDebugger extends EventEmitter {
         this.pending_responses[cmd_id] = OVMCommand.MEM_W;
         return this.preparePromise(cmd_id);
     }
+    disassemble(addr, count) {
+        if (this.client == null)
+            return Promise.resolve([]);
+        let data = new ArrayBuffer(16);
+        let view = new DataView(data);
+        let cmd_id = this.next_command_id;
+        view.setUint32(0, cmd_id, true);
+        view.setUint32(4, OVMCommand.DISASM, true);
+        view.setUint32(8, addr, true);
+        view.setUint32(12, count, true);
+        this.client.write(new Uint8Array(data));
+        this.pending_responses[cmd_id] = OVMCommand.DISASM;
+        return this.preparePromise(cmd_id);
+    }
     parseIncoming(data) {
         let parser = new DataParser(data);
         while (parser.offset != data.length) {
@@ -580,7 +599,8 @@ class OVMDebugger extends EventEmitter {
                     let funcname = parser.parseString();
                     let filename = parser.parseString();
                     let line = parser.parseInt32();
-                    result.push({ funcname, filename, line });
+                    let ip = parser.parseInt32();
+                    result.push({ funcname, filename, line, instructionPointer: ip });
                 }
                 this.resolvePromise(msg_id, result);
                 break;
@@ -620,6 +640,15 @@ class OVMDebugger extends EventEmitter {
                 this.resolvePromise(msg_id, count);
                 break;
             }
+            case OVMCommand.DISASM: {
+                let result = new Array();
+                while (parser.parseInt32() == 0) {
+                    let instr = parser.parseString();
+                    result.push({ instr });
+                }
+                this.resolvePromise(msg_id, result);
+                break;
+            }
             default:
             // console.log("Unrecognized command. ", cmd_id, msg_id);
         }
index b6680210aa17ecdc766eee580b3439af436ccdeb..940fb4f41a5a38f2d050850837a1f79377eae53c 100644 (file)
@@ -238,7 +238,7 @@ export class OVMDebugSession extends LoggingDebugSession {
                                });
 
                                let stack_frame = new StackFrame(frameRef, f.funcname, source, f.line);
-                               stack_frame.instructionPointerReference = "1234";
+                               stack_frame.instructionPointerReference = f.instructionPointer.toString();
                                
                                return stack_frame;
                        })
@@ -375,16 +375,17 @@ export class OVMDebugSession extends LoggingDebugSession {
                this.sendResponse(response);
        }
 
-       protected disassembleRequest(response: DebugProtocol.DisassembleResponse, args: DebugProtocol.DisassembleArguments, request?: DebugProtocol.Request): void {
-               console.log(args);
+       protected async disassembleRequest(response: DebugProtocol.DisassembleResponse, args: DebugProtocol.DisassembleArguments, request?: DebugProtocol.Request): Promise<void> {
+               let addr = parseInt(args.memoryReference) + args.offset;
+               let instrs = await this.debugger.disassemble(addr, args.instructionCount);
 
                response.body = {
-                       instructions: [
-                               {
-                                       address: "0",
-                                       instruction: "i32.add %3, %1, %2",
-                               }
-                       ]
+                       instructions: instrs.map((i, index) => {
+                               return {
+                                       address: (index + addr - 1).toString(),
+                                       instruction: i.instr,
+                               };
+                       })
                };
 
                this.sendResponse(response);
@@ -426,6 +427,7 @@ interface IFileLocation {
        funcname: string;
        filename: string;
        line: number;
+       instructionPointer: number;
 }
 
 interface IBreakpointValidation {
@@ -452,6 +454,10 @@ interface IReadMemory {
        data: ArrayBuffer;
 }
 
+interface IDisassembledInstruction {
+       instr: string;
+}
+
 enum OVMCommand {
        NOP     = 0,
        RES     = 1,
@@ -464,6 +470,7 @@ enum OVMCommand {
        VARS    = 8,
        MEM_R   = 9,
        MEM_W   = 10,
+       DISASM  = 11
 }
 
 enum OVMEvent {
@@ -712,6 +719,26 @@ class OVMDebugger extends EventEmitter {
                return this.preparePromise(cmd_id);
        }
 
+       disassemble(addr: number, count: number): Promise<IDisassembledInstruction[]> {
+               if (this.client == null) return Promise.resolve([]);
+
+        let data = new ArrayBuffer(16);
+        let view = new DataView(data);
+
+        let cmd_id = this.next_command_id;
+
+        view.setUint32(0, cmd_id, true);
+        view.setUint32(4, OVMCommand.DISASM, true);
+        view.setUint32(8, addr, true);
+        view.setUint32(12, count, true);
+
+        this.client.write(new Uint8Array(data));
+
+        this.pending_responses[cmd_id] = OVMCommand.DISASM;
+
+               return this.preparePromise(cmd_id);
+       }
+
        private parseIncoming(data: Buffer): void {
                let parser = new DataParser(data);
 
@@ -801,8 +828,9 @@ class OVMDebugger extends EventEmitter {
                                        let funcname = parser.parseString();
                                        let filename = parser.parseString();
                                        let line     = parser.parseInt32();
+                                       let ip       = parser.parseInt32();
 
-                                       result.push({funcname, filename, line});
+                                       result.push({funcname, filename, line, instructionPointer: ip});
                                }
                                
                                this.resolvePromise(msg_id, result);
@@ -854,6 +882,18 @@ class OVMDebugger extends EventEmitter {
                                break;
                        }
 
+                       case OVMCommand.DISASM: {
+                               let result = new Array<IDisassembledInstruction>();
+
+                               while (parser.parseInt32() == 0) {
+                                       let instr = parser.parseString();
+                                       result.push({ instr });
+                               }
+
+                               this.resolvePromise(msg_id, result);
+                               break;
+                       }
+
                        default:
                                // console.log("Unrecognized command. ", cmd_id, msg_id);
                }
index 2ba9dcbaa903b195338370a3ee6b3508dab5af4e..3e670e1482725e7a3c53eef326c34a6cdd9224a9 100755 (executable)
Binary files a/shared/lib/linux_x86_64/lib/libovmwasm.so and b/shared/lib/linux_x86_64/lib/libovmwasm.so differ