debugService.ts 53.8 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

I
isidor 已提交
6 7
import * as nls from 'vs/nls';
import * as lifecycle from 'vs/base/common/lifecycle';
J
Johannes Rieken 已提交
8
import Event, { Emitter } from 'vs/base/common/event';
I
isidor 已提交
9
import * as resources from 'vs/base/common/resources';
I
isidor 已提交
10
import * as strings from 'vs/base/common/strings';
I
isidor 已提交
11
import { generateUuid } from 'vs/base/common/uuid';
E
Erich Gamma 已提交
12
import uri from 'vs/base/common/uri';
I
isidor 已提交
13
import * as platform from 'vs/base/common/platform';
J
Johannes Rieken 已提交
14
import { Action } from 'vs/base/common/actions';
I
isidor 已提交
15 16 17
import { first, distinct } from 'vs/base/common/arrays';
import { isObject, isUndefinedOrNull } from 'vs/base/common/types';
import * as errors from 'vs/base/common/errors';
E
Erich Gamma 已提交
18
import severity from 'vs/base/common/severity';
J
Johannes Rieken 已提交
19
import { TPromise } from 'vs/base/common/winjs.base';
I
isidor 已提交
20
import * as aria from 'vs/base/browser/ui/aria/aria';
21
import { Client as TelemetryClient } from 'vs/base/parts/ipc/node/ipc.cp';
J
Johannes Rieken 已提交
22 23 24 25 26
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IMarkerService } from 'vs/platform/markers/common/markers';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
27
import { FileChangesEvent, FileChangeType, IFileService } from 'vs/platform/files/common/files';
J
Johannes Rieken 已提交
28
import { IMessageService, CloseAction } from 'vs/platform/message/common/message';
I
isidor 已提交
29
import { IWindowService } from 'vs/platform/windows/common/windows';
J
Johannes Rieken 已提交
30 31 32 33
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService';
import { TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
I
isidor 已提交
34
import * as debug from 'vs/workbench/parts/debug/common/debug';
J
Johannes Rieken 已提交
35
import { RawDebugSession } from 'vs/workbench/parts/debug/electron-browser/rawDebugSession';
I
isidor 已提交
36
import { Model, ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, Expression, RawObjectReplElement, ExpressionContainer, Process } from 'vs/workbench/parts/debug/common/debugModel';
I
isidor 已提交
37 38
import { ViewModel } from 'vs/workbench/parts/debug/common/debugViewModel';
import * as debugactions from 'vs/workbench/parts/debug/browser/debugActions';
39
import { ConfigurationManager } from 'vs/workbench/parts/debug/electron-browser/debugConfigurationManager';
I
isidor 已提交
40
import { ToggleMarkersPanelAction } from 'vs/workbench/parts/markers/browser/markersPanelActions';
I
isidor 已提交
41
import { ITaskService, TaskServiceEvents, ITaskSummary } from 'vs/workbench/parts/tasks/common/taskService';
42
import { TaskError } from 'vs/workbench/parts/tasks/common/taskSystem';
I
isidor 已提交
43
import { VIEWLET_ID as EXPLORER_VIEWLET_ID } from 'vs/workbench/parts/files/common/files';
B
Benjamin Pasero 已提交
44
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
J
Johannes Rieken 已提交
45
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
46
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
J
Johannes Rieken 已提交
47 48
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
S
Sandeep Somavarapu 已提交
49
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
J
Johannes Rieken 已提交
50
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
51
import { EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL, EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, EXTENSION_RELOAD_BROADCAST_CHANNEL } from 'vs/platform/extensions/common/extensionHost';
52
import { IBroadcastService, IBroadcast } from 'vs/platform/broadcast/electron-browser/broadcastService';
53
import { IRemoteConsoleLog, parse, getFirstFrame } from 'vs/base/node/console';
54
import { Source } from 'vs/workbench/parts/debug/common/debugSource';
E
Erich Gamma 已提交
55

I
isidor 已提交
56 57 58 59 60
const DEBUG_BREAKPOINTS_KEY = 'debug.breakpoint';
const DEBUG_BREAKPOINTS_ACTIVATED_KEY = 'debug.breakpointactivated';
const DEBUG_FUNCTION_BREAKPOINTS_KEY = 'debug.functionbreakpoint';
const DEBUG_EXCEPTION_BREAKPOINTS_KEY = 'debug.exceptionbreakpoint';
const DEBUG_WATCH_EXPRESSIONS_KEY = 'debug.watchexpressions';
E
Erich Gamma 已提交
61

62
export class DebugService implements debug.IDebugService {
63
	public _serviceBrand: any;
E
Erich Gamma 已提交
64

I
isidor 已提交
65
	private sessionStates: Map<string, debug.State>;
66
	private _onDidChangeState: Emitter<debug.State>;
67
	private _onDidNewProcess: Emitter<debug.IProcess>;
I
isidor 已提交
68
	private _onDidEndProcess: Emitter<debug.IProcess>;
69
	private _onDidCustomEvent: Emitter<debug.DebugEvent>;
I
isidor 已提交
70 71
	private model: Model;
	private viewModel: ViewModel;
72
	private allProcesses: Map<string, debug.IProcess>;
73 74
	private configurationManager: ConfigurationManager;
	private toDispose: lifecycle.IDisposable[];
I
isidor 已提交
75
	private toDisposeOnSessionEnd: Map<string, lifecycle.IDisposable[]>;
A
Alex Dima 已提交
76
	private inDebugMode: IContextKey<boolean>;
I
isidor 已提交
77
	private debugType: IContextKey<string>;
I
isidor 已提交
78
	private debugState: IContextKey<string>;
I
isidor 已提交
79
	private breakpointsToSendOnResourceSaved: Set<string>;
80
	private launchJsonChanged: boolean;
I
isidor 已提交
81
	private firstSessionStart: boolean;
82
	private previousState: debug.State;
E
Erich Gamma 已提交
83 84 85 86

	constructor(
		@IStorageService private storageService: IStorageService,
		@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
87 88
		@ITextFileService private textFileService: ITextFileService,
		@IViewletService private viewletService: IViewletService,
I
isidor 已提交
89
		@IPanelService private panelService: IPanelService,
E
Erich Gamma 已提交
90 91
		@IMessageService private messageService: IMessageService,
		@IPartService private partService: IPartService,
92 93
		@IWindowService private windowService: IWindowService,
		@IBroadcastService private broadcastService: IBroadcastService,
E
Erich Gamma 已提交
94 95
		@ITelemetryService private telemetryService: ITelemetryService,
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
96
		@IContextKeyService contextKeyService: IContextKeyService,
I
isidor 已提交
97
		@ILifecycleService lifecycleService: ILifecycleService,
I
isidor 已提交
98
		@IInstantiationService private instantiationService: IInstantiationService,
A
Alex Dima 已提交
99
		@IExtensionService private extensionService: IExtensionService,
100
		@IMarkerService private markerService: IMarkerService,
101
		@ITaskService private taskService: ITaskService,
102
		@IFileService private fileService: IFileService,
I
isidor 已提交
103
		@IConfigurationService private configurationService: IConfigurationService
E
Erich Gamma 已提交
104 105
	) {
		this.toDispose = [];
I
isidor 已提交
106 107
		this.toDisposeOnSessionEnd = new Map<string, lifecycle.IDisposable[]>();
		this.breakpointsToSendOnResourceSaved = new Set<string>();
108
		this._onDidChangeState = new Emitter<debug.State>();
109
		this._onDidNewProcess = new Emitter<debug.IProcess>();
I
isidor 已提交
110
		this._onDidEndProcess = new Emitter<debug.IProcess>();
111
		this._onDidCustomEvent = new Emitter<debug.DebugEvent>();
I
isidor 已提交
112
		this.sessionStates = new Map<string, debug.State>();
113
		this.allProcesses = new Map<string, debug.IProcess>();
E
Erich Gamma 已提交
114

115
		this.configurationManager = this.instantiationService.createInstance(ConfigurationManager);
116
		this.toDispose.push(this.configurationManager);
117
		this.inDebugMode = debug.CONTEXT_IN_DEBUG_MODE.bindTo(contextKeyService);
I
isidor 已提交
118
		this.debugType = debug.CONTEXT_DEBUG_TYPE.bindTo(contextKeyService);
I
isidor 已提交
119
		this.debugState = debug.CONTEXT_DEBUG_STATE.bindTo(contextKeyService);
E
Erich Gamma 已提交
120

I
isidor 已提交
121
		this.model = new Model(this.loadBreakpoints(), this.storageService.getBoolean(DEBUG_BREAKPOINTS_ACTIVATED_KEY, StorageScope.WORKSPACE, true), this.loadFunctionBreakpoints(),
E
Erich Gamma 已提交
122
			this.loadExceptionBreakpoints(), this.loadWatchExpressions());
I
isidor 已提交
123
		this.toDispose.push(this.model);
124
		this.viewModel = new ViewModel();
I
isidor 已提交
125
		this.firstSessionStart = true;
126

127
		this.registerListeners(lifecycleService);
E
Erich Gamma 已提交
128 129
	}

130 131
	private registerListeners(lifecycleService: ILifecycleService): void {
		this.toDispose.push(this.fileService.onFileChanges(e => this.onFileChanges(e)));
132 133
		lifecycleService.onShutdown(this.store, this);
		lifecycleService.onShutdown(this.dispose, this);
134
		this.toDispose.push(this.broadcastService.onBroadcast(this.onBroadcast, this));
135 136 137
	}

	private onBroadcast(broadcast: IBroadcast): void {
138

I
isidor 已提交
139
		// attach: PH is ready to be attached to
140 141
		const process = this.allProcesses.get(broadcast.payload.debugId);
		if (!process) {
142 143 144
			// Ignore attach events for sessions that never existed (wrong vscode windows)
			return;
		}
145
		const session = <RawDebugSession>process.session;
146

147
		if (broadcast.channel === EXTENSION_ATTACH_BROADCAST_CHANNEL) {
148
			this.onSessionEnd(session);
I
isidor 已提交
149

150 151 152
			process.configuration.request = 'attach';
			process.configuration.port = broadcast.payload.port;
			this.doCreateProcess(process.session.root, process.configuration, process.getId());
153 154
			return;
		}
155

156
		if (broadcast.channel === EXTENSION_TERMINATE_BROADCAST_CHANNEL) {
157
			this.onSessionEnd(session);
158 159 160
			return;
		}

B
Benjamin Pasero 已提交
161
		// an extension logged output, show it inside the REPL
162
		if (broadcast.channel === EXTENSION_LOG_BROADCAST_CHANNEL) {
163
			let extensionOutput: IRemoteConsoleLog = broadcast.payload.logEntry;
164 165
			let sev = extensionOutput.severity === 'warn' ? severity.Warning : extensionOutput.severity === 'error' ? severity.Error : severity.Info;

166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
			const { args, stack } = parse(extensionOutput);
			let source: debug.IReplElementSource;
			if (stack) {
				const frame = getFirstFrame(stack);
				if (frame) {
					source = {
						column: frame.column,
						lineNumber: frame.line,
						source: process.getSource({
							name: resources.basenameOrAuthority(frame.uri),
							path: frame.uri.fsPath
						})
					};
				}
			}
181

I
isidor 已提交
182
			// add output for each argument logged
183 184 185 186
			let simpleVals: any[] = [];
			for (let i = 0; i < args.length; i++) {
				let a = args[i];

I
isidor 已提交
187
				// undefined gets printed as 'undefined'
188 189 190 191
				if (typeof a === 'undefined') {
					simpleVals.push('undefined');
				}

I
isidor 已提交
192
				// null gets printed as 'null'
193 194 195 196
				else if (a === null) {
					simpleVals.push('null');
				}

I
isidor 已提交
197
				// objects & arrays are special because we want to inspect them in the REPL
I
isidor 已提交
198
				else if (isObject(a) || Array.isArray(a)) {
199

I
isidor 已提交
200
					// flush any existing simple values logged
201
					if (simpleVals.length) {
202
						this.logToRepl(simpleVals.join(' '), sev, source);
203 204 205
						simpleVals = [];
					}

I
isidor 已提交
206
					// show object
I
isidor 已提交
207
					this.logToRepl(new RawObjectReplElement((<any>a).prototype, a, undefined, nls.localize('snapshotObj', "Only primitive values are shown for this object.")), sev, source);
208 209
				}

I
isidor 已提交
210 211
				// string: watch out for % replacement directive
				// string substitution and formatting @ https://developer.chrome.com/devtools/docs/console
212 213 214 215 216 217
				else if (typeof a === 'string') {
					let buf = '';

					for (let j = 0, len = a.length; j < len; j++) {
						if (a[j] === '%' && (a[j + 1] === 's' || a[j + 1] === 'i' || a[j + 1] === 'd')) {
							i++; // read over substitution
I
isidor 已提交
218
							buf += !isUndefinedOrNull(args[i]) ? args[i] : ''; // replace
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
							j++; // read over directive
						} else {
							buf += a[j];
						}
					}

					simpleVals.push(buf);
				}

				// number or boolean is joined together
				else {
					simpleVals.push(a);
				}
			}

I
isidor 已提交
234
			// flush simple values
J
jens1o 已提交
235
			// always append a new line for output coming from an extension such that separate logs go to separate lines #23695
236
			if (simpleVals.length) {
237
				this.logToRepl(simpleVals.join(' ') + '\n', sev, source);
238 239
			}
		}
E
Erich Gamma 已提交
240 241
	}

242
	private tryToAutoFocusStackFrame(thread: debug.IThread): TPromise<any> {
I
isidor 已提交
243
		const callStack = thread.getCallStack();
I
isidor 已提交
244
		if (!callStack.length || (this.viewModel.focusedStackFrame && this.viewModel.focusedStackFrame.thread.getId() === thread.getId())) {
I
isidor 已提交
245 246 247
			return TPromise.as(null);
		}

248
		// focus first stack frame from top that has source location if no other stack frame is focused
I
isidor 已提交
249 250 251 252 253 254
		const stackFrameToFocus = first(callStack, sf => sf.source && sf.source.available, undefined);
		if (!stackFrameToFocus) {
			return TPromise.as(null);
		}

		this.focusStackFrameAndEvaluate(stackFrameToFocus).done(null, errors.onUnexpectedError);
I
isidor 已提交
255
		if (thread.stoppedDetails) {
256
			this.windowService.focusWindow();
I
isidor 已提交
257 258
			aria.alert(nls.localize('debuggingPaused', "Debugging paused, reason {0}, {1} {2}", thread.stoppedDetails.reason, stackFrameToFocus.source ? stackFrameToFocus.source.name : '', stackFrameToFocus.range.startLineNumber));
		}
I
isidor 已提交
259

I
isidor 已提交
260
		return stackFrameToFocus.openInEditor(this.editorService, true);
I
isidor 已提交
261 262
	}

I
isidor 已提交
263
	private registerSessionListeners(process: Process, session: RawDebugSession): void {
264

I
isidor 已提交
265
		this.toDisposeOnSessionEnd.get(session.getId()).push(session);
266

I
isidor 已提交
267
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidInitialize(event => {
268
			aria.status(nls.localize('debuggingStarted', "Debugging started."));
269
			const sendConfigurationDone = () => {
270
				if (session && session.capabilities.supportsConfigurationDoneRequest) {
271
					return session.configurationDone().done(null, e => {
272
						// Disconnect the debug session on configuration done error #10596
273 274
						if (session) {
							session.disconnect().done(null, errors.onUnexpectedError);
275 276 277
						}
						this.messageService.show(severity.Error, e.message);
					});
I
isidor 已提交
278
				}
279
			};
280 281 282

			this.sendAllBreakpoints(process).then(sendConfigurationDone, sendConfigurationDone)
				.done(() => this.fetchThreads(session), errors.onUnexpectedError);
I
isidor 已提交
283
		}));
E
Erich Gamma 已提交
284

I
isidor 已提交
285
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidStop(event => {
I
isidor 已提交
286
			this.updateStateAndEmit(session.getId(), debug.State.Stopped);
I
isidor 已提交
287 288
			this.fetchThreads(session, event.body).done(() => {
				const thread = process && process.getThread(event.body.threadId);
I
isidor 已提交
289
				if (thread) {
290 291 292
					// Call fetch call stack twice, the first only return the top stack frame.
					// Second retrieves the rest of the call stack. For performance reasons #25605
					this.model.fetchCallStack(thread).then(() => {
293
						return this.tryToAutoFocusStackFrame(thread);
I
isidor 已提交
294 295
					});
				}
E
Erich Gamma 已提交
296 297 298
			}, errors.onUnexpectedError);
		}));

I
isidor 已提交
299
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidThread(event => {
E
Erich Gamma 已提交
300
			if (event.body.reason === 'started') {
301
				this.fetchThreads(session).done(undefined, errors.onUnexpectedError);
E
Erich Gamma 已提交
302
			} else if (event.body.reason === 'exited') {
303
				this.model.clearThreads(session.getId(), true, event.body.threadId);
E
Erich Gamma 已提交
304 305 306
			}
		}));

I
isidor 已提交
307
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidTerminateDebugee(event => {
308
			aria.status(nls.localize('debuggingStopped', "Debugging stopped."));
309
			if (session && session.getId() === event.sessionId) {
310 311
				if (event.body && event.body.restart && process) {
					this.restartProcess(process, event.body.restart).done(null, err => this.messageService.show(severity.Error, err.message));
312
				} else {
313
					session.disconnect().done(null, errors.onUnexpectedError);
314
				}
E
Erich Gamma 已提交
315 316 317
			}
		}));

I
isidor 已提交
318
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidContinued(event => {
I
isidor 已提交
319
			const threadId = event.body.allThreadsContinued !== false ? undefined : event.body.threadId;
320 321
			this.model.clearThreads(session.getId(), false, threadId);
			if (this.viewModel.focusedProcess.getId() === session.getId()) {
322
				this.focusStackFrameAndEvaluate(null, this.viewModel.focusedProcess).done(null, errors.onUnexpectedError);
323
			}
I
isidor 已提交
324
			this.updateStateAndEmit(session.getId(), debug.State.Running);
I
isidor 已提交
325 326
		}));

I
isidor 已提交
327
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidOutput(event => {
328 329 330 331
			if (!event.body) {
				return;
			}

332
			const outputSeverity = event.body.category === 'stderr' ? severity.Error : event.body.category === 'console' ? severity.Warning : severity.Info;
333
			if (event.body.category === 'telemetry') {
334
				// only log telemetry events from debug adapter if the adapter provided the telemetry key
I
isidor 已提交
335
				// and the user opted in telemetry
336
				if (session.customTelemetryService && this.telemetryService.isOptedIn) {
K
kieferrm 已提交
337
					// __GDPR__TODO__ We're sending events in the name of the debug adapter and we can not ensure that those are declared correctly.
338
					session.customTelemetryService.publicLog(event.body.output, event.body.data);
339
				}
I
isidor 已提交
340 341 342 343

				return;
			}

I
isidor 已提交
344 345 346 347 348 349
			const source = event.body.source ? {
				lineNumber: event.body.line,
				column: event.body.column,
				source: process.getSource(event.body.source)
			} : undefined;

I
isidor 已提交
350
			if (event.body.variablesReference) {
351 352 353 354 355
				const container = new ExpressionContainer(process, event.body.variablesReference, generateUuid());
				container.getChildren().then(children => {
					children.forEach(child => {
						// Since we can not display multiple trees in a row, we are displaying these variables one after the other (ignoring their names)
						child.name = null;
I
isidor 已提交
356
						this.logToRepl(child, outputSeverity, source);
I
isidor 已提交
357
					});
358 359
				});
			} else if (typeof event.body.output === 'string') {
I
isidor 已提交
360
				this.logToRepl(event.body.output, outputSeverity, source);
E
Erich Gamma 已提交
361 362 363
			}
		}));

I
isidor 已提交
364
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidBreakpoint(event => {
I
isidor 已提交
365
			const id = event.body && event.body.breakpoint ? event.body.breakpoint.id : undefined;
366
			const breakpoint = this.model.getBreakpoints().filter(bp => bp.idFromAdapter === id).pop();
I
isidor 已提交
367 368 369
			const functionBreakpoint = this.model.getFunctionBreakpoints().filter(bp => bp.idFromAdapter === id).pop();

			if (event.body.reason === 'new' && event.body.breakpoint.source) {
I
isidor 已提交
370
				const source = process.getSource(event.body.breakpoint.source);
I
isidor 已提交
371
				const bps = this.model.addBreakpoints(source.uri, [{
I
isidor 已提交
372 373
					column: event.body.breakpoint.column,
					enabled: true,
I
isidor 已提交
374 375 376 377 378
					lineNumber: event.body.breakpoint.line,
				}], false);
				if (bps.length === 1) {
					this.model.updateBreakpoints({ [bps[0].getId()]: event.body.breakpoint });
				}
I
isidor 已提交
379 380 381 382 383 384 385 386 387 388 389 390
			}

			if (event.body.reason === 'removed') {
				if (breakpoint) {
					this.model.removeBreakpoints([breakpoint]);
				}
				if (functionBreakpoint) {
					this.model.removeFunctionBreakpoints(functionBreakpoint.getId());
				}
			}

			// For compatibilty reasons check if wrong reason and source not present
391 392
			// TODO@Isidor clean up these checks in October
			if (event.body.reason === 'changed' || (event.body.reason === 'new' && !event.body.breakpoint.source) || event.body.reason === 'update') {
I
isidor 已提交
393 394 395 396 397
				if (breakpoint) {
					if (!breakpoint.column) {
						event.body.breakpoint.column = undefined;
					}
					this.model.updateBreakpoints({ [breakpoint.getId()]: event.body.breakpoint });
I
isidor 已提交
398
				}
I
isidor 已提交
399 400 401 402 403 404
				if (functionBreakpoint) {
					this.model.updateFunctionBreakpoints({ [functionBreakpoint.getId()]: event.body.breakpoint });
				}
			}
		}));

I
isidor 已提交
405
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidExitAdapter(event => {
406
			// 'Run without debugging' mode VSCode must terminate the extension host. More details: #3905
I
isidor 已提交
407
			if (strings.equalsIgnoreCase(process.configuration.type, 'extensionhost') && this.sessionStates.get(session.getId()) === debug.State.Running &&
408
				process && process.session.root && process.configuration.noDebug) {
409 410
				this.broadcastService.broadcast({
					channel: EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL,
I
isidor 已提交
411
					payload: [process.session.root.uri.fsPath]
412
				});
413
			}
414
			if (session && session.getId() === event.sessionId) {
415
				this.onSessionEnd(session);
416
			}
E
Erich Gamma 已提交
417
		}));
418 419 420 421

		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidCustomEvent(event => {
			this._onDidCustomEvent.fire(event);
		}));
E
Erich Gamma 已提交
422 423
	}

I
isidor 已提交
424
	private fetchThreads(session: RawDebugSession, stoppedDetails?: debug.IRawStoppedDetails): TPromise<any> {
425 426
		return session.threads().then(response => {
			if (response && response.body && response.body.threads) {
427
				response.body.threads.forEach(thread => {
428 429 430
					this.model.rawUpdate({
						sessionId: session.getId(),
						threadId: thread.id,
I
isidor 已提交
431
						thread,
432 433 434
						stoppedDetails: stoppedDetails && thread.id === stoppedDetails.threadId ? stoppedDetails : undefined
					});
				});
435 436
			}
		});
E
Erich Gamma 已提交
437 438
	}

439 440
	private loadBreakpoints(): Breakpoint[] {
		let result: Breakpoint[];
E
Erich Gamma 已提交
441
		try {
442
			result = JSON.parse(this.storageService.get(DEBUG_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((breakpoint: any) => {
443
				return new Breakpoint(uri.parse(breakpoint.uri.external || breakpoint.source.uri.external), breakpoint.lineNumber, breakpoint.column, breakpoint.enabled, breakpoint.condition, breakpoint.hitCondition, breakpoint.adapterData);
E
Erich Gamma 已提交
444
			});
445 446 447
		} catch (e) { }

		return result || [];
E
Erich Gamma 已提交
448 449
	}

450 451
	private loadFunctionBreakpoints(): FunctionBreakpoint[] {
		let result: FunctionBreakpoint[];
I
isidor 已提交
452
		try {
453
			result = JSON.parse(this.storageService.get(DEBUG_FUNCTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((fb: any) => {
I
isidor 已提交
454
				return new FunctionBreakpoint(fb.name, fb.enabled, fb.hitCondition);
I
isidor 已提交
455
			});
456 457 458
		} catch (e) { }

		return result || [];
I
isidor 已提交
459 460
	}

461 462
	private loadExceptionBreakpoints(): ExceptionBreakpoint[] {
		let result: ExceptionBreakpoint[];
E
Erich Gamma 已提交
463 464
		try {
			result = JSON.parse(this.storageService.get(DEBUG_EXCEPTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((exBreakpoint: any) => {
I
isidor 已提交
465
				return new ExceptionBreakpoint(exBreakpoint.filter || exBreakpoint.name, exBreakpoint.label, exBreakpoint.enabled);
E
Erich Gamma 已提交
466
			});
467
		} catch (e) { }
E
Erich Gamma 已提交
468

469
		return result || [];
E
Erich Gamma 已提交
470 471
	}

I
isidor 已提交
472 473
	private loadWatchExpressions(): Expression[] {
		let result: Expression[];
E
Erich Gamma 已提交
474
		try {
J
Johannes Rieken 已提交
475
			result = JSON.parse(this.storageService.get(DEBUG_WATCH_EXPRESSIONS_KEY, StorageScope.WORKSPACE, '[]')).map((watchStoredData: { name: string, id: string }) => {
476
				return new Expression(watchStoredData.name, watchStoredData.id);
E
Erich Gamma 已提交
477
			});
478 479 480
		} catch (e) { }

		return result || [];
E
Erich Gamma 已提交
481 482
	}

I
isidor 已提交
483
	public get state(): debug.State {
484 485 486 487
		const focusedThread = this.viewModel.focusedThread;
		if (focusedThread && focusedThread.stopped) {
			return debug.State.Stopped;
		}
I
isidor 已提交
488
		const focusedProcess = this.viewModel.focusedProcess;
I
isidor 已提交
489
		if (focusedProcess && this.sessionStates.has(focusedProcess.getId())) {
I
isidor 已提交
490
			return this.sessionStates.get(focusedProcess.getId());
I
isidor 已提交
491
		}
I
isidor 已提交
492 493
		if (this.sessionStates.size > 0) {
			return debug.State.Initializing;
I
isidor 已提交
494 495 496
		}

		return debug.State.Inactive;
E
Erich Gamma 已提交
497 498
	}

499
	public get onDidChangeState(): Event<debug.State> {
500 501 502
		return this._onDidChangeState.event;
	}

503 504 505 506
	public get onDidNewProcess(): Event<debug.IProcess> {
		return this._onDidNewProcess.event;
	}

I
isidor 已提交
507 508 509 510
	public get onDidEndProcess(): Event<debug.IProcess> {
		return this._onDidEndProcess.event;
	}

511
	public get onDidCustomEvent(): Event<debug.DebugEvent> {
512 513 514
		return this._onDidCustomEvent.event;
	}

I
isidor 已提交
515 516 517 518 519 520 521
	private updateStateAndEmit(sessionId?: string, newState?: debug.State): void {
		if (sessionId) {
			if (newState === debug.State.Inactive) {
				this.sessionStates.delete(sessionId);
			} else {
				this.sessionStates.set(sessionId, newState);
			}
I
isidor 已提交
522
		}
I
isidor 已提交
523 524

		const state = this.state;
525
		if (this.previousState !== state) {
526 527 528 529
			const stateLabel = debug.State[state];
			if (stateLabel) {
				this.debugState.set(stateLabel.toLowerCase());
			}
530
			this.previousState = state;
531
			this._onDidChangeState.fire(state);
I
isidor 已提交
532
		}
E
Erich Gamma 已提交
533 534
	}

535
	public focusStackFrameAndEvaluate(stackFrame: debug.IStackFrame, process?: debug.IProcess, explicit?: boolean): TPromise<void> {
I
isidor 已提交
536
		if (!process) {
537
			const processes = this.model.getProcesses();
538
			process = stackFrame ? stackFrame.thread.process : processes.length ? processes[0] : null;
539 540 541
		}
		if (!stackFrame) {
			const threads = process ? process.getAllThreads() : null;
542
			const callStack = threads && threads.length === 1 ? threads[0].getCallStack() : null;
543
			stackFrame = callStack && callStack.length ? callStack[0] : null;
I
isidor 已提交
544
		}
I
isidor 已提交
545

546
		this.viewModel.setFocusedStackFrame(stackFrame, process, explicit);
I
isidor 已提交
547
		this.updateStateAndEmit();
548

549
		return this.model.evaluateWatchExpressions(process, stackFrame);
E
Erich Gamma 已提交
550 551
	}

I
isidor 已提交
552
	public enableOrDisableBreakpoints(enable: boolean, breakpoint?: debug.IEnablement): TPromise<void> {
553 554
		if (breakpoint) {
			this.model.setEnablement(breakpoint, enable);
I
isidor 已提交
555
			if (breakpoint instanceof Breakpoint) {
556
				return this.sendBreakpoints(breakpoint.uri);
I
isidor 已提交
557
			} else if (breakpoint instanceof FunctionBreakpoint) {
558 559
				return this.sendFunctionBreakpoints();
			}
E
Erich Gamma 已提交
560

561
			return this.sendExceptionBreakpoints();
E
Erich Gamma 已提交
562 563
		}

564 565
		this.model.enableOrDisableAllBreakpoints(enable);
		return this.sendAllBreakpoints();
E
Erich Gamma 已提交
566 567
	}

568 569 570
	public addBreakpoints(uri: uri, rawBreakpoints: debug.IRawBreakpoint[]): TPromise<void> {
		this.model.addBreakpoints(uri, rawBreakpoints);
		rawBreakpoints.forEach(rbp => aria.status(nls.localize('breakpointAdded', "Added breakpoint, line {0}, file {1}", rbp.lineNumber, uri.fsPath)));
571

572
		return this.sendBreakpoints(uri);
573 574
	}

575 576
	public removeBreakpoints(id?: string): TPromise<any> {
		const toRemove = this.model.getBreakpoints().filter(bp => !id || bp.getId() === id);
577 578
		toRemove.forEach(bp => aria.status(nls.localize('breakpointRemoved', "Removed breakpoint, line {0}, file {1}", bp.lineNumber, bp.uri.fsPath)));
		const urisToClear = distinct(toRemove, bp => bp.uri.toString()).map(bp => bp.uri);
E
Erich Gamma 已提交
579

580
		this.model.removeBreakpoints(toRemove);
I
isidor 已提交
581
		return TPromise.join(urisToClear.map(uri => this.sendBreakpoints(uri)));
E
Erich Gamma 已提交
582 583
	}

584 585
	public setBreakpointsActivated(activated: boolean): TPromise<void> {
		this.model.setBreakpointsActivated(activated);
E
Erich Gamma 已提交
586 587 588
		return this.sendAllBreakpoints();
	}

I
isidor 已提交
589 590
	public addFunctionBreakpoint(): void {
		this.model.addFunctionBreakpoint('');
591 592
	}

I
isidor 已提交
593
	public renameFunctionBreakpoint(id: string, newFunctionName: string): TPromise<void> {
I
isidor 已提交
594
		this.model.updateFunctionBreakpoints({ [id]: { name: newFunctionName } });
I
isidor 已提交
595
		return this.sendFunctionBreakpoints();
I
isidor 已提交
596 597
	}

I
isidor 已提交
598
	public removeFunctionBreakpoints(id?: string): TPromise<void> {
599
		this.model.removeFunctionBreakpoints(id);
I
isidor 已提交
600
		return this.sendFunctionBreakpoints();
I
isidor 已提交
601 602
	}

I
isidor 已提交
603
	public addReplExpression(name: string): TPromise<void> {
K
kieferrm 已提交
604
		/* __GDPR__
K
kieferrm 已提交
605 606
			"debugService/addReplExpression" : {}
		*/
P
Pierson Lee 已提交
607
		this.telemetryService.publicLog('debugService/addReplExpression');
608
		return this.model.addReplExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, name)
609
			// Evaluate all watch expressions and fetch variables again since repl evaluation might have changed some.
610
			.then(() => this.focusStackFrameAndEvaluate(this.viewModel.focusedStackFrame, this.viewModel.focusedProcess));
E
Erich Gamma 已提交
611 612
	}

613 614
	public removeReplExpressions(): void {
		this.model.removeReplExpressions();
E
Erich Gamma 已提交
615 616
	}

I
isidor 已提交
617
	public logToRepl(value: string | debug.IExpression, sev = severity.Info, source?: debug.IReplElementSource): void {
618 619 620 621
		if (typeof value === 'string' && '[2J'.localeCompare(value) === 0) {
			// [2J is the ansi escape sequence for clearing the display http://ascii-table.com/ansi-escape-sequences.php
			this.model.removeReplExpressions();
		} else {
I
isidor 已提交
622
			this.model.appendToRepl(value, sev, source);
623
		}
624 625
	}

I
isidor 已提交
626
	public addWatchExpression(name: string): TPromise<void> {
627
		return this.model.addWatchExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, name);
E
Erich Gamma 已提交
628 629
	}

I
isidor 已提交
630
	public renameWatchExpression(id: string, newName: string): TPromise<void> {
631
		return this.model.renameWatchExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, id, newName);
E
Erich Gamma 已提交
632 633
	}

I
isidor 已提交
634 635 636 637
	public moveWatchExpression(id: string, position: number): void {
		this.model.moveWatchExpression(id, position);
	}

638 639
	public removeWatchExpressions(id?: string): void {
		this.model.removeWatchExpressions(id);
E
Erich Gamma 已提交
640 641
	}

642 643 644 645
	public evaluateWatchExpressions(): TPromise<void> {
		return this.model.evaluateWatchExpressions(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame);
	}

S
Sandeep Somavarapu 已提交
646
	public startDebugging(root: IWorkspaceFolder, configOrName?: debug.IConfig | string, noDebug = false, topCompoundName?: string): TPromise<any> {
647

648
		// make sure to save all files and that the configuration is up to date
649
		return this.extensionService.activateByEvent('onDebug').then(() => this.textFileService.saveAll().then(() => this.configurationService.reloadConfiguration(root).then(() =>
650 651 652
			this.extensionService.onReady().then(() => {
				if (this.model.getProcesses().length === 0) {
					this.removeReplExpressions();
653
					this.allProcesses.clear();
654
				}
655
				this.launchJsonChanged = false;
656
				const manager = this.getConfigurationManager();
I
isidor 已提交
657
				const launch = root ? manager.getLaunches().filter(l => l.workspace.uri.toString() === root.uri.toString()).pop() : undefined;
658

659
				let config: debug.IConfig, compound: debug.ICompound;
660
				if (!configOrName) {
661
					configOrName = this.configurationManager.selectedName;
662
				}
663 664 665
				if (typeof configOrName === 'string' && launch) {
					config = launch.getConfiguration(configOrName);
					compound = launch.getCompound(configOrName);
666
				} else if (typeof configOrName !== 'string') {
667 668
					config = configOrName;
				}
669
				if (launch) {
670 671
					// in the drop down the name of the top most compound takes precedence over the launch config name
					manager.selectConfiguration(launch, topCompoundName || (typeof configOrName === 'string' ? configOrName : undefined), true);
672
				}
673

674 675 676 677 678
				if (compound) {
					if (!compound.configurations) {
						return TPromise.wrapError(new Error(nls.localize({ key: 'compoundMustHaveConfigurations', comment: ['compound indicates a "compounds" configuration item', '"configurations" is an attribute and should not be localized'] },
							"Compound must have \"configurations\" attribute set in order to start multiple configurations.")));
					}
679

680
					return TPromise.join(compound.configurations.map(name => name !== compound.name ? this.startDebugging(root, name, noDebug, topCompoundName || compound.name) : TPromise.as(null)));
681
				}
682 683
				if (configOrName && !config) {
					return TPromise.wrapError(new Error(nls.localize('configMissing', "Configuration '{0}' is missing in 'launch.json'.", configOrName)));
684 685
				}

I
isidor 已提交
686 687 688 689 690 691 692 693 694 695 696 697
				// We keep the debug type in a separate variable 'type' so that a no-folder config has no attributes.
				// Storing the type in the config would break extensions that assume that the no-folder case is indicated by an empty config.
				let type: string;
				if (config) {
					type = config.type;
				} else {
					// a no-folder workspace has no launch.config
					config = <debug.IConfig>{};
				}
				if (noDebug) {
					config.noDebug = true;
				}
698

I
isidor 已提交
699 700
				const sessionId = generateUuid();
				this.updateStateAndEmit(sessionId, debug.State.Initializing);
701 702 703 704 705 706
				const wrapUpState = () => {
					if (this.sessionStates.get(sessionId) === debug.State.Initializing) {
						this.updateStateAndEmit(sessionId, debug.State.Inactive);
					}
				};

I
isidor 已提交
707
				return (type ? TPromise.as(null) : this.configurationManager.guessAdapter().then(a => type = a && a.type)).then(() =>
I
isidor 已提交
708
					this.configurationManager.resolveConfigurationByProviders(launch ? launch.workspace.uri : undefined, type, config).then(config => {
I
isidor 已提交
709 710
						// a falsy config indicates an aborted launch
						if (config && config.type) {
I
isidor 已提交
711
							return this.createProcess(root, config, sessionId);
I
isidor 已提交
712
						}
713

I
isidor 已提交
714 715
						return <any>launch.openConfigFile(false, type); // cast to ignore weird compile error
					})
I
isidor 已提交
716
				).then(() => wrapUpState(), err => {
717
					wrapUpState();
I
isidor 已提交
718
					return <any>TPromise.wrapError(err);
719
				});
720
			})
B
Benjamin Pasero 已提交
721
		)));
722
	}
723

724
	private createProcess(root: IWorkspaceFolder, config: debug.IConfig, sessionId: string): TPromise<debug.IProcess> {
725
		return this.textFileService.saveAll().then(() =>
726
			(this.configurationManager.selectedLaunch ? this.configurationManager.selectedLaunch.resolveConfiguration(config) : TPromise.as(config)).then(resolvedConfig => {
727 728 729 730
				if (!resolvedConfig) {
					// User canceled resolving of interactive variables, silently return
					return undefined;
				}
I
isidor 已提交
731

732 733 734
				if (!this.configurationManager.getAdapter(resolvedConfig.type) || (config.request !== 'attach' && config.request !== 'launch')) {
					let message: string;
					if (config.request !== 'attach' && config.request !== 'launch') {
735
						message = config.request ? nls.localize('debugRequestNotSupported', "Attribute `{0}` has an unsupported value '{1}' in the chosen debug configuration.", 'request', config.request)
A
Andre Weinand 已提交
736
							: nls.localize('debugRequesMissing', "Attribute '{0}' is missing from the chosen debug configuration.", 'request');
737 738 739

					} else {
						message = resolvedConfig.type ? nls.localize('debugTypeNotSupported', "Configured debug type '{0}' is not supported.", resolvedConfig.type) :
740
							nls.localize('debugTypeMissing', "Missing property `type` for the chosen launch configuration.");
741 742
					}

743 744
					return TPromise.wrapError(errors.create(message, { actions: [this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL), CloseAction] }));
				}
J
Johannes Rieken 已提交
745

746 747 748 749 750
				const debugAnywayAction = new Action('debug.continue', nls.localize('debugAnyway', "Debug Anyway"), null, true, () => {
					this.messageService.hideAll();
					return this.doCreateProcess(root, resolvedConfig, sessionId);
				});

751
				return this.runPreLaunchTask(root, resolvedConfig.preLaunchTask).then((taskSummary: ITaskSummary) => {
752 753 754 755
					const errorCount = resolvedConfig.preLaunchTask ? this.markerService.getStatistics().errors : 0;
					const successExitCode = taskSummary && taskSummary.exitCode === 0;
					const failureExitCode = taskSummary && taskSummary.exitCode !== undefined && taskSummary.exitCode !== 0;
					if (successExitCode || (errorCount === 0 && !failureExitCode)) {
756
						return this.doCreateProcess(root, resolvedConfig, sessionId);
757
					}
I
isidor 已提交
758

759 760 761 762 763
					this.messageService.show(severity.Error, {
						message: errorCount > 1 ? nls.localize('preLaunchTaskErrors', "Build errors have been detected during preLaunchTask '{0}'.", resolvedConfig.preLaunchTask) :
							errorCount === 1 ? nls.localize('preLaunchTaskError', "Build error has been detected during preLaunchTask '{0}'.", resolvedConfig.preLaunchTask) :
								nls.localize('preLaunchTaskExitCode', "The preLaunchTask '{0}' terminated with exit code {1}.", resolvedConfig.preLaunchTask, taskSummary.exitCode),
						actions: [
764
							debugAnywayAction,
765 766 767 768 769 770 771 772
							this.instantiationService.createInstance(ToggleMarkersPanelAction, ToggleMarkersPanelAction.ID, ToggleMarkersPanelAction.LABEL),
							CloseAction
						]
					});
					return undefined;
				}, (err: TaskError) => {
					this.messageService.show(err.severity, {
						message: err.message,
773
						actions: [
774
							debugAnywayAction,
775 776 777 778
							this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL),
							this.taskService.configureAction(),
							CloseAction
						]
779
					});
780 781
				});
			}, err => {
782
				if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) {
R
Ron Buckton 已提交
783 784
					this.messageService.show(severity.Error, nls.localize('noFolderWorkspaceDebugError', "The active file can not be debugged. Make sure it is saved on disk and that you have a debug extension installed for that file type."));
					return undefined;
785
				}
E
Erich Gamma 已提交
786

787
				return this.configurationManager.selectedLaunch.openConfigFile(false).then(openend => {
788 789 790
					if (openend) {
						this.messageService.show(severity.Info, nls.localize('NewLaunchConfig', "Please set up the launch configuration file for your application. {0}", err.message));
					}
R
Ron Buckton 已提交
791
					return undefined;
792 793 794 795
				});
			})
		);
	}
796

797
	private doCreateProcess(root: IWorkspaceFolder, configuration: debug.IConfig, sessionId: string): TPromise<debug.IProcess> {
798
		configuration.__sessionId = sessionId;
799
		this.inDebugMode.set(true);
800

J
Joao Moreno 已提交
801
		return this.telemetryService.getTelemetryInfo().then(info => {
802
			const telemetryInfo: { [key: string]: string } = Object.create(null);
803 804
			telemetryInfo['common.vscodemachineid'] = info.machineId;
			telemetryInfo['common.vscodesessionid'] = info.sessionId;
805 806
			return telemetryInfo;
		}).then(data => {
807 808 809
			const adapter = this.configurationManager.getAdapter(configuration.type);
			const { aiKey, type } = adapter;
			const publisher = adapter.extensionDescription.publisher;
810
			let client: TelemetryClient;
811

812
			let customTelemetryService: TelemetryService;
813
			if (aiKey) {
814
				client = new TelemetryClient(
815 816 817 818
					uri.parse(require.toUrl('bootstrap')).fsPath,
					{
						serverName: 'Debug Telemetry',
						timeout: 1000 * 60 * 5,
J
Johannes Rieken 已提交
819
						args: [`${publisher}.${type}`, JSON.stringify(data), aiKey],
820
						env: {
B
Benjamin Pasero 已提交
821
							ELECTRON_RUN_AS_NODE: 1,
822 823 824
							PIPE_LOGGING: 'true',
							AMD_ENTRYPOINT: 'vs/workbench/parts/debug/node/telemetryApp'
						}
825
					}
826
				);
827

828 829
				const channel = client.getChannel('telemetryAppender');
				const appender = new TelemetryAppenderClient(channel);
830

831
				customTelemetryService = new TelemetryService({ appender }, this.configurationService);
832 833
			}

834
			const session = this.instantiationService.createInstance(RawDebugSession, sessionId, configuration.debugServer, adapter, customTelemetryService, root);
835
			const process = this.model.addProcess(configuration, session);
836
			this.allProcesses.set(process.getId(), process);
837

I
isidor 已提交
838
			this.toDisposeOnSessionEnd.set(session.getId(), []);
839
			if (client) {
I
isidor 已提交
840
				this.toDisposeOnSessionEnd.get(session.getId()).push(client);
841
			}
I
isidor 已提交
842
			this.registerSessionListeners(process, session);
J
Joao Moreno 已提交
843

844
			return session.initialize({
845
				clientID: 'vscode',
J
Joao Moreno 已提交
846 847 848
				adapterID: configuration.type,
				pathFormat: 'path',
				linesStartAt1: true,
849
				columnsStartAt1: true,
I
isidor 已提交
850
				supportsVariableType: true, // #8858
851
				supportsVariablePaging: true, // #9537
I
isidor 已提交
852 853
				supportsRunInTerminalRequest: true, // #10574
				locale: platform.locale
J
Joao Moreno 已提交
854
			}).then((result: DebugProtocol.InitializeResponse) => {
855
				this.model.setExceptionBreakpoints(session.capabilities.exceptionBreakpointFilters);
856
				return configuration.request === 'attach' ? session.attach(configuration) : session.launch(configuration);
J
Joao Moreno 已提交
857
			}).then((result: DebugProtocol.Response) => {
858
				if (session.disconnected) {
859 860
					return TPromise.as(null);
				}
861
				this._onDidNewProcess.fire(process);
I
isidor 已提交
862
				this.focusStackFrameAndEvaluate(null, process);
863 864

				const internalConsoleOptions = configuration.internalConsoleOptions || this.configurationService.getConfiguration<debug.IDebugConfiguration>('debug').internalConsoleOptions;
I
isidor 已提交
865
				if (internalConsoleOptions === 'openOnSessionStart' || (this.firstSessionStart && internalConsoleOptions === 'openOnFirstSessionStart')) {
J
Joao Moreno 已提交
866 867
					this.panelService.openPanel(debug.REPL_ID, false).done(undefined, errors.onUnexpectedError);
				}
868

A
Anton Vildyaev 已提交
869 870
				const openDebugOptions = this.configurationService.getConfiguration<debug.IDebugConfiguration>('debug').openDebug;
				// Open debug viewlet based on the visibility of the side bar and openDebug setting
I
isidor 已提交
871
				if (openDebugOptions === 'openOnSessionStart' || (openDebugOptions === 'openOnFirstSessionStart' && this.firstSessionStart)) {
J
Joao Moreno 已提交
872 873
					this.viewletService.openViewlet(debug.VIEWLET_ID);
				}
I
isidor 已提交
874
				this.firstSessionStart = false;
875

I
isidor 已提交
876
				this.debugType.set(configuration.type);
877 878 879
				if (this.model.getProcesses().length > 1) {
					this.viewModel.setMultiProcessView(true);
				}
I
isidor 已提交
880
				this.updateStateAndEmit(session.getId(), debug.State.Running);
J
Joao Moreno 已提交
881

K
kieferrm 已提交
882
				/* __GDPR__
K
kieferrm 已提交
883 884 885 886 887 888 889 890 891 892
					"debugSessionStart" : {
						"type": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
						"breakpointCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
						"exceptionBreakpoints": { "classification": "CustomerContent", "purpose": "FeatureInsight" },
						"watchExpressionsCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
						"extensionName": { "classification": "PublicPersonalData", "purpose": "FeatureInsight" },
						"isBuiltin": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
						"launchJsonExists": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
					}
				*/
893
				return this.telemetryService.publicLog('debugSessionStart', {
J
Joao Moreno 已提交
894 895 896 897
					type: configuration.type,
					breakpointCount: this.model.getBreakpoints().length,
					exceptionBreakpoints: this.model.getExceptionBreakpoints(),
					watchExpressionsCount: this.model.getWatchExpressions().length,
898
					extensionName: `${adapter.extensionDescription.publisher}.${adapter.extensionDescription.name}`,
899
					isBuiltin: adapter.extensionDescription.isBuiltin,
900
					launchJsonExists: root && !!this.configurationService.getConfiguration<debug.IGlobalConfig>('launch', { resource: root.uri })
I
isidor 已提交
901 902
				});
			}).then(() => process, (error: any) => {
903 904 905 906 907
				if (error instanceof Error && error.message === 'Canceled') {
					// Do not show 'canceled' error messages to the user #7906
					return TPromise.as(null);
				}

908
				const errorMessage = error instanceof Error ? error.message : error;
K
kieferrm 已提交
909
				/* __GDPR__
K
kieferrm 已提交
910 911 912 913 914
					"debugMisconfiguration" : {
						"type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
						"error": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
					}
				*/
915
				this.telemetryService.publicLog('debugMisconfiguration', { type: configuration ? configuration.type : undefined, error: errorMessage });
I
isidor 已提交
916
				this.updateStateAndEmit(session.getId(), debug.State.Inactive);
917 918
				if (!session.disconnected) {
					session.disconnect().done(null, errors.onUnexpectedError);
J
Joao Moreno 已提交
919
				}
I
isidor 已提交
920 921 922
				if (process) {
					this.model.removeProcess(process.getId());
				}
J
Joao Moreno 已提交
923 924 925
				// Show the repl if some error got logged there #5870
				if (this.model.getReplElements().length > 0) {
					this.panelService.openPanel(debug.REPL_ID, false).done(undefined, errors.onUnexpectedError);
926 927 928
				}
				if (this.model.getReplElements().length === 0) {
					this.inDebugMode.reset();
J
Joao Moreno 已提交
929
				}
I
isidor 已提交
930

J
Joao Moreno 已提交
931 932
				const configureAction = this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL);
				const actions = (error.actions && error.actions.length) ? error.actions.concat([configureAction]) : [CloseAction, configureAction];
933
				this.messageService.show(severity.Error, { message: errorMessage, actions });
934
				return undefined;
J
Joao Moreno 已提交
935
			});
E
Erich Gamma 已提交
936 937 938
		});
	}

S
Sandeep Somavarapu 已提交
939
	private runPreLaunchTask(root: IWorkspaceFolder, taskName: string): TPromise<ITaskSummary> {
940
		if (!taskName) {
I
isidor 已提交
941
			return TPromise.as(null);
E
Erich Gamma 已提交
942 943
		}

944
		// run a task before starting a debug session
945
		return this.taskService.getTask(root, taskName).then(task => {
946
			if (!task) {
947
				return TPromise.wrapError(errors.create(nls.localize('DebugTaskNotFound', "Could not find the preLaunchTask \'{0}\'.", taskName)));
E
Erich Gamma 已提交
948 949
			}

950
			const promise = this.taskService.getActiveTasks().then(tasks => {
I
isidor 已提交
951 952 953 954
				if (tasks.filter(t => t._id === task._id).length) {
					// task is already running - nothing to do.
					return TPromise.as(null);
				}
E
Erich Gamma 已提交
955

I
isidor 已提交
956 957
				const taskPromise = this.taskService.run(task);
				if (task.isBackground) {
958
					return new TPromise((c, e) => this.toDispose.push(this.taskService.addOneTimeListener(TaskServiceEvents.Inactive, () => c(null))));
I
isidor 已提交
959
				}
E
Erich Gamma 已提交
960

I
isidor 已提交
961
				return taskPromise;
E
Erich Gamma 已提交
962
			});
963 964 965 966 967 968 969 970 971 972 973 974

			return new TPromise((c, e) => {
				// If a task is missing the problem matcher the promise will never complete, so we need to have a workaround #35340
				let taskStarted = false;
				promise.then(result => {
					taskStarted = true;
					c(result);
				}, error => e(error));

				this.toDispose.push(this.taskService.addOneTimeListener(TaskServiceEvents.Active, () => taskStarted = true));
				setTimeout(() => {
					if (!taskStarted) {
A
Andre Weinand 已提交
975
						e({ severity: severity.Error, message: nls.localize('taskNotTracked', "The preLaunchTask '{0}' cannot be tracked.", taskName) });
976 977 978
					}
				}, 10000);
			});
E
Erich Gamma 已提交
979 980 981
		});
	}

982 983
	public sourceIsNotAvailable(uri: uri): void {
		this.model.sourceIsNotAvailable(uri);
I
isidor 已提交
984 985
	}

986
	public restartProcess(process: debug.IProcess, restartData?: any): TPromise<any> {
987
		if (process.session.capabilities.supportsRestartRequest) {
988
			return this.textFileService.saveAll().then(() => process.session.custom('restart', null));
989
		}
I
isidor 已提交
990 991
		const focusedProcess = this.viewModel.focusedProcess;
		const preserveFocus = focusedProcess && process.getId() === focusedProcess.getId();
992

993 994
		return process.session.disconnect(true).then(() => {
			if (strings.equalsIgnoreCase(process.configuration.type, 'extensionHost')) {
995
				return this.broadcastService.broadcast({
996
					channel: EXTENSION_RELOAD_BROADCAST_CHANNEL,
I
isidor 已提交
997
					payload: [process.session.root.uri.fsPath]
998 999 1000 1001
				});
			}

			return new TPromise<void>((c, e) => {
E
Erich Gamma 已提交
1002
				setTimeout(() => {
1003 1004
					// Read the configuration again if a launch.json has been changed, if not just use the inmemory configuration
					let config = process.configuration;
1005
					if (this.launchJsonChanged && this.configurationManager.selectedLaunch) {
1006
						this.launchJsonChanged = false;
1007
						config = this.configurationManager.selectedLaunch.getConfiguration(process.configuration.name) || config;
I
isidor 已提交
1008 1009 1010
						// Take the type from the process since the debug extension might overwrite it #21316
						config.type = process.configuration.type;
						config.noDebug = process.configuration.noDebug;
1011
					}
I
isidor 已提交
1012
					config.__restart = restartData;
1013
					this.createProcess(process.session.root, config, process.getId()).then(() => c(null), err => e(err));
E
Erich Gamma 已提交
1014
				}, 300);
1015 1016
			});
		}).then(() => {
I
isidor 已提交
1017 1018
			if (preserveFocus) {
				// Restart should preserve the focused process
1019
				const restartedProcess = this.model.getProcesses().filter(p => p.configuration.name === process.configuration.name).pop();
I
isidor 已提交
1020 1021 1022
				if (restartedProcess && restartedProcess !== this.viewModel.focusedProcess) {
					this.focusStackFrameAndEvaluate(null, restartedProcess);
				}
1023 1024
			}
		});
E
Erich Gamma 已提交
1025 1026
	}

1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041
	public stopProcess(process: debug.IProcess): TPromise<any> {
		if (process) {
			return process.session.disconnect(false, true);
		}

		const processes = this.model.getProcesses();
		if (processes.length) {
			return TPromise.join(processes.map(p => p.session.disconnect(false, true)));
		}

		this.sessionStates.clear();
		this._onDidChangeState.fire();
		return undefined;
	}

I
isidor 已提交
1042
	private onSessionEnd(session: RawDebugSession): void {
I
isidor 已提交
1043
		const bpsExist = this.model.getBreakpoints().length > 0;
1044
		const process = this.model.getProcesses().filter(p => p.getId() === session.getId()).pop();
K
kieferrm 已提交
1045
		/* __GDPR__
K
kieferrm 已提交
1046 1047 1048 1049 1050 1051 1052 1053
			"debugSessionStop" : {
				"type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
				"success": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
				"sessionLengthInSeconds": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
				"breakpointCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
				"watchExpressionsCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
			}
		*/
I
isidor 已提交
1054
		this.telemetryService.publicLog('debugSessionStop', {
I
isidor 已提交
1055
			type: process && process.configuration.type,
I
isidor 已提交
1056 1057 1058 1059 1060
			success: session.emittedStopped || !bpsExist,
			sessionLengthInSeconds: session.getLengthInSeconds(),
			breakpointCount: this.model.getBreakpoints().length,
			watchExpressionsCount: this.model.getWatchExpressions().length
		});
1061

I
isidor 已提交
1062
		this.model.removeProcess(session.getId());
1063
		if (process && process.state !== debug.ProcessState.INACTIVE) {
I
isidor 已提交
1064
			process.inactive = true;
I
isidor 已提交
1065 1066
			this._onDidEndProcess.fire(process);
		}
I
isidor 已提交
1067

I
isidor 已提交
1068
		this.toDisposeOnSessionEnd.set(session.getId(), lifecycle.dispose(this.toDisposeOnSessionEnd.get(session.getId())));
I
isidor 已提交
1069 1070
		const focusedProcess = this.viewModel.focusedProcess;
		if (focusedProcess && focusedProcess.getId() === session.getId()) {
1071 1072
			this.focusStackFrameAndEvaluate(null).done(null, errors.onUnexpectedError);
		}
I
isidor 已提交
1073
		this.updateStateAndEmit(session.getId(), debug.State.Inactive);
1074

1075 1076
		if (this.model.getProcesses().length === 0) {
			// set breakpoints back to unverified since the session ended.
1077
			const data: { [id: string]: { line: number, verified: boolean, column: number, endLine: number, endColumn: number } } = {};
1078
			this.model.getBreakpoints().forEach(bp => {
1079
				data[bp.getId()] = { line: bp.lineNumber, verified: false, column: bp.column, endLine: bp.endLineNumber, endColumn: bp.endColumn };
1080 1081
			});
			this.model.updateBreakpoints(data);
1082

1083
			this.inDebugMode.reset();
I
isidor 已提交
1084
			this.debugType.reset();
1085
			this.viewModel.setMultiProcessView(false);
I
isidor 已提交
1086

1087
			if (this.partService.isVisible(Parts.SIDEBAR_PART) && this.configurationService.getConfiguration<debug.IDebugConfiguration>('debug').openExplorerOnEnd) {
1088 1089
				this.viewletService.openViewlet(EXPLORER_VIEWLET_ID).done(null, errors.onUnexpectedError);
			}
I
isidor 已提交
1090
		}
E
Erich Gamma 已提交
1091 1092 1093 1094 1095 1096 1097 1098 1099 1100
	}

	public getModel(): debug.IModel {
		return this.model;
	}

	public getViewModel(): debug.IViewModel {
		return this.viewModel;
	}

1101 1102
	public getConfigurationManager(): debug.IConfigurationManager {
		return this.configurationManager;
1103 1104
	}

1105 1106 1107
	private sendAllBreakpoints(process?: debug.IProcess): TPromise<any> {
		return TPromise.join(distinct(this.model.getBreakpoints(), bp => bp.uri.toString()).map(bp => this.sendBreakpoints(bp.uri, false, process)))
			.then(() => this.sendFunctionBreakpoints(process))
I
isidor 已提交
1108
			// send exception breakpoints at the end since some debug adapters rely on the order
1109
			.then(() => this.sendExceptionBreakpoints(process));
E
Erich Gamma 已提交
1110 1111
	}

1112 1113 1114 1115
	private sendBreakpoints(modelUri: uri, sourceModified = false, targetProcess?: debug.IProcess): TPromise<void> {

		const sendBreakpointsToProcess = (process: debug.IProcess): TPromise<void> => {
			const session = <RawDebugSession>process.session;
I
isidor 已提交
1116 1117 1118 1119 1120
			if (!session.readyForBreakpoints) {
				return TPromise.as(null);
			}
			if (this.textFileService.isDirty(modelUri)) {
				// Only send breakpoints for a file once it is not dirty #8077
I
isidor 已提交
1121
				this.breakpointsToSendOnResourceSaved.add(modelUri.toString());
I
isidor 已提交
1122 1123
				return TPromise.as(null);
			}
1124

1125
			const breakpointsToSend = this.model.getBreakpoints().filter(bp => this.model.areBreakpointsActivated() && bp.enabled && bp.uri.toString() === modelUri.toString());
1126

1127
			const source = process.sources.get(modelUri.toString());
I
isidor 已提交
1128 1129 1130 1131 1132 1133 1134 1135
			let rawSource: DebugProtocol.Source;
			if (source) {
				rawSource = source.raw;
			} else {
				const data = Source.getEncodedDebugData(modelUri);
				rawSource = { name: data.name, path: data.path, sourceReference: data.sourceReference };
			}

I
isidor 已提交
1136
			if (breakpointsToSend.length && !rawSource.adapterData) {
1137 1138
				rawSource.adapterData = breakpointsToSend[0].adapterData;
			}
1139

I
isidor 已提交
1140 1141
			return session.setBreakpoints({
				source: rawSource,
1142
				lines: breakpointsToSend.map(bp => bp.lineNumber),
1143
				breakpoints: breakpointsToSend.map(bp => ({ line: bp.lineNumber, column: bp.column, condition: bp.condition, hitCondition: bp.hitCondition })),
I
isidor 已提交
1144 1145 1146 1147 1148
				sourceModified
			}).then(response => {
				if (!response || !response.body) {
					return;
				}
1149

1150
				const data: { [id: string]: DebugProtocol.Breakpoint } = {};
I
isidor 已提交
1151 1152
				for (let i = 0; i < breakpointsToSend.length; i++) {
					data[breakpointsToSend[i].getId()] = response.body.breakpoints[i];
1153 1154 1155 1156
					if (!breakpointsToSend[i].column) {
						// If there was no column sent ignore the breakpoint column response from the adapter
						data[breakpointsToSend[i].getId()].column = undefined;
					}
I
isidor 已提交
1157
				}
1158

I
isidor 已提交
1159 1160 1161
				this.model.updateBreakpoints(data);
			});
		};
1162

1163
		return this.sendToOneOrAllProcesses(targetProcess, sendBreakpointsToProcess);
E
Erich Gamma 已提交
1164 1165
	}

1166 1167 1168
	private sendFunctionBreakpoints(targetProcess?: debug.IProcess): TPromise<void> {
		const sendFunctionBreakpointsToProcess = (process: debug.IProcess): TPromise<void> => {
			const session = <RawDebugSession>process.session;
1169
			if (!session.readyForBreakpoints || !session.capabilities.supportsFunctionBreakpoints) {
I
isidor 已提交
1170
				return TPromise.as(null);
1171 1172
			}

I
isidor 已提交
1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187
			const breakpointsToSend = this.model.getFunctionBreakpoints().filter(fbp => fbp.enabled && this.model.areBreakpointsActivated());
			return session.setFunctionBreakpoints({ breakpoints: breakpointsToSend }).then(response => {
				if (!response || !response.body) {
					return;
				}

				const data: { [id: string]: { name?: string, verified?: boolean } } = {};
				for (let i = 0; i < breakpointsToSend.length; i++) {
					data[breakpointsToSend[i].getId()] = response.body.breakpoints[i];
				}

				this.model.updateFunctionBreakpoints(data);
			});
		};

1188
		return this.sendToOneOrAllProcesses(targetProcess, sendFunctionBreakpointsToProcess);
I
isidor 已提交
1189 1190
	}

1191 1192 1193 1194
	private sendExceptionBreakpoints(targetProcess?: debug.IProcess): TPromise<void> {
		const sendExceptionBreakpointsToProcess = (process: debug.IProcess): TPromise<any> => {
			const session = <RawDebugSession>process.session;
			if (!session.readyForBreakpoints || this.model.getExceptionBreakpoints().length === 0) {
I
isidor 已提交
1195
				return TPromise.as(null);
I
isidor 已提交
1196 1197
			}

I
isidor 已提交
1198 1199 1200 1201
			const enabledExceptionBps = this.model.getExceptionBreakpoints().filter(exb => exb.enabled);
			return session.setExceptionBreakpoints({ filters: enabledExceptionBps.map(exb => exb.filter) });
		};

1202
		return this.sendToOneOrAllProcesses(targetProcess, sendExceptionBreakpointsToProcess);
I
isidor 已提交
1203 1204
	}

1205 1206 1207
	private sendToOneOrAllProcesses(process: debug.IProcess, send: (process: debug.IProcess) => TPromise<void>): TPromise<void> {
		if (process) {
			return send(process);
I
isidor 已提交
1208
		}
I
isidor 已提交
1209

1210
		return TPromise.join(this.model.getProcesses().map(p => send(p))).then(() => void 0);
E
Erich Gamma 已提交
1211 1212 1213
	}

	private onFileChanges(fileChangesEvent: FileChangesEvent): void {
1214
		this.model.removeBreakpoints(this.model.getBreakpoints().filter(bp =>
1215
			fileChangesEvent.contains(bp.uri, FileChangeType.DELETED)));
1216 1217

		fileChangesEvent.getUpdated().forEach(event => {
I
isidor 已提交
1218 1219
			if (this.breakpointsToSendOnResourceSaved.has(event.resource.toString())) {
				this.breakpointsToSendOnResourceSaved.delete(event.resource.toString());
1220 1221
				this.sendBreakpoints(event.resource, true).done(null, errors.onUnexpectedError);
			}
1222 1223 1224
			if (event.resource.toString().indexOf('.vscode/launch.json') >= 0) {
				this.launchJsonChanged = true;
			}
1225
		});
E
Erich Gamma 已提交
1226 1227 1228
	}

	private store(): void {
1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261
		const breakpoints = this.model.getBreakpoints();
		if (breakpoints.length) {
			this.storageService.store(DEBUG_BREAKPOINTS_KEY, JSON.stringify(breakpoints), StorageScope.WORKSPACE);
		} else {
			this.storageService.remove(DEBUG_BREAKPOINTS_KEY, StorageScope.WORKSPACE);
		}

		if (!this.model.areBreakpointsActivated()) {
			this.storageService.store(DEBUG_BREAKPOINTS_ACTIVATED_KEY, 'false', StorageScope.WORKSPACE);
		} else {
			this.storageService.remove(DEBUG_BREAKPOINTS_ACTIVATED_KEY, StorageScope.WORKSPACE);
		}

		const functionBreakpoints = this.model.getFunctionBreakpoints();
		if (functionBreakpoints.length) {
			this.storageService.store(DEBUG_FUNCTION_BREAKPOINTS_KEY, JSON.stringify(functionBreakpoints), StorageScope.WORKSPACE);
		} else {
			this.storageService.remove(DEBUG_FUNCTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE);
		}

		const exceptionBreakpoints = this.model.getExceptionBreakpoints();
		if (exceptionBreakpoints.length) {
			this.storageService.store(DEBUG_EXCEPTION_BREAKPOINTS_KEY, JSON.stringify(exceptionBreakpoints), StorageScope.WORKSPACE);
		} else {
			this.storageService.remove(DEBUG_EXCEPTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE);
		}

		const watchExpressions = this.model.getWatchExpressions();
		if (watchExpressions.length) {
			this.storageService.store(DEBUG_WATCH_EXPRESSIONS_KEY, JSON.stringify(watchExpressions.map(we => ({ name: we.name, id: we.getId() }))), StorageScope.WORKSPACE);
		} else {
			this.storageService.remove(DEBUG_WATCH_EXPRESSIONS_KEY, StorageScope.WORKSPACE);
		}
E
Erich Gamma 已提交
1262 1263 1264
	}

	public dispose(): void {
I
isidor 已提交
1265
		this.toDisposeOnSessionEnd.forEach(toDispose => lifecycle.dispose(toDispose));
I
isidor 已提交
1266
		this.toDispose = lifecycle.dispose(this.toDispose);
E
Erich Gamma 已提交
1267 1268
	}
}