} 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
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 *);
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];
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);
}
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;
+ }
+}
+
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);
+}
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;
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;
}
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");
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),
}
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 };
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);
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;
}
parseBool() {
if (this.offset >= this.data.length)
- return 0;
+ return false;
this.offset += 1;
return this.view.getUint8(this.offset - 1) != 0;
}
threadId: number;
}
+interface IVariableReference {
+ frameIndex: number;
+ threadId: number;
+ variableChain: number[];
+}
+
export class OVMDebugSession extends LoggingDebugSession {
private debugger: OVMDebugger;
private _loadedSources: Map<string, Source>;
- private _variableReferences = new Handles<IFrameReference>();
+ private _variableReferences = new Handles<IVariableReference>();
private _frameReferences = new Handles<IFrameReference>();
public constructor() {
protected async stackTraceRequest(response: DebugProtocol.StackTraceResponse, args: DebugProtocol.StackTraceArguments, request?: DebugProtocol.Request): Promise<void> {
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(
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: [
}
protected async variablesRequest(response: DebugProtocol.VariablesResponse, args: DebugProtocol.VariablesArguments, request?: DebugProtocol.Request): Promise<void> {
- 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;
});
name: string;
value: string;
type: string;
+ symId: number;
+ hasChildren: boolean;
}
enum OVMCommand {
return this.preparePromise(cmd_id);
}
- variables(frame_index: number, thread_id: number): Promise<IVariableInfo[]> {
+ variables(frame_index: number, thread_id: number, descention: number[]): Promise<IVariableInfo[]> {
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(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));
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);
}
parseBool() {
- if (this.offset >= this.data.length) return 0;
+ if (this.offset >= this.data.length) return false;
this.offset += 1;
return this.view.getUint8(this.offset - 1) != 0;
"name": "onyx",
"displayName": "Onyx",
"description": "Onyx syntax highlighting.",
- "version": "0.0.3",
+ "version": "0.0.4",
"publisher": "brendanfh",
"license": "BSD-2-Clause",
"engines": {