From 47047adc066560649ed9122a7305c65a66eda531 Mon Sep 17 00:00:00 2001 From: xuhong Date: Wed, 12 Jun 2024 16:30:10 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E5=AE=9E=E7=8E=B0=E5=B0=86=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F=E8=BE=93=E5=85=A5=E8=BE=93=E5=87=BA=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E7=BB=91=E5=AE=9A=E5=9C=A8ide=E7=BB=88=E7=AB=AF=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/mi2/mi2.ts | 8 +++-- src/gdb.ts | 15 ++++++++- src/mibase.ts | 76 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 94 insertions(+), 5 deletions(-) diff --git a/src/backend/mi2/mi2.ts b/src/backend/mi2/mi2.ts index 3424e9e..6969a72 100644 --- a/src/backend/mi2/mi2.ts +++ b/src/backend/mi2/mi2.ts @@ -106,8 +106,8 @@ export class MI2 extends EventEmitter implements IBackend { resolve(undefined); }, reject); } else { - if (separateConsole !== undefined) { - linuxTerm.spawnTerminalEmulator(separateConsole).then(tty => { + if (separateConsole === 'external') { + linuxTerm.spawnTerminalEmulator('').then(tty => { promises.push(this.sendCommand("inferior-tty-set " + tty)); Promise.all(promises).then(() => { this.emit("debug-ready"); @@ -116,7 +116,9 @@ export class MI2 extends EventEmitter implements IBackend { }); } else { Promise.all(promises).then(() => { - this.emit("debug-ready"); + if(!this.application.includes('gdb')){ + this.emit("debug-ready"); + } resolve(undefined); }, reject); } diff --git a/src/gdb.ts b/src/gdb.ts index a1ecdfa..da19efe 100644 --- a/src/gdb.ts +++ b/src/gdb.ts @@ -44,6 +44,7 @@ export interface AttachRequestArguments extends DebugProtocol.AttachRequestArgum export class GDBDebugSession extends MI2DebugSession { public args:LaunchRequestArguments | AttachRequestArguments; protected disassember: GdbDisassembler; + protected supportsRunInTerminalRequest = false; protected initializeRequest(response: DebugProtocol.InitializeResponse, args: DebugProtocol.InitializeRequestArguments): void { response.body.supportsGotoTargetsRequest = true; @@ -63,9 +64,10 @@ export class GDBDebugSession extends MI2DebugSession { response.body.supportsDisassembleRequest = true; response.body.supportsReadMemoryRequest = true; response.body.supportsInstructionBreakpoints = true; - + args.supportsRunInTerminalRequest = true; this.sendResponse(response); } + // tslint:disable-next-line: max-line-length public sendErrorResponsePub(response: DebugProtocol.Response, codeOrMessage: number | DebugProtocol.Message, format?: string, variables?: any, dest?: any): void { this.sendErrorResponse(response, codeOrMessage, format, variables, dest); @@ -125,6 +127,17 @@ export class GDBDebugSession extends MI2DebugSession { args.autorun.forEach(command => { this.miDebugger.sendUserInput(command); }); + if(this.miDebugger.application === 'gdb'){ + if(args.terminal === 'integrated' || args.terminal === '' || args.terminal === undefined){ + let terminalRequestArgs:DebugProtocol.RunInTerminalRequestArguments = { + kind: "integrated", + title: this.miDebugger.application, + cwd: args.cwd || '', + args: [], + }; + this.createIntegratedTerminalLinux(terminalRequestArgs); + } + } this.sendResponse(response); }, err => { this.sendErrorResponse(response, 103, `Failed to load MI Debugger: ${err.toString()}`); diff --git a/src/mibase.ts b/src/mibase.ts index 89babc8..c3fd60c 100644 --- a/src/mibase.ts +++ b/src/mibase.ts @@ -11,17 +11,19 @@ import * as net from "net"; import * as os from "os"; import * as fs from "fs"; import { hexFormat } from './backend/common'; +export type InferiorTerminal = 'integrated' | 'external' | ''; class ExtendedVariable { constructor(public name, public options) { } } - export enum RunCommand { CONTINUE, RUN, NONE } const STACK_HANDLES_START = 1000; const VAR_HANDLES_START = 512 * 256 + 1000; const VARIABLES_TAG_REGISTER = 0xab; + + export class MI2DebugSession extends DebugSession { protected variableHandles = new Handles(VAR_HANDLES_START); protected variableHandlesReverse: { [id: string]: number } = {}; @@ -853,6 +855,78 @@ export class MI2DebugSession extends DebugSession { } } + public async createIntegratedTerminalLinux(args:DebugProtocol.RunInTerminalRequestArguments) { + const mkdirAsync = fs.promises.mkdir; + const mkdtempAsync = async (tempDir: string, prefix: string): Promise => { + const name = `${prefix}-${Date.now()}-${Math.floor(Math.random() * 1e9)}`; + const newDirPath = `${tempDir}/${name}`; + + try { + await mkdirAsync(newDirPath, { recursive: true }); + return newDirPath; + } catch (err) { + throw new Error(`Error creating temp directory: ${err.message}`); + } + }; + const ttyTmpDir = await mkdtempAsync(os.tmpdir(), 'debug'); + + (async () => { + try { + fs.writeFileSync( + `${ttyTmpDir}/get-tty`, + `#!/usr/bin/env sh + clear + echo "The input and output of program will be here." + echo "Warning of set controlling terminal fail can be ignored." + tty > ${ttyTmpDir}/ttynameTmp + mv ${ttyTmpDir}/ttynameTmp ${ttyTmpDir}/ttyname + # wait for debug to finish + # prefer using tail to detect PID exit, but that requires GNU tail + tail -f --pid=${process.pid} /dev/null 2>/dev/null || while kill -s 0 ${process.pid} 2>/dev/null; do sleep 1s; done + # cleanup + rm ${ttyTmpDir}/ttyname + rm ${ttyTmpDir}/get-tty + rmdir ${ttyTmpDir} + ` + ); + + let watcher: fs.FSWatcher | undefined; + const ttyNamePromise = new Promise((resolve) => { + watcher = fs.watch(ttyTmpDir, (_eventType, filename) => { + if (filename === 'ttyname') { + watcher?.close(); + resolve( + fs.readFileSync(`${ttyTmpDir}/ttyname`).toString().trim() + ); + } + }); + }); + + args.args = ['/bin/sh', `${ttyTmpDir}/get-tty`]; + const response = await new Promise( + (resolve) => + this.sendRequest( + 'runInTerminal', + args, + 3000, + resolve + ) + ); + if (response.success) { + const tty = await ttyNamePromise; + this.miDebugger.emit("debug-ready"); + await this.miDebugger.sendCommand(`inferior-tty-set ${tty}`); + return; + } else { + watcher?.close(); + const message = `could not start the terminal on the client: ${response.message}`; + throw new Error(message); + } + } catch (err) { + console.error(err); + } + })(); + } } function prettyStringArray(strings) {