From: Brendan Hansen Date: Mon, 19 Sep 2022 02:48:37 +0000 (-0500) Subject: added structured output to debug variables X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=552bc761605a432b2688bfab29a6323d2985f51b;p=onyx.git added structured output to debug variables --- diff --git a/interpreter/include/ovm_debug.h b/interpreter/include/ovm_debug.h index ac112c2e..37eecea2 100644 --- a/interpreter/include/ovm_debug.h +++ b/interpreter/include/ovm_debug.h @@ -30,6 +30,7 @@ typedef struct debug_file_info_t { } debug_file_info_t; typedef enum debug_sym_loc_kind_t { + debug_sym_loc_unknown = 0, debug_sym_loc_register = 1, debug_sym_loc_stack = 2, debug_sym_loc_global = 3 @@ -292,12 +293,33 @@ typedef struct debug_runtime_value_builder_t { debug_func_info_t func_info; debug_file_info_t file_info; debug_loc_info_t loc_info; - debug_sym_info_t sym_info; + + // "base_" refers to the top symbol. In a layered + // query, this information is not outputted, only + // used to lookup the values inside of it. + debug_sym_loc_kind_t base_loc_kind; + u32 base_loc; + u32 base_type; + + // "it_" refers to the current symbol to be output. + u32 max_index; + u32 it_index; + + debug_sym_loc_kind_t it_loc_kind; + u32 it_loc; + u32 it_type; + char *it_name; + bool it_has_children; + } debug_runtime_value_builder_t; void debug_runtime_value_build_init(debug_runtime_value_builder_t *builder, bh_allocator alloc); -void debug_runtime_value_build_free(debug_runtime_value_builder_t *builder); +void debug_runtime_value_build_set_location(debug_runtime_value_builder_t *builder, debug_sym_loc_kind_t loc_kind, u32 loc, u32 type, char *name); +void debug_runtime_value_build_descend(debug_runtime_value_builder_t *builder, u32 index); +bool debug_runtime_value_build_step(debug_runtime_value_builder_t *builder); void debug_runtime_value_build_string(debug_runtime_value_builder_t *builder); +void debug_runtime_value_build_clear(debug_runtime_value_builder_t *builder); +void debug_runtime_value_build_free(debug_runtime_value_builder_t *builder); void *__debug_thread_entry(void *); diff --git a/interpreter/src/debug/debug_runtime_values.c b/interpreter/src/debug/debug_runtime_values.c index 2edde566..a1ebb063 100644 --- a/interpreter/src/debug/debug_runtime_values.c +++ b/interpreter/src/debug/debug_runtime_values.c @@ -26,6 +26,16 @@ static bool lookup_register_in_frame(ovm_state_t *state, ovm_stack_frame_t *fram return true; } +static bool lookup_stack_pointer(debug_runtime_value_builder_t *builder, u32 *out) { + ovm_value_t stack_ptr; + if (!lookup_register_in_frame(builder->ovm_state, builder->ovm_frame, builder->func_info.stack_ptr_idx, &stack_ptr)) { + return false; + } + + *out = stack_ptr.u32; + return true; +} + static void append_value_from_memory_with_type(debug_runtime_value_builder_t *builder, void *base, u32 type_id) { debug_type_info_t *type = &builder->info->types[type_id]; @@ -205,18 +215,23 @@ static void append_ovm_value_with_type(debug_runtime_value_builder_t *builder, o break; } + case debug_type_kind_structure: { + append_ovm_value_with_type(builder, value, type->structure.members[0].type); + break; + } + default: WRITE("(unknown)"); break; } } static void append_value_from_stack(debug_runtime_value_builder_t *builder, u32 offset, u32 type_id) { - ovm_value_t stack_ptr; - if (!lookup_register_in_frame(builder->ovm_state, builder->ovm_frame, builder->func_info.stack_ptr_idx, &stack_ptr)) { + u32 stack_ptr; + if (!lookup_stack_pointer(builder, &stack_ptr)) { WRITE("(no stack ptr)"); return; } - void *base = bh_pointer_add(builder->state->ovm_engine->memory, stack_ptr.u32 + offset); + void *base = bh_pointer_add(builder->state->ovm_engine->memory, stack_ptr + offset); append_value_from_memory_with_type(builder, base, type_id); } @@ -253,6 +268,27 @@ static void append_value_from_register(debug_runtime_value_builder_t *builder, u append_ovm_value_with_type(builder, value, type_id); } +static u32 get_subvalues_for_type(debug_runtime_value_builder_t *builder, u32 type) { + debug_type_info_t *t = &builder->info->types[type]; + switch (t->kind) { + case debug_type_kind_primitive: return 0; + case debug_type_kind_function: return 0; + + case debug_type_kind_modifier: + if (t->modifier.modifier_kind == debug_type_modifier_kind_pointer) return 1; + return 0; + + case debug_type_kind_alias: + return get_subvalues_for_type(builder, t->alias.aliased_type); + + case debug_type_kind_structure: + return t->structure.member_count; + + // TEMPORARY this will be the array elements + case debug_type_kind_array: return 0; + } +} + @@ -260,20 +296,162 @@ void debug_runtime_value_build_init(debug_runtime_value_builder_t *builder, bh_a bh_buffer_init(&builder->output, alloc, 1024); } -void debug_runtime_value_build_free(debug_runtime_value_builder_t *builder) { - bh_buffer_free(&builder->output); +void debug_runtime_value_build_set_location(debug_runtime_value_builder_t *builder, + debug_sym_loc_kind_t loc_kind, u32 loc, u32 type, char *name) { + builder->base_loc_kind = loc_kind; + builder->base_loc = loc; + builder->base_type = type; + + builder->max_index = get_subvalues_for_type(builder, type); + builder->it_index = 0; + builder->it_name = name; + builder->it_loc = loc; + builder->it_type = type; + builder->it_loc_kind = loc_kind; + builder->it_has_children = builder->max_index > 0; +} + +void debug_runtime_value_build_descend(debug_runtime_value_builder_t *builder, u32 index) { + builder->it_index = 0; + + debug_type_info_t *type = &builder->info->types[builder->base_type]; + if (type->kind == debug_type_kind_modifier && type->modifier.modifier_kind == debug_type_modifier_kind_pointer) { + if (index > 0) { + goto bad_case; + } + + builder->base_type = type->modifier.modified_type; + type = &builder->info->types[builder->base_type]; + + builder->max_index = get_subvalues_for_type(builder, builder->base_type); + builder->it_index = 0; + + if (builder->base_loc_kind == debug_sym_loc_register) { + ovm_value_t value; + if (!lookup_register_in_frame(builder->ovm_state, builder->ovm_frame, builder->base_loc, &value)) { + goto bad_case; + } + + builder->base_loc_kind = debug_sym_loc_global; + builder->base_loc = value.u32; + } + + else if (builder->base_loc_kind == debug_sym_loc_stack) { + u32 stack_ptr; + if (!lookup_stack_pointer(builder, &stack_ptr)) { + goto bad_case; + } + + u32 *ptr_loc = bh_pointer_add(builder->state->ovm_engine->memory, stack_ptr + builder->base_loc); + builder->base_loc = *ptr_loc; + builder->base_loc_kind = debug_sym_loc_global; + } + + else if (builder->base_loc_kind == debug_sym_loc_global) { + u32 *ptr_loc = bh_pointer_add(builder->state->ovm_engine->memory, builder->base_loc); + builder->base_loc = *ptr_loc; + } + + return; + } + + if (type->kind == debug_type_kind_structure) { + if (index >= type->structure.member_count) { + goto bad_case; + } + + debug_type_structure_member_t *mem = &type->structure.members[index]; + builder->base_type = mem->type; + builder->max_index = get_subvalues_for_type(builder, builder->base_type); + builder->it_name = mem->name; + + if (builder->base_loc_kind == debug_sym_loc_register) { + builder->base_loc += index; + } + + else if (builder->base_loc_kind == debug_sym_loc_stack || builder->base_loc_kind == debug_sym_loc_global) { + builder->base_loc += mem->offset; + } + + return; + } + + bad_case: + builder->base_loc_kind = debug_sym_loc_unknown; + return; +} + +bool debug_runtime_value_build_step(debug_runtime_value_builder_t *builder) { + if (builder->it_index >= builder->max_index) return false; + + debug_type_info_t *type = &builder->info->types[builder->base_type]; + + if (type->kind == debug_type_kind_modifier && type->modifier.modifier_kind == debug_type_modifier_kind_pointer) { + // Double buffering here so if there are multiple + // pointer descentions, the names don't get mangled. + static char name_buffer[2048]; + static char tmp_buffer[2048]; + snprintf(tmp_buffer, 2048, "*%s", builder->it_name); + strncpy(name_buffer, tmp_buffer, 2048); + + builder->it_loc_kind = builder->base_loc_kind; + builder->it_loc = builder->base_loc; + builder->it_name = name_buffer; + builder->it_type = type->modifier.modified_type; + builder->it_has_children = get_subvalues_for_type(builder, builder->it_type) > 0; + } + + if (type->kind == debug_type_kind_structure) { + debug_type_structure_member_t *mem = &type->structure.members[builder->it_index]; + builder->it_name = mem->name; + builder->it_has_children = get_subvalues_for_type(builder, mem->type) > 0; + builder->it_type = mem->type; + + if (builder->base_loc_kind == debug_sym_loc_register) { + builder->it_loc_kind = debug_sym_loc_register; + builder->it_loc = builder->base_loc + builder->it_index; + } + + if (builder->base_loc_kind == debug_sym_loc_stack) { + builder->it_loc_kind = debug_sym_loc_stack; + builder->it_loc = builder->base_loc + mem->offset; + } + + if (builder->base_loc_kind == debug_sym_loc_global) { + builder->it_loc_kind = debug_sym_loc_global; + builder->it_loc = builder->base_loc + mem->offset; + } + } + + + builder->it_index++; + return true; } void debug_runtime_value_build_string(debug_runtime_value_builder_t *builder) { - if (builder->sym_info.loc_kind == debug_sym_loc_register) { - append_value_from_register(builder, builder->sym_info.loc, builder->sym_info.type); + if (builder->it_loc_kind == debug_sym_loc_register) { + append_value_from_register(builder, builder->it_loc, builder->it_type); return; } - if (builder->sym_info.loc_kind == debug_sym_loc_stack) { - append_value_from_stack(builder, builder->sym_info.loc, builder->sym_info.type); + if (builder->it_loc_kind == debug_sym_loc_stack) { + append_value_from_stack(builder, builder->it_loc, builder->it_type); + return; + } + + if (builder->it_loc_kind == debug_sym_loc_global) { + void *base = bh_pointer_add(builder->state->ovm_engine->memory, builder->it_loc); + append_value_from_memory_with_type(builder, base, builder->it_type); return; } WRITE("(location unknown)"); } + +void debug_runtime_value_build_clear(debug_runtime_value_builder_t *builder) { + bh_buffer_clear(&builder->output); +} + +void debug_runtime_value_build_free(debug_runtime_value_builder_t *builder) { + bh_buffer_free(&builder->output); +} diff --git a/interpreter/src/debug/debug_thread.c b/interpreter/src/debug/debug_thread.c index 5d52312c..d8e2dcef 100644 --- a/interpreter/src/debug/debug_thread.c +++ b/interpreter/src/debug/debug_thread.c @@ -344,6 +344,7 @@ static void process_command(debug_state_t *debug, struct msg_parse_ctx_t *ctx) { case CMD_VARS: { i32 stack_frame = parse_int(debug, ctx); u32 thread_id = parse_int(debug, ctx); + u32 layers = parse_int(debug, ctx); ON_THREAD(thread_id) { bh_arr(ovm_stack_frame_t) frames = (*thread)->ovm_state->stack_frames; @@ -361,40 +362,90 @@ static void process_command(debug_state_t *debug, struct msg_parse_ctx_t *ctx) { send_response_header(debug, msg_id); + debug_runtime_value_builder_t builder; + builder.state = debug; + builder.info = debug->info; + builder.ovm_state = (*thread)->ovm_state; + builder.ovm_frame = frame; + builder.func_info = func_info; + builder.file_info = file_info; + builder.loc_info = loc_info; + debug_runtime_value_build_init(&builder, bh_heap_allocator()); + + // + // The first layer specified is the symbol id to drill into. + // Therefore, the first layer is peeled off here before the + // following while loop. This makes the assumption that no + // symbol will have the id 0xffffffff. This is probably safe + // to assume, especially since just one more symbol and this + // whole system crashes... + u32 sym_id_to_match = 0xffffffff; + if (layers > 0) { + sym_id_to_match = parse_int(debug, ctx); + layers--; + } + i32 symbol_scope = loc_info.symbol_scope; while (symbol_scope != -1) { debug_sym_scope_t sym_scope = debug->info->symbol_scopes[symbol_scope]; + builder.sym_scope = sym_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); - - debug_runtime_value_builder_t builder; - builder.state = debug; - builder.info = debug->info; - builder.ovm_state = (*thread)->ovm_state; - builder.ovm_frame = frame; - builder.sym_scope = sym_scope; - builder.func_info = func_info; - builder.file_info = file_info; - builder.loc_info = loc_info; - builder.sym_info = *sym; - - debug_runtime_value_build_init(&builder, bh_heap_allocator()); - debug_runtime_value_build_string(&builder); - send_bytes(debug, builder.output.data, builder.output.length); - debug_runtime_value_build_free(&builder); - - debug_type_info_t *type = &debug->info->types[sym->type]; - send_string(debug, type->name); + debug_runtime_value_build_set_location(&builder, sym->loc_kind, sym->loc, sym->type, sym->name); + + // + // If we are drilling to a particular symbol, and this is that symbol, + // we have to do the generation a little differently. We first have to + // pull the other layer queries in, and descend into those layers. + // Then, we loop through each value at that layer and print their values. + if (sym->sym_id == sym_id_to_match && sym_id_to_match != 0xffffffff) { + while (layers--) { + u32 desc = parse_int(debug, ctx); + debug_runtime_value_build_descend(&builder, desc); + } + + while (debug_runtime_value_build_step(&builder)) { + debug_runtime_value_build_string(&builder); + debug_type_info_t *type = &debug->info->types[builder.it_type]; + + send_int(debug, 0); + send_string(debug, builder.it_name); + send_bytes(debug, builder.output.data, builder.output.length); + send_string(debug, type->name); + send_int(debug, builder.it_index - 1); // CLEANUP This should be 0 indexed, but because this is after the while loop condition, it is 1 indexed. + send_bool(debug, builder.it_has_children); + + debug_runtime_value_build_clear(&builder); + } + + // This is important, as when doing a layered query, only one symbol + // should be considered, and once found, should immediate stop. + goto syms_done; + + } else if (sym_id_to_match == 0xffffffff) { + // Otherwise, we simply print the value of the symbol as is. + debug_type_info_t *type = &debug->info->types[sym->type]; + + debug_runtime_value_build_string(&builder); + + send_int(debug, 0); + send_string(debug, sym->name); + send_bytes(debug, builder.output.data, builder.output.length); + send_string(debug, type->name); + send_int(debug, sym->sym_id); + send_bool(debug, builder.it_has_children); + + debug_runtime_value_build_clear(&builder); + } } symbol_scope = sym_scope.parent; } + syms_done: send_int(debug, 1); + debug_runtime_value_build_free(&builder); return; } diff --git a/misc/vscode/onyx-0.0.4.vsix b/misc/vscode/onyx-0.0.4.vsix new file mode 100644 index 00000000..6f7437f6 Binary files /dev/null and b/misc/vscode/onyx-0.0.4.vsix differ diff --git a/misc/vscode/out/ovmDebug.js b/misc/vscode/out/ovmDebug.js index 5fdde9ed..6a3f2d7c 100644 --- a/misc/vscode/out/ovmDebug.js +++ b/misc/vscode/out/ovmDebug.js @@ -137,6 +137,8 @@ class OVMDebugSession extends debugadapter_1.LoggingDebugSession { stackTraceRequest(response, args, request) { return __awaiter(this, void 0, void 0, function* () { let frames = yield this.debugger.trace(args.threadId); + this._frameReferences.reset(); + this._variableReferences.reset(); response.body = { stackFrames: frames.map((f, i) => { let source = new debugadapter_1.Source(this.fileNameToShortName(f.filename), this.convertDebuggerPathToClient(f.filename), undefined, undefined, "ovm-debug-src"); @@ -166,7 +168,7 @@ class OVMDebugSession extends debugadapter_1.LoggingDebugSession { scopesRequest(response, args, request) { let frameId = args.frameId; let frameRef = this._frameReferences.get(frameId); - let varRef = this._variableReferences.create(frameRef); + let varRef = this._variableReferences.create(Object.assign(Object.assign({}, frameRef), { variableChain: [] })); response.body = { scopes: [ new debugadapter_1.Scope("Locals", varRef, false), @@ -176,11 +178,14 @@ class OVMDebugSession extends debugadapter_1.LoggingDebugSession { } variablesRequest(response, args, request) { return __awaiter(this, void 0, void 0, function* () { - const frameRef = this._variableReferences.get(args.variablesReference, { frameIndex: 0, threadId: 0 }); - let vs = (yield this.debugger.variables(frameRef.frameIndex, frameRef.threadId)) + const frameRef = this._variableReferences.get(args.variablesReference, { frameIndex: 0, threadId: 0, variableChain: [] }); + let vs = (yield this.debugger.variables(frameRef.frameIndex, frameRef.threadId, frameRef.variableChain)) .map(v => { let nv = new debugadapter_1.Variable(v.name, v.value); nv.type = v.type; + if (v.hasChildren) { + nv.variablesReference = this._variableReferences.create(Object.assign(Object.assign({}, frameRef), { variableChain: frameRef.variableChain.concat([v.symId]) })); + } return nv; }); response.body = { variables: vs }; @@ -406,16 +411,20 @@ class OVMDebugger extends EventEmitter { this.pending_responses[cmd_id] = OVMCommand.THREADS; return this.preparePromise(cmd_id); } - variables(frame_index, thread_id) { + variables(frame_index, thread_id, descention) { if (this.client == null) return Promise.resolve([]); - let data = new ArrayBuffer(16); + let data = new ArrayBuffer(20 + 4 * descention.length); let view = new DataView(data); let cmd_id = this.next_command_id; view.setUint32(0, cmd_id, true); view.setUint32(4, OVMCommand.VARS, true); view.setUint32(8, frame_index, true); view.setUint32(12, thread_id, true); + view.setUint32(16, descention.length, true); + for (let i = 0; i < descention.length; i++) { + view.setUint32(20 + i * 4, descention[i], true); + } this.client.write(new Uint8Array(data)); this.pending_responses[cmd_id] = OVMCommand.VARS; return this.preparePromise(cmd_id); @@ -515,7 +524,9 @@ class OVMDebugger extends EventEmitter { let name = parser.parseString(); let value = parser.parseString(); let type = parser.parseString(); - result.push({ name, value, type }); + let symId = parser.parseUint32(); + let hasChildren = parser.parseBool(); + result.push({ name, value, type, symId, hasChildren }); } this.resolvePromise(msg_id, result); break; @@ -581,7 +592,7 @@ class DataParser { } parseBool() { if (this.offset >= this.data.length) - return 0; + return false; this.offset += 1; return this.view.getUint8(this.offset - 1) != 0; } diff --git a/misc/vscode/ovmDebug.ts b/misc/vscode/ovmDebug.ts index f646927f..0fa1c54f 100644 --- a/misc/vscode/ovmDebug.ts +++ b/misc/vscode/ovmDebug.ts @@ -32,6 +32,12 @@ interface IFrameReference { threadId: number; } +interface IVariableReference { + frameIndex: number; + threadId: number; + variableChain: number[]; +} + export class OVMDebugSession extends LoggingDebugSession { private debugger: OVMDebugger; @@ -43,7 +49,7 @@ export class OVMDebugSession extends LoggingDebugSession { private _loadedSources: Map; - private _variableReferences = new Handles(); + private _variableReferences = new Handles(); private _frameReferences = new Handles(); public constructor() { @@ -197,6 +203,9 @@ export class OVMDebugSession extends LoggingDebugSession { protected async stackTraceRequest(response: DebugProtocol.StackTraceResponse, args: DebugProtocol.StackTraceArguments, request?: DebugProtocol.Request): Promise { let frames = await this.debugger.trace(args.threadId); + this._frameReferences.reset(); + this._variableReferences.reset(); + response.body = { stackFrames: frames.map((f, i) => { let source = new Source( @@ -236,7 +245,10 @@ export class OVMDebugSession extends LoggingDebugSession { let frameId = args.frameId; let frameRef = this._frameReferences.get(frameId); - let varRef = this._variableReferences.create(frameRef); + let varRef = this._variableReferences.create({ + ...frameRef, + variableChain: [] + }); response.body = { scopes: [ @@ -247,11 +259,19 @@ export class OVMDebugSession extends LoggingDebugSession { } protected async variablesRequest(response: DebugProtocol.VariablesResponse, args: DebugProtocol.VariablesArguments, request?: DebugProtocol.Request): Promise { - const frameRef = this._variableReferences.get(args.variablesReference, {frameIndex: 0, threadId: 0}); - let vs: Variable[] = (await this.debugger.variables(frameRef.frameIndex, frameRef.threadId)) + const frameRef = this._variableReferences.get(args.variablesReference, {frameIndex:0, threadId:0, variableChain:[]}); + + let vs: Variable[] = (await this.debugger.variables(frameRef.frameIndex, frameRef.threadId, frameRef.variableChain)) .map(v => { let nv = new Variable(v.name, v.value) as DebugProtocol.Variable; nv.type = v.type; + if (v.hasChildren) { + nv.variablesReference = this._variableReferences.create({ + ...frameRef, + variableChain: frameRef.variableChain.concat([v.symId]), + }); + } + return nv; }); @@ -363,6 +383,8 @@ interface IVariableInfo { name: string; value: string; type: string; + symId: number; + hasChildren: boolean; } enum OVMCommand { @@ -556,10 +578,10 @@ class OVMDebugger extends EventEmitter { return this.preparePromise(cmd_id); } - variables(frame_index: number, thread_id: number): Promise { + variables(frame_index: number, thread_id: number, descention: number[]): Promise { if (this.client == null) return Promise.resolve([]); - let data = new ArrayBuffer(16); + let data = new ArrayBuffer(20 + 4 * descention.length); let view = new DataView(data); let cmd_id = this.next_command_id; @@ -568,6 +590,11 @@ class OVMDebugger extends EventEmitter { view.setUint32(4, OVMCommand.VARS, true); view.setUint32(8, frame_index, true); view.setUint32(12, thread_id, true); + view.setUint32(16, descention.length, true); + + for (let i=0; i= this.data.length) return 0; + if (this.offset >= this.data.length) return false; this.offset += 1; return this.view.getUint8(this.offset - 1) != 0; diff --git a/misc/vscode/package.json b/misc/vscode/package.json index 7bbd3ff0..83d5e276 100644 --- a/misc/vscode/package.json +++ b/misc/vscode/package.json @@ -2,7 +2,7 @@ "name": "onyx", "displayName": "Onyx", "description": "Onyx syntax highlighting.", - "version": "0.0.3", + "version": "0.0.4", "publisher": "brendanfh", "license": "BSD-2-Clause", "engines": {