debugService.ts 53.7 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';
9
import * as paths from 'vs/base/common/paths';
I
isidor 已提交
10
import * as resources from 'vs/base/common/resources';
I
isidor 已提交
11
import * as strings from 'vs/base/common/strings';
I
isidor 已提交
12
import { generateUuid } from 'vs/base/common/uuid';
E
Erich Gamma 已提交
13
import uri from 'vs/base/common/uri';
I
isidor 已提交
14
import * as platform from 'vs/base/common/platform';
J
Johannes Rieken 已提交
15
import { Action } from 'vs/base/common/actions';
I
isidor 已提交
16 17 18
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 已提交
19
import severity from 'vs/base/common/severity';
J
Johannes Rieken 已提交
20
import { TPromise } from 'vs/base/common/winjs.base';
I
isidor 已提交
21
import * as aria from 'vs/base/browser/ui/aria/aria';
22
import { Client as TelemetryClient } from 'vs/base/parts/ipc/node/ipc.cp';
J
Johannes Rieken 已提交
23 24 25 26 27
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';
28
import { FileChangesEvent, FileChangeType, IFileService } from 'vs/platform/files/common/files';
J
Johannes Rieken 已提交
29
import { IMessageService, CloseAction } from 'vs/platform/message/common/message';
30
import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows';
J
Johannes Rieken 已提交
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';
34
import { ICommandService } from 'vs/platform/commands/common/commands';
J
Johannes Rieken 已提交
35
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
I
isidor 已提交
36
import * as debug from 'vs/workbench/parts/debug/common/debug';
J
Johannes Rieken 已提交
37
import { RawDebugSession } from 'vs/workbench/parts/debug/electron-browser/rawDebugSession';
I
isidor 已提交
38
import { Model, ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, Expression, RawObjectReplElement, ExpressionContainer, Process } from 'vs/workbench/parts/debug/common/debugModel';
I
isidor 已提交
39 40
import { ViewModel } from 'vs/workbench/parts/debug/common/debugViewModel';
import * as debugactions from 'vs/workbench/parts/debug/browser/debugActions';
41
import { ConfigurationManager } from 'vs/workbench/parts/debug/electron-browser/debugConfigurationManager';
I
isidor 已提交
42
import { ToggleMarkersPanelAction } from 'vs/workbench/parts/markers/browser/markersPanelActions';
I
isidor 已提交
43
import { ITaskService, TaskServiceEvents, ITaskSummary } from 'vs/workbench/parts/tasks/common/taskService';
44
import { TaskError } from 'vs/workbench/parts/tasks/common/taskSystem';
I
isidor 已提交
45
import { VIEWLET_ID as EXPLORER_VIEWLET_ID } from 'vs/workbench/parts/files/common/files';
B
Benjamin Pasero 已提交
46
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
J
Johannes Rieken 已提交
47
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
48
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
J
Johannes Rieken 已提交
49 50
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
S
Sandeep Somavarapu 已提交
51
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
J
Johannes Rieken 已提交
52
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
53
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';
54
import { IBroadcastService, IBroadcast } from 'vs/platform/broadcast/electron-browser/broadcastService';
55
import { IRemoteConsoleLog, parse, getFirstFrame } from 'vs/base/node/console';
E
Erich Gamma 已提交
56

I
isidor 已提交
57 58 59 60 61
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 已提交
62

63 64 65 66 67
interface StartSessionResult {
	status: 'ok' | 'initialConfiguration' | 'saveConfiguration';
	content?: string;
};

68
export class DebugService implements debug.IDebugService {
69
	public _serviceBrand: any;
E
Erich Gamma 已提交
70

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

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

121
		this.configurationManager = this.instantiationService.createInstance(ConfigurationManager);
122
		this.toDispose.push(this.configurationManager);
123
		this.inDebugMode = debug.CONTEXT_IN_DEBUG_MODE.bindTo(contextKeyService);
I
isidor 已提交
124
		this.debugType = debug.CONTEXT_DEBUG_TYPE.bindTo(contextKeyService);
I
isidor 已提交
125
		this.debugState = debug.CONTEXT_DEBUG_STATE.bindTo(contextKeyService);
E
Erich Gamma 已提交
126

I
isidor 已提交
127
		this.model = new Model(this.loadBreakpoints(), this.storageService.getBoolean(DEBUG_BREAKPOINTS_ACTIVATED_KEY, StorageScope.WORKSPACE, true), this.loadFunctionBreakpoints(),
E
Erich Gamma 已提交
128
			this.loadExceptionBreakpoints(), this.loadWatchExpressions());
I
isidor 已提交
129
		this.toDispose.push(this.model);
130
		this.viewModel = new ViewModel();
131

132
		this.registerListeners(lifecycleService);
E
Erich Gamma 已提交
133 134
	}

135 136
	private registerListeners(lifecycleService: ILifecycleService): void {
		this.toDispose.push(this.fileService.onFileChanges(e => this.onFileChanges(e)));
137 138
		lifecycleService.onShutdown(this.store, this);
		lifecycleService.onShutdown(this.dispose, this);
139
		this.toDispose.push(this.broadcastService.onBroadcast(this.onBroadcast, this));
140 141 142
	}

	private onBroadcast(broadcast: IBroadcast): void {
143

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

152
		if (broadcast.channel === EXTENSION_ATTACH_BROADCAST_CHANNEL) {
153
			this.onSessionEnd(session);
I
isidor 已提交
154

155 156 157
			process.configuration.request = 'attach';
			process.configuration.port = broadcast.payload.port;
			this.doCreateProcess(process.session.root, process.configuration, process.getId());
158 159
			return;
		}
160

161
		if (broadcast.channel === EXTENSION_TERMINATE_BROADCAST_CHANNEL) {
162
			this.onSessionEnd(session);
163 164 165
			return;
		}

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

171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
			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
						})
					};
				}
			}
186

I
isidor 已提交
187
			// add output for each argument logged
188 189 190 191
			let simpleVals: any[] = [];
			for (let i = 0; i < args.length; i++) {
				let a = args[i];

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

I
isidor 已提交
197
				// null gets printed as 'null'
198 199 200 201
				else if (a === null) {
					simpleVals.push('null');
				}

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

I
isidor 已提交
205
					// flush any existing simple values logged
206
					if (simpleVals.length) {
207
						this.logToRepl(simpleVals.join(' '), sev, source);
208 209 210
						simpleVals = [];
					}

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

I
isidor 已提交
215 216
				// string: watch out for % replacement directive
				// string substitution and formatting @ https://developer.chrome.com/devtools/docs/console
217 218 219 220 221 222
				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 已提交
223
							buf += !isUndefinedOrNull(args[i]) ? args[i] : ''; // replace
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
							j++; // read over directive
						} else {
							buf += a[j];
						}
					}

					simpleVals.push(buf);
				}

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

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

247
	private tryToAutoFocusStackFrame(thread: debug.IThread): TPromise<any> {
I
isidor 已提交
248
		const callStack = thread.getCallStack();
I
isidor 已提交
249
		if (!callStack.length || (this.viewModel.focusedStackFrame && this.viewModel.focusedStackFrame.thread.getId() === thread.getId())) {
I
isidor 已提交
250 251 252
			return TPromise.as(null);
		}

253
		// focus first stack frame from top that has source location if no other stack frame is focused
I
isidor 已提交
254 255 256 257 258 259
		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 已提交
260
		if (thread.stoppedDetails) {
261
			this.windowService.focusWindow();
I
isidor 已提交
262 263
			aria.alert(nls.localize('debuggingPaused', "Debugging paused, reason {0}, {1} {2}", thread.stoppedDetails.reason, stackFrameToFocus.source ? stackFrameToFocus.source.name : '', stackFrameToFocus.range.startLineNumber));
		}
I
isidor 已提交
264

I
isidor 已提交
265
		return stackFrameToFocus.openInEditor(this.editorService, true);
I
isidor 已提交
266 267
	}

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

I
isidor 已提交
270
		this.toDisposeOnSessionEnd.get(session.getId()).push(session);
271

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

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

I
isidor 已提交
290
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidStop(event => {
I
isidor 已提交
291
			this.updateStateAndEmit(session.getId(), debug.State.Stopped);
I
isidor 已提交
292 293
			this.fetchThreads(session, event.body).done(() => {
				const thread = process && process.getThread(event.body.threadId);
I
isidor 已提交
294
				if (thread) {
295 296 297
					// 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(() => {
298
						return this.tryToAutoFocusStackFrame(thread);
I
isidor 已提交
299 300
					});
				}
E
Erich Gamma 已提交
301 302 303
			}, errors.onUnexpectedError);
		}));

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

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

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

I
isidor 已提交
332
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidOutput(event => {
333 334 335 336
			if (!event.body) {
				return;
			}

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

				return;
			}

I
isidor 已提交
349 350 351 352 353 354
			const source = event.body.source ? {
				lineNumber: event.body.line,
				column: event.body.column,
				source: process.getSource(event.body.source)
			} : undefined;

I
isidor 已提交
355
			if (event.body.variablesReference) {
356 357 358 359 360
				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 已提交
361
						this.logToRepl(child, outputSeverity, source);
I
isidor 已提交
362
					});
363 364
				});
			} else if (typeof event.body.output === 'string') {
I
isidor 已提交
365
				this.logToRepl(event.body.output, outputSeverity, source);
E
Erich Gamma 已提交
366 367 368
			}
		}));

I
isidor 已提交
369
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidBreakpoint(event => {
I
isidor 已提交
370
			const id = event.body && event.body.breakpoint ? event.body.breakpoint.id : undefined;
371
			const breakpoint = this.model.getBreakpoints().filter(bp => bp.idFromAdapter === id).pop();
I
isidor 已提交
372 373 374
			const functionBreakpoint = this.model.getFunctionBreakpoints().filter(bp => bp.idFromAdapter === id).pop();

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

			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
396 397
			// 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 已提交
398 399 400 401 402
				if (breakpoint) {
					if (!breakpoint.column) {
						event.body.breakpoint.column = undefined;
					}
					this.model.updateBreakpoints({ [breakpoint.getId()]: event.body.breakpoint });
I
isidor 已提交
403
				}
I
isidor 已提交
404 405 406 407 408 409
				if (functionBreakpoint) {
					this.model.updateFunctionBreakpoints({ [functionBreakpoint.getId()]: event.body.breakpoint });
				}
			}
		}));

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

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

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

444 445
	private loadBreakpoints(): Breakpoint[] {
		let result: Breakpoint[];
E
Erich Gamma 已提交
446
		try {
447
			result = JSON.parse(this.storageService.get(DEBUG_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((breakpoint: any) => {
448
				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 已提交
449
			});
450 451 452
		} catch (e) { }

		return result || [];
E
Erich Gamma 已提交
453 454
	}

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

		return result || [];
I
isidor 已提交
464 465
	}

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

474
		return result || [];
E
Erich Gamma 已提交
475 476
	}

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

		return result || [];
E
Erich Gamma 已提交
486 487
	}

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

		return debug.State.Inactive;
E
Erich Gamma 已提交
502 503
	}

504
	public get onDidChangeState(): Event<debug.State> {
505 506 507
		return this._onDidChangeState.event;
	}

508 509 510 511
	public get onDidNewProcess(): Event<debug.IProcess> {
		return this._onDidNewProcess.event;
	}

I
isidor 已提交
512 513 514 515
	public get onDidEndProcess(): Event<debug.IProcess> {
		return this._onDidEndProcess.event;
	}

516
	public get onDidCustomEvent(): Event<debug.DebugEvent> {
517 518 519
		return this._onDidCustomEvent.event;
	}

I
isidor 已提交
520
	private updateStateAndEmit(sessionId?: string, newState?: debug.State): void {
521
		const previousState = this.state;
I
isidor 已提交
522 523 524 525 526 527
		if (sessionId) {
			if (newState === debug.State.Inactive) {
				this.sessionStates.delete(sessionId);
			} else {
				this.sessionStates.set(sessionId, newState);
			}
I
isidor 已提交
528
		}
I
isidor 已提交
529 530

		const state = this.state;
531 532 533 534 535 536
		if (previousState !== state) {
			const stateLabel = debug.State[state];
			if (stateLabel) {
				this.debugState.set(stateLabel.toLowerCase());
			}
			this._onDidChangeState.fire(state);
I
isidor 已提交
537
		}
E
Erich Gamma 已提交
538 539
	}

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

551
		this.viewModel.setFocusedStackFrame(stackFrame, process, explicit);
I
isidor 已提交
552
		this.updateStateAndEmit();
553

554
		return this.model.evaluateWatchExpressions(process, stackFrame);
E
Erich Gamma 已提交
555 556
	}

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

566
			return this.sendExceptionBreakpoints();
E
Erich Gamma 已提交
567 568
		}

569 570
		this.model.enableOrDisableAllBreakpoints(enable);
		return this.sendAllBreakpoints();
E
Erich Gamma 已提交
571 572
	}

573 574 575
	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)));
576

577
		return this.sendBreakpoints(uri);
578 579
	}

580 581
	public removeBreakpoints(id?: string): TPromise<any> {
		const toRemove = this.model.getBreakpoints().filter(bp => !id || bp.getId() === id);
582 583
		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 已提交
584

585
		this.model.removeBreakpoints(toRemove);
I
isidor 已提交
586
		return TPromise.join(urisToClear.map(uri => this.sendBreakpoints(uri)));
E
Erich Gamma 已提交
587 588
	}

589 590
	public setBreakpointsActivated(activated: boolean): TPromise<void> {
		this.model.setBreakpointsActivated(activated);
E
Erich Gamma 已提交
591 592 593
		return this.sendAllBreakpoints();
	}

I
isidor 已提交
594 595
	public addFunctionBreakpoint(): void {
		this.model.addFunctionBreakpoint('');
596 597
	}

I
isidor 已提交
598
	public renameFunctionBreakpoint(id: string, newFunctionName: string): TPromise<void> {
I
isidor 已提交
599
		this.model.updateFunctionBreakpoints({ [id]: { name: newFunctionName } });
I
isidor 已提交
600
		return this.sendFunctionBreakpoints();
I
isidor 已提交
601 602
	}

I
isidor 已提交
603
	public removeFunctionBreakpoints(id?: string): TPromise<void> {
604
		this.model.removeFunctionBreakpoints(id);
I
isidor 已提交
605
		return this.sendFunctionBreakpoints();
I
isidor 已提交
606 607
	}

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

618 619
	public removeReplExpressions(): void {
		this.model.removeReplExpressions();
E
Erich Gamma 已提交
620 621
	}

I
isidor 已提交
622
	public logToRepl(value: string | debug.IExpression, sev = severity.Info, source?: debug.IReplElementSource): void {
623 624 625 626
		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 已提交
627
			this.model.appendToRepl(value, sev, source);
628
		}
629 630
	}

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

I
isidor 已提交
635
	public renameWatchExpression(id: string, newName: string): TPromise<void> {
636
		return this.model.renameWatchExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, id, newName);
E
Erich Gamma 已提交
637 638
	}

I
isidor 已提交
639 640 641 642
	public moveWatchExpression(id: string, position: number): void {
		this.model.moveWatchExpression(id, position);
	}

643 644
	public removeWatchExpressions(id?: string): void {
		this.model.removeWatchExpressions(id);
E
Erich Gamma 已提交
645 646
	}

647 648 649 650
	public evaluateWatchExpressions(): TPromise<void> {
		return this.model.evaluateWatchExpressions(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame);
	}

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

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

664
				let config: debug.IConfig, compound: debug.ICompound;
665
				if (!configOrName) {
666
					configOrName = this.configurationManager.selectedName;
667
				}
668 669 670
				if (typeof configOrName === 'string' && launch) {
					config = launch.getConfiguration(configOrName);
					compound = launch.getCompound(configOrName);
671
				} else if (typeof configOrName !== 'string') {
672 673
					config = configOrName;
				}
674
				if (launch) {
675 676
					// 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);
677
				}
678

679 680 681 682 683
				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.")));
					}
684

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

I
isidor 已提交
691 692 693 694 695 696 697 698 699 700 701 702
				// 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;
				}
703

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

I
isidor 已提交
713
						this.updateStateAndEmit(sessionId, debug.State.Inactive);
I
isidor 已提交
714 715 716
						return <any>launch.openConfigFile(false, type); // cast to ignore weird compile error
					})
				);
717
			})
B
Benjamin Pasero 已提交
718
		)));
719
	}
720

721 722 723 724 725 726 727 728 729
	public findProcessByUUID(uuid: string): debug.IProcess | null {
		const processes = this.getModel().getProcesses();
		const result = processes.filter(process => process.getId() === uuid);
		if (result.length > 0) {
			return result[0];	// there can only be one
		}
		return null;
	}

I
isidor 已提交
730
	public createProcess(root: IWorkspaceFolder, config: debug.IConfig, sessionId?: string): TPromise<debug.IProcess> {
731
		return this.textFileService.saveAll().then(() =>
732
			(this.configurationManager.selectedLaunch ? this.configurationManager.selectedLaunch.resolveConfiguration(config) : TPromise.as(config)).then(resolvedConfig => {
733 734 735 736
				if (!resolvedConfig) {
					// User canceled resolving of interactive variables, silently return
					return undefined;
				}
I
isidor 已提交
737

738 739 740
				if (!this.configurationManager.getAdapter(resolvedConfig.type) || (config.request !== 'attach' && config.request !== 'launch')) {
					let message: string;
					if (config.request !== 'attach' && config.request !== 'launch') {
741 742
						message = config.request ? nls.localize('debugRequestNotSupported', "Attribute `{0}` has an unsupported value '{1}' in the chosen debug configuration.", 'request', config.request)
							: nls.localize('debugRequesMissing', "Attribute `{0}` is missing from the chosen debug configuration.", 'request');
743 744 745

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

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

I
isidor 已提交
752 753 754 755
				if (!sessionId) {
					sessionId = generateUuid();
					this.updateStateAndEmit(sessionId, debug.State.Initializing);
				}
756

757
				return this.runPreLaunchTask(root, resolvedConfig.preLaunchTask).then((taskSummary: ITaskSummary) => {
758 759 760 761
					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)) {
762
						return this.doCreateProcess(root, resolvedConfig, sessionId);
763
					}
I
isidor 已提交
764

765 766 767 768 769 770 771
					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: [
							new Action('debug.continue', nls.localize('debugAnyway', "Debug Anyway"), null, true, () => {
								this.messageService.hideAll();
772
								return this.doCreateProcess(root, resolvedConfig, sessionId);
773 774 775 776 777 778 779
							}),
							this.instantiationService.createInstance(ToggleMarkersPanelAction, ToggleMarkersPanelAction.ID, ToggleMarkersPanelAction.LABEL),
							CloseAction
						]
					});
					return undefined;
				}, (err: TaskError) => {
780
					this.updateStateAndEmit(sessionId, debug.State.Inactive);
781 782
					this.messageService.show(err.severity, {
						message: err.message,
783 784 785 786 787
						actions: [
							this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL),
							this.taskService.configureAction(),
							CloseAction
						]
788
					});
789 790
				});
			}, err => {
I
isidor 已提交
791
				this.updateStateAndEmit(sessionId, debug.State.Inactive);
792
				if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) {
R
Ron Buckton 已提交
793 794
					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;
795
				}
E
Erich Gamma 已提交
796

797
				return this.configurationManager.selectedLaunch.openConfigFile(false).then(openend => {
798 799 800
					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 已提交
801
					return undefined;
802 803 804 805
				});
			})
		);
	}
806

807
	private doCreateProcess(root: IWorkspaceFolder, configuration: debug.IConfig, sessionId: string): TPromise<debug.IProcess> {
808
		configuration.__sessionId = sessionId;
809
		this.inDebugMode.set(true);
810

J
Joao Moreno 已提交
811
		return this.telemetryService.getTelemetryInfo().then(info => {
812
			const telemetryInfo: { [key: string]: string } = Object.create(null);
813 814
			telemetryInfo['common.vscodemachineid'] = info.machineId;
			telemetryInfo['common.vscodesessionid'] = info.sessionId;
815 816
			return telemetryInfo;
		}).then(data => {
817 818 819
			const adapter = this.configurationManager.getAdapter(configuration.type);
			const { aiKey, type } = adapter;
			const publisher = adapter.extensionDescription.publisher;
820
			let client: TelemetryClient;
821

822
			let customTelemetryService: TelemetryService;
823
			if (aiKey) {
824
				client = new TelemetryClient(
825 826 827 828
					uri.parse(require.toUrl('bootstrap')).fsPath,
					{
						serverName: 'Debug Telemetry',
						timeout: 1000 * 60 * 5,
J
Johannes Rieken 已提交
829
						args: [`${publisher}.${type}`, JSON.stringify(data), aiKey],
830
						env: {
B
Benjamin Pasero 已提交
831
							ELECTRON_RUN_AS_NODE: 1,
832 833 834
							PIPE_LOGGING: 'true',
							AMD_ENTRYPOINT: 'vs/workbench/parts/debug/node/telemetryApp'
						}
835
					}
836
				);
837

838 839
				const channel = client.getChannel('telemetryAppender');
				const appender = new TelemetryAppenderClient(channel);
840

841
				customTelemetryService = new TelemetryService({ appender }, this.configurationService);
842 843
			}

844
			const session = this.instantiationService.createInstance(RawDebugSession, sessionId, configuration.debugServer, adapter, customTelemetryService, root);
845
			const process = this.model.addProcess(configuration, session);
846
			this.allProcesses.set(process.getId(), process);
847

I
isidor 已提交
848
			this.toDisposeOnSessionEnd.set(session.getId(), []);
849
			if (client) {
I
isidor 已提交
850
				this.toDisposeOnSessionEnd.get(session.getId()).push(client);
851
			}
I
isidor 已提交
852
			this.registerSessionListeners(process, session);
J
Joao Moreno 已提交
853

854
			return session.initialize({
855
				clientID: 'vscode',
J
Joao Moreno 已提交
856 857 858
				adapterID: configuration.type,
				pathFormat: 'path',
				linesStartAt1: true,
859
				columnsStartAt1: true,
I
isidor 已提交
860
				supportsVariableType: true, // #8858
861
				supportsVariablePaging: true, // #9537
I
isidor 已提交
862 863
				supportsRunInTerminalRequest: true, // #10574
				locale: platform.locale
J
Joao Moreno 已提交
864
			}).then((result: DebugProtocol.InitializeResponse) => {
865
				this.model.setExceptionBreakpoints(session.capabilities.exceptionBreakpointFilters);
866
				return configuration.request === 'attach' ? session.attach(configuration) : session.launch(configuration);
J
Joao Moreno 已提交
867
			}).then((result: DebugProtocol.Response) => {
868
				if (session.disconnected) {
869 870
					return TPromise.as(null);
				}
871
				this._onDidNewProcess.fire(process);
I
isidor 已提交
872
				this.focusStackFrameAndEvaluate(null, process);
873 874 875

				const internalConsoleOptions = configuration.internalConsoleOptions || this.configurationService.getConfiguration<debug.IDebugConfiguration>('debug').internalConsoleOptions;
				if (internalConsoleOptions === 'openOnSessionStart' || (!this.viewModel.changedWorkbenchViewState && internalConsoleOptions === 'openOnFirstSessionStart')) {
J
Joao Moreno 已提交
876 877
					this.panelService.openPanel(debug.REPL_ID, false).done(undefined, errors.onUnexpectedError);
				}
878

879
				if (!this.viewModel.changedWorkbenchViewState && (this.partService.isVisible(Parts.SIDEBAR_PART) || this.contextService.getWorkbenchState() === WorkbenchState.EMPTY)) {
J
Joao Moreno 已提交
880 881 882 883
					// We only want to change the workbench view state on the first debug session #5738 and if the side bar is not hidden
					this.viewModel.changedWorkbenchViewState = true;
					this.viewletService.openViewlet(debug.VIEWLET_ID);
				}
884

I
isidor 已提交
885
				this.debugType.set(configuration.type);
886 887 888
				if (this.model.getProcesses().length > 1) {
					this.viewModel.setMultiProcessView(true);
				}
I
isidor 已提交
889
				this.updateStateAndEmit(session.getId(), debug.State.Running);
J
Joao Moreno 已提交
890

K
kieferrm 已提交
891
				/* __GDPR__
K
kieferrm 已提交
892 893 894 895 896 897 898 899 900 901
					"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" }
					}
				*/
902
				return this.telemetryService.publicLog('debugSessionStart', {
J
Joao Moreno 已提交
903 904 905 906
					type: configuration.type,
					breakpointCount: this.model.getBreakpoints().length,
					exceptionBreakpoints: this.model.getExceptionBreakpoints(),
					watchExpressionsCount: this.model.getWatchExpressions().length,
907
					extensionName: `${adapter.extensionDescription.publisher}.${adapter.extensionDescription.name}`,
908
					isBuiltin: adapter.extensionDescription.isBuiltin,
I
isidor 已提交
909
					launchJsonExists: this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY && !!this.configurationService.getConfiguration<debug.IGlobalConfig>('launch', { resource: root.uri })
I
isidor 已提交
910 911
				});
			}).then(() => process, (error: any) => {
912 913 914 915 916
				if (error instanceof Error && error.message === 'Canceled') {
					// Do not show 'canceled' error messages to the user #7906
					return TPromise.as(null);
				}

917
				const errorMessage = error instanceof Error ? error.message : error;
K
kieferrm 已提交
918
				/* __GDPR__
K
kieferrm 已提交
919 920 921 922 923
					"debugMisconfiguration" : {
						"type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
						"error": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
					}
				*/
924
				this.telemetryService.publicLog('debugMisconfiguration', { type: configuration ? configuration.type : undefined, error: errorMessage });
I
isidor 已提交
925
				this.updateStateAndEmit(session.getId(), debug.State.Inactive);
926 927
				if (!session.disconnected) {
					session.disconnect().done(null, errors.onUnexpectedError);
J
Joao Moreno 已提交
928
				}
I
isidor 已提交
929 930 931
				if (process) {
					this.model.removeProcess(process.getId());
				}
J
Joao Moreno 已提交
932 933 934
				// 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);
935 936 937
				}
				if (this.model.getReplElements().length === 0) {
					this.inDebugMode.reset();
J
Joao Moreno 已提交
938
				}
I
isidor 已提交
939

J
Joao Moreno 已提交
940 941
				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];
942
				this.messageService.show(severity.Error, { message: errorMessage, actions });
943
				return undefined;
J
Joao Moreno 已提交
944
			});
E
Erich Gamma 已提交
945 946 947
		});
	}

S
Sandeep Somavarapu 已提交
948
	private runPreLaunchTask(root: IWorkspaceFolder, taskName: string): TPromise<ITaskSummary> {
949
		if (!taskName) {
I
isidor 已提交
950
			return TPromise.as(null);
E
Erich Gamma 已提交
951 952
		}

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

I
isidor 已提交
959 960 961 962 963
			return this.taskService.getActiveTasks().then(tasks => {
				if (tasks.filter(t => t._id === task._id).length) {
					// task is already running - nothing to do.
					return TPromise.as(null);
				}
E
Erich Gamma 已提交
964

I
isidor 已提交
965 966 967 968
				const taskPromise = this.taskService.run(task);
				if (task.isBackground) {
					return new TPromise((c, e) => this.taskService.addOneTimeListener(TaskServiceEvents.Inactive, () => c(null)));
				}
E
Erich Gamma 已提交
969

I
isidor 已提交
970
				return taskPromise;
E
Erich Gamma 已提交
971 972 973 974
			});
		});
	}

975 976
	public sourceIsNotAvailable(uri: uri): void {
		this.model.sourceIsNotAvailable(uri);
I
isidor 已提交
977 978
	}

979
	public restartProcess(process: debug.IProcess, restartData?: any): TPromise<any> {
980
		if (process.session.capabilities.supportsRestartRequest) {
981
			return this.textFileService.saveAll().then(() => process.session.custom('restart', null));
982
		}
I
isidor 已提交
983 984
		const focusedProcess = this.viewModel.focusedProcess;
		const preserveFocus = focusedProcess && process.getId() === focusedProcess.getId();
985

986 987
		return process.session.disconnect(true).then(() => {
			if (strings.equalsIgnoreCase(process.configuration.type, 'extensionHost')) {
988
				return this.broadcastService.broadcast({
989
					channel: EXTENSION_RELOAD_BROADCAST_CHANNEL,
I
isidor 已提交
990
					payload: [process.session.root.uri.fsPath]
991 992 993 994
				});
			}

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

1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034
	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 已提交
1035
	private onSessionEnd(session: RawDebugSession): void {
I
isidor 已提交
1036
		const bpsExist = this.model.getBreakpoints().length > 0;
1037
		const process = this.model.getProcesses().filter(p => p.getId() === session.getId()).pop();
K
kieferrm 已提交
1038
		/* __GDPR__
K
kieferrm 已提交
1039 1040 1041 1042 1043 1044 1045 1046
			"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 已提交
1047
		this.telemetryService.publicLog('debugSessionStop', {
I
isidor 已提交
1048
			type: process && process.configuration.type,
I
isidor 已提交
1049 1050 1051 1052 1053
			success: session.emittedStopped || !bpsExist,
			sessionLengthInSeconds: session.getLengthInSeconds(),
			breakpointCount: this.model.getBreakpoints().length,
			watchExpressionsCount: this.model.getWatchExpressions().length
		});
1054

I
isidor 已提交
1055
		this.model.removeProcess(session.getId());
1056
		if (process && process.state !== debug.ProcessState.INACTIVE) {
I
isidor 已提交
1057
			process.inactive = true;
I
isidor 已提交
1058 1059
			this._onDidEndProcess.fire(process);
		}
I
isidor 已提交
1060

I
isidor 已提交
1061
		this.toDisposeOnSessionEnd.set(session.getId(), lifecycle.dispose(this.toDisposeOnSessionEnd.get(session.getId())));
I
isidor 已提交
1062 1063
		const focusedProcess = this.viewModel.focusedProcess;
		if (focusedProcess && focusedProcess.getId() === session.getId()) {
1064 1065
			this.focusStackFrameAndEvaluate(null).done(null, errors.onUnexpectedError);
		}
I
isidor 已提交
1066
		this.updateStateAndEmit(session.getId(), debug.State.Inactive);
1067

1068 1069
		if (this.model.getProcesses().length === 0) {
			// set breakpoints back to unverified since the session ended.
1070
			const data: { [id: string]: { line: number, verified: boolean, column: number, endLine: number, endColumn: number } } = {};
1071
			this.model.getBreakpoints().forEach(bp => {
1072
				data[bp.getId()] = { line: bp.lineNumber, verified: false, column: bp.column, endLine: bp.endLineNumber, endColumn: bp.endColumn };
1073 1074
			});
			this.model.updateBreakpoints(data);
1075

1076
			this.inDebugMode.reset();
I
isidor 已提交
1077
			this.debugType.reset();
1078
			this.viewModel.setMultiProcessView(false);
I
isidor 已提交
1079

1080
			if (this.partService.isVisible(Parts.SIDEBAR_PART) && this.configurationService.getConfiguration<debug.IDebugConfiguration>('debug').openExplorerOnEnd) {
1081 1082
				this.viewletService.openViewlet(EXPLORER_VIEWLET_ID).done(null, errors.onUnexpectedError);
			}
I
isidor 已提交
1083
		}
E
Erich Gamma 已提交
1084 1085 1086 1087 1088 1089 1090 1091 1092 1093
	}

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

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

1094 1095
	public getConfigurationManager(): debug.IConfigurationManager {
		return this.configurationManager;
1096 1097
	}

1098 1099 1100
	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 已提交
1101
			// send exception breakpoints at the end since some debug adapters rely on the order
1102
			.then(() => this.sendExceptionBreakpoints(process));
E
Erich Gamma 已提交
1103 1104
	}

1105 1106 1107 1108
	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 已提交
1109 1110 1111 1112 1113
			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 已提交
1114
				this.breakpointsToSendOnResourceSaved.add(modelUri.toString());
I
isidor 已提交
1115 1116
				return TPromise.as(null);
			}
1117

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

1120
			const source = process.sources.get(modelUri.toString());
1121
			const rawSource = source ? source.raw : { path: modelUri.scheme === 'file' || modelUri.scheme === debug.DEBUG_SCHEME ? paths.normalize(modelUri.fsPath, true) : modelUri.toString(), name: resources.basenameOrAuthority(modelUri) };
I
isidor 已提交
1122
			if (breakpointsToSend.length && !rawSource.adapterData) {
1123 1124
				rawSource.adapterData = breakpointsToSend[0].adapterData;
			}
1125

I
isidor 已提交
1126 1127
			return session.setBreakpoints({
				source: rawSource,
1128
				lines: breakpointsToSend.map(bp => bp.lineNumber),
1129
				breakpoints: breakpointsToSend.map(bp => ({ line: bp.lineNumber, column: bp.column, condition: bp.condition, hitCondition: bp.hitCondition })),
I
isidor 已提交
1130 1131 1132 1133 1134
				sourceModified
			}).then(response => {
				if (!response || !response.body) {
					return;
				}
1135

1136
				const data: { [id: string]: DebugProtocol.Breakpoint } = {};
I
isidor 已提交
1137 1138
				for (let i = 0; i < breakpointsToSend.length; i++) {
					data[breakpointsToSend[i].getId()] = response.body.breakpoints[i];
1139 1140 1141 1142
					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 已提交
1143
				}
1144

I
isidor 已提交
1145 1146 1147
				this.model.updateBreakpoints(data);
			});
		};
1148

1149
		return this.sendToOneOrAllProcesses(targetProcess, sendBreakpointsToProcess);
E
Erich Gamma 已提交
1150 1151
	}

1152 1153 1154
	private sendFunctionBreakpoints(targetProcess?: debug.IProcess): TPromise<void> {
		const sendFunctionBreakpointsToProcess = (process: debug.IProcess): TPromise<void> => {
			const session = <RawDebugSession>process.session;
1155
			if (!session.readyForBreakpoints || !session.capabilities.supportsFunctionBreakpoints) {
I
isidor 已提交
1156
				return TPromise.as(null);
1157 1158
			}

I
isidor 已提交
1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173
			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);
			});
		};

1174
		return this.sendToOneOrAllProcesses(targetProcess, sendFunctionBreakpointsToProcess);
I
isidor 已提交
1175 1176
	}

1177 1178 1179 1180
	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 已提交
1181
				return TPromise.as(null);
I
isidor 已提交
1182 1183
			}

I
isidor 已提交
1184 1185 1186 1187
			const enabledExceptionBps = this.model.getExceptionBreakpoints().filter(exb => exb.enabled);
			return session.setExceptionBreakpoints({ filters: enabledExceptionBps.map(exb => exb.filter) });
		};

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

1191 1192 1193
	private sendToOneOrAllProcesses(process: debug.IProcess, send: (process: debug.IProcess) => TPromise<void>): TPromise<void> {
		if (process) {
			return send(process);
I
isidor 已提交
1194
		}
I
isidor 已提交
1195

1196
		return TPromise.join(this.model.getProcesses().map(p => send(p))).then(() => void 0);
E
Erich Gamma 已提交
1197 1198 1199
	}

	private onFileChanges(fileChangesEvent: FileChangesEvent): void {
1200
		this.model.removeBreakpoints(this.model.getBreakpoints().filter(bp =>
1201
			fileChangesEvent.contains(bp.uri, FileChangeType.DELETED)));
1202 1203

		fileChangesEvent.getUpdated().forEach(event => {
I
isidor 已提交
1204 1205
			if (this.breakpointsToSendOnResourceSaved.has(event.resource.toString())) {
				this.breakpointsToSendOnResourceSaved.delete(event.resource.toString());
1206 1207
				this.sendBreakpoints(event.resource, true).done(null, errors.onUnexpectedError);
			}
1208 1209 1210
			if (event.resource.toString().indexOf('.vscode/launch.json') >= 0) {
				this.launchJsonChanged = true;
			}
1211
		});
E
Erich Gamma 已提交
1212 1213 1214
	}

	private store(): void {
1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247
		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 已提交
1248 1249 1250
	}

	public dispose(): void {
I
isidor 已提交
1251
		this.toDisposeOnSessionEnd.forEach(toDispose => lifecycle.dispose(toDispose));
I
isidor 已提交
1252
		this.toDispose = lifecycle.dispose(this.toDispose);
E
Erich Gamma 已提交
1253 1254
	}
}