return cast(i32) a.state - cast(i32) b.state;
});
- client_count = array.count_where(clients, x => x != null);
+ client_count = array.count_where(clients, #(it != null));
return server.alive;
}
typedef struct DebugSymInfo {
u32 sym_id;
u32 location_type;
- u32 location_num;
+ u64 location_num;
char *name;
u32 type;
} DebugSymInfo;
+typedef struct DebugSymPatch {
+ u32 func_idx;
+ u32 sym_id;
+ u64 local_idx;
+} DebugSymPatch;
+
typedef struct DebugContext {
bh_allocator allocator;
Table(DebugFileInfo) file_info;
u32 next_file_id;
- bh_arr(DebugSymInfo) sym_info;
+ bh_arr(DebugSymInfo) sym_info;
+ bh_arr(DebugSymPatch) sym_patches;
u32 next_sym_id;
bh_arr(DebugFuncContext) funcs;
});
}
initializeRequest(response, args) {
- console.log("INITIALIZE");
response.body = response.body || {};
// the adapter implements the configurationDone request.
response.body.supportsConfigurationDoneRequest = true;
}
});
}
+ pauseRequest(response, args, request) {
+ this.debugger.pause(args.threadId);
+ this.sendResponse(response);
+ }
continueRequest(response, args, request) {
let thread_id = args.threadId;
if (!args.singleThread) {
(function (OVMCommand) {
OVMCommand[OVMCommand["NOP"] = 0] = "NOP";
OVMCommand[OVMCommand["RES"] = 1] = "RES";
- OVMCommand[OVMCommand["BRK"] = 2] = "BRK";
- OVMCommand[OVMCommand["CLR_BRK"] = 3] = "CLR_BRK";
+ OVMCommand[OVMCommand["PAUSE"] = 2] = "PAUSE";
+ OVMCommand[OVMCommand["BRK"] = 3] = "BRK";
+ OVMCommand[OVMCommand["CLR_BRK"] = 4] = "CLR_BRK";
OVMCommand[OVMCommand["STEP"] = 5] = "STEP";
OVMCommand[OVMCommand["TRACE"] = 6] = "TRACE";
OVMCommand[OVMCommand["THREADS"] = 7] = "THREADS";
this.client.on("connect", res);
});
}
+ pause(thread_id = 0xffffffff) {
+ let data = new ArrayBuffer(12);
+ let view = new DataView(data);
+ let cmd_id = this.next_command_id;
+ view.setUint32(0, cmd_id, true);
+ view.setUint32(4, OVMCommand.PAUSE, true);
+ view.setUint32(8, thread_id, true);
+ this.client.write(new Uint8Array(data));
+ this.pending_responses[cmd_id] = OVMCommand.PAUSE;
+ }
resume(thread_id = 0xffffffff) {
let data = new ArrayBuffer(12);
let view = new DataView(data);
break;
}
default:
- console.log("Unknown event: ", event_id, data);
+ // console.log("Unknown event: ", event_id, data);
}
}
}
break;
}
default:
- console.log("Unrecognized command. ", cmd_id, msg_id);
+ // console.log("Unrecognized command. ", cmd_id, msg_id);
}
}
preparePromise(msg_id) {
}
protected initializeRequest(response: DebugProtocol.InitializeResponse, args: DebugProtocol.InitializeRequestArguments): void {
- console.log("INITIALIZE");
-
response.body = response.body || {};
// the adapter implements the configurationDone request.
}
}
+ protected pauseRequest(response: DebugProtocol.PauseResponse, args: DebugProtocol.PauseArguments, request?: DebugProtocol.Request): void {
+ this.debugger.pause(args.threadId);
+ this.sendResponse(response);
+ }
+
protected continueRequest(response: DebugProtocol.ContinueResponse, args: DebugProtocol.ContinueArguments, request?: DebugProtocol.Request): void {
let thread_id = args.threadId;
if (!args.singleThread) {
enum OVMCommand {
NOP = 0,
RES = 1,
- BRK = 2,
- CLR_BRK = 3,
+ PAUSE = 2,
+ BRK = 3,
+ CLR_BRK = 4,
STEP = 5,
TRACE = 6,
THREADS = 7,
});
}
+ pause(thread_id: number = 0xffffffff): void {
+ let data = new ArrayBuffer(12);
+ let view = new DataView(data);
+
+ let cmd_id = this.next_command_id;
+
+ view.setUint32(0, cmd_id, true);
+ view.setUint32(4, OVMCommand.PAUSE, true);
+ view.setUint32(8, thread_id, true);
+
+ this.client.write(new Uint8Array(data));
+
+ this.pending_responses[cmd_id] = OVMCommand.PAUSE;
+ }
+
resume(thread_id: number = 0xffffffff): void {
let data = new ArrayBuffer(12);
let view = new DataView(data);
}
default:
- console.log("Unknown event: ", event_id, data);
+ // console.log("Unknown event: ", event_id, data);
}
}
}
}
default:
- console.log("Unrecognized command. ", cmd_id, msg_id);
+ // console.log("Unrecognized command. ", cmd_id, msg_id);
}
}
CHECK(constraint_context, &s_node->constraints, s_node->scope, pos);
}
+ //
+ // This ensures that all procedures defined inside of a structure are
+ // not pruned and omitted from the binary. This is because a large
+ // use-case of procedures in structures is dynamically linking them
+ // using the type info data.
if (s_node->scope) {
fori (i, 0, shlen(s_node->scope->symbols)) {
AstNode* node = s_node->scope->symbols[i].value;
#ifdef ENABLE_DEBUG_INFO
-static u32 debug_introduce_symbol(OnyxWasmModule *mod, OnyxToken *token, DebugSymLoc loc, u32 num, Type* type) {
+static u32 debug_introduce_symbol(OnyxWasmModule *mod, OnyxToken *token, DebugSymLoc loc, u64 num, Type* type) {
u32 id = mod->debug_context->next_sym_id++;
bh_arr_push(mod->debug_context->sym_info, sym_info);
+ if (loc == DSL_REGISTER) {
+ assert(mod->local_alloc);
+ DebugSymPatch patch;
+ patch.func_idx = mod->current_func_idx;
+ patch.sym_id = id;
+ patch.local_idx = num;
+ bh_arr_push(mod->debug_context->sym_patches, patch);
+ }
+
bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_SYM);
u32 leb_len=0;
u8 *bytes = uint_to_uleb128(id, &leb_len);
}
if (fd->body != NULL) {
+ mod->local_alloc = &wasm_func.locals;
+
// NOTE: Generate the local map
u64 localidx = 0;
bh_arr_each(AstParam, param, fd->params) {
switch (type_get_param_pass(param->local->type)) {
case Param_Pass_By_Value: {
if (type_is_structlike_strict(param->local->type)) {
- debug_introduce_symbol(mod, param->local->token, DSL_REGISTER, localidx, param->local->type);
+ debug_introduce_symbol(mod, param->local->token, DSL_REGISTER, localidx | LOCAL_IS_WASM, param->local->type);
bh_imap_put(&mod->local_map, (u64) param->local, localidx | LOCAL_IS_WASM);
localidx += type_structlike_mem_count(param->local->type);
}
case Param_Pass_By_Implicit_Pointer: {
- debug_introduce_symbol(mod, param->local->token, DSL_REGISTER, localidx, param->local->type);
+ debug_introduce_symbol(mod, param->local->token, DSL_REGISTER, localidx | LOCAL_IS_WASM, param->local->type);
bh_imap_put(&mod->local_map, (u64) param->local, localidx++ | LOCAL_IS_WASM);
break;
}
}
}
- mod->local_alloc = &wasm_func.locals;
mod->local_alloc->param_count = localidx;
mod->curr_cc = type_function_get_cc(fd->type);
sh_new_arena(module.debug_context->file_info);
bh_arr_new(global_heap_allocator, module.debug_context->sym_info, 32);
+ bh_arr_new(global_heap_allocator, module.debug_context->sym_patches, 32);
bh_arr_new(global_heap_allocator, module.debug_context->funcs, 16);
bh_buffer_init(&module.debug_context->op_buffer, global_heap_allocator, 1024);
output_name(func->name, func->name_length, §ion_buff);
output_unsigned_integer(1, §ion_buff);
output_unsigned_integer(func->op_offset, §ion_buff);
- output_unsigned_integer(func->stack_ptr_idx, §ion_buff);
+
+ LocalAllocator *locals = &module->funcs[i].locals;
+ if (func->stack_ptr_idx > 0) {
+ u32 local_idx = local_lookup_idx(locals, func->stack_ptr_idx);
+ output_unsigned_integer(local_idx, §ion_buff);
+ } else {
+ output_unsigned_integer(0, §ion_buff);
+ }
+
output_unsigned_integer(0, §ion_buff);
}
}
{
- // ovm_debug_syms sections
+ // ovm_debug_syms section
+
+ // First, apply patches for register locations
+ bh_arr_each(DebugSymPatch, patch, ctx->sym_patches) {
+ // CLEANUP: This is (kind of) incorrect, as there is nothing guarenteeing
+ // that the symbol with id a will be a position a, other than the way
+ // that this has been implemented right now.
+ assert(ctx->sym_info[patch->sym_id].location_type == DSL_REGISTER);
+
+ LocalAllocator *locals = &module->funcs[patch->func_idx - module->foreign_function_count].locals;
+ ctx->sym_info[patch->sym_id].location_num = local_lookup_idx(locals, patch->local_idx);
+ }
+
bh_buffer_clear(§ion_buff);
bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
bh_buffer_concat(buff, section_buff);
}
+ {
+ // ovm_debug_types section
+ bh_buffer_clear(§ion_buff);
+ bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
+
+ output_custom_section_name("ovm_debug_types", §ion_buff);
+
+ i32 type_count = bh_arr_length(type_map.entries);
+ output_unsigned_integer(type_count, §ion_buff);
+
+ bh_arr_each(bh__imap_entry, entry, type_map.entries) {
+ u32 id = entry->key;
+ Type *type = (Type *) entry->value;
+ const char *name = type_get_name(type);
+
+ output_unsigned_integer(id, §ion_buff);
+ output_name(name, strlen(name), §ion_buff);
+ output_unsigned_integer(type_size_of(type), §ion_buff);
+
+ if (type->kind == Type_Kind_Basic) {
+ // Type indicies are special because they are encoded
+ // as a "distinct" unsigned 32-bit integer, which is
+ // effectively how they are used in the code anyway.
+ //
+ // This is probably a change that will be made throughout
+ // the entire compiler, but for now they will remain as
+ // a special type.
+ if (type->Basic.kind == Basic_Kind_Type_Index) {
+ output_unsigned_integer(5, §ion_buff);
+ output_unsigned_integer(2, §ion_buff);
+ output_unsigned_integer(basic_types[Basic_Kind_U32].id, §ion_buff);
+ continue;
+ }
+
+ if (type->Basic.kind == Basic_Kind_Rawptr) {
+ // rawptr -> ^void
+ output_unsigned_integer(2, §ion_buff);
+ output_unsigned_integer(1, §ion_buff);
+ output_unsigned_integer(basic_types[Basic_Kind_Void].id, §ion_buff);
+ continue;
+ }
+
+ output_unsigned_integer(1, §ion_buff);
+ if (type->Basic.kind == Basic_Kind_Void) output_unsigned_integer(0, §ion_buff);
+ else if (type_is_bool(type)) output_unsigned_integer(4, §ion_buff);
+ else if (type_is_integer(type)) {
+ if (type->Basic.flags & Basic_Flag_Unsigned) output_unsigned_integer(2, §ion_buff);
+ else output_unsigned_integer(1, §ion_buff);
+ }
+ else if (type->Basic.flags & Basic_Flag_Float) output_unsigned_integer(3, §ion_buff);
+ else if (type_is_simd(type)) output_unsigned_integer(6, §ion_buff);
+ else {
+ output_unsigned_integer(0, §ion_buff);
+ }
+
+ continue;
+ }
+
+ if (type->kind == Type_Kind_Pointer) {
+ output_unsigned_integer(2, §ion_buff);
+ output_unsigned_integer(1, §ion_buff);
+ output_unsigned_integer(type->Pointer.elem->id, §ion_buff);
+ continue;
+ }
+
+ if (type->kind == Type_Kind_Enum) {
+ output_unsigned_integer(5, §ion_buff);
+ output_unsigned_integer(2, §ion_buff);
+ output_unsigned_integer(type->Enum.backing->id, §ion_buff);
+ continue;
+ }
+
+ if (type->kind == Type_Kind_Array) {
+ output_unsigned_integer(4, §ion_buff);
+ output_unsigned_integer(type->Array.count, §ion_buff);
+ output_unsigned_integer(type->Array.elem->id, §ion_buff);
+ continue;
+ }
+
+ if (type_is_structlike_strict(type)) {
+ output_unsigned_integer(3, §ion_buff);
+
+ i32 mem_count = type_structlike_mem_count(type);
+ output_unsigned_integer(mem_count, §ion_buff);
+
+ fori (i, 0, mem_count) {
+ StructMember smem;
+ type_lookup_member_by_idx(type, i, &smem);
+
+ output_unsigned_integer(smem.offset, §ion_buff);
+ output_unsigned_integer(smem.type->id, §ion_buff);
+ output_name(smem.name, strlen(smem.name), §ion_buff);
+ }
+
+ continue;
+ }
+
+ if (type->kind == Type_Kind_Function) {
+ output_unsigned_integer(6, §ion_buff);
+ output_unsigned_integer(type->Function.param_count, §ion_buff);
+
+ fori (i, 0, (i32) type->Function.param_count) {
+ output_unsigned_integer(type->Function.params[i]->id, §ion_buff);
+ }
+
+ output_unsigned_integer(type->Function.return_type->id, §ion_buff);
+ continue;
+ }
+
+ if (type->kind == Type_Kind_Distinct) {
+ output_unsigned_integer(5, §ion_buff);
+ output_unsigned_integer(2, §ion_buff);
+ output_unsigned_integer(type->Distinct.base_type->id, §ion_buff);
+ continue;
+ }
+
+ // No debug information will be given about the poly struct
+ // or compound types.
+ // Outside of runtime type information, they provide no useful
+ // debugging information (I don't think at least...).
+ if (type->kind == Type_Kind_PolyStruct ||
+ type->kind == Type_Kind_Compound) {
+ output_unsigned_integer(1, §ion_buff);
+ output_unsigned_integer(0, §ion_buff);
+ continue;
+ }
+
+ assert(("Unhandled type", 0));
+ }
+
+ output_unsigned_integer(section_buff.length, buff);
+
+ bh_buffer_concat(buff, section_buff);
+ }
+
{
// ovm_debug_ops section
bh_buffer_clear(§ion_buff);