From 934c4d0e992f31fc114490cb8fdb766d4a97e002 Mon Sep 17 00:00:00 2001 From: Fatima Qarni Date: Tue, 9 Jul 2024 11:23:52 -0700 Subject: [PATCH] Fix debugger hang on restart (#3846) * telemetry check for trigger, also include trigger and folder in obj for debugger * update origin telemetry * add enum for debug origin * pass in from command info and cleanup * Update cmakeFileApiDriver.ts take out import * move to 1.19 changelog --------- Co-authored-by: Garrett Campbell --- CHANGELOG.md | 1 + src/cmakeProject.ts | 10 +- src/debug/cmakeDebuggerTelemetry.ts | 5 +- ...AdapterNamedPipeServerDescriptorFactory.ts | 100 ++++++++++-------- src/debug/debuggerConfigureDriver.ts | 2 +- src/drivers/cmakeFileApiDriver.ts | 21 +--- src/extension.ts | 42 +++++++- 7 files changed, 109 insertions(+), 72 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4dea87f93..2243e9380 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Bug Fixes: - Fix issue where `cmake.preferredGenerators` wasn't falling back to the next entry when the first entry didn't exist [#2709](https://github.com/microsoft/vscode-cmake-tools/issues/2709) - Potential fix for attempting to load a non-variants file as a variants file and throwing a parse exception [#3727](https://github.com/microsoft/vscode-cmake-tools/issues/3727) +- Fix issue where `Configure with CMake Debugger` fails on restart because the previously used pipe to CMake Debugger is no longer available. [#3582](https://github.com/microsoft/vscode-cmake-tools/issues/3582) ## 1.18.43 diff --git a/src/cmakeProject.ts b/src/cmakeProject.ts index 597a10c6e..e59f9971e 100644 --- a/src/cmakeProject.ts +++ b/src/cmakeProject.ts @@ -1636,8 +1636,14 @@ export class CMakeProject { .then(async chosen => { if (chosen) { if (chosen.title === yesButtonTitle) { - await this.configureInternal(ConfigureTrigger.configureFailedConfigureWithDebuggerButton, extraArgs, ConfigureType.NormalWithDebugger, { - pipeName: getDebuggerPipeName() + await vscode.debug.startDebugging(undefined, { + name: localize("cmake.debug.name", "CMake Debugger"), + request: "launch", + type: "cmake", + cmakeDebugType: "configure", + pipeName: getDebuggerPipeName(), + trigger: ConfigureTrigger.configureFailedConfigureWithDebuggerButton, + fromCommand: true }); } else if (chosen.title === doNotShowAgainTitle) { await cmakeConfiguration.update(showDebuggerConfigurationString, false, vscode.ConfigurationTarget.Global); diff --git a/src/debug/cmakeDebuggerTelemetry.ts b/src/debug/cmakeDebuggerTelemetry.ts index d86201430..307d740ae 100644 --- a/src/debug/cmakeDebuggerTelemetry.ts +++ b/src/debug/cmakeDebuggerTelemetry.ts @@ -1,6 +1,9 @@ import * as telemetry from "@cmt/telemetry"; -export const originatedFromLaunchConfiguration: string = "launchConfiguration"; +export enum DebugOrigin { + originatedFromLaunchConfiguration = "launchConfiguration", + originatedFromCommand = "command" +} export function logCMakeDebuggerTelemetry(origin: string, debugType: string) { telemetry.logEvent("cmakeDebugger", { diff --git a/src/debug/debugAdapterNamedPipeServerDescriptorFactory.ts b/src/debug/debugAdapterNamedPipeServerDescriptorFactory.ts index 6df9e2ec4..e16ce4132 100644 --- a/src/debug/debugAdapterNamedPipeServerDescriptorFactory.ts +++ b/src/debug/debugAdapterNamedPipeServerDescriptorFactory.ts @@ -6,7 +6,8 @@ import { executeScriptWithDebugger } from "./debuggerScriptDriver"; import * as logging from '../logging'; import * as nls from "vscode-nls"; import { fs } from "../pr"; -import { logCMakeDebuggerTelemetry, originatedFromLaunchConfiguration } from "./cmakeDebuggerTelemetry"; +import { DebugOrigin, logCMakeDebuggerTelemetry} from "./cmakeDebuggerTelemetry"; +import { ConfigureTrigger } from "@cmt/cmakeProject"; nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })(); const localize: nls.LocalizeFunc = nls.loadMessageBundle(); @@ -17,60 +18,67 @@ export class DebugAdapterNamedPipeServerDescriptorFactory implements vscode.Debu async createDebugAdapterDescriptor(session: vscode.DebugSession, _executable: vscode.DebugAdapterExecutable | undefined): Promise> { const pipeName = session.configuration.pipeName ?? getDebuggerPipeName(); + const origin = + session.configuration.fromCommand ? DebugOrigin.originatedFromCommand : DebugOrigin.originatedFromLaunchConfiguration; - // undocumented configuration field that lets us know if the session is being invoked from a command - // This should only be used from inside the extension from a command that invokes the debugger. - if (!session.configuration.fromCommand) { - const debuggerInformation: DebuggerInformation = { - pipeName, - dapLog: session.configuration.dapLog, - debuggerIsReady: () => undefined - }; + const debuggerInformation: DebuggerInformation = { + pipeName, + dapLog: session.configuration.dapLog, + debuggerIsReady: () => undefined + }; - const cmakeDebugType: "configure" | "script" | "external" = session.configuration.cmakeDebugType; - if (cmakeDebugType === "configure" || cmakeDebugType === "script") { - const promise = new Promise((resolve) => { - debuggerInformation.debuggerIsReady = resolve; - }); + const cmakeDebugType: "configure" | "script" | "external" = session.configuration.cmakeDebugType; + if (cmakeDebugType === "configure" || cmakeDebugType === "script") { + const promise = new Promise((resolve) => { + debuggerInformation.debuggerIsReady = resolve; + }); - if (cmakeDebugType === "script") { - const script = session.configuration.scriptPath; - if (!fs.existsSync(script)) { - throw new Error(localize("cmake.debug.scriptPath.does.not.exist", "The script path, \"{0}\", could not be found.", script)); + if (cmakeDebugType === "script") { + const script = session.configuration.scriptPath; + if (!fs.existsSync(script)) { + throw new Error(localize("cmake.debug.scriptPath.does.not.exist", "The script path, \"{0}\", could not be found.", script)); + } + const args: string[] = session.configuration.scriptArgs ?? []; + const env = new Map(session.configuration.scriptEnv?.map((e: {name: string; value: string}) => [e.name, e.value])) ?? new Map(); + logCMakeDebuggerTelemetry(origin, cmakeDebugType); + void executeScriptWithDebugger(script, args, env, debuggerInformation); + } else { + logCMakeDebuggerTelemetry(origin, cmakeDebugType); + + if (session.configuration.trigger && !Object.values(ConfigureTrigger).includes(session.configuration.trigger)) { + session.configuration.trigger = undefined; + } + + if (session.configuration.clean) { + if (session.configuration.configureAll) { + void extensionManager?.cleanConfigureAllWithDebuggerInternal( + debuggerInformation, + session.configuration.trigger + ); + } else { + void extensionManager?.cleanConfigureWithDebuggerInternal( + debuggerInformation, + session.configuration.folder + ); } - const args: string[] = session.configuration.scriptArgs ?? []; - const env = new Map(session.configuration.scriptEnv?.map((e: {name: string; value: string}) => [e.name, e.value])) ?? new Map(); - logCMakeDebuggerTelemetry(originatedFromLaunchConfiguration, cmakeDebugType); - void executeScriptWithDebugger(script, args, env, debuggerInformation); } else { - logCMakeDebuggerTelemetry(originatedFromLaunchConfiguration, cmakeDebugType); - if (session.configuration.clean) { - if (session.configuration.configureAll) { - void extensionManager?.cleanConfigureAllWithDebuggerInternal( - debuggerInformation - ); - } else { - void extensionManager?.cleanConfigureWithDebuggerInternal( - debuggerInformation - ); - } + if (session.configuration.configureAll) { + void extensionManager?.configureAllWithDebuggerInternal( + debuggerInformation, + session.configuration.trigger + ); } else { - if (session.configuration.configureAll) { - void extensionManager?.configureAllWithDebuggerInternal( - debuggerInformation - ); - } else { - void extensionManager?.configureWithDebuggerInternal( - debuggerInformation - ); - } + void extensionManager?.configureWithDebuggerInternal( + debuggerInformation, + session.configuration.folder, session.configuration.sourceDir, session.configuration.trigger + ); } } - - await promise; - } else if (cmakeDebugType === "external") { - logCMakeDebuggerTelemetry(originatedFromLaunchConfiguration, cmakeDebugType); } + + await promise; + } else if (cmakeDebugType === "external") { + logCMakeDebuggerTelemetry(origin, cmakeDebugType); } logger.info(localize('debugger.create.descriptor', 'Connecting debugger on named pipe: \"{0}\"', pipeName)); diff --git a/src/debug/debuggerConfigureDriver.ts b/src/debug/debuggerConfigureDriver.ts index e54cf03d5..20397c8b4 100644 --- a/src/debug/debuggerConfigureDriver.ts +++ b/src/debug/debuggerConfigureDriver.ts @@ -4,7 +4,7 @@ import * as vscode from "vscode"; export interface DebuggerInformation { pipeName: string; dapLog?: string; - debuggerIsReady?(): void; + debuggerIsReady(): void; } export function getDebuggerPipeName(): string { diff --git a/src/drivers/cmakeFileApiDriver.ts b/src/drivers/cmakeFileApiDriver.ts index 95156b9f3..5baba77bf 100644 --- a/src/drivers/cmakeFileApiDriver.ts +++ b/src/drivers/cmakeFileApiDriver.ts @@ -31,7 +31,6 @@ import * as nls from 'vscode-nls'; import { DebuggerInformation } from '@cmt/debug/debuggerConfigureDriver'; import { CMakeOutputConsumer, StateMessage } from '@cmt/diagnostics/cmake'; import { ConfigureTrigger } from '@cmt/cmakeProject'; -import { logCMakeDebuggerTelemetry } from '@cmt/debug/cmakeDebuggerTelemetry'; import { onConfigureSettingsChange } from '@cmt/ui/util'; nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })(); @@ -290,23 +289,9 @@ export class CMakeFileApiDriver extends CMakeDriver { await new Promise(resolve => setTimeout(resolve, 50)); } - // if there isn't a `debuggerIsReady` callback provided, this means that this invocation was - // started by a command, rather than by a launch configuration, and the debug session will start from here. - if (debuggerInformation.debuggerIsReady) { - // This cmake debug invocation came from a launch configuration. All telemetry is handled in the createDebugAdapterDescriptor handler. - debuggerInformation.debuggerIsReady(); - } else { - const cmakeDebugType = "configure"; - logCMakeDebuggerTelemetry(trigger ?? "", cmakeDebugType); - await vscode.debug.startDebugging(undefined, { - name: localize("cmake.debug.name", "CMake Debugger"), - request: "launch", - type: "cmake", - cmakeDebugType, - pipeName: debuggerInformation.pipeName, - fromCommand: true - }); - } + // This cmake debug invocation was started from a startDebugging command. + // All telemetry is handled in the createDebugAdapterDescriptor handler. + debuggerInformation.debuggerIsReady(); } } diff --git a/src/extension.ts b/src/extension.ts index 832fcda27..6533e7292 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1286,7 +1286,15 @@ export class ExtensionManager implements vscode.Disposable { } cleanConfigureWithDebugger(folder?: vscode.WorkspaceFolder) { - return this.cleanConfigureWithDebuggerInternal({pipeName: getDebuggerPipeName()}, folder); + return vscode.debug.startDebugging(undefined, { + name: localize("cmake.debug.name", "CMake Debugger"), + request: "launch", + type: "cmake", + cmakeDebugType: "configure", + pipeName: getDebuggerPipeName(), + folder, + fromCommand: true + }); } cleanConfigureWithDebuggerInternal(debuggerInformation: DebuggerInformation, folder?: vscode.WorkspaceFolder) { @@ -1300,7 +1308,15 @@ export class ExtensionManager implements vscode.Disposable { } cleanConfigureAllWithDebugger(trigger?: ConfigureTrigger) { - return this.cleanConfigureAllWithDebuggerInternal({pipeName: getDebuggerPipeName()}, trigger); + return vscode.debug.startDebugging(undefined, { + name: localize("cmake.debug.name", "CMake Debugger"), + request: "launch", + type: "cmake", + cmakeDebugType: "configure", + pipeName: getDebuggerPipeName(), + trigger, + fromCommand: true + }); } cleanConfigureAllWithDebuggerInternal(debuggerInformation: DebuggerInformation, trigger?: ConfigureTrigger) { @@ -1315,7 +1331,17 @@ export class ExtensionManager implements vscode.Disposable { } configureWithDebugger(folder?: vscode.WorkspaceFolder, sourceDir?: string, trigger?: ConfigureTrigger) { - return this.configureWithDebuggerInternal({pipeName: getDebuggerPipeName()}, folder, undefined, sourceDir, trigger); + return vscode.debug.startDebugging(undefined, { + name: localize("cmake.debug.name", "CMake Debugger"), + request: "launch", + type: "cmake", + cmakeDebugType: "configure", + pipeName: getDebuggerPipeName(), + folder, + sourceDir, + trigger, + fromCommand: true + }); } configureWithDebuggerInternal(debuggerInformation: DebuggerInformation, folder?: vscode.WorkspaceFolder, showCommandOnly?: boolean, sourceDir?: string, trigger?: ConfigureTrigger) { @@ -1333,7 +1359,15 @@ export class ExtensionManager implements vscode.Disposable { } configureAllWithDebugger(trigger?: ConfigureTrigger) { - return this.configureAllWithDebuggerInternal({pipeName: getDebuggerPipeName()}, trigger); + return vscode.debug.startDebugging(undefined, { + name: localize("cmake.debug.name", "CMake Debugger"), + request: "launch", + type: "cmake", + cmakeDebugType: "configure", + pipeName: getDebuggerPipeName(), + trigger, + fromCommand: true + }); } configureAllWithDebuggerInternal(debuggerInformation: DebuggerInformation, trigger?: ConfigureTrigger) {