Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import * as nls from '../../../../nls.js';
7
import { Event, Emitter } from '../../../../base/common/event.js';
8
import * as objects from '../../../../base/common/objects.js';
9
import { toAction } from '../../../../base/common/actions.js';
10
import * as errors from '../../../../base/common/errors.js';
11
import { createErrorWithActions } from '../../../../base/common/errorMessage.js';
12
import { formatPII, isUri } from '../common/debugUtils.js';
13
import { IDebugAdapter, IConfig, AdapterEndEvent, IDebugger } from '../common/debug.js';
14
import { IExtensionHostDebugService, IOpenExtensionWindowResult } from '../../../../platform/debug/common/extensionHostDebug.js';
15
import { URI } from '../../../../base/common/uri.js';
16
import { IOpenerService } from '../../../../platform/opener/common/opener.js';
17
import { IDisposable, dispose } from '../../../../base/common/lifecycle.js';
18
import { CancellationToken } from '../../../../base/common/cancellation.js';
19
import { INotificationService, Severity } from '../../../../platform/notification/common/notification.js';
20
import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js';
21
import { Schemas } from '../../../../base/common/network.js';
22
23
/**
24
* This interface represents a single command line argument split into a "prefix" and a "path" half.
25
* The optional "prefix" contains arbitrary text and the optional "path" contains a file system path.
26
* Concatenating both results in the original command line argument.
27
*/
28
interface ILaunchVSCodeArgument {
29
prefix?: string;
30
path?: string;
31
}
32
33
interface ILaunchVSCodeArguments {
34
args: ILaunchVSCodeArgument[];
35
debugRenderer?: boolean;
36
env?: { [key: string]: string | null };
37
}
38
39
/**
40
* Encapsulates the DebugAdapter lifecycle and some idiosyncrasies of the Debug Adapter Protocol.
41
*/
42
export class RawDebugSession implements IDisposable {
43
44
private allThreadsContinued = true;
45
private _readyForBreakpoints = false;
46
private _capabilities: DebugProtocol.Capabilities;
47
48
// shutdown
49
private debugAdapterStopped = false;
50
private inShutdown = false;
51
private terminated = false;
52
private firedAdapterExitEvent = false;
53
54
// telemetry
55
private startTime = 0;
56
private didReceiveStoppedEvent = false;
57
58
// DAP events
59
private readonly _onDidInitialize = new Emitter<DebugProtocol.InitializedEvent>();
60
private readonly _onDidStop = new Emitter<DebugProtocol.StoppedEvent>();
61
private readonly _onDidContinued = new Emitter<DebugProtocol.ContinuedEvent>();
62
private readonly _onDidTerminateDebugee = new Emitter<DebugProtocol.TerminatedEvent>();
63
private readonly _onDidExitDebugee = new Emitter<DebugProtocol.ExitedEvent>();
64
private readonly _onDidThread = new Emitter<DebugProtocol.ThreadEvent>();
65
private readonly _onDidOutput = new Emitter<DebugProtocol.OutputEvent>();
66
private readonly _onDidBreakpoint = new Emitter<DebugProtocol.BreakpointEvent>();
67
private readonly _onDidLoadedSource = new Emitter<DebugProtocol.LoadedSourceEvent>();
68
private readonly _onDidProgressStart = new Emitter<DebugProtocol.ProgressStartEvent>();
69
private readonly _onDidProgressUpdate = new Emitter<DebugProtocol.ProgressUpdateEvent>();
70
private readonly _onDidProgressEnd = new Emitter<DebugProtocol.ProgressEndEvent>();
71
private readonly _onDidInvalidated = new Emitter<DebugProtocol.InvalidatedEvent>();
72
private readonly _onDidInvalidateMemory = new Emitter<DebugProtocol.MemoryEvent>();
73
private readonly _onDidCustomEvent = new Emitter<DebugProtocol.Event>();
74
private readonly _onDidEvent = new Emitter<DebugProtocol.Event>();
75
76
// DA events
77
private readonly _onDidExitAdapter = new Emitter<AdapterEndEvent>();
78
private debugAdapter: IDebugAdapter | null;
79
private stoppedSinceLastStep = false;
80
81
private toDispose: IDisposable[] = [];
82
83
constructor(
84
debugAdapter: IDebugAdapter,
85
public readonly dbgr: IDebugger,
86
private readonly sessionId: string,
87
private readonly name: string,
88
@IExtensionHostDebugService private readonly extensionHostDebugService: IExtensionHostDebugService,
89
@IOpenerService private readonly openerService: IOpenerService,
90
@INotificationService private readonly notificationService: INotificationService,
91
@IDialogService private readonly dialogSerivce: IDialogService,
92
) {
93
this.debugAdapter = debugAdapter;
94
this._capabilities = Object.create(null);
95
96
this.toDispose.push(this.debugAdapter.onError(err => {
97
this.shutdown(err);
98
}));
99
100
this.toDispose.push(this.debugAdapter.onExit(code => {
101
if (code !== 0) {
102
this.shutdown(new Error(`exit code: ${code}`));
103
} else {
104
// normal exit
105
this.shutdown();
106
}
107
}));
108
109
this.debugAdapter.onEvent(event => {
110
switch (event.event) {
111
case 'initialized':
112
this._readyForBreakpoints = true;
113
this._onDidInitialize.fire(event);
114
break;
115
case 'loadedSource':
116
this._onDidLoadedSource.fire(<DebugProtocol.LoadedSourceEvent>event);
117
break;
118
case 'capabilities':
119
if (event.body) {
120
const capabilities = (<DebugProtocol.CapabilitiesEvent>event).body.capabilities;
121
this.mergeCapabilities(capabilities);
122
}
123
break;
124
case 'stopped':
125
this.didReceiveStoppedEvent = true; // telemetry: remember that debugger stopped successfully
126
this.stoppedSinceLastStep = true;
127
this._onDidStop.fire(<DebugProtocol.StoppedEvent>event);
128
break;
129
case 'continued':
130
this.allThreadsContinued = (<DebugProtocol.ContinuedEvent>event).body.allThreadsContinued === false ? false : true;
131
this._onDidContinued.fire(<DebugProtocol.ContinuedEvent>event);
132
break;
133
case 'thread':
134
this._onDidThread.fire(<DebugProtocol.ThreadEvent>event);
135
break;
136
case 'output':
137
this._onDidOutput.fire(<DebugProtocol.OutputEvent>event);
138
break;
139
case 'breakpoint':
140
this._onDidBreakpoint.fire(<DebugProtocol.BreakpointEvent>event);
141
break;
142
case 'terminated':
143
this._onDidTerminateDebugee.fire(<DebugProtocol.TerminatedEvent>event);
144
break;
145
case 'exited':
146
this._onDidExitDebugee.fire(<DebugProtocol.ExitedEvent>event);
147
break;
148
case 'progressStart':
149
this._onDidProgressStart.fire(event as DebugProtocol.ProgressStartEvent);
150
break;
151
case 'progressUpdate':
152
this._onDidProgressUpdate.fire(event as DebugProtocol.ProgressUpdateEvent);
153
break;
154
case 'progressEnd':
155
this._onDidProgressEnd.fire(event as DebugProtocol.ProgressEndEvent);
156
break;
157
case 'invalidated':
158
this._onDidInvalidated.fire(event as DebugProtocol.InvalidatedEvent);
159
break;
160
case 'memory':
161
this._onDidInvalidateMemory.fire(event as DebugProtocol.MemoryEvent);
162
break;
163
case 'process':
164
break;
165
case 'module':
166
break;
167
default:
168
this._onDidCustomEvent.fire(event);
169
break;
170
}
171
this._onDidEvent.fire(event);
172
});
173
174
this.debugAdapter.onRequest(request => this.dispatchRequest(request));
175
}
176
177
get isInShutdown() {
178
return this.inShutdown;
179
}
180
181
get onDidExitAdapter(): Event<AdapterEndEvent> {
182
return this._onDidExitAdapter.event;
183
}
184
185
get capabilities(): DebugProtocol.Capabilities {
186
return this._capabilities;
187
}
188
189
/**
190
* DA is ready to accepts setBreakpoint requests.
191
* Becomes true after "initialized" events has been received.
192
*/
193
get readyForBreakpoints(): boolean {
194
return this._readyForBreakpoints;
195
}
196
197
//---- DAP events
198
199
get onDidInitialize(): Event<DebugProtocol.InitializedEvent> {
200
return this._onDidInitialize.event;
201
}
202
203
get onDidStop(): Event<DebugProtocol.StoppedEvent> {
204
return this._onDidStop.event;
205
}
206
207
get onDidContinued(): Event<DebugProtocol.ContinuedEvent> {
208
return this._onDidContinued.event;
209
}
210
211
get onDidTerminateDebugee(): Event<DebugProtocol.TerminatedEvent> {
212
return this._onDidTerminateDebugee.event;
213
}
214
215
get onDidExitDebugee(): Event<DebugProtocol.ExitedEvent> {
216
return this._onDidExitDebugee.event;
217
}
218
219
get onDidThread(): Event<DebugProtocol.ThreadEvent> {
220
return this._onDidThread.event;
221
}
222
223
get onDidOutput(): Event<DebugProtocol.OutputEvent> {
224
return this._onDidOutput.event;
225
}
226
227
get onDidBreakpoint(): Event<DebugProtocol.BreakpointEvent> {
228
return this._onDidBreakpoint.event;
229
}
230
231
get onDidLoadedSource(): Event<DebugProtocol.LoadedSourceEvent> {
232
return this._onDidLoadedSource.event;
233
}
234
235
get onDidCustomEvent(): Event<DebugProtocol.Event> {
236
return this._onDidCustomEvent.event;
237
}
238
239
get onDidProgressStart(): Event<DebugProtocol.ProgressStartEvent> {
240
return this._onDidProgressStart.event;
241
}
242
243
get onDidProgressUpdate(): Event<DebugProtocol.ProgressUpdateEvent> {
244
return this._onDidProgressUpdate.event;
245
}
246
247
get onDidProgressEnd(): Event<DebugProtocol.ProgressEndEvent> {
248
return this._onDidProgressEnd.event;
249
}
250
251
get onDidInvalidated(): Event<DebugProtocol.InvalidatedEvent> {
252
return this._onDidInvalidated.event;
253
}
254
255
get onDidInvalidateMemory(): Event<DebugProtocol.MemoryEvent> {
256
return this._onDidInvalidateMemory.event;
257
}
258
259
get onDidEvent(): Event<DebugProtocol.Event> {
260
return this._onDidEvent.event;
261
}
262
263
//---- DebugAdapter lifecycle
264
265
/**
266
* Starts the underlying debug adapter and tracks the session time for telemetry.
267
*/
268
async start(): Promise<void> {
269
if (!this.debugAdapter) {
270
return Promise.reject(new Error(nls.localize('noDebugAdapterStart', "No debug adapter, can not start debug session.")));
271
}
272
273
await this.debugAdapter.startSession();
274
this.startTime = new Date().getTime();
275
}
276
277
/**
278
* Send client capabilities to the debug adapter and receive DA capabilities in return.
279
*/
280
async initialize(args: DebugProtocol.InitializeRequestArguments): Promise<DebugProtocol.InitializeResponse | undefined> {
281
const response = await this.send('initialize', args, undefined, undefined, false);
282
if (response) {
283
this.mergeCapabilities(response.body);
284
}
285
286
return response;
287
}
288
289
/**
290
* Terminate the debuggee and shutdown the adapter
291
*/
292
disconnect(args: DebugProtocol.DisconnectArguments): Promise<any> {
293
const terminateDebuggee = this.capabilities.supportTerminateDebuggee ? args.terminateDebuggee : undefined;
294
const suspendDebuggee = this.capabilities.supportTerminateDebuggee && this.capabilities.supportSuspendDebuggee ? args.suspendDebuggee : undefined;
295
return this.shutdown(undefined, args.restart, terminateDebuggee, suspendDebuggee);
296
}
297
298
//---- DAP requests
299
300
async launchOrAttach(config: IConfig): Promise<DebugProtocol.Response | undefined> {
301
const response = await this.send(config.request, config, undefined, undefined, false);
302
if (response) {
303
this.mergeCapabilities(response.body);
304
}
305
306
return response;
307
}
308
309
/**
310
* Try killing the debuggee softly...
311
*/
312
terminate(restart = false): Promise<DebugProtocol.TerminateResponse | undefined> {
313
if (this.capabilities.supportsTerminateRequest) {
314
if (!this.terminated) {
315
this.terminated = true;
316
return this.send('terminate', { restart }, undefined);
317
}
318
return this.disconnect({ terminateDebuggee: true, restart });
319
}
320
return Promise.reject(new Error('terminated not supported'));
321
}
322
323
restart(args: DebugProtocol.RestartArguments): Promise<DebugProtocol.RestartResponse | undefined> {
324
if (this.capabilities.supportsRestartRequest) {
325
return this.send('restart', args);
326
}
327
return Promise.reject(new Error('restart not supported'));
328
}
329
330
async next(args: DebugProtocol.NextArguments): Promise<DebugProtocol.NextResponse | undefined> {
331
this.stoppedSinceLastStep = false;
332
const response = await this.send('next', args);
333
if (!this.stoppedSinceLastStep) {
334
this.fireSimulatedContinuedEvent(args.threadId);
335
}
336
return response;
337
}
338
339
async stepIn(args: DebugProtocol.StepInArguments): Promise<DebugProtocol.StepInResponse | undefined> {
340
this.stoppedSinceLastStep = false;
341
const response = await this.send('stepIn', args);
342
if (!this.stoppedSinceLastStep) {
343
this.fireSimulatedContinuedEvent(args.threadId);
344
}
345
return response;
346
}
347
348
async stepOut(args: DebugProtocol.StepOutArguments): Promise<DebugProtocol.StepOutResponse | undefined> {
349
this.stoppedSinceLastStep = false;
350
const response = await this.send('stepOut', args);
351
if (!this.stoppedSinceLastStep) {
352
this.fireSimulatedContinuedEvent(args.threadId);
353
}
354
return response;
355
}
356
357
async continue(args: DebugProtocol.ContinueArguments): Promise<DebugProtocol.ContinueResponse | undefined> {
358
this.stoppedSinceLastStep = false;
359
const response = await this.send<DebugProtocol.ContinueResponse>('continue', args);
360
if (response && response.body && response.body.allThreadsContinued !== undefined) {
361
this.allThreadsContinued = response.body.allThreadsContinued;
362
}
363
if (!this.stoppedSinceLastStep) {
364
this.fireSimulatedContinuedEvent(args.threadId, this.allThreadsContinued);
365
}
366
367
return response;
368
}
369
370
pause(args: DebugProtocol.PauseArguments): Promise<DebugProtocol.PauseResponse | undefined> {
371
return this.send('pause', args);
372
}
373
374
terminateThreads(args: DebugProtocol.TerminateThreadsArguments): Promise<DebugProtocol.TerminateThreadsResponse | undefined> {
375
if (this.capabilities.supportsTerminateThreadsRequest) {
376
return this.send('terminateThreads', args);
377
}
378
return Promise.reject(new Error('terminateThreads not supported'));
379
}
380
381
setVariable(args: DebugProtocol.SetVariableArguments): Promise<DebugProtocol.SetVariableResponse | undefined> {
382
if (this.capabilities.supportsSetVariable) {
383
return this.send<DebugProtocol.SetVariableResponse>('setVariable', args);
384
}
385
return Promise.reject(new Error('setVariable not supported'));
386
}
387
388
setExpression(args: DebugProtocol.SetExpressionArguments): Promise<DebugProtocol.SetExpressionResponse | undefined> {
389
if (this.capabilities.supportsSetExpression) {
390
return this.send<DebugProtocol.SetExpressionResponse>('setExpression', args);
391
}
392
return Promise.reject(new Error('setExpression not supported'));
393
}
394
395
async restartFrame(args: DebugProtocol.RestartFrameArguments, threadId: number): Promise<DebugProtocol.RestartFrameResponse | undefined> {
396
if (this.capabilities.supportsRestartFrame) {
397
this.stoppedSinceLastStep = false;
398
const response = await this.send('restartFrame', args);
399
if (!this.stoppedSinceLastStep) {
400
this.fireSimulatedContinuedEvent(threadId);
401
}
402
return response;
403
}
404
return Promise.reject(new Error('restartFrame not supported'));
405
}
406
407
stepInTargets(args: DebugProtocol.StepInTargetsArguments): Promise<DebugProtocol.StepInTargetsResponse | undefined> {
408
if (this.capabilities.supportsStepInTargetsRequest) {
409
return this.send('stepInTargets', args);
410
}
411
return Promise.reject(new Error('stepInTargets not supported'));
412
}
413
414
completions(args: DebugProtocol.CompletionsArguments, token: CancellationToken): Promise<DebugProtocol.CompletionsResponse | undefined> {
415
if (this.capabilities.supportsCompletionsRequest) {
416
return this.send<DebugProtocol.CompletionsResponse>('completions', args, token);
417
}
418
return Promise.reject(new Error('completions not supported'));
419
}
420
421
setBreakpoints(args: DebugProtocol.SetBreakpointsArguments): Promise<DebugProtocol.SetBreakpointsResponse | undefined> {
422
return this.send<DebugProtocol.SetBreakpointsResponse>('setBreakpoints', args);
423
}
424
425
setFunctionBreakpoints(args: DebugProtocol.SetFunctionBreakpointsArguments): Promise<DebugProtocol.SetFunctionBreakpointsResponse | undefined> {
426
if (this.capabilities.supportsFunctionBreakpoints) {
427
return this.send<DebugProtocol.SetFunctionBreakpointsResponse>('setFunctionBreakpoints', args);
428
}
429
return Promise.reject(new Error('setFunctionBreakpoints not supported'));
430
}
431
432
dataBreakpointInfo(args: DebugProtocol.DataBreakpointInfoArguments): Promise<DebugProtocol.DataBreakpointInfoResponse | undefined> {
433
if (this.capabilities.supportsDataBreakpoints) {
434
return this.send<DebugProtocol.DataBreakpointInfoResponse>('dataBreakpointInfo', args);
435
}
436
return Promise.reject(new Error('dataBreakpointInfo not supported'));
437
}
438
439
setDataBreakpoints(args: DebugProtocol.SetDataBreakpointsArguments): Promise<DebugProtocol.SetDataBreakpointsResponse | undefined> {
440
if (this.capabilities.supportsDataBreakpoints) {
441
return this.send<DebugProtocol.SetDataBreakpointsResponse>('setDataBreakpoints', args);
442
}
443
return Promise.reject(new Error('setDataBreakpoints not supported'));
444
}
445
446
setExceptionBreakpoints(args: DebugProtocol.SetExceptionBreakpointsArguments): Promise<DebugProtocol.SetExceptionBreakpointsResponse | undefined> {
447
return this.send<DebugProtocol.SetExceptionBreakpointsResponse>('setExceptionBreakpoints', args);
448
}
449
450
breakpointLocations(args: DebugProtocol.BreakpointLocationsArguments): Promise<DebugProtocol.BreakpointLocationsResponse | undefined> {
451
if (this.capabilities.supportsBreakpointLocationsRequest) {
452
return this.send('breakpointLocations', args);
453
}
454
return Promise.reject(new Error('breakpointLocations is not supported'));
455
}
456
457
configurationDone(): Promise<DebugProtocol.ConfigurationDoneResponse | undefined> {
458
if (this.capabilities.supportsConfigurationDoneRequest) {
459
return this.send('configurationDone', null);
460
}
461
return Promise.reject(new Error('configurationDone not supported'));
462
}
463
464
stackTrace(args: DebugProtocol.StackTraceArguments, token: CancellationToken): Promise<DebugProtocol.StackTraceResponse | undefined> {
465
return this.send<DebugProtocol.StackTraceResponse>('stackTrace', args, token);
466
}
467
468
exceptionInfo(args: DebugProtocol.ExceptionInfoArguments): Promise<DebugProtocol.ExceptionInfoResponse | undefined> {
469
if (this.capabilities.supportsExceptionInfoRequest) {
470
return this.send<DebugProtocol.ExceptionInfoResponse>('exceptionInfo', args);
471
}
472
return Promise.reject(new Error('exceptionInfo not supported'));
473
}
474
475
scopes(args: DebugProtocol.ScopesArguments, token: CancellationToken): Promise<DebugProtocol.ScopesResponse | undefined> {
476
return this.send<DebugProtocol.ScopesResponse>('scopes', args, token);
477
}
478
479
variables(args: DebugProtocol.VariablesArguments, token?: CancellationToken): Promise<DebugProtocol.VariablesResponse | undefined> {
480
return this.send<DebugProtocol.VariablesResponse>('variables', args, token);
481
}
482
483
source(args: DebugProtocol.SourceArguments): Promise<DebugProtocol.SourceResponse | undefined> {
484
return this.send<DebugProtocol.SourceResponse>('source', args);
485
}
486
487
locations(args: DebugProtocol.LocationsArguments): Promise<DebugProtocol.LocationsResponse | undefined> {
488
return this.send<DebugProtocol.LocationsResponse>('locations', args);
489
}
490
491
loadedSources(args: DebugProtocol.LoadedSourcesArguments): Promise<DebugProtocol.LoadedSourcesResponse | undefined> {
492
if (this.capabilities.supportsLoadedSourcesRequest) {
493
return this.send<DebugProtocol.LoadedSourcesResponse>('loadedSources', args);
494
}
495
return Promise.reject(new Error('loadedSources not supported'));
496
}
497
498
threads(): Promise<DebugProtocol.ThreadsResponse | undefined> {
499
return this.send<DebugProtocol.ThreadsResponse>('threads', null);
500
}
501
502
evaluate(args: DebugProtocol.EvaluateArguments): Promise<DebugProtocol.EvaluateResponse | undefined> {
503
return this.send<DebugProtocol.EvaluateResponse>('evaluate', args);
504
}
505
506
async stepBack(args: DebugProtocol.StepBackArguments): Promise<DebugProtocol.StepBackResponse | undefined> {
507
if (this.capabilities.supportsStepBack) {
508
this.stoppedSinceLastStep = false;
509
const response = await this.send('stepBack', args);
510
if (!this.stoppedSinceLastStep) {
511
this.fireSimulatedContinuedEvent(args.threadId);
512
}
513
return response;
514
}
515
return Promise.reject(new Error('stepBack not supported'));
516
}
517
518
async reverseContinue(args: DebugProtocol.ReverseContinueArguments): Promise<DebugProtocol.ReverseContinueResponse | undefined> {
519
if (this.capabilities.supportsStepBack) {
520
this.stoppedSinceLastStep = false;
521
const response = await this.send('reverseContinue', args);
522
if (!this.stoppedSinceLastStep) {
523
this.fireSimulatedContinuedEvent(args.threadId);
524
}
525
return response;
526
}
527
return Promise.reject(new Error('reverseContinue not supported'));
528
}
529
530
gotoTargets(args: DebugProtocol.GotoTargetsArguments): Promise<DebugProtocol.GotoTargetsResponse | undefined> {
531
if (this.capabilities.supportsGotoTargetsRequest) {
532
return this.send('gotoTargets', args);
533
}
534
return Promise.reject(new Error('gotoTargets is not supported'));
535
}
536
537
async goto(args: DebugProtocol.GotoArguments): Promise<DebugProtocol.GotoResponse | undefined> {
538
if (this.capabilities.supportsGotoTargetsRequest) {
539
this.stoppedSinceLastStep = false;
540
const response = await this.send('goto', args);
541
if (!this.stoppedSinceLastStep) {
542
this.fireSimulatedContinuedEvent(args.threadId);
543
}
544
return response;
545
}
546
547
return Promise.reject(new Error('goto is not supported'));
548
}
549
550
async setInstructionBreakpoints(args: DebugProtocol.SetInstructionBreakpointsArguments): Promise<DebugProtocol.SetInstructionBreakpointsResponse | undefined> {
551
if (this.capabilities.supportsInstructionBreakpoints) {
552
return await this.send('setInstructionBreakpoints', args);
553
}
554
555
return Promise.reject(new Error('setInstructionBreakpoints is not supported'));
556
}
557
558
async disassemble(args: DebugProtocol.DisassembleArguments): Promise<DebugProtocol.DisassembleResponse | undefined> {
559
if (this.capabilities.supportsDisassembleRequest) {
560
return await this.send('disassemble', args);
561
}
562
563
return Promise.reject(new Error('disassemble is not supported'));
564
}
565
566
async readMemory(args: DebugProtocol.ReadMemoryArguments): Promise<DebugProtocol.ReadMemoryResponse | undefined> {
567
if (this.capabilities.supportsReadMemoryRequest) {
568
return await this.send('readMemory', args);
569
}
570
571
return Promise.reject(new Error('readMemory is not supported'));
572
}
573
574
async writeMemory(args: DebugProtocol.WriteMemoryArguments): Promise<DebugProtocol.WriteMemoryResponse | undefined> {
575
if (this.capabilities.supportsWriteMemoryRequest) {
576
return await this.send('writeMemory', args);
577
}
578
579
return Promise.reject(new Error('writeMemory is not supported'));
580
}
581
582
cancel(args: DebugProtocol.CancelArguments): Promise<DebugProtocol.CancelResponse | undefined> {
583
return this.send('cancel', args);
584
}
585
586
custom(request: string, args: any): Promise<DebugProtocol.Response | undefined> {
587
return this.send(request, args);
588
}
589
590
//---- private
591
592
private async shutdown(error?: Error, restart = false, terminateDebuggee: boolean | undefined = undefined, suspendDebuggee: boolean | undefined = undefined): Promise<void> {
593
if (!this.inShutdown) {
594
this.inShutdown = true;
595
if (this.debugAdapter) {
596
try {
597
const args: DebugProtocol.DisconnectArguments = { restart };
598
if (typeof terminateDebuggee === 'boolean') {
599
args.terminateDebuggee = terminateDebuggee;
600
}
601
602
if (typeof suspendDebuggee === 'boolean') {
603
args.suspendDebuggee = suspendDebuggee;
604
}
605
606
// if there's an error, the DA is probably already gone, so give it a much shorter timeout.
607
await this.send('disconnect', args, undefined, error ? 200 : 2000);
608
} catch (e) {
609
// Catch the potential 'disconnect' error - no need to show it to the user since the adapter is shutting down
610
} finally {
611
await this.stopAdapter(error);
612
}
613
} else {
614
return this.stopAdapter(error);
615
}
616
}
617
}
618
619
private async stopAdapter(error?: Error): Promise<void> {
620
try {
621
if (this.debugAdapter) {
622
const da = this.debugAdapter;
623
this.debugAdapter = null;
624
await da.stopSession();
625
this.debugAdapterStopped = true;
626
}
627
} finally {
628
this.fireAdapterExitEvent(error);
629
}
630
}
631
632
private fireAdapterExitEvent(error?: Error): void {
633
if (!this.firedAdapterExitEvent) {
634
this.firedAdapterExitEvent = true;
635
636
const e: AdapterEndEvent = {
637
emittedStopped: this.didReceiveStoppedEvent,
638
sessionLengthInSeconds: (new Date().getTime() - this.startTime) / 1000
639
};
640
if (error && !this.debugAdapterStopped) {
641
e.error = error;
642
}
643
this._onDidExitAdapter.fire(e);
644
}
645
}
646
647
private async dispatchRequest(request: DebugProtocol.Request): Promise<void> {
648
649
const response: DebugProtocol.Response = {
650
type: 'response',
651
seq: 0,
652
command: request.command,
653
request_seq: request.seq,
654
success: true
655
};
656
657
const safeSendResponse = (response: DebugProtocol.Response) => this.debugAdapter && this.debugAdapter.sendResponse(response);
658
659
if (request.command === 'launchVSCode') {
660
try {
661
let result = await this.launchVsCode(<ILaunchVSCodeArguments>request.arguments);
662
if (!result.success) {
663
const { confirmed } = await this.dialogSerivce.confirm({
664
type: Severity.Warning,
665
message: 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."),
666
primaryButton: nls.localize({ key: 'continue', comment: ['&& denotes a mnemonic'] }, "&&Continue")
667
});
668
if (confirmed) {
669
result = await this.launchVsCode(<ILaunchVSCodeArguments>request.arguments);
670
} else {
671
response.success = false;
672
safeSendResponse(response);
673
await this.shutdown();
674
}
675
}
676
response.body = {
677
rendererDebugAddr: result.rendererDebugAddr,
678
};
679
safeSendResponse(response);
680
} catch (err) {
681
response.success = false;
682
response.message = err.message;
683
safeSendResponse(response);
684
}
685
} else if (request.command === 'runInTerminal') {
686
try {
687
const shellProcessId = await this.dbgr.runInTerminal(request.arguments as DebugProtocol.RunInTerminalRequestArguments, this.sessionId);
688
const resp = response as DebugProtocol.RunInTerminalResponse;
689
resp.body = {};
690
if (typeof shellProcessId === 'number') {
691
resp.body.shellProcessId = shellProcessId;
692
}
693
safeSendResponse(resp);
694
} catch (err) {
695
response.success = false;
696
response.message = err.message;
697
safeSendResponse(response);
698
}
699
} else if (request.command === 'startDebugging') {
700
try {
701
const args = (request.arguments as DebugProtocol.StartDebuggingRequestArguments);
702
const config: IConfig = {
703
...args.configuration,
704
...{
705
request: args.request,
706
type: this.dbgr.type,
707
name: args.configuration.name || this.name
708
}
709
};
710
const success = await this.dbgr.startDebugging(config, this.sessionId);
711
if (success) {
712
safeSendResponse(response);
713
} else {
714
response.success = false;
715
response.message = 'Failed to start debugging';
716
safeSendResponse(response);
717
}
718
} catch (err) {
719
response.success = false;
720
response.message = err.message;
721
safeSendResponse(response);
722
}
723
} else {
724
response.success = false;
725
response.message = `unknown request '${request.command}'`;
726
safeSendResponse(response);
727
}
728
}
729
730
private launchVsCode(vscodeArgs: ILaunchVSCodeArguments): Promise<IOpenExtensionWindowResult> {
731
732
const args: string[] = [];
733
734
for (const arg of vscodeArgs.args) {
735
const a2 = (arg.prefix || '') + (arg.path || '');
736
const match = /^--(.+)=(.+)$/.exec(a2);
737
if (match && match.length === 3) {
738
const key = match[1];
739
let value = match[2];
740
741
if ((key === 'file-uri' || key === 'folder-uri') && !isUri(arg.path)) {
742
value = isUri(value) ? value : URI.file(value).toString();
743
}
744
args.push(`--${key}=${value}`);
745
} else {
746
args.push(a2);
747
}
748
}
749
750
if (vscodeArgs.env) {
751
args.push(`--extensionEnvironment=${JSON.stringify(vscodeArgs.env)}`);
752
}
753
754
return this.extensionHostDebugService.openExtensionDevelopmentHostWindow(args, !!vscodeArgs.debugRenderer);
755
}
756
757
private send<R extends DebugProtocol.Response>(command: string, args: any, token?: CancellationToken, timeout?: number, showErrors = true): Promise<R | undefined> {
758
return new Promise<DebugProtocol.Response | undefined>((completeDispatch, errorDispatch) => {
759
if (!this.debugAdapter) {
760
if (this.inShutdown) {
761
// We are in shutdown silently complete
762
completeDispatch(undefined);
763
} else {
764
errorDispatch(new Error(nls.localize('noDebugAdapter', "No debugger available found. Can not send '{0}'.", command)));
765
}
766
return;
767
}
768
769
let cancelationListener: IDisposable;
770
const requestId = this.debugAdapter.sendRequest(command, args, (response: DebugProtocol.Response) => {
771
cancelationListener?.dispose();
772
773
if (response.success) {
774
completeDispatch(response);
775
} else {
776
errorDispatch(response);
777
}
778
}, timeout);
779
780
if (token) {
781
cancelationListener = token.onCancellationRequested(() => {
782
cancelationListener.dispose();
783
if (this.capabilities.supportsCancelRequest) {
784
this.cancel({ requestId });
785
}
786
});
787
}
788
}).then(undefined, err => Promise.reject(this.handleErrorResponse(err, showErrors)));
789
}
790
791
private handleErrorResponse(errorResponse: DebugProtocol.Response, showErrors: boolean): Error {
792
793
if (errorResponse.command === 'canceled' && errorResponse.message === 'canceled') {
794
return new errors.CancellationError();
795
}
796
797
const error: DebugProtocol.Message | undefined = errorResponse?.body?.error;
798
const errorMessage = errorResponse?.message || '';
799
800
const userMessage = error ? formatPII(error.format, false, error.variables) : errorMessage;
801
const url = error?.url;
802
if (error && url) {
803
const label = error.urlLabel ? error.urlLabel : nls.localize('moreInfo', "More Info");
804
const uri = URI.parse(url);
805
// Use a suffixed id if uri invokes a command, so default 'Open launch.json' command is suppressed on dialog
806
const actionId = uri.scheme === Schemas.command ? 'debug.moreInfo.command' : 'debug.moreInfo';
807
return createErrorWithActions(userMessage, [toAction({ id: actionId, label, run: () => this.openerService.open(uri, { allowCommands: true }) })]);
808
}
809
if (showErrors && error && error.format && error.showUser) {
810
this.notificationService.error(userMessage);
811
}
812
const result = new errors.ErrorNoTelemetry(userMessage);
813
(<any>result).showUser = error?.showUser;
814
815
return result;
816
}
817
818
private mergeCapabilities(capabilities: DebugProtocol.Capabilities | undefined): void {
819
if (capabilities) {
820
this._capabilities = objects.mixin(this._capabilities, capabilities);
821
}
822
}
823
824
private fireSimulatedContinuedEvent(threadId: number, allThreadsContinued = false): void {
825
this._onDidContinued.fire({
826
type: 'event',
827
event: 'continued',
828
body: {
829
threadId,
830
allThreadsContinued
831
},
832
seq: undefined!
833
});
834
}
835
836
dispose(): void {
837
dispose(this.toDispose);
838
}
839
}
840
841