Path: blob/main/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as nls from '../../../../nls.js';6import { Event, Emitter } from '../../../../base/common/event.js';7import * as objects from '../../../../base/common/objects.js';8import { toAction } from '../../../../base/common/actions.js';9import * as errors from '../../../../base/common/errors.js';10import { createErrorWithActions } from '../../../../base/common/errorMessage.js';11import { formatPII, isUri } from '../common/debugUtils.js';12import { IDebugAdapter, IConfig, AdapterEndEvent, IDebugger } from '../common/debug.js';13import { IExtensionHostDebugService, IOpenExtensionWindowResult } from '../../../../platform/debug/common/extensionHostDebug.js';14import { URI } from '../../../../base/common/uri.js';15import { IOpenerService } from '../../../../platform/opener/common/opener.js';16import { IDisposable, dispose } from '../../../../base/common/lifecycle.js';17import { CancellationToken } from '../../../../base/common/cancellation.js';18import { INotificationService, Severity } from '../../../../platform/notification/common/notification.js';19import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js';20import { Schemas } from '../../../../base/common/network.js';2122/**23* This interface represents a single command line argument split into a "prefix" and a "path" half.24* The optional "prefix" contains arbitrary text and the optional "path" contains a file system path.25* Concatenating both results in the original command line argument.26*/27interface ILaunchVSCodeArgument {28prefix?: string;29path?: string;30}3132interface ILaunchVSCodeArguments {33args: ILaunchVSCodeArgument[];34debugRenderer?: boolean;35env?: { [key: string]: string | null };36}3738/**39* Encapsulates the DebugAdapter lifecycle and some idiosyncrasies of the Debug Adapter Protocol.40*/41export class RawDebugSession implements IDisposable {4243private allThreadsContinued = true;44private _readyForBreakpoints = false;45private _capabilities: DebugProtocol.Capabilities;4647// shutdown48private debugAdapterStopped = false;49private inShutdown = false;50private terminated = false;51private firedAdapterExitEvent = false;5253// telemetry54private startTime = 0;55private didReceiveStoppedEvent = false;5657// DAP events58private readonly _onDidInitialize = new Emitter<DebugProtocol.InitializedEvent>();59private readonly _onDidStop = new Emitter<DebugProtocol.StoppedEvent>();60private readonly _onDidContinued = new Emitter<DebugProtocol.ContinuedEvent>();61private readonly _onDidTerminateDebugee = new Emitter<DebugProtocol.TerminatedEvent>();62private readonly _onDidExitDebugee = new Emitter<DebugProtocol.ExitedEvent>();63private readonly _onDidThread = new Emitter<DebugProtocol.ThreadEvent>();64private readonly _onDidOutput = new Emitter<DebugProtocol.OutputEvent>();65private readonly _onDidBreakpoint = new Emitter<DebugProtocol.BreakpointEvent>();66private readonly _onDidLoadedSource = new Emitter<DebugProtocol.LoadedSourceEvent>();67private readonly _onDidProgressStart = new Emitter<DebugProtocol.ProgressStartEvent>();68private readonly _onDidProgressUpdate = new Emitter<DebugProtocol.ProgressUpdateEvent>();69private readonly _onDidProgressEnd = new Emitter<DebugProtocol.ProgressEndEvent>();70private readonly _onDidInvalidated = new Emitter<DebugProtocol.InvalidatedEvent>();71private readonly _onDidInvalidateMemory = new Emitter<DebugProtocol.MemoryEvent>();72private readonly _onDidCustomEvent = new Emitter<DebugProtocol.Event>();73private readonly _onDidEvent = new Emitter<DebugProtocol.Event>();7475// DA events76private readonly _onDidExitAdapter = new Emitter<AdapterEndEvent>();77private debugAdapter: IDebugAdapter | null;78private stoppedSinceLastStep = false;7980private toDispose: IDisposable[] = [];8182constructor(83debugAdapter: IDebugAdapter,84public readonly dbgr: IDebugger,85private readonly sessionId: string,86private readonly name: string,87@IExtensionHostDebugService private readonly extensionHostDebugService: IExtensionHostDebugService,88@IOpenerService private readonly openerService: IOpenerService,89@INotificationService private readonly notificationService: INotificationService,90@IDialogService private readonly dialogSerivce: IDialogService,91) {92this.debugAdapter = debugAdapter;93this._capabilities = Object.create(null);9495this.toDispose.push(this.debugAdapter.onError(err => {96this.shutdown(err);97}));9899this.toDispose.push(this.debugAdapter.onExit(code => {100if (code !== 0) {101this.shutdown(new Error(`exit code: ${code}`));102} else {103// normal exit104this.shutdown();105}106}));107108this.debugAdapter.onEvent(event => {109switch (event.event) {110case 'initialized':111this._readyForBreakpoints = true;112this._onDidInitialize.fire(event);113break;114case 'loadedSource':115this._onDidLoadedSource.fire(<DebugProtocol.LoadedSourceEvent>event);116break;117case 'capabilities':118if (event.body) {119const capabilities = (<DebugProtocol.CapabilitiesEvent>event).body.capabilities;120this.mergeCapabilities(capabilities);121}122break;123case 'stopped':124this.didReceiveStoppedEvent = true; // telemetry: remember that debugger stopped successfully125this.stoppedSinceLastStep = true;126this._onDidStop.fire(<DebugProtocol.StoppedEvent>event);127break;128case 'continued':129this.allThreadsContinued = (<DebugProtocol.ContinuedEvent>event).body.allThreadsContinued === false ? false : true;130this._onDidContinued.fire(<DebugProtocol.ContinuedEvent>event);131break;132case 'thread':133this._onDidThread.fire(<DebugProtocol.ThreadEvent>event);134break;135case 'output':136this._onDidOutput.fire(<DebugProtocol.OutputEvent>event);137break;138case 'breakpoint':139this._onDidBreakpoint.fire(<DebugProtocol.BreakpointEvent>event);140break;141case 'terminated':142this._onDidTerminateDebugee.fire(<DebugProtocol.TerminatedEvent>event);143break;144case 'exited':145this._onDidExitDebugee.fire(<DebugProtocol.ExitedEvent>event);146break;147case 'progressStart':148this._onDidProgressStart.fire(event as DebugProtocol.ProgressStartEvent);149break;150case 'progressUpdate':151this._onDidProgressUpdate.fire(event as DebugProtocol.ProgressUpdateEvent);152break;153case 'progressEnd':154this._onDidProgressEnd.fire(event as DebugProtocol.ProgressEndEvent);155break;156case 'invalidated':157this._onDidInvalidated.fire(event as DebugProtocol.InvalidatedEvent);158break;159case 'memory':160this._onDidInvalidateMemory.fire(event as DebugProtocol.MemoryEvent);161break;162case 'process':163break;164case 'module':165break;166default:167this._onDidCustomEvent.fire(event);168break;169}170this._onDidEvent.fire(event);171});172173this.debugAdapter.onRequest(request => this.dispatchRequest(request));174}175176get isInShutdown() {177return this.inShutdown;178}179180get onDidExitAdapter(): Event<AdapterEndEvent> {181return this._onDidExitAdapter.event;182}183184get capabilities(): DebugProtocol.Capabilities {185return this._capabilities;186}187188/**189* DA is ready to accepts setBreakpoint requests.190* Becomes true after "initialized" events has been received.191*/192get readyForBreakpoints(): boolean {193return this._readyForBreakpoints;194}195196//---- DAP events197198get onDidInitialize(): Event<DebugProtocol.InitializedEvent> {199return this._onDidInitialize.event;200}201202get onDidStop(): Event<DebugProtocol.StoppedEvent> {203return this._onDidStop.event;204}205206get onDidContinued(): Event<DebugProtocol.ContinuedEvent> {207return this._onDidContinued.event;208}209210get onDidTerminateDebugee(): Event<DebugProtocol.TerminatedEvent> {211return this._onDidTerminateDebugee.event;212}213214get onDidExitDebugee(): Event<DebugProtocol.ExitedEvent> {215return this._onDidExitDebugee.event;216}217218get onDidThread(): Event<DebugProtocol.ThreadEvent> {219return this._onDidThread.event;220}221222get onDidOutput(): Event<DebugProtocol.OutputEvent> {223return this._onDidOutput.event;224}225226get onDidBreakpoint(): Event<DebugProtocol.BreakpointEvent> {227return this._onDidBreakpoint.event;228}229230get onDidLoadedSource(): Event<DebugProtocol.LoadedSourceEvent> {231return this._onDidLoadedSource.event;232}233234get onDidCustomEvent(): Event<DebugProtocol.Event> {235return this._onDidCustomEvent.event;236}237238get onDidProgressStart(): Event<DebugProtocol.ProgressStartEvent> {239return this._onDidProgressStart.event;240}241242get onDidProgressUpdate(): Event<DebugProtocol.ProgressUpdateEvent> {243return this._onDidProgressUpdate.event;244}245246get onDidProgressEnd(): Event<DebugProtocol.ProgressEndEvent> {247return this._onDidProgressEnd.event;248}249250get onDidInvalidated(): Event<DebugProtocol.InvalidatedEvent> {251return this._onDidInvalidated.event;252}253254get onDidInvalidateMemory(): Event<DebugProtocol.MemoryEvent> {255return this._onDidInvalidateMemory.event;256}257258get onDidEvent(): Event<DebugProtocol.Event> {259return this._onDidEvent.event;260}261262//---- DebugAdapter lifecycle263264/**265* Starts the underlying debug adapter and tracks the session time for telemetry.266*/267async start(): Promise<void> {268if (!this.debugAdapter) {269return Promise.reject(new Error(nls.localize('noDebugAdapterStart', "No debug adapter, can not start debug session.")));270}271272await this.debugAdapter.startSession();273this.startTime = new Date().getTime();274}275276/**277* Send client capabilities to the debug adapter and receive DA capabilities in return.278*/279async initialize(args: DebugProtocol.InitializeRequestArguments): Promise<DebugProtocol.InitializeResponse | undefined> {280const response = await this.send('initialize', args, undefined, undefined, false);281if (response) {282this.mergeCapabilities(response.body);283}284285return response;286}287288/**289* Terminate the debuggee and shutdown the adapter290*/291disconnect(args: DebugProtocol.DisconnectArguments): Promise<any> {292const terminateDebuggee = this.capabilities.supportTerminateDebuggee ? args.terminateDebuggee : undefined;293const suspendDebuggee = this.capabilities.supportTerminateDebuggee && this.capabilities.supportSuspendDebuggee ? args.suspendDebuggee : undefined;294return this.shutdown(undefined, args.restart, terminateDebuggee, suspendDebuggee);295}296297//---- DAP requests298299async launchOrAttach(config: IConfig): Promise<DebugProtocol.Response | undefined> {300const response = await this.send(config.request, config, undefined, undefined, false);301if (response) {302this.mergeCapabilities(response.body);303}304305return response;306}307308/**309* Try killing the debuggee softly...310*/311terminate(restart = false): Promise<DebugProtocol.TerminateResponse | undefined> {312if (this.capabilities.supportsTerminateRequest) {313if (!this.terminated) {314this.terminated = true;315return this.send('terminate', { restart }, undefined);316}317return this.disconnect({ terminateDebuggee: true, restart });318}319return Promise.reject(new Error('terminated not supported'));320}321322restart(args: DebugProtocol.RestartArguments): Promise<DebugProtocol.RestartResponse | undefined> {323if (this.capabilities.supportsRestartRequest) {324return this.send('restart', args);325}326return Promise.reject(new Error('restart not supported'));327}328329async next(args: DebugProtocol.NextArguments): Promise<DebugProtocol.NextResponse | undefined> {330this.stoppedSinceLastStep = false;331const response = await this.send('next', args);332if (!this.stoppedSinceLastStep) {333this.fireSimulatedContinuedEvent(args.threadId);334}335return response;336}337338async stepIn(args: DebugProtocol.StepInArguments): Promise<DebugProtocol.StepInResponse | undefined> {339this.stoppedSinceLastStep = false;340const response = await this.send('stepIn', args);341if (!this.stoppedSinceLastStep) {342this.fireSimulatedContinuedEvent(args.threadId);343}344return response;345}346347async stepOut(args: DebugProtocol.StepOutArguments): Promise<DebugProtocol.StepOutResponse | undefined> {348this.stoppedSinceLastStep = false;349const response = await this.send('stepOut', args);350if (!this.stoppedSinceLastStep) {351this.fireSimulatedContinuedEvent(args.threadId);352}353return response;354}355356async continue(args: DebugProtocol.ContinueArguments): Promise<DebugProtocol.ContinueResponse | undefined> {357this.stoppedSinceLastStep = false;358const response = await this.send<DebugProtocol.ContinueResponse>('continue', args);359if (response && response.body && response.body.allThreadsContinued !== undefined) {360this.allThreadsContinued = response.body.allThreadsContinued;361}362if (!this.stoppedSinceLastStep) {363this.fireSimulatedContinuedEvent(args.threadId, this.allThreadsContinued);364}365366return response;367}368369pause(args: DebugProtocol.PauseArguments): Promise<DebugProtocol.PauseResponse | undefined> {370return this.send('pause', args);371}372373terminateThreads(args: DebugProtocol.TerminateThreadsArguments): Promise<DebugProtocol.TerminateThreadsResponse | undefined> {374if (this.capabilities.supportsTerminateThreadsRequest) {375return this.send('terminateThreads', args);376}377return Promise.reject(new Error('terminateThreads not supported'));378}379380setVariable(args: DebugProtocol.SetVariableArguments): Promise<DebugProtocol.SetVariableResponse | undefined> {381if (this.capabilities.supportsSetVariable) {382return this.send<DebugProtocol.SetVariableResponse>('setVariable', args);383}384return Promise.reject(new Error('setVariable not supported'));385}386387setExpression(args: DebugProtocol.SetExpressionArguments): Promise<DebugProtocol.SetExpressionResponse | undefined> {388if (this.capabilities.supportsSetExpression) {389return this.send<DebugProtocol.SetExpressionResponse>('setExpression', args);390}391return Promise.reject(new Error('setExpression not supported'));392}393394async restartFrame(args: DebugProtocol.RestartFrameArguments, threadId: number): Promise<DebugProtocol.RestartFrameResponse | undefined> {395if (this.capabilities.supportsRestartFrame) {396this.stoppedSinceLastStep = false;397const response = await this.send('restartFrame', args);398if (!this.stoppedSinceLastStep) {399this.fireSimulatedContinuedEvent(threadId);400}401return response;402}403return Promise.reject(new Error('restartFrame not supported'));404}405406stepInTargets(args: DebugProtocol.StepInTargetsArguments): Promise<DebugProtocol.StepInTargetsResponse | undefined> {407if (this.capabilities.supportsStepInTargetsRequest) {408return this.send('stepInTargets', args);409}410return Promise.reject(new Error('stepInTargets not supported'));411}412413completions(args: DebugProtocol.CompletionsArguments, token: CancellationToken): Promise<DebugProtocol.CompletionsResponse | undefined> {414if (this.capabilities.supportsCompletionsRequest) {415return this.send<DebugProtocol.CompletionsResponse>('completions', args, token);416}417return Promise.reject(new Error('completions not supported'));418}419420setBreakpoints(args: DebugProtocol.SetBreakpointsArguments): Promise<DebugProtocol.SetBreakpointsResponse | undefined> {421return this.send<DebugProtocol.SetBreakpointsResponse>('setBreakpoints', args);422}423424setFunctionBreakpoints(args: DebugProtocol.SetFunctionBreakpointsArguments): Promise<DebugProtocol.SetFunctionBreakpointsResponse | undefined> {425if (this.capabilities.supportsFunctionBreakpoints) {426return this.send<DebugProtocol.SetFunctionBreakpointsResponse>('setFunctionBreakpoints', args);427}428return Promise.reject(new Error('setFunctionBreakpoints not supported'));429}430431dataBreakpointInfo(args: DebugProtocol.DataBreakpointInfoArguments): Promise<DebugProtocol.DataBreakpointInfoResponse | undefined> {432if (this.capabilities.supportsDataBreakpoints) {433return this.send<DebugProtocol.DataBreakpointInfoResponse>('dataBreakpointInfo', args);434}435return Promise.reject(new Error('dataBreakpointInfo not supported'));436}437438setDataBreakpoints(args: DebugProtocol.SetDataBreakpointsArguments): Promise<DebugProtocol.SetDataBreakpointsResponse | undefined> {439if (this.capabilities.supportsDataBreakpoints) {440return this.send<DebugProtocol.SetDataBreakpointsResponse>('setDataBreakpoints', args);441}442return Promise.reject(new Error('setDataBreakpoints not supported'));443}444445setExceptionBreakpoints(args: DebugProtocol.SetExceptionBreakpointsArguments): Promise<DebugProtocol.SetExceptionBreakpointsResponse | undefined> {446return this.send<DebugProtocol.SetExceptionBreakpointsResponse>('setExceptionBreakpoints', args);447}448449breakpointLocations(args: DebugProtocol.BreakpointLocationsArguments): Promise<DebugProtocol.BreakpointLocationsResponse | undefined> {450if (this.capabilities.supportsBreakpointLocationsRequest) {451return this.send('breakpointLocations', args);452}453return Promise.reject(new Error('breakpointLocations is not supported'));454}455456configurationDone(): Promise<DebugProtocol.ConfigurationDoneResponse | undefined> {457if (this.capabilities.supportsConfigurationDoneRequest) {458return this.send('configurationDone', null);459}460return Promise.reject(new Error('configurationDone not supported'));461}462463stackTrace(args: DebugProtocol.StackTraceArguments, token: CancellationToken): Promise<DebugProtocol.StackTraceResponse | undefined> {464return this.send<DebugProtocol.StackTraceResponse>('stackTrace', args, token);465}466467exceptionInfo(args: DebugProtocol.ExceptionInfoArguments): Promise<DebugProtocol.ExceptionInfoResponse | undefined> {468if (this.capabilities.supportsExceptionInfoRequest) {469return this.send<DebugProtocol.ExceptionInfoResponse>('exceptionInfo', args);470}471return Promise.reject(new Error('exceptionInfo not supported'));472}473474scopes(args: DebugProtocol.ScopesArguments, token: CancellationToken): Promise<DebugProtocol.ScopesResponse | undefined> {475return this.send<DebugProtocol.ScopesResponse>('scopes', args, token);476}477478variables(args: DebugProtocol.VariablesArguments, token?: CancellationToken): Promise<DebugProtocol.VariablesResponse | undefined> {479return this.send<DebugProtocol.VariablesResponse>('variables', args, token);480}481482source(args: DebugProtocol.SourceArguments): Promise<DebugProtocol.SourceResponse | undefined> {483return this.send<DebugProtocol.SourceResponse>('source', args);484}485486locations(args: DebugProtocol.LocationsArguments): Promise<DebugProtocol.LocationsResponse | undefined> {487return this.send<DebugProtocol.LocationsResponse>('locations', args);488}489490loadedSources(args: DebugProtocol.LoadedSourcesArguments): Promise<DebugProtocol.LoadedSourcesResponse | undefined> {491if (this.capabilities.supportsLoadedSourcesRequest) {492return this.send<DebugProtocol.LoadedSourcesResponse>('loadedSources', args);493}494return Promise.reject(new Error('loadedSources not supported'));495}496497threads(): Promise<DebugProtocol.ThreadsResponse | undefined> {498return this.send<DebugProtocol.ThreadsResponse>('threads', null);499}500501evaluate(args: DebugProtocol.EvaluateArguments): Promise<DebugProtocol.EvaluateResponse | undefined> {502return this.send<DebugProtocol.EvaluateResponse>('evaluate', args);503}504505async stepBack(args: DebugProtocol.StepBackArguments): Promise<DebugProtocol.StepBackResponse | undefined> {506if (this.capabilities.supportsStepBack) {507this.stoppedSinceLastStep = false;508const response = await this.send('stepBack', args);509if (!this.stoppedSinceLastStep) {510this.fireSimulatedContinuedEvent(args.threadId);511}512return response;513}514return Promise.reject(new Error('stepBack not supported'));515}516517async reverseContinue(args: DebugProtocol.ReverseContinueArguments): Promise<DebugProtocol.ReverseContinueResponse | undefined> {518if (this.capabilities.supportsStepBack) {519this.stoppedSinceLastStep = false;520const response = await this.send('reverseContinue', args);521if (!this.stoppedSinceLastStep) {522this.fireSimulatedContinuedEvent(args.threadId);523}524return response;525}526return Promise.reject(new Error('reverseContinue not supported'));527}528529gotoTargets(args: DebugProtocol.GotoTargetsArguments): Promise<DebugProtocol.GotoTargetsResponse | undefined> {530if (this.capabilities.supportsGotoTargetsRequest) {531return this.send('gotoTargets', args);532}533return Promise.reject(new Error('gotoTargets is not supported'));534}535536async goto(args: DebugProtocol.GotoArguments): Promise<DebugProtocol.GotoResponse | undefined> {537if (this.capabilities.supportsGotoTargetsRequest) {538this.stoppedSinceLastStep = false;539const response = await this.send('goto', args);540if (!this.stoppedSinceLastStep) {541this.fireSimulatedContinuedEvent(args.threadId);542}543return response;544}545546return Promise.reject(new Error('goto is not supported'));547}548549async setInstructionBreakpoints(args: DebugProtocol.SetInstructionBreakpointsArguments): Promise<DebugProtocol.SetInstructionBreakpointsResponse | undefined> {550if (this.capabilities.supportsInstructionBreakpoints) {551return await this.send('setInstructionBreakpoints', args);552}553554return Promise.reject(new Error('setInstructionBreakpoints is not supported'));555}556557async disassemble(args: DebugProtocol.DisassembleArguments): Promise<DebugProtocol.DisassembleResponse | undefined> {558if (this.capabilities.supportsDisassembleRequest) {559return await this.send('disassemble', args);560}561562return Promise.reject(new Error('disassemble is not supported'));563}564565async readMemory(args: DebugProtocol.ReadMemoryArguments): Promise<DebugProtocol.ReadMemoryResponse | undefined> {566if (this.capabilities.supportsReadMemoryRequest) {567return await this.send('readMemory', args);568}569570return Promise.reject(new Error('readMemory is not supported'));571}572573async writeMemory(args: DebugProtocol.WriteMemoryArguments): Promise<DebugProtocol.WriteMemoryResponse | undefined> {574if (this.capabilities.supportsWriteMemoryRequest) {575return await this.send('writeMemory', args);576}577578return Promise.reject(new Error('writeMemory is not supported'));579}580581cancel(args: DebugProtocol.CancelArguments): Promise<DebugProtocol.CancelResponse | undefined> {582return this.send('cancel', args);583}584585custom(request: string, args: any): Promise<DebugProtocol.Response | undefined> {586return this.send(request, args);587}588589//---- private590591private async shutdown(error?: Error, restart = false, terminateDebuggee: boolean | undefined = undefined, suspendDebuggee: boolean | undefined = undefined): Promise<void> {592if (!this.inShutdown) {593this.inShutdown = true;594if (this.debugAdapter) {595try {596const args: DebugProtocol.DisconnectArguments = { restart };597if (typeof terminateDebuggee === 'boolean') {598args.terminateDebuggee = terminateDebuggee;599}600601if (typeof suspendDebuggee === 'boolean') {602args.suspendDebuggee = suspendDebuggee;603}604605// if there's an error, the DA is probably already gone, so give it a much shorter timeout.606await this.send('disconnect', args, undefined, error ? 200 : 2000);607} catch (e) {608// Catch the potential 'disconnect' error - no need to show it to the user since the adapter is shutting down609} finally {610await this.stopAdapter(error);611}612} else {613return this.stopAdapter(error);614}615}616}617618private async stopAdapter(error?: Error): Promise<void> {619try {620if (this.debugAdapter) {621const da = this.debugAdapter;622this.debugAdapter = null;623await da.stopSession();624this.debugAdapterStopped = true;625}626} finally {627this.fireAdapterExitEvent(error);628}629}630631private fireAdapterExitEvent(error?: Error): void {632if (!this.firedAdapterExitEvent) {633this.firedAdapterExitEvent = true;634635const e: AdapterEndEvent = {636emittedStopped: this.didReceiveStoppedEvent,637sessionLengthInSeconds: (new Date().getTime() - this.startTime) / 1000638};639if (error && !this.debugAdapterStopped) {640e.error = error;641}642this._onDidExitAdapter.fire(e);643}644}645646private async dispatchRequest(request: DebugProtocol.Request): Promise<void> {647648const response: DebugProtocol.Response = {649type: 'response',650seq: 0,651command: request.command,652request_seq: request.seq,653success: true654};655656const safeSendResponse = (response: DebugProtocol.Response) => this.debugAdapter && this.debugAdapter.sendResponse(response);657658if (request.command === 'launchVSCode') {659try {660let result = await this.launchVsCode(<ILaunchVSCodeArguments>request.arguments);661if (!result.success) {662const { confirmed } = await this.dialogSerivce.confirm({663type: Severity.Warning,664message: nls.localize('canNotStart', "The debugger needs to open a new tab or window for the debuggee but the browser prevented this. You must give permission to continue."),665primaryButton: nls.localize({ key: 'continue', comment: ['&& denotes a mnemonic'] }, "&&Continue")666});667if (confirmed) {668result = await this.launchVsCode(<ILaunchVSCodeArguments>request.arguments);669} else {670response.success = false;671safeSendResponse(response);672await this.shutdown();673}674}675response.body = {676rendererDebugAddr: result.rendererDebugAddr,677};678safeSendResponse(response);679} catch (err) {680response.success = false;681response.message = err.message;682safeSendResponse(response);683}684} else if (request.command === 'runInTerminal') {685try {686const shellProcessId = await this.dbgr.runInTerminal(request.arguments as DebugProtocol.RunInTerminalRequestArguments, this.sessionId);687const resp = response as DebugProtocol.RunInTerminalResponse;688resp.body = {};689if (typeof shellProcessId === 'number') {690resp.body.shellProcessId = shellProcessId;691}692safeSendResponse(resp);693} catch (err) {694response.success = false;695response.message = err.message;696safeSendResponse(response);697}698} else if (request.command === 'startDebugging') {699try {700const args = (request.arguments as DebugProtocol.StartDebuggingRequestArguments);701const config: IConfig = {702...args.configuration,703...{704request: args.request,705type: this.dbgr.type,706name: args.configuration.name || this.name707}708};709const success = await this.dbgr.startDebugging(config, this.sessionId);710if (success) {711safeSendResponse(response);712} else {713response.success = false;714response.message = 'Failed to start debugging';715safeSendResponse(response);716}717} catch (err) {718response.success = false;719response.message = err.message;720safeSendResponse(response);721}722} else {723response.success = false;724response.message = `unknown request '${request.command}'`;725safeSendResponse(response);726}727}728729private launchVsCode(vscodeArgs: ILaunchVSCodeArguments): Promise<IOpenExtensionWindowResult> {730731const args: string[] = [];732733for (const arg of vscodeArgs.args) {734const a2 = (arg.prefix || '') + (arg.path || '');735const match = /^--(.+)=(.+)$/.exec(a2);736if (match && match.length === 3) {737const key = match[1];738let value = match[2];739740if ((key === 'file-uri' || key === 'folder-uri') && !isUri(arg.path)) {741value = isUri(value) ? value : URI.file(value).toString();742}743args.push(`--${key}=${value}`);744} else {745args.push(a2);746}747}748749if (vscodeArgs.env) {750args.push(`--extensionEnvironment=${JSON.stringify(vscodeArgs.env)}`);751}752753return this.extensionHostDebugService.openExtensionDevelopmentHostWindow(args, !!vscodeArgs.debugRenderer);754}755756private send<R extends DebugProtocol.Response>(command: string, args: any, token?: CancellationToken, timeout?: number, showErrors = true): Promise<R | undefined> {757return new Promise<DebugProtocol.Response | undefined>((completeDispatch, errorDispatch) => {758if (!this.debugAdapter) {759if (this.inShutdown) {760// We are in shutdown silently complete761completeDispatch(undefined);762} else {763errorDispatch(new Error(nls.localize('noDebugAdapter', "No debugger available found. Can not send '{0}'.", command)));764}765return;766}767768let cancelationListener: IDisposable;769const requestId = this.debugAdapter.sendRequest(command, args, (response: DebugProtocol.Response) => {770cancelationListener?.dispose();771772if (response.success) {773completeDispatch(response);774} else {775errorDispatch(response);776}777}, timeout);778779if (token) {780cancelationListener = token.onCancellationRequested(() => {781cancelationListener.dispose();782if (this.capabilities.supportsCancelRequest) {783this.cancel({ requestId });784}785});786}787}).then(undefined, err => Promise.reject(this.handleErrorResponse(err, showErrors)));788}789790private handleErrorResponse(errorResponse: DebugProtocol.Response, showErrors: boolean): Error {791792if (errorResponse.command === 'canceled' && errorResponse.message === 'canceled') {793return new errors.CancellationError();794}795796const error: DebugProtocol.Message | undefined = errorResponse?.body?.error;797const errorMessage = errorResponse?.message || '';798799const userMessage = error ? formatPII(error.format, false, error.variables) : errorMessage;800const url = error?.url;801if (error && url) {802const label = error.urlLabel ? error.urlLabel : nls.localize('moreInfo', "More Info");803const uri = URI.parse(url);804// Use a suffixed id if uri invokes a command, so default 'Open launch.json' command is suppressed on dialog805const actionId = uri.scheme === Schemas.command ? 'debug.moreInfo.command' : 'debug.moreInfo';806return createErrorWithActions(userMessage, [toAction({ id: actionId, label, run: () => this.openerService.open(uri, { allowCommands: true }) })]);807}808if (showErrors && error && error.format && error.showUser) {809this.notificationService.error(userMessage);810}811const result = new errors.ErrorNoTelemetry(userMessage);812(<any>result).showUser = error?.showUser;813814return result;815}816817private mergeCapabilities(capabilities: DebugProtocol.Capabilities | undefined): void {818if (capabilities) {819this._capabilities = objects.mixin(this._capabilities, capabilities);820}821}822823private fireSimulatedContinuedEvent(threadId: number, allThreadsContinued = false): void {824this._onDidContinued.fire({825type: 'event',826event: 'continued',827body: {828threadId,829allThreadsContinued830},831seq: undefined!832});833}834835dispose(): void {836dispose(this.toDispose);837}838}839840841