From 6009d3a26b3ffbde1810430f12513ff8c0c39118 Mon Sep 17 00:00:00 2001 From: Aleksandr Kovalev Date: Sun, 7 Aug 2022 20:31:58 +0100 Subject: [PATCH 1/7] pull support for breakpoints update on pipes --- debugger/debugger.ts | 26 ++++++++++++ debugger/lldebugger.ts | 5 +++ debugger/protocol.d.ts | 1 + extension/debugPipe.ts | 79 ++++++++++++++++++++++++++++++++++++ extension/launchConfig.ts | 1 + extension/luaDebugSession.ts | 43 +++++++++++++++----- package-lock.json | 4 +- package.json | 5 +++ 8 files changed, 152 insertions(+), 12 deletions(-) diff --git a/debugger/debugger.ts b/debugger/debugger.ts index dc6c4ad..8b3820c 100644 --- a/debugger/debugger.ts +++ b/debugger/debugger.ts @@ -76,6 +76,22 @@ export namespace Debugger { inputFile = io.stdin; } + const pullFileEnv: LuaDebug.PullFileEnv = "LOCAL_LUA_DEBUGGER_PULL_FILE"; + const pullFilePath = os.getenv(pullFileEnv); + let lastPullSeek = 0; + let pullFile: LuaFile | null; + if (pullFilePath && pullFilePath.length > 0) { + const [file, err] = io.open(pullFilePath, "r+"); + if (!file) { + luaError(`Failed to open pull file "${pullFilePath}": ${err}\n`); + } + pullFile = file as LuaFile; + pullFile.setvbuf("no"); + lastPullSeek = pullFile.seek("end")[0] as number; + } else { + pullFile = null; + } + let skipNextBreak = false; const enum HookType { @@ -1251,4 +1267,14 @@ export namespace Debugger { return luaError(message, 2); } } + + export function pullBreakpoints(): void { + if (pullFile) { + const newPullSeek = pullFile.seek("end")[0] as number; + if (newPullSeek > lastPullSeek) { + lastPullSeek = newPullSeek; + triggerBreak(); + } + } + } } diff --git a/debugger/lldebugger.ts b/debugger/lldebugger.ts index a940acb..2b3e6bf 100644 --- a/debugger/lldebugger.ts +++ b/debugger/lldebugger.ts @@ -55,6 +55,11 @@ export function stop(): void { Debugger.clearHook(); } +//Pull breakpoints change +export function pullBreakpoints():void { + Debugger.pullBreakpoints(); +} + //Load and debug the specified file export function runFile(filePath: unknown, breakImmediately?: boolean, arg?: unknown[]): LuaMultiReturn { if (typeof filePath !== "string") { diff --git a/debugger/protocol.d.ts b/debugger/protocol.d.ts index b56b4b4..879cea4 100644 --- a/debugger/protocol.d.ts +++ b/debugger/protocol.d.ts @@ -119,4 +119,5 @@ declare namespace LuaDebug { type StepUnmappedLinesEnv = "LOCAL_LUA_DEBUGGER_STEP_UNMAPPED_LINES"; type InputFileEnv = "LOCAL_LUA_DEBUGGER_INPUT_FILE"; type OutputFileEnv = "LOCAL_LUA_DEBUGGER_OUTPUT_FILE"; + type PullFileEnv = "LOCAL_LUA_DEBUGGER_PULL_FILE"; } diff --git a/extension/debugPipe.ts b/extension/debugPipe.ts index da8da86..bfa662c 100644 --- a/extension/debugPipe.ts +++ b/extension/debugPipe.ts @@ -7,19 +7,27 @@ export interface DebugPipe { open: (onData: (data: unknown) => void, onError: (err: unknown) => void) => void; close: () => void; write: (data: string) => void; + openPull: (onError: (err: unknown) => void) => void; + requestPull: () => void; getOutputPipePath: () => string; getInputPipePath: () => string; + getPullPipePath: () => string, } export function createNamedPipe(): DebugPipe { const pipeId = crypto.randomBytes(16).toString("hex"); const outputPipePath = `\\\\.\\pipe\\lldbg_out_${pipeId}`; const inputPipePath = `\\\\.\\pipe\\lldbg_in_${pipeId}`; + const pullPipePath = `\\\\.\\pipe\\lldbg_pull_${pipeId}`; let outputPipe: net.Server | null = null; let inputPipe: net.Server | null = null; + let pullPipe: net.Server | null = null; let inputStream: net.Socket | null; + let pullStream: net.Socket | null; + let onErrorCallback: ((err: unknown) => void) | null = null; return { open: (onData, onError) => { + onErrorCallback = onError; outputPipe = net.createServer( stream => { stream.on("data", onData); @@ -35,6 +43,22 @@ export function createNamedPipe(): DebugPipe { ); inputPipe.listen(inputPipePath); }, + openPull: (onError: (err: unknown) => void) => + { + if (!onErrorCallback) { + onErrorCallback = onError; + } + + pullPipe = net.createServer( + stream => { + stream.on("error", err =>{ + onError(`error on pull pipe: ${err}`) + }); + pullStream = stream; + } + ); + pullPipe.listen(pullPipePath); + }, close: () => { outputPipe?.close(); @@ -42,14 +66,20 @@ export function createNamedPipe(): DebugPipe { inputPipe?.close(); inputPipe = null; inputStream = null; + pullPipe = null; + pullStream = null; }, write: data => { inputStream?.write(data); }, + requestPull: () => { + pullStream?.write("pull|\n"); + }, getOutputPipePath: () => outputPipePath, getInputPipePath: () => inputPipePath, + getPullPipePath: () => pullPipePath, }; } @@ -57,9 +87,12 @@ export function createFifoPipe(): DebugPipe { const pipeId = crypto.randomBytes(16).toString("hex"); const outputPipePath = `/tmp/lldbg_out_${pipeId}`; const inputPipePath = `/tmp/lldbg_in_${pipeId}`; + const pullPipePath = `/tmp/lldbg_pull_${pipeId}`; let outputFd: number | null; let inputFd: number | null; + let pullFd: number | null; let inputStream: fs.WriteStream | null = null; + let pullStream: fs.WriteStream | null = null; let onErrorCallback: ((err: unknown) => void) | null = null; return { open: (onData, onError) => { @@ -113,7 +146,37 @@ export function createFifoPipe(): DebugPipe { ); } ); + }, + + openPull: (onError: (err: unknown) => void) => + { + if (!onErrorCallback) { + onErrorCallback = onError; + } + + childProcess.exec( + `mkfifo ${pullPipePath}`, + fifoErr => { + if (fifoErr) { + onError(`error executing mkfifo for input pipe: ${fifoErr}`); + return; + } + fs.open( + pullPipePath, + fs.constants.O_WRONLY, + (fdErr, fd) => { + if (fdErr) { + onError(`error opening fifo for pull pipe: ${fdErr}`); + return; + } + + pullFd = fd; + pullStream = fs.createWriteStream(null as unknown as fs.PathLike, {fd}); + } + ); + } + ); }, close: () => { @@ -141,13 +204,29 @@ export function createFifoPipe(): DebugPipe { } ); } + if (pullFd !== null) { + fs.close(pullFd); + pullFd = null; + fs.rm( + pullPipePath, + err => { + if (err) { + onErrorCallback?.(`error removing fifo for pull pipe: ${err}`); + } + } + ); + } }, write: data => { inputStream?.write(data); }, + requestPull: () => { + pullStream?.write("pull|\n"); + }, getOutputPipePath: () => outputPipePath, getInputPipePath: () => inputPipePath, + getPullPipePath: () => pullPipePath, }; } diff --git a/extension/launchConfig.ts b/extension/launchConfig.ts index 40c6c9e..aa5bcbe 100644 --- a/extension/launchConfig.ts +++ b/extension/launchConfig.ts @@ -42,6 +42,7 @@ export interface LaunchConfig { verbose?: boolean; stopOnEntry?: boolean; breakInCoroutines?: boolean; + pullBreakpointsSupport?: boolean; stepUnmappedLines?: boolean; scriptFiles?: string[]; ignorePatterns?: string[]; diff --git a/extension/luaDebugSession.ts b/extension/luaDebugSession.ts index c887992..9783c87 100644 --- a/extension/luaDebugSession.ts +++ b/extension/luaDebugSession.ts @@ -76,6 +76,7 @@ const breakInCoroutinesEnv: LuaDebug.BreakInCoroutinesEnv = "LOCAL_LUA_DEBUGGER_ const stepUnmappedLinesEnv: LuaDebug.StepUnmappedLinesEnv = "LOCAL_LUA_DEBUGGER_STEP_UNMAPPED_LINES"; const inputFileEnv: LuaDebug.InputFileEnv = "LOCAL_LUA_DEBUGGER_INPUT_FILE"; const outputFileEnv: LuaDebug.OutputFileEnv = "LOCAL_LUA_DEBUGGER_OUTPUT_FILE"; +const pullFileEnv: LuaDebug.PullFileEnv = "LOCAL_LUA_DEBUGGER_PULL_FILE"; function getEnvKey(env: NodeJS.ProcessEnv, searchKey: string) { const upperSearchKey = searchKey.toUpperCase(); @@ -146,6 +147,8 @@ export class LuaDebugSession extends LoggingDebugSession { private autoContinueNext = false; private readonly activeThreads = new Map(); private isRunning = false; + private pullBreakpointsSupport = false; + private usePipeCommutication = false; public constructor() { super("lldebugger-log.txt"); @@ -223,6 +226,10 @@ export class LuaDebugSession extends LoggingDebugSession { } } + if (typeof this.config.pullBreakpointsSupport !== "undefined") { + this.pullBreakpointsSupport = this.config.pullBreakpointsSupport; + } + //Set an environment variable so the debugger can detect the attached extension processOptions.env[envVariable] = "1"; processOptions.env[filePathEnvVariable] @@ -239,20 +246,30 @@ export class LuaDebugSession extends LoggingDebugSession { processOptions.env[stepUnmappedLinesEnv] = this.config.stepUnmappedLines ? "1" : "0"; } + this.usePipeCommutication = this.config.program.communication === "pipe" + //Open pipes - if (this.config.program.communication === "pipe") { + if (this.usePipeCommutication || this.pullBreakpointsSupport) { if (process.platform === "win32") { this.debugPipe = createNamedPipe(); } else { this.debugPipe = createFifoPipe(); } - this.debugPipe.open( - data => { void this.onDebuggerOutput(data); }, - err => { this.showOutput(`${err}`, OutputCategory.Error); } - ); - processOptions.env[outputFileEnv] = this.debugPipe.getOutputPipePath(); - processOptions.env[inputFileEnv] = this.debugPipe.getInputPipePath(); + if (this.usePipeCommutication) { + this.debugPipe.open( + data => { void this.onDebuggerOutput(data); }, + err => { this.showOutput(`${err}`, OutputCategory.Error); } + ); + + processOptions.env[outputFileEnv] = this.debugPipe.getOutputPipePath(); + processOptions.env[inputFileEnv] = this.debugPipe.getInputPipePath(); + } + } + + if (this.pullBreakpointsSupport) { + this.debugPipe?.openPull(err => { this.showOutput(`${err}`, OutputCategory.Error); }) + processOptions.env[pullFileEnv] = this.debugPipe?.getPullPipePath(); } //Append lua path so it can find debugger script @@ -290,7 +307,7 @@ export class LuaDebugSession extends LoggingDebugSession { ); //Process callbacks - if (this.debugPipe) { + if (this.usePipeCommutication) { this.assert(this.process.stdout).on("data", data => { this.showOutput(`${data}`, OutputCategory.StdOut); }); } else { this.assert(this.process.stdout).on("data", data => { void this.onDebuggerOutput(data); }); @@ -322,6 +339,12 @@ export class LuaDebugSession extends LoggingDebugSession { const filePath = args.source.path as string; if (this.process !== null && !this.isRunning) { + if (this.pullBreakpointsSupport) { + this.breakpointsPending = true; + this.autoContinueNext = true; + this.debugPipe?.requestPull(); + } + const oldBreakpoints = this.fileBreakpoints[filePath]; if (typeof oldBreakpoints !== "undefined") { for (const breakpoint of oldBreakpoints) { @@ -940,8 +963,8 @@ export class LuaDebugSession extends LoggingDebugSession { } this.showOutput(cmd, OutputCategory.Command); - if (this.debugPipe) { - this.debugPipe.write(`${cmd}\n`); + if (this.usePipeCommutication) { + this.debugPipe?.write(`${cmd}\n`); } else { this.assert(this.process.stdin).write(`${cmd}\n`); } diff --git a/package-lock.json b/package-lock.json index 66423e6..0aa31f8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "local-lua-debugger-vscode", - "version": "0.3.1", + "version": "0.3.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "local-lua-debugger-vscode", - "version": "0.3.1", + "version": "0.3.3", "license": "MIT", "dependencies": { "vscode-debugadapter": "^1.48.0" diff --git a/package.json b/package.json index 5b61c09..304be11 100644 --- a/package.json +++ b/package.json @@ -117,6 +117,11 @@ "description": "Break on errors inside of coroutines", "default": true }, + "pullBreakpointsSupport": { + "type": "boolean", + "description": "Runtime supports pulling of breakpoints. (need to periodically call lldebugger.pullBreakpoints())", + "default": true + }, "scriptFiles": { "type": "array", "items": { From 42a03cc96713d05cf5e4ff07d939e282965e82f3 Mon Sep 17 00:00:00 2001 From: Aleksandr Kovalev Date: Mon, 8 Aug 2022 10:15:50 +0100 Subject: [PATCH 2/7] synk default value pullBreakpointsSupport with settings --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 304be11..30c6e92 100644 --- a/package.json +++ b/package.json @@ -120,7 +120,7 @@ "pullBreakpointsSupport": { "type": "boolean", "description": "Runtime supports pulling of breakpoints. (need to periodically call lldebugger.pullBreakpoints())", - "default": true + "default": false }, "scriptFiles": { "type": "array", From a2577904d80fed1ddf6b03a0d056cad20e481bdb Mon Sep 17 00:00:00 2001 From: Aleksandr Kovalev Date: Sat, 13 Aug 2022 17:19:10 +0100 Subject: [PATCH 3/7] Fix autocontinue on first breakpoint while breakpoint set inside breakpoint --- extension/luaDebugSession.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/extension/luaDebugSession.ts b/extension/luaDebugSession.ts index 9783c87..9adcd48 100644 --- a/extension/luaDebugSession.ts +++ b/extension/luaDebugSession.ts @@ -147,6 +147,7 @@ export class LuaDebugSession extends LoggingDebugSession { private autoContinueNext = false; private readonly activeThreads = new Map(); private isRunning = false; + private inDebuggerBreakpoint = false; private pullBreakpointsSupport = false; private usePipeCommutication = false; @@ -325,6 +326,7 @@ export class LuaDebugSession extends LoggingDebugSession { this.process.on("exit", (code, signal) => this.onDebuggerTerminated(`${code !== null ? code : signal}`)); this.isRunning = true; + this.inDebuggerBreakpoint = false; this.showOutput("process launched", OutputCategory.Info); this.sendResponse(response); @@ -339,7 +341,7 @@ export class LuaDebugSession extends LoggingDebugSession { const filePath = args.source.path as string; if (this.process !== null && !this.isRunning) { - if (this.pullBreakpointsSupport) { + if (!this.inDebuggerBreakpoint && this.pullBreakpointsSupport) { this.breakpointsPending = true; this.autoContinueNext = true; this.debugPipe?.requestPull(); @@ -359,7 +361,13 @@ export class LuaDebugSession extends LoggingDebugSession { } } else { - this.breakpointsPending = true; + if (this.pullBreakpointsSupport && this.process !== null) { + this.breakpointsPending = true; + this.autoContinueNext = true; + this.debugPipe?.requestPull(); + } else { + this.breakpointsPending = true; + } } this.fileBreakpoints[filePath] = args.breakpoints; @@ -574,6 +582,7 @@ export class LuaDebugSession extends LoggingDebugSession { if (this.sendCommand("cont")) { this.variableHandles.reset(); this.isRunning = true; + this.inDebuggerBreakpoint = false; } else { response.success = false; } @@ -585,6 +594,7 @@ export class LuaDebugSession extends LoggingDebugSession { if (this.sendCommand("step")) { this.variableHandles.reset(); this.isRunning = true; + this.inDebuggerBreakpoint = false; } else { response.success = false; } @@ -596,6 +606,7 @@ export class LuaDebugSession extends LoggingDebugSession { if (this.sendCommand("stepin")) { this.variableHandles.reset(); this.isRunning = true; + this.inDebuggerBreakpoint = false; } else { response.success = false; } @@ -607,6 +618,7 @@ export class LuaDebugSession extends LoggingDebugSession { if (this.sendCommand("stepout")) { this.variableHandles.reset(); this.isRunning = true; + this.inDebuggerBreakpoint = false; } else { response.success = false; } @@ -702,6 +714,7 @@ export class LuaDebugSession extends LoggingDebugSession { } this.isRunning = false; + this.inDebuggerBreakpoint = false; this.sendResponse(response); } @@ -839,6 +852,7 @@ export class LuaDebugSession extends LoggingDebugSession { private async onDebuggerStop(msg: LuaDebug.DebugBreak) { this.isRunning = false; + this.inDebuggerBreakpoint = true; if (this.pendingScripts) { for (const scriptFile of this.pendingScripts) { @@ -947,6 +961,7 @@ export class LuaDebugSession extends LoggingDebugSession { this.process = null; this.isRunning = false; + this.inDebuggerBreakpoint = false; if (this.outputText.length > 0) { this.showOutput(this.outputText, OutputCategory.StdOut); From a9cc3d5b9dee296f9402164df334459562669682 Mon Sep 17 00:00:00 2001 From: Aleksandr Kovalev Date: Sun, 13 Nov 2022 11:49:09 +0000 Subject: [PATCH 4/7] fix breakpoints pulling after first connect if no breakpoints were triggered --- extension/luaDebugSession.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extension/luaDebugSession.ts b/extension/luaDebugSession.ts index 9adcd48..aad099a 100644 --- a/extension/luaDebugSession.ts +++ b/extension/luaDebugSession.ts @@ -852,6 +852,7 @@ export class LuaDebugSession extends LoggingDebugSession { private async onDebuggerStop(msg: LuaDebug.DebugBreak) { this.isRunning = false; + let prevInDebugger = this.inDebuggerBreakpoint; this.inDebuggerBreakpoint = true; if (this.pendingScripts) { @@ -910,6 +911,7 @@ export class LuaDebugSession extends LoggingDebugSession { if (this.autoContinueNext) { this.autoContinueNext = false; + this.inDebuggerBreakpoint = prevInDebugger; this.assert(this.sendCommand("autocont")); } else { From 0970405d96068073cb8923d78cf457df83771e76 Mon Sep 17 00:00:00 2001 From: Aleksandr Kovalev Date: Sun, 13 Nov 2022 15:39:55 +0000 Subject: [PATCH 5/7] Do not disable debughook in luajit with pull breakpoints support enabled --- debugger/debugger.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/debugger/debugger.ts b/debugger/debugger.ts index 8b3820c..b65f5cd 100644 --- a/debugger/debugger.ts +++ b/debugger/debugger.ts @@ -491,6 +491,7 @@ export namespace Debugger { let breakAtDepth = -1; let breakInThread: Thread | undefined; let updateHook: { (): void }; + let isDebugHookDisabled = true; let ignorePatterns: string[] | undefined; let inDebugBreak = false; @@ -864,6 +865,10 @@ export namespace Debugger { const skipUnmappedLines = (os.getenv(stepUnmappedLinesEnv) !== "1"); function debugHook(event: "call" | "return" | "tail return" | "count" | "line", line?: number) { + if (isDebugHookDisabled) { + return; + } + //Stepping if (breakAtDepth >= 0) { const activeThread = getActiveThread(); @@ -1159,7 +1164,10 @@ export namespace Debugger { } updateHook = function() { - if (breakAtDepth < 0 && Breakpoint.getCount() === 0) { + isDebugHookDisabled = breakAtDepth < 0 && Breakpoint.getCount() === 0; + // Do not disable debugging in luajit environment with pull breakpoints support enabled + // or functions will be jitted and will lose debug info of lines and files + if (isDebugHookDisabled && (_G['jit'] === null || pullFile == null)) { debug.sethook(); for (const [thread] of pairs(threadIds)) { @@ -1189,6 +1197,7 @@ export namespace Debugger { coroutine.wrap = luaCoroutineWrap; coroutine.resume = luaCoroutineResume; + isDebugHookDisabled = true; debug.sethook(); for (const [thread] of pairs(threadIds)) { From abdb8165a2e5baa97aa26c577e04a1a1b36a3480 Mon Sep 17 00:00:00 2001 From: Aleksandr Kovalev Date: Mon, 14 Nov 2022 15:39:23 +0000 Subject: [PATCH 6/7] On mac and linux we use regular file to comminicate because fifo pipes have no support for non blocking read or seek --- debugger/debugger.ts | 11 ++++++-- debugger/lldebugger.ts | 2 +- extension/debugPipe.ts | 55 ++++++++++++++++++------------------ extension/luaDebugSession.ts | 10 +++---- 4 files changed, 41 insertions(+), 37 deletions(-) diff --git a/debugger/debugger.ts b/debugger/debugger.ts index b65f5cd..927d98d 100644 --- a/debugger/debugger.ts +++ b/debugger/debugger.ts @@ -87,7 +87,12 @@ export namespace Debugger { } pullFile = file as LuaFile; pullFile.setvbuf("no"); - lastPullSeek = pullFile.seek("end")[0] as number; + const [fileSize, errorSeek] = pullFile.seek("end"); + if (!fileSize) { + luaError(`Failed to read pull file "${pullFilePath}": ${errorSeek}\n`); + } else { + lastPullSeek = fileSize; + } } else { pullFile = null; } @@ -868,7 +873,7 @@ export namespace Debugger { if (isDebugHookDisabled) { return; } - + //Stepping if (breakAtDepth >= 0) { const activeThread = getActiveThread(); @@ -1167,7 +1172,7 @@ export namespace Debugger { isDebugHookDisabled = breakAtDepth < 0 && Breakpoint.getCount() === 0; // Do not disable debugging in luajit environment with pull breakpoints support enabled // or functions will be jitted and will lose debug info of lines and files - if (isDebugHookDisabled && (_G['jit'] === null || pullFile == null)) { + if (isDebugHookDisabled && (_G["jit"] === null || pullFile === null)) { debug.sethook(); for (const [thread] of pairs(threadIds)) { diff --git a/debugger/lldebugger.ts b/debugger/lldebugger.ts index 2b3e6bf..e63ea66 100644 --- a/debugger/lldebugger.ts +++ b/debugger/lldebugger.ts @@ -56,7 +56,7 @@ export function stop(): void { } //Pull breakpoints change -export function pullBreakpoints():void { +export function pullBreakpoints(): void { Debugger.pullBreakpoints(); } diff --git a/extension/debugPipe.ts b/extension/debugPipe.ts index bfa662c..500c71f 100644 --- a/extension/debugPipe.ts +++ b/extension/debugPipe.ts @@ -2,6 +2,8 @@ import * as crypto from "crypto"; import * as net from "net"; import * as childProcess from "child_process"; import * as fs from "fs"; +import * as os from "os"; +import * as path from "path"; export interface DebugPipe { open: (onData: (data: unknown) => void, onError: (err: unknown) => void) => void; @@ -11,7 +13,7 @@ export interface DebugPipe { requestPull: () => void; getOutputPipePath: () => string; getInputPipePath: () => string; - getPullPipePath: () => string, + getPullPipePath: () => string; } export function createNamedPipe(): DebugPipe { @@ -43,16 +45,15 @@ export function createNamedPipe(): DebugPipe { ); inputPipe.listen(inputPipePath); }, - openPull: (onError: (err: unknown) => void) => - { + openPull: (onError: (err: unknown) => void) => { if (!onErrorCallback) { onErrorCallback = onError; } pullPipe = net.createServer( stream => { - stream.on("error", err =>{ - onError(`error on pull pipe: ${err}`) + stream.on("error", err => { + onError(`error on pull pipe: ${err}`); }); pullStream = stream; } @@ -87,7 +88,16 @@ export function createFifoPipe(): DebugPipe { const pipeId = crypto.randomBytes(16).toString("hex"); const outputPipePath = `/tmp/lldbg_out_${pipeId}`; const inputPipePath = `/tmp/lldbg_in_${pipeId}`; - const pullPipePath = `/tmp/lldbg_pull_${pipeId}`; + let pullPipePath = ""; + + const appPrefix = 'lldebugger'; + try { + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), appPrefix)); + pullPipePath = path.join(tmpDir, "pull.txt"); + } catch { + // handle error + } + let outputFd: number | null; let inputFd: number | null; let pullFd: number | null; @@ -148,33 +158,22 @@ export function createFifoPipe(): DebugPipe { ); }, - openPull: (onError: (err: unknown) => void) => - { + openPull: (onError: (err: unknown) => void) => { if (!onErrorCallback) { onErrorCallback = onError; } - - childProcess.exec( - `mkfifo ${pullPipePath}`, - fifoErr => { - if (fifoErr) { - onError(`error executing mkfifo for input pipe: ${fifoErr}`); + + fs.open( + pullPipePath, + fs.constants.O_WRONLY | fs.constants.O_CREAT, + (fdErr, fd) => { + if (fdErr) { + onError(`error opening input pipe: ${fdErr}`); return; } - fs.open( - pullPipePath, - fs.constants.O_WRONLY, - (fdErr, fd) => { - if (fdErr) { - onError(`error opening fifo for pull pipe: ${fdErr}`); - return; - } - - pullFd = fd; - pullStream = fs.createWriteStream(null as unknown as fs.PathLike, {fd}); - } - ); + pullFd = fd; + pullStream = fs.createWriteStream(null as unknown as fs.PathLike, {fd}); } ); }, @@ -211,7 +210,7 @@ export function createFifoPipe(): DebugPipe { pullPipePath, err => { if (err) { - onErrorCallback?.(`error removing fifo for pull pipe: ${err}`); + onErrorCallback?.(`error removing pull file '${pullPipePath}': ${err}`); } } ); diff --git a/extension/luaDebugSession.ts b/extension/luaDebugSession.ts index aad099a..ba6edd3 100644 --- a/extension/luaDebugSession.ts +++ b/extension/luaDebugSession.ts @@ -247,7 +247,7 @@ export class LuaDebugSession extends LoggingDebugSession { processOptions.env[stepUnmappedLinesEnv] = this.config.stepUnmappedLines ? "1" : "0"; } - this.usePipeCommutication = this.config.program.communication === "pipe" + this.usePipeCommutication = this.config.program.communication === "pipe"; //Open pipes if (this.usePipeCommutication || this.pullBreakpointsSupport) { @@ -262,14 +262,14 @@ export class LuaDebugSession extends LoggingDebugSession { data => { void this.onDebuggerOutput(data); }, err => { this.showOutput(`${err}`, OutputCategory.Error); } ); - + processOptions.env[outputFileEnv] = this.debugPipe.getOutputPipePath(); processOptions.env[inputFileEnv] = this.debugPipe.getInputPipePath(); } } if (this.pullBreakpointsSupport) { - this.debugPipe?.openPull(err => { this.showOutput(`${err}`, OutputCategory.Error); }) + this.debugPipe?.openPull(err => { this.showOutput(`${err}`, OutputCategory.Error); }); processOptions.env[pullFileEnv] = this.debugPipe?.getPullPipePath(); } @@ -346,7 +346,7 @@ export class LuaDebugSession extends LoggingDebugSession { this.autoContinueNext = true; this.debugPipe?.requestPull(); } - + const oldBreakpoints = this.fileBreakpoints[filePath]; if (typeof oldBreakpoints !== "undefined") { for (const breakpoint of oldBreakpoints) { @@ -852,7 +852,7 @@ export class LuaDebugSession extends LoggingDebugSession { private async onDebuggerStop(msg: LuaDebug.DebugBreak) { this.isRunning = false; - let prevInDebugger = this.inDebuggerBreakpoint; + const prevInDebugger = this.inDebuggerBreakpoint; this.inDebuggerBreakpoint = true; if (this.pendingScripts) { From b6b2b138d3dbeeb4853b67b3b79be56ba6f3e92f Mon Sep 17 00:00:00 2001 From: Aleksandr Kovalev Date: Wed, 7 Dec 2022 18:22:12 +0000 Subject: [PATCH 7/7] Fix hung on close + optional temp folder create (only if breakpoints pulling enabled) + cleanup temp folder on close --- extension/debugPipe.ts | 65 +++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 35 deletions(-) diff --git a/extension/debugPipe.ts b/extension/debugPipe.ts index 500c71f..c15ddc4 100644 --- a/extension/debugPipe.ts +++ b/extension/debugPipe.ts @@ -89,18 +89,10 @@ export function createFifoPipe(): DebugPipe { const outputPipePath = `/tmp/lldbg_out_${pipeId}`; const inputPipePath = `/tmp/lldbg_in_${pipeId}`; let pullPipePath = ""; - - const appPrefix = 'lldebugger'; - try { - const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), appPrefix)); - pullPipePath = path.join(tmpDir, "pull.txt"); - } catch { - // handle error - } - - let outputFd: number | null; - let inputFd: number | null; - let pullFd: number | null; + let debuggerTmpDir = ""; + let outputFd: number | null = null; + let inputFd: number | null = null; + let pullFd: number | null = null; let inputStream: fs.WriteStream | null = null; let pullStream: fs.WriteStream | null = null; let onErrorCallback: ((err: unknown) => void) | null = null; @@ -163,19 +155,20 @@ export function createFifoPipe(): DebugPipe { onErrorCallback = onError; } - fs.open( - pullPipePath, - fs.constants.O_WRONLY | fs.constants.O_CREAT, - (fdErr, fd) => { - if (fdErr) { - onError(`error opening input pipe: ${fdErr}`); - return; - } + const appPrefix = "lldebugger"; + try { + debuggerTmpDir = fs.mkdtempSync(path.join(os.tmpdir(), appPrefix)); + pullPipePath = path.join(debuggerTmpDir, "pull.txt"); - pullFd = fd; - pullStream = fs.createWriteStream(null as unknown as fs.PathLike, {fd}); - } - ); + const fd = fs.openSync( + pullPipePath, + fs.constants.O_WRONLY | fs.constants.O_CREAT + ); + pullFd = fd; + pullStream = fs.createWriteStream(null as unknown as fs.PathLike, {fd}); + } catch (e: unknown) { + onErrorCallback(e); + } }, close: () => { @@ -203,17 +196,19 @@ export function createFifoPipe(): DebugPipe { } ); } - if (pullFd !== null) { - fs.close(pullFd); - pullFd = null; - fs.rm( - pullPipePath, - err => { - if (err) { - onErrorCallback?.(`error removing pull file '${pullPipePath}': ${err}`); - } - } - ); + inputStream = null; + try { + if (pullFd !== null) { + fs.close(pullFd); + fs.rmSync(pullPipePath); + pullFd = null; + } + pullStream = null; + if (debuggerTmpDir.length > 0) { + fs.rmdirSync(debuggerTmpDir); + } + } catch (e: unknown) { + onErrorCallback?.(e); } },