// for this particular function.
OnyxToken* exported_name;
+ OnyxToken* closing_brace;
+
union {
OnyxToken* intrinsic_name;
typedef struct DeferredStmt {
DeferredStmtType type;
u32 depth;
+ AstDefer *defer_node;
union {
AstNode *stmt;
u32 line_count;
} DebugFileInfo;
+typedef enum DebugSymLoc {
+ DSL_REGISTER = 1,
+ DSL_STACK = 2,
+ DSL_GLOBAL = 3,
+} DebugSymLoc;
+
+typedef struct DebugSymInfo {
+ u32 sym_id;
+ u32 location_type;
+ u32 location_num;
+ char *name;
+ u32 type;
+} DebugSymInfo;
+
typedef struct DebugContext {
bh_allocator allocator;
Table(DebugFileInfo) file_info;
u32 next_file_id;
+ bh_arr(DebugSymInfo) sym_info;
+ u32 next_sym_id;
+
bh_arr(DebugFuncContext) funcs;
bh_buffer op_buffer;
super("ovm-debug-log.txt");
this._configurationDone = new await_notify_1.Subject();
this._clientConnectedNotifier = new await_notify_1.Subject();
+ this._variableReferences = new debugadapter_1.Handles();
+ this._frameReferences = new debugadapter_1.Handles();
this.setDebuggerLinesStartAt1(true);
this.setDebuggerColumnsStartAt1(true);
this._loadedSources = new Map();
this.sendEvent(new debugadapter_1.InitializedEvent());
}
configurationDoneRequest(response, args, request) {
- console.log("CONFIGURATION DONE");
super.configurationDoneRequest(response, args);
this._configurationDone.notify();
}
disconnectRequest(response, args, request) {
- console.log(`disconnectRequest suspend: ${args.suspendDebuggee}, terminate: ${args.terminateDebuggee}`);
if (args.terminateDebuggee) {
- console.log("TERMINATE");
if (this.running_process) {
this.running_process.kill('SIGTERM');
}
}
setBreakPointsRequest(response, args, request) {
return __awaiter(this, void 0, void 0, function* () {
- console.log("BREAKPOINTS", args, response);
while (!this._clientConnected) {
yield this._clientConnectedNotifier.wait();
}
this._loadedSources.set(source.name, source);
this.sendEvent(new debugadapter_1.LoadedSourceEvent("new", source));
}
- return new debugadapter_1.StackFrame(i, f.funcname, source, f.line);
+ let frameRef = this._frameReferences.create({
+ threadId: args.threadId,
+ frameIndex: i
+ });
+ return new debugadapter_1.StackFrame(frameRef, f.funcname, source, f.line);
})
};
this.sendResponse(response);
});
}
threadsRequest(response, request) {
- console.log("THREADS");
- response.body = {
- threads: [
- new debugadapter_1.Thread(1, "main thread"),
- ]
- };
- this.sendResponse(response);
+ return __awaiter(this, void 0, void 0, function* () {
+ let threads = yield this.debugger.threads();
+ response.body = {
+ threads: threads.map(t => new debugadapter_1.Thread(t.id, t.name))
+ };
+ this.sendResponse(response);
+ });
}
scopesRequest(response, args, request) {
- console.log("SCOPES");
+ let frameId = args.frameId;
+ let frameRef = this._frameReferences.get(frameId);
+ let varRef = this._variableReferences.create(frameRef);
response.body = {
scopes: [
- new debugadapter_1.Scope("Locals", 1, false),
- new debugadapter_1.Scope("Globals", 2, true)
+ new debugadapter_1.Scope("Locals", varRef, false),
]
};
this.sendResponse(response);
}
+ 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))
+ .map(v => {
+ let nv = new debugadapter_1.Variable(v.name, v.value);
+ nv.type = v.type;
+ return nv;
+ });
+ response.body = { variables: vs };
+ this.sendResponse(response);
+ });
+ }
launchRequest(response, args, request) {
this.running_process = child_process.spawn("onyx-run", ["--debug", args.wasmFile], {
"cwd": args.workingDir,
}
attachRequest(response, args, request) {
return __awaiter(this, void 0, void 0, function* () {
- console.log("ATTACH");
yield this._configurationDone.wait(1000);
yield this.debugger.connect(args.socketPath);
this._clientConnected = true;
OVMCommand[OVMCommand["CLR_BRK"] = 3] = "CLR_BRK";
OVMCommand[OVMCommand["STEP"] = 5] = "STEP";
OVMCommand[OVMCommand["TRACE"] = 6] = "TRACE";
+ OVMCommand[OVMCommand["THREADS"] = 7] = "THREADS";
+ OVMCommand[OVMCommand["VARS"] = 8] = "VARS";
})(OVMCommand || (OVMCommand = {}));
var OVMEvent;
(function (OVMEvent) {
this.pending_responses[cmd_id] = OVMCommand.TRACE;
return this.preparePromise(cmd_id);
}
+ threads() {
+ let data = new ArrayBuffer(8);
+ let view = new DataView(data);
+ let cmd_id = this.next_command_id;
+ view.setUint32(0, cmd_id, true);
+ view.setUint32(4, OVMCommand.THREADS, true);
+ this.client.write(new Uint8Array(data));
+ this.pending_responses[cmd_id] = OVMCommand.THREADS;
+ return this.preparePromise(cmd_id);
+ }
+ variables(frame_index, thread_id) {
+ 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.VARS, true);
+ view.setUint32(8, frame_index, true);
+ view.setUint32(12, thread_id, true);
+ this.client.write(new Uint8Array(data));
+ this.pending_responses[cmd_id] = OVMCommand.VARS;
+ return this.preparePromise(cmd_id);
+ }
parseIncoming(data) {
let parser = new DataParser(data);
while (parser.offset != data.length) {
this.resolvePromise(msg_id, result);
break;
}
+ case OVMCommand.THREADS: {
+ let result = new Array();
+ let count = parser.parseUint32();
+ for (let i = 0; i < count; i++) {
+ let id = parser.parseUint32();
+ let name = parser.parseString();
+ result.push({ id, name });
+ }
+ this.resolvePromise(msg_id, result);
+ break;
+ }
+ case OVMCommand.VARS: {
+ let result = new Array();
+ while (parser.parseInt32() == 0) {
+ let name = parser.parseString();
+ let value = parser.parseString();
+ let type = parser.parseString();
+ result.push({ name, value, type });
+ }
+ this.resolvePromise(msg_id, result);
+ break;
+ }
default:
console.log("Unrecognized command. ", cmd_id, msg_id);
}
constructor(data) {
this.data = data;
this.view = new DataView(data.buffer);
- console.log("PARSING", this.data);
this.offset = 0;
}
parseInt32() {
+ if (this.offset >= this.data.length)
+ return 0;
this.offset += 4;
return this.view.getInt32(this.offset - 4, true);
}
parseUint32() {
+ if (this.offset >= this.data.length)
+ return 0;
this.offset += 4;
return this.view.getUint32(this.offset - 4, true);
}
return str;
}
parseBool() {
+ if (this.offset >= this.data.length)
+ return 0;
this.offset += 1;
return this.view.getUint8(this.offset - 1) != 0;
}
LoggingDebugSession,
InitializedEvent, TerminatedEvent, StoppedEvent, BreakpointEvent, OutputEvent,
ProgressStartEvent, ProgressUpdateEvent, ProgressEndEvent, InvalidatedEvent,
- Thread, StackFrame, Scope, Source, Handles, Breakpoint, MemoryEvent, ThreadEvent, LoadedSourceEvent
+ Thread, StackFrame, Scope, Source, Handles, Breakpoint, MemoryEvent, ThreadEvent, LoadedSourceEvent, Variable
} from '@vscode/debugadapter';
import { DebugProtocol } from '@vscode/debugprotocol';
import EventEmitter = require('node:events');
import * as net from "node:net";
import * as child_process from "node:child_process";
import { ChildProcess } from 'node:child_process';
-import { Message } from '@vscode/debugadapter/lib/messages';
interface IOVMAttachRequestArguments extends DebugProtocol.AttachRequestArguments {
stopOnEntry?: boolean;
}
+interface IFrameReference {
+ frameIndex: number;
+ threadId: number;
+}
+
export class OVMDebugSession extends LoggingDebugSession {
private debugger: OVMDebugger;
private _loadedSources: Map<string, Source>;
+ private _variableReferences = new Handles<IFrameReference>();
+ private _frameReferences = new Handles<IFrameReference>();
+
public constructor() {
super("ovm-debug-log.txt");
}
protected configurationDoneRequest(response: DebugProtocol.ConfigurationDoneResponse, args: DebugProtocol.ConfigurationDoneArguments, request?: DebugProtocol.Request): void {
- console.log("CONFIGURATION DONE");
super.configurationDoneRequest(response, args);
this._configurationDone.notify();
}
protected disconnectRequest(response: DebugProtocol.DisconnectResponse, args: DebugProtocol.DisconnectArguments, request?: DebugProtocol.Request): void {
- console.log(`disconnectRequest suspend: ${args.suspendDebuggee}, terminate: ${args.terminateDebuggee}`);
-
if (args.terminateDebuggee) {
- console.log("TERMINATE");
-
if (this.running_process) {
this.running_process.kill('SIGTERM');
}
}
protected async setBreakPointsRequest(response: DebugProtocol.SetBreakpointsResponse, args: DebugProtocol.SetBreakpointsArguments, request?: DebugProtocol.Request): Promise<void> {
- console.log("BREAKPOINTS", args, response);
-
while (!this._clientConnected) {
await this._clientConnectedNotifier.wait();
}
this.sendEvent(new LoadedSourceEvent("new", source));
}
- return new StackFrame(i, f.funcname, source, f.line);
+ let frameRef = this._frameReferences.create({
+ threadId: args.threadId,
+ frameIndex: i
+ });
+ return new StackFrame(frameRef, f.funcname, source, f.line);
})
};
this.sendResponse(response);
}
- protected threadsRequest(response: DebugProtocol.ThreadsResponse, request?: DebugProtocol.Request): void {
- console.log("THREADS");
+ protected async threadsRequest(response: DebugProtocol.ThreadsResponse, request?: DebugProtocol.Request): Promise<void> {
+ let threads = await this.debugger.threads();
response.body = {
- threads: [
- new Thread(1, "main thread"),
- ]
+ threads: threads.map(t => new Thread(t.id, t.name))
};
this.sendResponse(response);
}
protected scopesRequest(response: DebugProtocol.ScopesResponse, args: DebugProtocol.ScopesArguments, request?: DebugProtocol.Request): void {
- console.log("SCOPES");
+ let frameId = args.frameId;
+
+ let frameRef = this._frameReferences.get(frameId);
+ let varRef = this._variableReferences.create(frameRef);
response.body = {
scopes: [
- new Scope("Locals", 1, false),
- new Scope("Globals", 2, true)
+ new Scope("Locals", varRef, false),
]
};
this.sendResponse(response);
}
+ 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))
+ .map(v => {
+ let nv = new Variable(v.name, v.value) as DebugProtocol.Variable;
+ nv.type = v.type;
+ return nv;
+ });
+
+ response.body = { variables: vs };
+ this.sendResponse(response);
+ }
+
protected launchRequest(response: DebugProtocol.LaunchResponse, args: IOVMLaunchRequestArguments, request?: DebugProtocol.Request): void {
this.running_process = child_process.spawn("onyx-run", ["--debug", args.wasmFile], {
"cwd": args.workingDir,
}
protected async attachRequest(response: DebugProtocol.AttachResponse, args: IOVMAttachRequestArguments, request?: DebugProtocol.Request): Promise<void> {
- console.log("ATTACH");
await this._configurationDone.wait(1000);
await this.debugger.connect(args.socketPath);
line: number;
}
+interface IThreadInfo {
+ id: number;
+ name: string;
+}
+
+interface IVariableInfo {
+ name: string;
+ value: string;
+ type: string;
+}
+
enum OVMCommand {
NOP = 0,
RES = 1,
BRK = 2,
CLR_BRK = 3,
STEP = 5,
- TRACE = 6
+ TRACE = 6,
+ THREADS = 7,
+ VARS = 8
}
enum OVMEvent {
return this.preparePromise(cmd_id);
}
+ threads(): Promise<IThreadInfo[]> {
+ let data = new ArrayBuffer(8);
+ let view = new DataView(data);
+
+ let cmd_id = this.next_command_id;
+
+ view.setUint32(0, cmd_id, true);
+ view.setUint32(4, OVMCommand.THREADS, true);
+
+ this.client.write(new Uint8Array(data));
+
+ this.pending_responses[cmd_id] = OVMCommand.THREADS;
+
+ return this.preparePromise(cmd_id);
+ }
+
+ variables(frame_index: number, thread_id: number): Promise<IVariableInfo[]> {
+ 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.VARS, true);
+ view.setUint32(8, frame_index, true);
+ view.setUint32(12, thread_id, true);
+
+ this.client.write(new Uint8Array(data));
+
+ this.pending_responses[cmd_id] = OVMCommand.VARS;
+
+ return this.preparePromise(cmd_id);
+ }
+
private parseIncoming(data: Buffer): void {
let parser = new DataParser(data);
break;
}
+ case OVMCommand.THREADS: {
+ let result = new Array<IThreadInfo>();
+
+ let count = parser.parseUint32();
+ for (let i = 0; i < count; i++) {
+ let id = parser.parseUint32();
+ let name = parser.parseString();
+ result.push({id, name});
+ }
+
+ this.resolvePromise(msg_id, result);
+ break;
+ }
+
+ case OVMCommand.VARS: {
+ let result = new Array<IVariableInfo>();
+
+ while (parser.parseInt32() == 0) {
+ let name = parser.parseString();
+ let value = parser.parseString();
+ let type = parser.parseString();
+ result.push({name, value, type});
+ }
+
+ this.resolvePromise(msg_id, result);
+ break;
+ }
+
default:
console.log("Unrecognized command. ", cmd_id, msg_id);
}
constructor(data: Buffer) {
this.data = data;
this.view = new DataView(data.buffer);
- console.log("PARSING", this.data);
this.offset = 0;
}
parseInt32() {
+ if (this.offset >= this.data.length) return 0;
+
this.offset += 4;
return this.view.getInt32(this.offset - 4, true);
}
parseUint32() {
+ if (this.offset >= this.data.length) return 0;
+
this.offset += 4;
return this.view.getUint32(this.offset - 4, true);
}
}
parseBool() {
+ if (this.offset >= this.data.length) return 0;
+
this.offset += 1;
return this.view.getUint8(this.offset - 1) != 0;
}
wasm_file_idx = 2;
}
- if (argc < 3) {
+ if (debug && argc < 3) {
fprintf(stderr, "Expected a WASM file to run.\n");
return 1;
}
bh_buffer data;
data.data = wasm_data.data;
data.length = wasm_data.length;
- return onyx_run_wasm(data, argc - 1, argv + 1);
+ return onyx_run_wasm(data, argc - 1 - debug, argv + 1 + debug);
}
func_def->body = parse_block(parser, 1, name);
+ // :LinearTokenDependent
+ func_def->closing_brace = parser->curr - 1;
+
function_defined:
if (bh_arr_length(polymorphic_vars) > 0) {
func_def->kind = Ast_Kind_Polymorphic_Proc;
#ifdef ENABLE_DEBUG_INFO
+static u32 debug_introduce_symbol(OnyxWasmModule *mod, OnyxToken *token, DebugSymLoc loc, u32 num, Type* type) {
+
+ u32 id = mod->debug_context->next_sym_id++;
+
+ DebugSymInfo sym_info;
+ sym_info.sym_id = id;
+ sym_info.location_type = loc;
+ sym_info.location_num = num;
+ sym_info.type = type->id;
+
+ if (token) {
+ token_toggle_end(token);
+ sym_info.name = bh_strdup(context.ast_alloc, token->text);
+ token_toggle_end(token);
+ } else {
+ sym_info.name = NULL;
+ }
+
+ bh_arr_push(mod->debug_context->sym_info, sym_info);
+
+ bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_SYM);
+ u32 leb_len=0;
+ u8 *bytes = uint_to_uleb128(id, &leb_len);
+ bh_buffer_append(&mod->debug_context->op_buffer, bytes, leb_len);
+
+ mod->debug_context->last_op_was_rep = 0;
+ return id;
+}
+
static u32 debug_get_file_id(OnyxWasmModule *mod, const char *name) {
assert(mod && mod->debug_context);
mod->debug_context->last_token = NULL;
}
+static void debug_enter_symbol_frame(OnyxWasmModule *mod) {
+ bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_PUSHF);
+ mod->debug_context->last_op_was_rep = 0;
+}
+
+static void debug_leave_symbol_frame(OnyxWasmModule *mod) {
+ bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_POPF);
+ mod->debug_context->last_op_was_rep = 0;
+}
+
#else
+#define debug_introduce_symbol(mod, name, loc, num, type) (void)0
#define debug_get_file_id(mod, name) (void)0
#define debug_set_position(mod, token) (void)0
#define debug_emit_instruction(mod, token) (void)0
#define debug_begin_function(mod, idx, token, name) (void)0
#define debug_end_function(mod) (void)0
+#define debug_enter_symbol_frame(mod) (void)0
+#define debug_leave_symbol_frame(mod) (void)0
#endif
? SBT_Return_Block
: SBT_Breakable_Block,
block->token);
+ debug_enter_symbol_frame(mod);
}
forll (AstNode, stmt, block->body, next) {
emit_free_local_allocations(mod, &code);
}
- if (generate_block_headers)
+ if (generate_block_headers) {
emit_leave_structured_block(mod, &code);
+ debug_leave_symbol_frame(mod);
+ }
*pcode = code;
}
u64 local_idx = local_allocate(mod->local_alloc, stmt);
bh_imap_put(&mod->local_map, (u64) stmt, local_idx);
+ if (local_is_wasm_local(stmt)) {
+ debug_introduce_symbol(mod, stmt->token, DSL_REGISTER, local_idx, stmt->type);
+ } else {
+ debug_introduce_symbol(mod, stmt->token, DSL_STACK, local_idx, stmt->type);
+ }
+
if (stmt->kind == Ast_Kind_Local && !(stmt->flags & Ast_Flag_Decl_Followed_By_Init)) {
bh_arr(WasmInstruction) code = *pcode;
if (local_is_wasm_local(stmt)) {
u64 iter_local = local_allocate(mod->local_alloc, (AstTyped *) var);
bh_imap_put(&mod->local_map, (u64) var, iter_local);
+ debug_enter_symbol_frame(mod);
+ debug_introduce_symbol(mod, var->token,
+ local_is_wasm_local((AstTyped *) var) ? DSL_REGISTER : DSL_STACK,
+ iter_local, var->type);
+
emit_expression(mod, &code, for_node->iter);
switch (for_node->loop_type) {
}
local_free(mod->local_alloc, (AstTyped *) var);
+ debug_leave_symbol_frame(mod);
*pcode = code;
}
u64 bn = bh_imap_get(&block_map, (u64) sc->block);
+ // Maybe the Symbol Frame idea should be controlled as a block_flag?
+ debug_enter_symbol_frame(mod);
emit_block(mod, &code, sc->block, 0);
+ debug_leave_symbol_frame(mod);
if (bh_arr_last(code).type != WI_JUMP)
WID(NULL, WI_JUMP, block_num - bn);
bh_arr_push(mod->deferred_stmts, ((DeferredStmt) {
.type = Deferred_Stmt_Node,
.depth = bh_arr_length(mod->structured_jump_target),
+ .defer_node= defer,
.stmt = defer->stmt,
}));
}
bh_arr_push(mod->deferred_stmts, ((DeferredStmt) {
.type = Deferred_Stmt_Code,
.depth = bh_arr_length(mod->structured_jump_target),
+ .defer_node= NULL,
.instructions = deferred_code,
.instruction_count = code_count,
}));
EMIT_FUNC(deferred_stmt, DeferredStmt deferred_stmt) {
bh_arr(WasmInstruction) code = *pcode;
+ if (deferred_stmt.defer_node) {
+ WI(deferred_stmt.defer_node->token, WI_NOP);
+ }
+
switch (deferred_stmt.type) {
case Deferred_Stmt_Node: emit_statement(mod, &code, deferred_stmt.stmt); break;
case Deferred_Stmt_Code: {
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);
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);
bh_imap_put(&mod->local_map, (u64) param->local, localidx++ | LOCAL_IS_WASM);
break;
}
WasmFuncType* ft = mod->types[type_idx];
emit_zero_value(mod, &wasm_func.code, ft->return_type);
+ if (fd->closing_brace) {
+ debug_emit_instruction(mod, fd->closing_brace);
+ bh_arr_push(wasm_func.code, ((WasmInstruction){ WI_NOP, 0x00 }));
+ }
+
debug_emit_instruction(mod, NULL);
bh_arr_push(wasm_func.code, ((WasmInstruction){ WI_BLOCK_END, 0x00 }));
module.debug_context = bh_alloc_item(context.ast_alloc, DebugContext);
module.debug_context->allocator = global_heap_allocator;
module.debug_context->next_file_id = 0;
+ module.debug_context->next_sym_id = 0;
module.debug_context->last_token = NULL;
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->funcs, 16);
bh_buffer_init(&module.debug_context->op_buffer, global_heap_allocator, 1024);
bh_buffer_concat(buff, section_buff);
}
+ {
+ // ovm_debug_syms sections
+ bh_buffer_clear(§ion_buff);
+ bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
+
+ output_custom_section_name("ovm_debug_syms", §ion_buff);
+
+ i32 sym_count = bh_arr_length(ctx->sym_info);
+ output_unsigned_integer(sym_count, §ion_buff);
+
+ fori (i, 0, sym_count) {
+ DebugSymInfo *sym = &ctx->sym_info[i];
+ output_unsigned_integer(sym->sym_id, §ion_buff);
+ if (sym->name) {
+ output_name(sym->name, strlen(sym->name), §ion_buff);
+ } else {
+ output_unsigned_integer(0, §ion_buff);
+ }
+ output_unsigned_integer(sym->location_type, §ion_buff);
+ output_unsigned_integer(sym->location_num, §ion_buff);
+ output_unsigned_integer(sym->type, §ion_buff);
+ }
+
+ output_unsigned_integer(section_buff.length, buff);
+
+ bh_buffer_concat(buff, section_buff);
+ }
+
{
// ovm_debug_ops section
bh_buffer_clear(§ion_buff);