From bd2a5cc8b5046de654b42a857e8f409c1da68f85 Mon Sep 17 00:00:00 2001 From: dd86k Date: Mon, 5 Aug 2024 17:40:14 -0400 Subject: [PATCH 1/9] mi2: Follow tsc type recommendations for some parameters --- src/backend/mi2/mi2.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/backend/mi2/mi2.ts b/src/backend/mi2/mi2.ts index 706ae03..b2ca3aa 100644 --- a/src/backend/mi2/mi2.ts +++ b/src/backend/mi2/mi2.ts @@ -33,7 +33,7 @@ class LogMessage { protected logReplaceTest = /{([^}]*)}/g; public logMsgBrkList: Breakpoint[] = []; - logMsgOutput(record){ + logMsgOutput(record: { isStream?: boolean; type: any; asyncClass?: string; output?: [string, any][]; content: any; }){ if ((record.type === 'console')) { if(record.content.startsWith("$")){ const content = record.content; @@ -54,7 +54,7 @@ class LogMessage { } } - logMsgProcess(parsed){ + logMsgProcess(parsed: MINode){ this.logMsgBrkList.forEach((brk)=>{ if(parsed.outOfBandRecord[0].output[0][1] == "breakpoint-hit" && parsed.outOfBandRecord[0].output[2][1] == brk.id){ this.logMsgVar = brk?.logMessage; @@ -626,7 +626,7 @@ export class MI2 extends EventEmitter implements IBackend { return this.sendCommand("break-condition " + bkptNum + " " + condition); } - setLogPoint(bkptNum, command): Thenable { + setLogPoint(bkptNum: number, command: string): Thenable { const regex = /{([a-z0-9A-Z-_\.\>\&\*\[\]]*)}/gm; let m:RegExpExecArray; let commands:string = ""; From 91c2249100abbad89b950aca6d7319b2404d8a7b Mon Sep 17 00:00:00 2001 From: dd86k Date: Mon, 5 Aug 2024 18:08:37 -0400 Subject: [PATCH 2/9] Add Aliceserver as a test --- package.json | 148 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/package.json b/package.json index fb20330..04bc5ed 100644 --- a/package.json +++ b/package.json @@ -1087,6 +1087,154 @@ } } ] + }, + { + "type": "aliceserver-mi", + "program": "./out/src/aliceserver-mi.js", + "runtime": "node", + "label": "Aliceserver", + "languages": [ + "c", + "cpp", + "d" + ], + "configurationAttributes": { + "launch": { + "required": [ + "target" + ], + "properties": { + "target": { + "type": "string", + "description": "Path of executable" + }, + "arguments": { + "type": "string", + "description": "Arguments to append after the executable" + }, + "cwd": { + "type": "string", + "description": "project path" + }, + "aliceserverpath": { + "type": "string", + "description": "Path to the aliceserver executable or the command if in PATH", + "default": "aliceserver" + }, + "env": { + "type": "object", + "description": "Environment overriding the aliceserver (and in turn also the process) environment", + "default": null + }, + "debugger_args": { + "type": "array", + "description": "Additional arguments to pass to aliceserver", + "default": [ "-a", "mi" ] + } + } + }, + "attach": { + "required": [ + "target" + ], + "properties": { + "target": { + "type": "string", + "description": "PID of running program or program name" + }, + "valuesFormatting": { + "type": "string", + "description": "Set the way of showing variable values. 'disabled' - show value as is, 'parseText' - parse debuggers output text into structure, 'prettyPrinters' - enable debuggers custom pretty-printers if there are any", + "default": "parseText", + "enum": [ + "disabled", + "parseText", + "prettyPrinters" + ] + }, + "printCalls": { + "type": "boolean", + "description": "Prints all mago calls to the console", + "default": false + }, + "showDevDebugOutput": { + "type": "boolean", + "description": "Prints all mago responses to the console", + "default": false + }, + "executable": { + "type": "string", + "description": "Path of executable for debugging symbols" + }, + "magomipath": { + "type": "string", + "description": "Path to the mago-mi executable or the command if in PATH", + "default": "mago-mi" + }, + "env": { + "type": "object", + "description": "Environment overriding the mago-mi (and in turn also the process) environment", + "default": null + }, + "debugger_args": { + "type": "array", + "description": "Additional arguments to pass to mago", + "default": [] + }, + "cwd": { + "type": "string", + "description": "project path", + "default": "${workspaceRoot}" + }, + "autorun": { + "type": "array", + "description": "mago commands to run when starting to debug", + "default": [] + }, + "stopAtConnect": { + "type": "boolean", + "description": "Whether debugger should stop after connecting to target", + "default": false + } + } + } + }, + "initialConfigurations": [ + { + "name": "Debug", + "type": "mago-mi", + "request": "launch", + "target": "./bin/executable", + "cwd": "${workspaceRoot}", + "valuesFormatting": "parseText" + } + ], + "configurationSnippets": [ + { + "label": "Aliceserver: Launch Program", + "description": "Starts the program using aliceserver", + "body": { + "type": "aliceserver", + "request": "launch", + "name": "${2:Launch Program}", + "target": "${1:./executable}", + "cwd": "^\"\\${workspaceRoot}\"", + "valuesFormatting": "parseText" + } + }, + { + "label": "Aliceserver: Attach to PID", + "description": "Attaches to a running program pid using aliceserver", + "body": { + "type": "aliceserver", + "request": "attach", + "name": "${2:Attach to PID}", + "target": "${1:[PID]}", + "cwd": "^\"\\${workspaceRoot}\"", + "valuesFormatting": "parseText" + } + } + ] } ] }, From bc62abe201c130e7b024b94bb2d736dd10d4c2e4 Mon Sep 17 00:00:00 2001 From: dd86k Date: Tue, 6 Aug 2024 23:53:35 -0400 Subject: [PATCH 3/9] Update Initial configuration --- package.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 04bc5ed..7b791c4 100644 --- a/package.json +++ b/package.json @@ -1202,11 +1202,10 @@ "initialConfigurations": [ { "name": "Debug", - "type": "mago-mi", + "type": "aliceserver-mi", "request": "launch", "target": "./bin/executable", - "cwd": "${workspaceRoot}", - "valuesFormatting": "parseText" + "cwd": "${workspaceRoot}" } ], "configurationSnippets": [ From 6fa64205906409a5e3b64e673a00320848cbb86b Mon Sep 17 00:00:00 2001 From: dd86k Date: Thu, 8 Aug 2024 12:21:08 -0400 Subject: [PATCH 4/9] Add code to support Aliceserver --- package.json | 4 +- src/aliceserver.ts | 130 ++++++++++++++++++++++++++++++ src/backend/mi2/mi2aliceserver.ts | 75 +++++++++++++++++ 3 files changed, 207 insertions(+), 2 deletions(-) create mode 100644 src/aliceserver.ts create mode 100644 src/backend/mi2/mi2aliceserver.ts diff --git a/package.json b/package.json index 7b791c4..fd59a6a 100644 --- a/package.json +++ b/package.json @@ -1090,7 +1090,7 @@ }, { "type": "aliceserver-mi", - "program": "./out/src/aliceserver-mi.js", + "program": "./out/src/aliceserver.js", "runtime": "node", "label": "Aliceserver", "languages": [ @@ -1129,7 +1129,7 @@ "debugger_args": { "type": "array", "description": "Additional arguments to pass to aliceserver", - "default": [ "-a", "mi" ] + "default": [] } } }, diff --git a/src/aliceserver.ts b/src/aliceserver.ts new file mode 100644 index 0000000..9fa325c --- /dev/null +++ b/src/aliceserver.ts @@ -0,0 +1,130 @@ +import { MI2DebugSession, RunCommand } from './mibase'; +import { DebugSession, InitializedEvent, TerminatedEvent, StoppedEvent, OutputEvent, Thread, StackFrame, Scope, Source, Handles } from 'vscode-debugadapter'; +import { DebugProtocol } from 'vscode-debugprotocol'; +import { MI2_ALICE } from "./backend/mi2/mi2aliceserver"; +import { SSHArguments, ValuesFormattingMode } from './backend/backend'; + +export interface LaunchRequestArguments extends DebugProtocol.LaunchRequestArguments { + cwd: string; + target: string; + aliceserverpath: string; + env: any; + debugger_args: string[]; + pathSubstitutions: { [index: string]: string }; + arguments: string; + autorun: string[]; + stopAtEntry: boolean | string; + ssh: SSHArguments; + valuesFormatting: ValuesFormattingMode; + printCalls: boolean; + showDevDebugOutput: boolean; +} + +export interface AttachRequestArguments extends DebugProtocol.AttachRequestArguments { + cwd: string; + target: string; + aliceserverpath: string; + env: any; + debugger_args: string[]; + pathSubstitutions: { [index: string]: string }; + executable: string; + autorun: string[]; + stopAtConnect: boolean; + stopAtEntry: boolean | string; + valuesFormatting: ValuesFormattingMode; + printCalls: boolean; + showDevDebugOutput: boolean; +} + +class AliceserverDebugSession extends MI2DebugSession { + protected override initializeRequest(response: DebugProtocol.InitializeResponse, args: DebugProtocol.InitializeRequestArguments): void { + response.body.supportsGotoTargetsRequest = true; + response.body.supportsHitConditionalBreakpoints = true; + response.body.supportsConfigurationDoneRequest = true; + response.body.supportsConditionalBreakpoints = true; + response.body.supportsFunctionBreakpoints = true; + response.body.supportsEvaluateForHovers = true; + this.sendResponse(response); + } + + protected override launchRequest(response: DebugProtocol.LaunchResponse, args: LaunchRequestArguments): void { + const dbgCommand = args.aliceserverpath || "lldb-mi"; + if (this.checkCommand(dbgCommand)) { + this.sendErrorResponse(response, 104, `Configured debugger ${dbgCommand} not found.`); + return; + } + this.miDebugger = new MI2_ALICE(dbgCommand, [], args.debugger_args, args.env); + this.setPathSubstitutions(args.pathSubstitutions); + this.initDebugger(); + this.quit = false; + this.attached = false; + this.initialRunCommand = RunCommand.RUN; + this.isSSH = false; + this.started = false; + this.crashed = false; + this.setValuesFormattingMode(args.valuesFormatting); + this.miDebugger.printCalls = !!args.printCalls; + this.miDebugger.debugOutput = !!args.showDevDebugOutput; + this.stopAtEntry = args.stopAtEntry; + if (args.ssh !== undefined) { + if (args.ssh.forwardX11 === undefined) + args.ssh.forwardX11 = true; + if (args.ssh.port === undefined) + args.ssh.port = 22; + if (args.ssh.x11port === undefined) + args.ssh.x11port = 6000; + if (args.ssh.x11host === undefined) + args.ssh.x11host = "localhost"; + if (args.ssh.remotex11screen === undefined) + args.ssh.remotex11screen = 0; + this.isSSH = true; + this.setSourceFileMap(args.ssh.sourceFileMap, args.ssh.cwd, args.cwd); + this.miDebugger.ssh(args.ssh, args.ssh.cwd, args.target, args.arguments, undefined, false, args.autorun || []).then(() => { + this.sendResponse(response); + }, err => { + this.sendErrorResponse(response, 106, `Failed to SSH: ${err.toString()}`); + }); + } else { + this.miDebugger.load(args.cwd, args.target, args.arguments, undefined, args.autorun || []).then(() => { + this.sendResponse(response); + }, err => { + this.sendErrorResponse(response, 107, `Failed to load MI Debugger: ${err.toString()}`); + }); + } + } + + protected override attachRequest(response: DebugProtocol.AttachResponse, args: AttachRequestArguments): void { + const dbgCommand = args.aliceserverpath || "lldb-mi"; + if (this.checkCommand(dbgCommand)) { + this.sendErrorResponse(response, 104, `Configured debugger ${dbgCommand} not found.`); + return; + } + this.miDebugger = new MI2_ALICE(dbgCommand, [], args.debugger_args, args.env); + this.setPathSubstitutions(args.pathSubstitutions); + this.initDebugger(); + this.quit = false; + this.attached = true; + this.initialRunCommand = args.stopAtConnect ? RunCommand.NONE : RunCommand.CONTINUE; + this.isSSH = false; + this.setValuesFormattingMode(args.valuesFormatting); + this.miDebugger.printCalls = !!args.printCalls; + this.miDebugger.debugOutput = !!args.showDevDebugOutput; + this.stopAtEntry = args.stopAtEntry; + this.miDebugger.attach(args.cwd, args.executable, args.target, args.autorun || []).then(() => { + this.sendResponse(response); + }, err => { + this.sendErrorResponse(response, 108, `Failed to attach: ${err.toString()}`); + }); + } + + // Add extra commands for source file path substitution in LLDB-specific syntax + protected setPathSubstitutions(substitutions: { [index: string]: string }): void { + if (substitutions) { + Object.keys(substitutions).forEach(source => { + this.miDebugger.extraCommands.push("settings append target.source-map " + source + " " + substitutions[source]); + }); + } + } +} + +DebugSession.run(AliceserverDebugSession); diff --git a/src/backend/mi2/mi2aliceserver.ts b/src/backend/mi2/mi2aliceserver.ts new file mode 100644 index 0000000..edebac9 --- /dev/null +++ b/src/backend/mi2/mi2aliceserver.ts @@ -0,0 +1,75 @@ +import { MI2, escape } from "./mi2"; +import { Breakpoint } from "../backend"; +import * as ChildProcess from "child_process"; +import * as path from "path"; +import { MINode } from "../mi_parse"; + +export class MI2_ALICE extends MI2 { + protected override initCommands(target: string, cwd: string, attach: boolean = false) { + // We need to account for the possibility of the path type used by the debugger being different + // than the path type where the extension is running (e.g., SSH from Linux to Windows machine). + // Since the CWD is expected to be an absolute path in the debugger's environment, we can test + // that to determine the path type used by the debugger and use the result of that test to + // select the correct API to check whether the target path is an absolute path. + const debuggerPath = path.posix.isAbsolute(cwd) ? path.posix : path.win32; + + if (!debuggerPath.isAbsolute(target)) + target = debuggerPath.join(cwd, target); + + const cmds = [ + // Aliceserver is already async by default + //this.sendCommand("gdb-set target-async on"), + new Promise(resolve => { + this.sendCommand("list-features").then(done => { + this.features = done.result("features"); + resolve(undefined); + }, err => { + this.features = []; + resolve(undefined); + }); + }) as Thenable + ]; + if (!attach) + cmds.push(this.sendCommand("file-exec-and-symbols \"" + escape(target) + "\"")); + for (const cmd of this.extraCommands) { + cmds.push(this.sendCliCommand(cmd)); + } + return cmds; + } + + override attach(cwd: string, executable: string, target: string, autorun: string[]): Thenable { + return new Promise((resolve, reject) => { + const args = this.preargs.concat(this.extraargs || []); + this.process = ChildProcess.spawn(this.application, args, { cwd: cwd, env: this.procEnv }); + this.process.stdout.on("data", this.stdout.bind(this)); + this.process.stderr.on("data", this.stderr.bind(this)); + this.process.on("exit", () => this.emit("quit")); + this.process.on("error", err => this.emit("launcherror", err)); + const promises = this.initCommands(target, cwd, true); + promises.push(this.sendCommand("file-exec-and-symbols \"" + escape(executable) + "\"")); + promises.push(this.sendCommand("attach " + target)); + promises.push(...autorun.map(value => { return this.sendUserInput(value); })); + Promise.all(promises).then(() => { + this.emit("debug-ready"); + resolve(undefined); + }, reject); + }); + } + + override setBreakPointCondition(bkptNum: number, condition: string): Thenable { + return this.sendCommand("break-condition " + bkptNum + " \"" + escape(condition) + "\" 1"); + } + + override goto(filename: string, line: number): Thenable { + return new Promise((resolve, reject) => { + // LLDB parses the file differently than GDB... + // GDB doesn't allow quoting only the file but only the whole argument + // LLDB doesn't allow quoting the whole argument but rather only the file + const target: string = (filename ? '"' + escape(filename) + '":' : "") + line; + this.sendCliCommand("jump " + target).then(() => { + this.emit("step-other", undefined); + resolve(true); + }, reject); + }); + } +} From 92e983a656ce05d6411fea0960bb74abaefc8dc6 Mon Sep 17 00:00:00 2001 From: dd86k Date: Mon, 26 Aug 2024 09:44:43 -0400 Subject: [PATCH 5/9] package: Add printCalls and showDevDebugOutput for aliceserver --- package.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/package.json b/package.json index fd59a6a..7408e81 100644 --- a/package.json +++ b/package.json @@ -1130,6 +1130,16 @@ "type": "array", "description": "Additional arguments to pass to aliceserver", "default": [] + }, + "printCalls": { + "type": "boolean", + "description": "Prints all aliceserver calls to the console", + "default": false + }, + "showDevDebugOutput": { + "type": "boolean", + "description": "Prints all aliceserver responses to the console", + "default": false } } }, From baa76e836555b8cab796809e6a3419848b262897 Mon Sep 17 00:00:00 2001 From: dd86k Date: Mon, 26 Aug 2024 09:44:53 -0400 Subject: [PATCH 6/9] Misc. progress --- src/aliceserver.ts | 22 ++++++++++++++-------- src/backend/mi2/mi2aliceserver.ts | 3 ++- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/aliceserver.ts b/src/aliceserver.ts index 9fa325c..1c4232a 100644 --- a/src/aliceserver.ts +++ b/src/aliceserver.ts @@ -48,13 +48,18 @@ class AliceserverDebugSession extends MI2DebugSession { } protected override launchRequest(response: DebugProtocol.LaunchResponse, args: LaunchRequestArguments): void { - const dbgCommand = args.aliceserverpath || "lldb-mi"; - if (this.checkCommand(dbgCommand)) { + const dbgCommand = args.aliceserverpath || "aliceserver"; + // NOTE: checkCommand + // On non-Win32 platforms, this uses `command -v`, which only confirms if + // commands, including full paths, are within PATH. + // This fix bypasses this check. + // Temporary fix. + if (args.aliceserverpath === undefined && this.checkCommand(dbgCommand)) { this.sendErrorResponse(response, 104, `Configured debugger ${dbgCommand} not found.`); return; } - this.miDebugger = new MI2_ALICE(dbgCommand, [], args.debugger_args, args.env); - this.setPathSubstitutions(args.pathSubstitutions); + this.miDebugger = new MI2_ALICE(dbgCommand, ["-a", "mi"], args.debugger_args, args.env); + //this.setPathSubstitutions(args.pathSubstitutions); this.initDebugger(); this.quit = false; this.attached = false; @@ -62,7 +67,7 @@ class AliceserverDebugSession extends MI2DebugSession { this.isSSH = false; this.started = false; this.crashed = false; - this.setValuesFormattingMode(args.valuesFormatting); + //this.setValuesFormattingMode(args.valuesFormatting); this.miDebugger.printCalls = !!args.printCalls; this.miDebugger.debugOutput = !!args.showDevDebugOutput; this.stopAtEntry = args.stopAtEntry; @@ -94,12 +99,13 @@ class AliceserverDebugSession extends MI2DebugSession { } protected override attachRequest(response: DebugProtocol.AttachResponse, args: AttachRequestArguments): void { - const dbgCommand = args.aliceserverpath || "lldb-mi"; - if (this.checkCommand(dbgCommand)) { + const dbgCommand = args.aliceserverpath || "aliceserver"; + // See NOTE in launchRequest + if (args.aliceserverpath === undefined && this.checkCommand(dbgCommand)) { this.sendErrorResponse(response, 104, `Configured debugger ${dbgCommand} not found.`); return; } - this.miDebugger = new MI2_ALICE(dbgCommand, [], args.debugger_args, args.env); + this.miDebugger = new MI2_ALICE(dbgCommand, ["-a", "mi"], args.debugger_args, args.env); this.setPathSubstitutions(args.pathSubstitutions); this.initDebugger(); this.quit = false; diff --git a/src/backend/mi2/mi2aliceserver.ts b/src/backend/mi2/mi2aliceserver.ts index edebac9..2857f93 100644 --- a/src/backend/mi2/mi2aliceserver.ts +++ b/src/backend/mi2/mi2aliceserver.ts @@ -27,7 +27,8 @@ export class MI2_ALICE extends MI2 { this.features = []; resolve(undefined); }); - }) as Thenable + }) as Thenable, + //this.sendCommand("environment-directory \"" + escape(cwd) + "\"", true) ]; if (!attach) cmds.push(this.sendCommand("file-exec-and-symbols \"" + escape(target) + "\"")); From f9e183f319a3051af8355a52ef53f118599d4c56 Mon Sep 17 00:00:00 2001 From: dd86k Date: Wed, 28 Aug 2024 12:06:11 -0400 Subject: [PATCH 7/9] package: forgot to set proper descriptions --- package.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index cd8694a..a77a8cd 100644 --- a/package.json +++ b/package.json @@ -1133,12 +1133,12 @@ }, "env": { "type": "object", - "description": "Environment overriding the aliceserver (and in turn also the process) environment", + "description": "Environment overriding the aliceserver environment variables", "default": null }, "debugger_args": { "type": "array", - "description": "Additional arguments to pass to aliceserver", + "description": "Additional arguments to pass to aliceserver specifically", "default": [] }, "printCalls": { @@ -1174,26 +1174,26 @@ }, "printCalls": { "type": "boolean", - "description": "Prints all mago calls to the console", + "description": "Prints all aliceserver calls to the console", "default": false }, "showDevDebugOutput": { "type": "boolean", - "description": "Prints all mago responses to the console", + "description": "Prints all aliceserver responses to the console", "default": false }, "executable": { "type": "string", "description": "Path of executable for debugging symbols" }, - "magomipath": { + "aliceserverpath": { "type": "string", - "description": "Path to the mago-mi executable or the command if in PATH", - "default": "mago-mi" + "description": "Path to the aliceserver executable or the command if in PATH", + "default": "aliceservere" }, "env": { "type": "object", - "description": "Environment overriding the mago-mi (and in turn also the process) environment", + "description": "Environment overriding the aliceserver environment variables", "default": null }, "debugger_args": { @@ -1208,7 +1208,7 @@ }, "autorun": { "type": "array", - "description": "mago commands to run when starting to debug", + "description": "aliceserver commands to run when starting to debug", "default": [] }, "stopAtConnect": { From e2e2a00cf51df2f5658f27cb88db7b8c78d99507 Mon Sep 17 00:00:00 2001 From: dd86k Date: Wed, 28 Aug 2024 16:01:06 -0400 Subject: [PATCH 8/9] Progress on launching requests --- package.json | 4 +- src/aliceserver.ts | 106 ++++++++++++++++-------------- src/backend/mi2/mi2aliceserver.ts | 32 +++++++-- 3 files changed, 85 insertions(+), 57 deletions(-) diff --git a/package.json b/package.json index a77a8cd..8da0ba4 100644 --- a/package.json +++ b/package.json @@ -1126,7 +1126,7 @@ "type": "string", "description": "project path" }, - "aliceserverpath": { + "aliceserver_path": { "type": "string", "description": "Path to the aliceserver executable or the command if in PATH", "default": "aliceserver" @@ -1138,7 +1138,7 @@ }, "debugger_args": { "type": "array", - "description": "Additional arguments to pass to aliceserver specifically", + "description": "Additional arguments to pass to specifically to aliceserver", "default": [] }, "printCalls": { diff --git a/src/aliceserver.ts b/src/aliceserver.ts index 1c4232a..4def9c6 100644 --- a/src/aliceserver.ts +++ b/src/aliceserver.ts @@ -1,17 +1,22 @@ +// Manages a debugging session using Aliceserver. +// +// This imports and uses the MI2_ALICE class to manage its session using +// supported commands. + import { MI2DebugSession, RunCommand } from './mibase'; import { DebugSession, InitializedEvent, TerminatedEvent, StoppedEvent, OutputEvent, Thread, StackFrame, Scope, Source, Handles } from 'vscode-debugadapter'; import { DebugProtocol } from 'vscode-debugprotocol'; import { MI2_ALICE } from "./backend/mi2/mi2aliceserver"; import { SSHArguments, ValuesFormattingMode } from './backend/backend'; +import { execSync } from 'child_process'; // Temporary import export interface LaunchRequestArguments extends DebugProtocol.LaunchRequestArguments { cwd: string; target: string; - aliceserverpath: string; + target_arguments: string; + aliceserver_path: string; env: any; debugger_args: string[]; - pathSubstitutions: { [index: string]: string }; - arguments: string; autorun: string[]; stopAtEntry: boolean | string; ssh: SSHArguments; @@ -23,74 +28,80 @@ export interface LaunchRequestArguments extends DebugProtocol.LaunchRequestArgum export interface AttachRequestArguments extends DebugProtocol.AttachRequestArguments { cwd: string; target: string; - aliceserverpath: string; + aliceserver_path: string; env: any; debugger_args: string[]; - pathSubstitutions: { [index: string]: string }; executable: string; autorun: string[]; stopAtConnect: boolean; stopAtEntry: boolean | string; - valuesFormatting: ValuesFormattingMode; printCalls: boolean; showDevDebugOutput: boolean; } class AliceserverDebugSession extends MI2DebugSession { protected override initializeRequest(response: DebugProtocol.InitializeResponse, args: DebugProtocol.InitializeRequestArguments): void { - response.body.supportsGotoTargetsRequest = true; - response.body.supportsHitConditionalBreakpoints = true; - response.body.supportsConfigurationDoneRequest = true; - response.body.supportsConditionalBreakpoints = true; - response.body.supportsFunctionBreakpoints = true; - response.body.supportsEvaluateForHovers = true; + response.body.supportsGotoTargetsRequest = false; + response.body.supportsHitConditionalBreakpoints = false; + response.body.supportsConfigurationDoneRequest = false; + response.body.supportsConditionalBreakpoints = false; + response.body.supportsFunctionBreakpoints = false; + response.body.supportsEvaluateForHovers = false; this.sendResponse(response); } + // NOTE: Temporary fix that allows absolute executable paths outside of PATH + // Until Aliceserver is fully implemented. + // This fix bypasses the PATH check (performed by Windows' WHERE and + // POSIX's command(1)) by directly invoking the compiler. + protected checkCommand(debuggerName: string): boolean { + try { + execSync(`${debuggerName} --version`, { stdio: 'ignore' }); + return true; + } catch (error) { + return false; + } + } + protected override launchRequest(response: DebugProtocol.LaunchResponse, args: LaunchRequestArguments): void { - const dbgCommand = args.aliceserverpath || "aliceserver"; - // NOTE: checkCommand - // On non-Win32 platforms, this uses `command -v`, which only confirms if - // commands, including full paths, are within PATH. - // This fix bypasses this check. - // Temporary fix. - if (args.aliceserverpath === undefined && this.checkCommand(dbgCommand)) { + const dbgCommand = args.aliceserver_path || "aliceserver"; + if (args.aliceserver_path === undefined && this.checkCommand(dbgCommand)) { this.sendErrorResponse(response, 104, `Configured debugger ${dbgCommand} not found.`); return; } - this.miDebugger = new MI2_ALICE(dbgCommand, ["-a", "mi"], args.debugger_args, args.env); - //this.setPathSubstitutions(args.pathSubstitutions); + + this.miDebugger = new MI2_ALICE(dbgCommand, [ "-a", "mi" ], args.debugger_args, args.env); this.initDebugger(); + + // Defaults this.quit = false; this.attached = false; this.initialRunCommand = RunCommand.RUN; this.isSSH = false; this.started = false; this.crashed = false; - //this.setValuesFormattingMode(args.valuesFormatting); this.miDebugger.printCalls = !!args.printCalls; this.miDebugger.debugOutput = !!args.showDevDebugOutput; this.stopAtEntry = args.stopAtEntry; - if (args.ssh !== undefined) { - if (args.ssh.forwardX11 === undefined) - args.ssh.forwardX11 = true; - if (args.ssh.port === undefined) - args.ssh.port = 22; - if (args.ssh.x11port === undefined) - args.ssh.x11port = 6000; - if (args.ssh.x11host === undefined) - args.ssh.x11host = "localhost"; - if (args.ssh.remotex11screen === undefined) - args.ssh.remotex11screen = 0; - this.isSSH = true; + + // Initiate session + this.isSSH = args.ssh !== undefined; + if (this.isSSH) { + // Set defaults if these are unset + args.ssh.forwardX11 ??= true; + args.ssh.port ??= 22; + args.ssh.x11port ??= 6000; + args.ssh.x11host ??= "localhost"; + args.ssh.remotex11screen ??= 0; + this.setSourceFileMap(args.ssh.sourceFileMap, args.ssh.cwd, args.cwd); - this.miDebugger.ssh(args.ssh, args.ssh.cwd, args.target, args.arguments, undefined, false, args.autorun || []).then(() => { + this.miDebugger.ssh(args.ssh, args.ssh.cwd, args.target, args.target_arguments, undefined, false, args.autorun || []).then(() => { this.sendResponse(response); }, err => { this.sendErrorResponse(response, 106, `Failed to SSH: ${err.toString()}`); }); - } else { - this.miDebugger.load(args.cwd, args.target, args.arguments, undefined, args.autorun || []).then(() => { + } else { // Local session + this.miDebugger.load(args.cwd, args.target, args.target_arguments, undefined, args.autorun || []).then(() => { this.sendResponse(response); }, err => { this.sendErrorResponse(response, 107, `Failed to load MI Debugger: ${err.toString()}`); @@ -99,38 +110,31 @@ class AliceserverDebugSession extends MI2DebugSession { } protected override attachRequest(response: DebugProtocol.AttachResponse, args: AttachRequestArguments): void { - const dbgCommand = args.aliceserverpath || "aliceserver"; - // See NOTE in launchRequest - if (args.aliceserverpath === undefined && this.checkCommand(dbgCommand)) { + const dbgCommand = args.aliceserver_path || "aliceserver"; + if (args.aliceserver_path === undefined && this.checkCommand(dbgCommand)) { this.sendErrorResponse(response, 104, `Configured debugger ${dbgCommand} not found.`); return; } + this.miDebugger = new MI2_ALICE(dbgCommand, ["-a", "mi"], args.debugger_args, args.env); - this.setPathSubstitutions(args.pathSubstitutions); this.initDebugger(); + + // Defaults this.quit = false; this.attached = true; this.initialRunCommand = args.stopAtConnect ? RunCommand.NONE : RunCommand.CONTINUE; this.isSSH = false; - this.setValuesFormattingMode(args.valuesFormatting); this.miDebugger.printCalls = !!args.printCalls; this.miDebugger.debugOutput = !!args.showDevDebugOutput; this.stopAtEntry = args.stopAtEntry; + + // Start session this.miDebugger.attach(args.cwd, args.executable, args.target, args.autorun || []).then(() => { this.sendResponse(response); }, err => { this.sendErrorResponse(response, 108, `Failed to attach: ${err.toString()}`); }); } - - // Add extra commands for source file path substitution in LLDB-specific syntax - protected setPathSubstitutions(substitutions: { [index: string]: string }): void { - if (substitutions) { - Object.keys(substitutions).forEach(source => { - this.miDebugger.extraCommands.push("settings append target.source-map " + source + " " + substitutions[source]); - }); - } - } } DebugSession.run(AliceserverDebugSession); diff --git a/src/backend/mi2/mi2aliceserver.ts b/src/backend/mi2/mi2aliceserver.ts index 2857f93..3add7b3 100644 --- a/src/backend/mi2/mi2aliceserver.ts +++ b/src/backend/mi2/mi2aliceserver.ts @@ -1,3 +1,5 @@ +// Directly manages an Aliceserver instance by managing MI requests. + import { MI2, escape } from "./mi2"; import { Breakpoint } from "../backend"; import * as ChildProcess from "child_process"; @@ -19,6 +21,8 @@ export class MI2_ALICE extends MI2 { const cmds = [ // Aliceserver is already async by default //this.sendCommand("gdb-set target-async on"), + + /* Format unknown since I'm too lazy to compile lldb-mi new Promise(resolve => { this.sendCommand("list-features").then(done => { this.features = done.result("features"); @@ -28,16 +32,36 @@ export class MI2_ALICE extends MI2 { resolve(undefined); }); }) as Thenable, + */ + + // TODO: environment-directory + // Command not currently supported //this.sendCommand("environment-directory \"" + escape(cwd) + "\"", true) - ]; - if (!attach) + ] as Thenable[]; + if (!attach) // When launching cmds.push(this.sendCommand("file-exec-and-symbols \"" + escape(target) + "\"")); - for (const cmd of this.extraCommands) { + for (const cmd of this.extraCommands) // For the target process cmds.push(this.sendCliCommand(cmd)); - } return cmds; } + // Start debugging target + override start(runToStart: boolean): Thenable { + const options: string[] = []; + if (runToStart) + options.push("--start"); + const startCommand: string = ["exec-run"].concat(options).join(" "); + return new Promise((resolve, reject) => { + this.log("console", "Running executable"); + this.sendCommand(startCommand).then((info) => { + if (info.resultRecords.resultClass == "running") + resolve(undefined); + else + reject(); + }, reject); + }); + } + override attach(cwd: string, executable: string, target: string, autorun: string[]): Thenable { return new Promise((resolve, reject) => { const args = this.preargs.concat(this.extraargs || []); From f79eb5946cf5db70ceee5aa3fb183af540afd8d6 Mon Sep 17 00:00:00 2001 From: dd86k Date: Thu, 29 Aug 2024 20:33:57 -0400 Subject: [PATCH 9/9] mi2aliceserver: Redo MI2.stop() with a try-catch when killing debugger process --- src/backend/mi2/mi2aliceserver.ts | 33 +++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/backend/mi2/mi2aliceserver.ts b/src/backend/mi2/mi2aliceserver.ts index 3add7b3..3979135 100644 --- a/src/backend/mi2/mi2aliceserver.ts +++ b/src/backend/mi2/mi2aliceserver.ts @@ -81,6 +81,39 @@ export class MI2_ALICE extends MI2 { }); } + override stop(): void { + this.sendRaw("-gdb-exit"); + if (this.isSSH) { + const proc = this.stream; + const to = setTimeout(() => { + proc.signal("KILL"); + }, 1000); + this.stream.on("exit", function (code) { + clearTimeout(to); + }); + } else { + const proc = this.process; + const to = setTimeout(() => { + // When tinkering with Aliceserver: + // - the proc.pid field might be undefined (when exited too early) + // - the process could no longer be found after sending requests (crashed or exited) + try + { + process.kill(-proc.pid); + } + catch (error) + { + // Warning, since it does not prevent the intent of + // continuing to shut down the server. + console.warn("Failed to terminate process: " + error); + } + }, 1000); + this.process.on("exit", function (code) { + clearTimeout(to); + }); + } + } + override setBreakPointCondition(bkptNum: number, condition: string): Thenable { return this.sendCommand("break-condition " + bkptNum + " \"" + escape(condition) + "\" 1"); }