From 33bf9484ca4e8709f648e92f1f1b3c74758cd506 Mon Sep 17 00:00:00 2001 From: Isadora Rodopoulos Date: Tue, 3 Oct 2023 13:26:05 -0700 Subject: [PATCH 1/3] Track debugging sessions until csdevkit is initialized. --- src/coreclrDebug/activate.ts | 18 ---- .../provisionalDebugSessionTracker.ts | 91 +++++++++++++++++++ src/main.ts | 9 +- src/shared/configurationProvider.ts | 4 +- 4 files changed, 99 insertions(+), 23 deletions(-) create mode 100644 src/coreclrDebug/provisionalDebugSessionTracker.ts diff --git a/src/coreclrDebug/activate.ts b/src/coreclrDebug/activate.ts index 01becfc7b..1bffd2924 100644 --- a/src/coreclrDebug/activate.ts +++ b/src/coreclrDebug/activate.ts @@ -310,21 +310,3 @@ export class DebugAdapterExecutableFactory implements vscode.DebugAdapterDescrip return executable; } } - -let _brokeredServicePipeName: string | undefined; - -/** - * Initialize brokered service pipe name from C# dev kit, if available. - * - * @param csDevKitPipeName Activated brokered service pipe name activated by {@link CSharpDevKitExports}. - */ -export function initializeBrokeredServicePipeName(csDevKitPipeName: string) { - _brokeredServicePipeName = csDevKitPipeName; -} - -/** - * Fetch the brokered service pipe name from C# dev kit, if available. - */ -export function getBrokeredServicePipeName(): string | undefined { - return _brokeredServicePipeName; -} diff --git a/src/coreclrDebug/provisionalDebugSessionTracker.ts b/src/coreclrDebug/provisionalDebugSessionTracker.ts new file mode 100644 index 000000000..d1d748fd6 --- /dev/null +++ b/src/coreclrDebug/provisionalDebugSessionTracker.ts @@ -0,0 +1,91 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; + +/** + * This tracks a debug session until C# dev kit is loaded (if present) and STOPS tracking any existing + * debug session after C# dev kit is loaded. + * The idea is to avoid a race condition if a debugging session starts before C# dev kit is initialized, + * since the brokered service pipe name will be sent with a 'launch' command. + * If the extension loads during an existing session, rather than not initializing any C# dev kit services (such as hot reload), + * this sends a custom request to the engine with the brokered service pipe name in order to initialize it. + */ +export class ProvisionalDebugSessionTracker { + private _session: vscode.DebugSession | undefined; + + private _onDidStartDebugSession: vscode.Disposable | undefined; + private _onDidTerminateDebugSession: vscode.Disposable | undefined; + + private _brokeredServicePipeName: string | undefined; + + DebuggerSessionTracker() { + this._session = undefined; + } + + /** + * Initializes the debug session handlers. + */ + async initializeDebugSessionHandlers(context: vscode.ExtensionContext): Promise { + this._onDidStartDebugSession = vscode.debug.onDidStartDebugSession(this.onDidStartDebugSession.bind(this)); + + this._onDidTerminateDebugSession = vscode.debug.onDidTerminateDebugSession( + this.onDidTerminateDebugSession.bind(this) + ); + + context.subscriptions.push(this._onDidStartDebugSession); + context.subscriptions.push(this._onDidTerminateDebugSession); + } + + /** + * Tracks a debug session until it is terminated. + * @param session Debug session. + */ + async onDidStartDebugSession(session: vscode.DebugSession): Promise { + this._session = session; + } + + /** + * Notifies that a debug session has been terminated. + */ + async onDidTerminateDebugSession(): Promise { + this._session = undefined; + } + + /** + * If there is any active debug session, this notifies the engine that csdevkit was loaded. + * @param csDevKitPipeName Brokered service pipe name activated by {@link CSharpDevKitExports}. + */ + async onCsDevKitInitialized(csDevKitPipeName: string): Promise { + if (this._session != undefined) { + // Debugging session already started, send a custom DAP request to the engine. + await this._session.customRequest('initializeBrokeredServicePipeName', csDevKitPipeName); + } + + this._brokeredServicePipeName = csDevKitPipeName; + + // Since C# dev kit was initialized, we no longer need to track debugging sessions. + this.cleanup(); + } + + /** + * Fetches the brokered service pipe name from C# dev kit, if available. + */ + getBrokeredServicePipeName(): string | undefined { + return this._brokeredServicePipeName; + } + + /** + * No longer tracks any debugging session going forward. + */ + cleanup(): void { + this._session = undefined; + + this._onDidStartDebugSession?.dispose(); + this._onDidTerminateDebugSession?.dispose(); + } +} + +export const debuggerSessionTracker = new ProvisionalDebugSessionTracker(); diff --git a/src/main.ts b/src/main.ts index ec66c1673..1adab56a5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -57,6 +57,7 @@ import { ServerStateChange } from './lsptoolshost/serverStateChange'; import { SolutionSnapshotProvider } from './lsptoolshost/services/solutionSnapshotProvider'; import { RazorTelemetryDownloader } from './razor/razorTelemetryDownloader'; import { commonOptions, omnisharpOptions, razorOptions } from './shared/options'; +import { debuggerSessionTracker } from './coreclrDebug/provisionalDebugSessionTracker'; export async function activate( context: vscode.ExtensionContext @@ -335,6 +336,8 @@ export async function activate( reporter.sendTelemetryEvent('CSharpActivated', activationProperties); if (!useOmnisharpServer) { + debuggerSessionTracker.initializeDebugSessionHandlers(context); + tryGetCSharpDevKitExtensionExports(csharpLogObserver); // If we got here, the server should definitely have been created. @@ -401,9 +404,9 @@ function tryGetCSharpDevKitExtensionExports(csharpLogObserver: CsharpLoggerObser ]); // Notify the vsdbg configuration provider that C# dev kit has been loaded. - exports.serverProcessLoaded(async () => - coreclrdebug.initializeBrokeredServicePipeName(await exports.getBrokeredServiceServerPipeName()) - ); + exports.serverProcessLoaded(async () => { + debuggerSessionTracker.onCsDevKitInitialized(await exports.getBrokeredServiceServerPipeName()); + }); vscode.commands.executeCommand('setContext', 'dotnet.debug.serviceBrokerAvailable', true); } else { diff --git a/src/shared/configurationProvider.ts b/src/shared/configurationProvider.ts index e1ba91747..5e5c64823 100644 --- a/src/shared/configurationProvider.ts +++ b/src/shared/configurationProvider.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode'; import { ParsedEnvironmentFile } from '../coreclrDebug/parsedEnvironmentFile'; -import { getBrokeredServicePipeName } from '../coreclrDebug/activate'; +import { debuggerSessionTracker } from '../coreclrDebug/provisionalDebugSessionTracker'; import { MessageItem } from '../vscodeAdapter'; import { CertToolStatusCodes, createSelfSignedCert, hasDotnetDevCertsHttps } from '../utils/dotnetDevCertsHttps'; @@ -66,7 +66,7 @@ export class BaseVsDbgConfigurationProvider implements vscode.DebugConfiguration return null; } - const brokeredServicePipeName = getBrokeredServicePipeName(); + const brokeredServicePipeName = debuggerSessionTracker.getBrokeredServicePipeName(); if (brokeredServicePipeName !== undefined) { debugConfiguration.brokeredServicePipeName = brokeredServicePipeName; } From 4489fa4b3e5e3080f3b8627088dae05ba21e92e7 Mon Sep 17 00:00:00 2001 From: Isadora Rodopoulos Date: Fri, 6 Oct 2023 15:22:43 -0700 Subject: [PATCH 2/3] Address code review suggestions. --- .../provisionalDebugSessionTracker.ts | 34 ++++++++++--------- src/main.ts | 6 ++-- src/shared/configurationProvider.ts | 4 +-- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/coreclrDebug/provisionalDebugSessionTracker.ts b/src/coreclrDebug/provisionalDebugSessionTracker.ts index d1d748fd6..e41d5acfe 100644 --- a/src/coreclrDebug/provisionalDebugSessionTracker.ts +++ b/src/coreclrDebug/provisionalDebugSessionTracker.ts @@ -14,21 +14,17 @@ import * as vscode from 'vscode'; * this sends a custom request to the engine with the brokered service pipe name in order to initialize it. */ export class ProvisionalDebugSessionTracker { - private _session: vscode.DebugSession | undefined; + private _sessions: Set | undefined = new Set(); private _onDidStartDebugSession: vscode.Disposable | undefined; private _onDidTerminateDebugSession: vscode.Disposable | undefined; private _brokeredServicePipeName: string | undefined; - DebuggerSessionTracker() { - this._session = undefined; - } - /** * Initializes the debug session handlers. */ - async initializeDebugSessionHandlers(context: vscode.ExtensionContext): Promise { + initializeDebugSessionHandlers(context: vscode.ExtensionContext): void { this._onDidStartDebugSession = vscode.debug.onDidStartDebugSession(this.onDidStartDebugSession.bind(this)); this._onDidTerminateDebugSession = vscode.debug.onDidTerminateDebugSession( @@ -43,15 +39,19 @@ export class ProvisionalDebugSessionTracker { * Tracks a debug session until it is terminated. * @param session Debug session. */ - async onDidStartDebugSession(session: vscode.DebugSession): Promise { - this._session = session; + onDidStartDebugSession(session: vscode.DebugSession): void { + if (session.type !== 'coreclr') { + return; + } + + this._sessions?.add(session); } /** * Notifies that a debug session has been terminated. */ - async onDidTerminateDebugSession(): Promise { - this._session = undefined; + onDidTerminateDebugSession(): void { + this._sessions?.clear(); } /** @@ -59,13 +59,14 @@ export class ProvisionalDebugSessionTracker { * @param csDevKitPipeName Brokered service pipe name activated by {@link CSharpDevKitExports}. */ async onCsDevKitInitialized(csDevKitPipeName: string): Promise { - if (this._session != undefined) { + this._brokeredServicePipeName = csDevKitPipeName; + + const sessions = this._sessions; + if (sessions != undefined) { // Debugging session already started, send a custom DAP request to the engine. - await this._session.customRequest('initializeBrokeredServicePipeName', csDevKitPipeName); + sessions.forEach((s) => s.customRequest('initializeBrokeredServicePipeName', csDevKitPipeName)); } - this._brokeredServicePipeName = csDevKitPipeName; - // Since C# dev kit was initialized, we no longer need to track debugging sessions. this.cleanup(); } @@ -81,11 +82,12 @@ export class ProvisionalDebugSessionTracker { * No longer tracks any debugging session going forward. */ cleanup(): void { - this._session = undefined; + this._sessions?.clear(); + this._sessions = undefined; this._onDidStartDebugSession?.dispose(); this._onDidTerminateDebugSession?.dispose(); } } -export const debuggerSessionTracker = new ProvisionalDebugSessionTracker(); +export const debugSessionTracker = new ProvisionalDebugSessionTracker(); diff --git a/src/main.ts b/src/main.ts index 1adab56a5..e5f046716 100644 --- a/src/main.ts +++ b/src/main.ts @@ -57,7 +57,7 @@ import { ServerStateChange } from './lsptoolshost/serverStateChange'; import { SolutionSnapshotProvider } from './lsptoolshost/services/solutionSnapshotProvider'; import { RazorTelemetryDownloader } from './razor/razorTelemetryDownloader'; import { commonOptions, omnisharpOptions, razorOptions } from './shared/options'; -import { debuggerSessionTracker } from './coreclrDebug/provisionalDebugSessionTracker'; +import { debugSessionTracker } from './coreclrDebug/provisionalDebugSessionTracker'; export async function activate( context: vscode.ExtensionContext @@ -336,7 +336,7 @@ export async function activate( reporter.sendTelemetryEvent('CSharpActivated', activationProperties); if (!useOmnisharpServer) { - debuggerSessionTracker.initializeDebugSessionHandlers(context); + debugSessionTracker.initializeDebugSessionHandlers(context); tryGetCSharpDevKitExtensionExports(csharpLogObserver); @@ -405,7 +405,7 @@ function tryGetCSharpDevKitExtensionExports(csharpLogObserver: CsharpLoggerObser // Notify the vsdbg configuration provider that C# dev kit has been loaded. exports.serverProcessLoaded(async () => { - debuggerSessionTracker.onCsDevKitInitialized(await exports.getBrokeredServiceServerPipeName()); + debugSessionTracker.onCsDevKitInitialized(await exports.getBrokeredServiceServerPipeName()); }); vscode.commands.executeCommand('setContext', 'dotnet.debug.serviceBrokerAvailable', true); diff --git a/src/shared/configurationProvider.ts b/src/shared/configurationProvider.ts index 5e5c64823..12b98581e 100644 --- a/src/shared/configurationProvider.ts +++ b/src/shared/configurationProvider.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode'; import { ParsedEnvironmentFile } from '../coreclrDebug/parsedEnvironmentFile'; -import { debuggerSessionTracker } from '../coreclrDebug/provisionalDebugSessionTracker'; +import { debugSessionTracker } from '../coreclrDebug/provisionalDebugSessionTracker'; import { MessageItem } from '../vscodeAdapter'; import { CertToolStatusCodes, createSelfSignedCert, hasDotnetDevCertsHttps } from '../utils/dotnetDevCertsHttps'; @@ -66,7 +66,7 @@ export class BaseVsDbgConfigurationProvider implements vscode.DebugConfiguration return null; } - const brokeredServicePipeName = debuggerSessionTracker.getBrokeredServicePipeName(); + const brokeredServicePipeName = debugSessionTracker.getBrokeredServicePipeName(); if (brokeredServicePipeName !== undefined) { debugConfiguration.brokeredServicePipeName = brokeredServicePipeName; } From 48de52560074c53a91a1cc5b545a735ea2618ab1 Mon Sep 17 00:00:00 2001 From: Isadora Rodopoulos Date: Tue, 10 Oct 2023 17:43:58 -0700 Subject: [PATCH 3/3] Use the session parameter to untrack a session. --- src/coreclrDebug/provisionalDebugSessionTracker.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/coreclrDebug/provisionalDebugSessionTracker.ts b/src/coreclrDebug/provisionalDebugSessionTracker.ts index e41d5acfe..28bf067bd 100644 --- a/src/coreclrDebug/provisionalDebugSessionTracker.ts +++ b/src/coreclrDebug/provisionalDebugSessionTracker.ts @@ -27,9 +27,9 @@ export class ProvisionalDebugSessionTracker { initializeDebugSessionHandlers(context: vscode.ExtensionContext): void { this._onDidStartDebugSession = vscode.debug.onDidStartDebugSession(this.onDidStartDebugSession.bind(this)); - this._onDidTerminateDebugSession = vscode.debug.onDidTerminateDebugSession( - this.onDidTerminateDebugSession.bind(this) - ); + this._onDidTerminateDebugSession = vscode.debug.onDidTerminateDebugSession((session: vscode.DebugSession) => { + this.onDidTerminateDebugSession(session); + }); context.subscriptions.push(this._onDidStartDebugSession); context.subscriptions.push(this._onDidTerminateDebugSession); @@ -50,8 +50,8 @@ export class ProvisionalDebugSessionTracker { /** * Notifies that a debug session has been terminated. */ - onDidTerminateDebugSession(): void { - this._sessions?.clear(); + onDidTerminateDebugSession(session: vscode.DebugSession): void { + this._sessions?.delete(session); } /**