debugService.ts 50.2 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 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';
J
Johannes Rieken 已提交
13
import { Action } from 'vs/base/common/actions';
I
isidor 已提交
14 15 16
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 已提交
17
import severity from 'vs/base/common/severity';
J
Johannes Rieken 已提交
18
import { TPromise } from 'vs/base/common/winjs.base';
I
isidor 已提交
19
import * as aria from 'vs/base/browser/ui/aria/aria';
20
import { Client as TelemetryClient } from 'vs/base/parts/ipc/node/ipc.cp';
J
Johannes Rieken 已提交
21 22 23 24 25
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';
26
import { FileChangesEvent, FileChangeType, IFileService } from 'vs/platform/files/common/files';
J
Johannes Rieken 已提交
27
import { IMessageService, CloseAction } from 'vs/platform/message/common/message';
28
import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows';
J
Johannes Rieken 已提交
29 30 31
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService';
import { TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc';
32
import { ICommandService } from 'vs/platform/commands/common/commands';
J
Johannes Rieken 已提交
33
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, OutputNameValueElement, 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 49 50
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
51
import { ILogEntry, 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';
E
Erich Gamma 已提交
53

I
isidor 已提交
54 55 56 57 58
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 已提交
59

60 61 62 63 64
interface StartSessionResult {
	status: 'ok' | 'initialConfiguration' | 'saveConfiguration';
	content?: string;
};

65
export class DebugService implements debug.IDebugService {
66
	public _serviceBrand: any;
E
Erich Gamma 已提交
67

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

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

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

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

130
		this.registerListeners(lifecycleService);
E
Erich Gamma 已提交
131 132
	}

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

	private onBroadcast(broadcast: IBroadcast): void {
141

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

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

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

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

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

			let args: any[] = [];
			try {
				let parsed = JSON.parse(extensionOutput.arguments);
				args.push(...Object.getOwnPropertyNames(parsed).map(o => parsed[o]));
			} catch (error) {
				args.push(extensionOutput.arguments);
			}

I
isidor 已提交
177
			// add output for each argument logged
178 179 180 181
			let simpleVals: any[] = [];
			for (let i = 0; i < args.length; i++) {
				let a = args[i];

I
isidor 已提交
182
				// undefined gets printed as 'undefined'
183 184 185 186
				if (typeof a === 'undefined') {
					simpleVals.push('undefined');
				}

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

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

I
isidor 已提交
195
					// flush any existing simple values logged
196
					if (simpleVals.length) {
197
						this.logToRepl(simpleVals.join(' '), sev);
198 199 200
						simpleVals = [];
					}

I
isidor 已提交
201
					// show object
202
					this.logToRepl(new OutputNameValueElement((<any>a).prototype, a, nls.localize('snapshotObj', "Only primitive values are shown for this object.")), sev);
203 204
				}

I
isidor 已提交
205 206
				// string: watch out for % replacement directive
				// string substitution and formatting @ https://developer.chrome.com/devtools/docs/console
207 208 209 210 211 212
				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 已提交
213
							buf += !isUndefinedOrNull(args[i]) ? args[i] : ''; // replace
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
							j++; // read over directive
						} else {
							buf += a[j];
						}
					}

					simpleVals.push(buf);
				}

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

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

I
isidor 已提交
237 238 239 240 241 242
	private tryToAutoFocusStackFrame(thread: debug.IThread): TPromise<any> {
		const callStack = thread.getCallStack();
		if (!callStack.length || this.viewModel.focusedStackFrame) {
			return TPromise.as(null);
		}

243
		// focus first stack frame from top that has source location if no other stack frame is focused
I
isidor 已提交
244 245 246 247 248 249
		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 已提交
250
		if (thread.stoppedDetails) {
251
			this.windowService.focusWindow();
I
isidor 已提交
252 253
			aria.alert(nls.localize('debuggingPaused', "Debugging paused, reason {0}, {1} {2}", thread.stoppedDetails.reason, stackFrameToFocus.source ? stackFrameToFocus.source.name : '', stackFrameToFocus.range.startLineNumber));
		}
I
isidor 已提交
254 255 256 257

		return stackFrameToFocus.openInEditor(this.editorService);
	}

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

I
isidor 已提交
260
		this.toDisposeOnSessionEnd.get(session.getId()).push(session);
261

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

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

I
isidor 已提交
280
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidStop(event => {
I
isidor 已提交
281
			this.updateStateAndEmit(session.getId(), debug.State.Stopped);
I
isidor 已提交
282 283
			this.fetchThreads(session, event.body).done(() => {
				const thread = process && process.getThread(event.body.threadId);
I
isidor 已提交
284
				if (thread) {
285 286 287
					// 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(() => {
I
isidor 已提交
288
						return this.tryToAutoFocusStackFrame(thread);
I
isidor 已提交
289 290
					});
				}
E
Erich Gamma 已提交
291 292 293
			}, errors.onUnexpectedError);
		}));

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

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

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

I
isidor 已提交
322
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidOutput(event => {
323 324 325 326
			if (!event.body) {
				return;
			}

327
			const outputSeverity = event.body.category === 'stderr' ? severity.Error : event.body.category === 'console' ? severity.Warning : severity.Info;
328
			if (event.body.category === 'telemetry') {
329
				// only log telemetry events from debug adapter if the adapter provided the telemetry key
I
isidor 已提交
330
				// and the user opted in telemetry
331 332
				if (this.customTelemetryService && this.telemetryService.isOptedIn) {
					this.customTelemetryService.publicLog(event.body.output, event.body.data);
333
				}
334 335 336 337 338 339
			} else if (event.body.variablesReference) {
				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;
340
						this.logToRepl(child, outputSeverity);
I
isidor 已提交
341
					});
342 343
				});
			} else if (typeof event.body.output === 'string') {
344
				this.logToRepl(event.body.output, outputSeverity);
E
Erich Gamma 已提交
345 346 347
			}
		}));

I
isidor 已提交
348
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidBreakpoint(event => {
I
isidor 已提交
349
			const id = event.body && event.body.breakpoint ? event.body.breakpoint.id : undefined;
350
			const breakpoint = this.model.getBreakpoints().filter(bp => bp.idFromAdapter === id).pop();
I
isidor 已提交
351
			if (breakpoint) {
I
isidor 已提交
352 353 354
				if (!breakpoint.column) {
					event.body.breakpoint.column = undefined;
				}
I
isidor 已提交
355 356
				this.model.updateBreakpoints({ [breakpoint.getId()]: event.body.breakpoint });
			} else {
357
				const functionBreakpoint = this.model.getFunctionBreakpoints().filter(bp => bp.idFromAdapter === id).pop();
I
isidor 已提交
358 359 360 361 362 363
				if (functionBreakpoint) {
					this.model.updateFunctionBreakpoints({ [functionBreakpoint.getId()]: event.body.breakpoint });
				}
			}
		}));

I
isidor 已提交
364
		this.toDisposeOnSessionEnd.get(session.getId()).push(session.onDidExitAdapter(event => {
365
			// 'Run without debugging' mode VSCode must terminate the extension host. More details: #3905
I
isidor 已提交
366
			const process = this.viewModel.focusedProcess;
I
isidor 已提交
367
			if (process && session && process.getId() === session.getId() && strings.equalsIgnoreCase(process.configuration.type, 'extensionhost') && this.sessionStates.get(session.getId()) === debug.State.Running &&
B
Benjamin Pasero 已提交
368
				process && this.contextService.hasWorkspace() && process.configuration.noDebug) {
369 370
				this.broadcastService.broadcast({
					channel: EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL,
371
					payload: [process.session.root.fsPath]
372
				});
373
			}
374
			if (session && session.getId() === event.sessionId) {
375
				this.onSessionEnd(session);
376
			}
E
Erich Gamma 已提交
377
		}));
378 379 380 381

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

I
isidor 已提交
384
	private fetchThreads(session: RawDebugSession, stoppedDetails?: debug.IRawStoppedDetails): TPromise<any> {
385 386 387 388 389 390
		return session.threads().then(response => {
			if (response && response.body && response.body.threads) {
				response.body.threads.forEach(thread =>
					this.model.rawUpdate({
						sessionId: session.getId(),
						threadId: thread.id,
I
isidor 已提交
391 392 393
						thread,
						stoppedDetails,
						allThreadsStopped: stoppedDetails ? stoppedDetails.allThreadsStopped : undefined
394 395 396
					}));
			}
		});
E
Erich Gamma 已提交
397 398
	}

399 400
	private loadBreakpoints(): Breakpoint[] {
		let result: Breakpoint[];
E
Erich Gamma 已提交
401
		try {
402
			result = JSON.parse(this.storageService.get(DEBUG_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((breakpoint: any) => {
403
				return new Breakpoint(uri.parse(breakpoint.uri.external || breakpoint.source.uri.external), breakpoint.lineNumber, breakpoint.column, breakpoint.enabled, breakpoint.condition, breakpoint.hitCondition);
E
Erich Gamma 已提交
404
			});
405 406 407
		} catch (e) { }

		return result || [];
E
Erich Gamma 已提交
408 409
	}

410 411
	private loadFunctionBreakpoints(): FunctionBreakpoint[] {
		let result: FunctionBreakpoint[];
I
isidor 已提交
412
		try {
413
			result = JSON.parse(this.storageService.get(DEBUG_FUNCTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((fb: any) => {
I
isidor 已提交
414
				return new FunctionBreakpoint(fb.name, fb.enabled, fb.hitCondition);
I
isidor 已提交
415
			});
416 417 418
		} catch (e) { }

		return result || [];
I
isidor 已提交
419 420
	}

421 422
	private loadExceptionBreakpoints(): ExceptionBreakpoint[] {
		let result: ExceptionBreakpoint[];
E
Erich Gamma 已提交
423 424
		try {
			result = JSON.parse(this.storageService.get(DEBUG_EXCEPTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((exBreakpoint: any) => {
I
isidor 已提交
425
				return new ExceptionBreakpoint(exBreakpoint.filter || exBreakpoint.name, exBreakpoint.label, exBreakpoint.enabled);
E
Erich Gamma 已提交
426
			});
427
		} catch (e) { }
E
Erich Gamma 已提交
428

429
		return result || [];
E
Erich Gamma 已提交
430 431
	}

I
isidor 已提交
432 433
	private loadWatchExpressions(): Expression[] {
		let result: Expression[];
E
Erich Gamma 已提交
434
		try {
J
Johannes Rieken 已提交
435
			result = JSON.parse(this.storageService.get(DEBUG_WATCH_EXPRESSIONS_KEY, StorageScope.WORKSPACE, '[]')).map((watchStoredData: { name: string, id: string }) => {
436
				return new Expression(watchStoredData.name, watchStoredData.id);
E
Erich Gamma 已提交
437
			});
438 439 440
		} catch (e) { }

		return result || [];
E
Erich Gamma 已提交
441 442
	}

I
isidor 已提交
443
	public get state(): debug.State {
444 445 446 447
		const focusedThread = this.viewModel.focusedThread;
		if (focusedThread && focusedThread.stopped) {
			return debug.State.Stopped;
		}
I
isidor 已提交
448
		const focusedProcess = this.viewModel.focusedProcess;
I
isidor 已提交
449
		if (focusedProcess && this.sessionStates.has(focusedProcess.getId())) {
I
isidor 已提交
450
			return this.sessionStates.get(focusedProcess.getId());
I
isidor 已提交
451
		}
I
isidor 已提交
452 453
		if (this.sessionStates.size > 0) {
			return debug.State.Initializing;
I
isidor 已提交
454 455 456
		}

		return debug.State.Inactive;
E
Erich Gamma 已提交
457 458
	}

459
	public get onDidChangeState(): Event<debug.State> {
460 461 462
		return this._onDidChangeState.event;
	}

463 464 465 466
	public get onDidNewProcess(): Event<debug.IProcess> {
		return this._onDidNewProcess.event;
	}

I
isidor 已提交
467 468 469 470
	public get onDidEndProcess(): Event<debug.IProcess> {
		return this._onDidEndProcess.event;
	}

471
	public get onDidCustomEvent(): Event<debug.DebugEvent> {
472 473 474
		return this._onDidCustomEvent.event;
	}

I
isidor 已提交
475 476 477 478 479 480 481
	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 已提交
482
		}
I
isidor 已提交
483 484

		const state = this.state;
I
isidor 已提交
485 486 487 488
		const stateLabel = debug.State[state];
		if (stateLabel) {
			this.debugState.set(stateLabel.toLowerCase());
		}
I
isidor 已提交
489
		this._onDidChangeState.fire(state);
E
Erich Gamma 已提交
490 491
	}

492
	public focusStackFrameAndEvaluate(stackFrame: debug.IStackFrame, process?: debug.IProcess, explicit?: boolean): TPromise<void> {
I
isidor 已提交
493
		if (!process) {
494 495 496 497 498 499 500
			const processes = this.model.getProcesses();
			process = stackFrame ? stackFrame.thread.process : processes.length ? processes[0] : null;
		}
		if (!stackFrame) {
			const threads = process ? process.getAllThreads() : null;
			const callStack = threads && threads.length ? threads[0].getCallStack() : null;
			stackFrame = callStack && callStack.length ? callStack[0] : null;
I
isidor 已提交
501
		}
I
isidor 已提交
502

503
		this.viewModel.setFocusedStackFrame(stackFrame, process, explicit);
I
isidor 已提交
504
		this.updateStateAndEmit();
505

506
		return this.model.evaluateWatchExpressions(process, stackFrame);
E
Erich Gamma 已提交
507 508
	}

I
isidor 已提交
509
	public enableOrDisableBreakpoints(enable: boolean, breakpoint?: debug.IEnablement): TPromise<void> {
510 511
		if (breakpoint) {
			this.model.setEnablement(breakpoint, enable);
I
isidor 已提交
512
			if (breakpoint instanceof Breakpoint) {
513
				return this.sendBreakpoints(breakpoint.uri);
I
isidor 已提交
514
			} else if (breakpoint instanceof FunctionBreakpoint) {
515 516
				return this.sendFunctionBreakpoints();
			}
E
Erich Gamma 已提交
517

518
			return this.sendExceptionBreakpoints();
E
Erich Gamma 已提交
519 520
		}

521 522
		this.model.enableOrDisableAllBreakpoints(enable);
		return this.sendAllBreakpoints();
E
Erich Gamma 已提交
523 524
	}

525 526 527
	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)));
528

529
		return this.sendBreakpoints(uri);
530 531
	}

532 533
	public removeBreakpoints(id?: string): TPromise<any> {
		const toRemove = this.model.getBreakpoints().filter(bp => !id || bp.getId() === id);
534 535
		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 已提交
536

537
		this.model.removeBreakpoints(toRemove);
I
isidor 已提交
538
		return TPromise.join(urisToClear.map(uri => this.sendBreakpoints(uri)));
E
Erich Gamma 已提交
539 540
	}

541 542
	public setBreakpointsActivated(activated: boolean): TPromise<void> {
		this.model.setBreakpointsActivated(activated);
E
Erich Gamma 已提交
543 544 545
		return this.sendAllBreakpoints();
	}

I
isidor 已提交
546 547
	public addFunctionBreakpoint(): void {
		this.model.addFunctionBreakpoint('');
548 549
	}

I
isidor 已提交
550
	public renameFunctionBreakpoint(id: string, newFunctionName: string): TPromise<void> {
I
isidor 已提交
551
		this.model.updateFunctionBreakpoints({ [id]: { name: newFunctionName } });
I
isidor 已提交
552
		return this.sendFunctionBreakpoints();
I
isidor 已提交
553 554
	}

I
isidor 已提交
555
	public removeFunctionBreakpoints(id?: string): TPromise<void> {
556
		this.model.removeFunctionBreakpoints(id);
I
isidor 已提交
557
		return this.sendFunctionBreakpoints();
I
isidor 已提交
558 559
	}

I
isidor 已提交
560
	public addReplExpression(name: string): TPromise<void> {
P
Pierson Lee 已提交
561
		this.telemetryService.publicLog('debugService/addReplExpression');
562
		return this.model.addReplExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, name)
563
			// Evaluate all watch expressions and fetch variables again since repl evaluation might have changed some.
564
			.then(() => this.focusStackFrameAndEvaluate(this.viewModel.focusedStackFrame, this.viewModel.focusedProcess));
E
Erich Gamma 已提交
565 566
	}

567 568
	public removeReplExpressions(): void {
		this.model.removeReplExpressions();
E
Erich Gamma 已提交
569 570
	}

571 572 573 574 575 576 577
	public logToRepl(value: string | debug.IExpression, sev = severity.Info): void {
		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 {
			this.model.appendToRepl(value, sev);
		}
578 579
	}

I
isidor 已提交
580
	public addWatchExpression(name: string): TPromise<void> {
581
		return this.model.addWatchExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, name);
E
Erich Gamma 已提交
582 583
	}

I
isidor 已提交
584
	public renameWatchExpression(id: string, newName: string): TPromise<void> {
585
		return this.model.renameWatchExpression(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame, id, newName);
E
Erich Gamma 已提交
586 587
	}

I
isidor 已提交
588 589 590 591
	public moveWatchExpression(id: string, position: number): void {
		this.model.moveWatchExpression(id, position);
	}

592 593
	public removeWatchExpressions(id?: string): void {
		this.model.removeWatchExpressions(id);
E
Erich Gamma 已提交
594 595
	}

596 597 598 599
	public evaluateWatchExpressions(): TPromise<void> {
		return this.model.evaluateWatchExpressions(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame);
	}

600
	public startDebugging(root: uri, configOrName?: debug.IConfig | string, noDebug = false, topCompoundName?: string): TPromise<any> {
601

602
		// make sure to save all files and that the configuration is up to date
603
		return this.extensionService.activateByEvent('onDebug').then(() => this.textFileService.saveAll().then(() => this.configurationService.reloadConfiguration().then(() =>
604 605 606
			this.extensionService.onReady().then(() => {
				if (this.model.getProcesses().length === 0) {
					this.removeReplExpressions();
607
					this.allProcesses.clear();
608
				}
609
				this.launchJsonChanged = false;
610
				const manager = this.getConfigurationManager();
611
				const launch = root ? manager.getLaunches().filter(l => l.workspaceUri.toString() === root.toString()).pop() : undefined;
612

613
				let config: debug.IConfig, compound: debug.ICompound;
614
				if (!configOrName) {
615
					configOrName = this.configurationManager.selectedName;
616
				}
617 618 619
				if (typeof configOrName === 'string' && launch) {
					config = launch.getConfiguration(configOrName);
					compound = launch.getCompound(configOrName);
620
				} else if (typeof configOrName !== 'string') {
621 622
					config = configOrName;
				}
623
				if (launch) {
624 625
					// 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);
626
				}
627

628 629 630 631 632
				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.")));
					}
633

634
					return TPromise.join(compound.configurations.map(name => name !== compound.name ? this.startDebugging(root, name, noDebug, topCompoundName || compound.name) : TPromise.as(null)));
635
				}
636 637
				if (configOrName && !config) {
					return TPromise.wrapError(new Error(nls.localize('configMissing', "Configuration '{0}' is missing in 'launch.json'.", configOrName)));
638 639
				}

R
Ron Buckton 已提交
640
				return manager.getStartSessionCommand(config ? config.type : undefined).then<any>(commandAndType => {
641

642 643 644 645 646 647 648 649
					// 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
650
						config = <debug.IConfig>{};
651 652 653 654
					}

					if (!type && commandAndType && commandAndType.type) {
						type = commandAndType.type;
655
					}
656

657 658
					if (noDebug) {
						config.noDebug = true;
659 660
					}

661
					return this.configurationManager.resolveDebugConfiguration(launch ? launch.workspaceUri : undefined, type, config).then(config => {
662

663 664
						// a falsy config indicates an aborted launch
						if (config) {
665

666 667 668 669 670 671 672
							// deprecated code: use DebugConfigurationProvider instead of startSessionCommand
							if (commandAndType && commandAndType.command) {
								return this.commandService.executeCommand(commandAndType.command, config, launch ? launch.workspaceUri : undefined).then((result: StartSessionResult) => {
									if (launch) {
										if (result && result.status === 'initialConfiguration') {
											return launch.openConfigFile(false, commandAndType.type);
										}
673

674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689
										if (result && result.status === 'saveConfiguration') {
											return this.fileService.updateContent(launch.uri, result.content).then(() => launch.openConfigFile(false));
										}
									}
									return <TPromise>undefined;
								});
							}
							// end of deprecation

							if (config.type) {
								return this.createProcess(root, config);
							}

							if (launch && commandAndType) {
								return launch.openConfigFile(false, commandAndType.type);
							}
690 691 692
						}
						return undefined;
					});
693 694
				});
			})
B
Benjamin Pasero 已提交
695
		)));
696
	}
697

698 699 700 701 702 703 704 705 706
	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;
	}

707
	public createProcess(root: uri, config: debug.IConfig): TPromise<debug.IProcess> {
708
		return this.textFileService.saveAll().then(() =>
709
			(this.configurationManager.selectedLaunch ? this.configurationManager.selectedLaunch.resolveConfiguration(config) : TPromise.as(config)).then(resolvedConfig => {
710 711 712 713
				if (!resolvedConfig) {
					// User canceled resolving of interactive variables, silently return
					return undefined;
				}
I
isidor 已提交
714

715 716 717 718 719
				if (!this.configurationManager.getAdapter(resolvedConfig.type)) {
					const message = resolvedConfig.type ? nls.localize('debugTypeNotSupported', "Configured debug type '{0}' is not supported.", resolvedConfig.type) :
						nls.localize('debugTypeMissing', "Missing property 'type' for the chosen launch configuration.");
					return TPromise.wrapError(errors.create(message, { actions: [this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL), CloseAction] }));
				}
J
Johannes Rieken 已提交
720

721 722 723 724 725
				return this.runPreLaunchTask(resolvedConfig.preLaunchTask).then((taskSummary: ITaskSummary) => {
					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)) {
726
						return this.doCreateProcess(root, resolvedConfig);
727
					}
I
isidor 已提交
728

729 730 731 732 733 734 735
					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();
736
								return this.doCreateProcess(root, resolvedConfig);
737 738 739 740 741 742 743 744 745
							}),
							this.instantiationService.createInstance(ToggleMarkersPanelAction, ToggleMarkersPanelAction.ID, ToggleMarkersPanelAction.LABEL),
							CloseAction
						]
					});
					return undefined;
				}, (err: TaskError) => {
					this.messageService.show(err.severity, {
						message: err.message,
746 747 748 749 750
						actions: [
							this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL),
							this.taskService.configureAction(),
							CloseAction
						]
751
					});
752 753
				});
			}, err => {
B
Benjamin Pasero 已提交
754
				if (!this.contextService.hasWorkspace()) {
R
Ron Buckton 已提交
755 756
					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;
757
				}
E
Erich Gamma 已提交
758

759
				return this.configurationManager.selectedLaunch.openConfigFile(false).then(openend => {
760 761 762
					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 已提交
763
					return undefined;
764 765 766 767
				});
			})
		);
	}
768

769
	private doCreateProcess(root: uri, configuration: debug.IConfig, sessionId = generateUuid()): TPromise<debug.IProcess> {
770
		configuration.__sessionId = sessionId;
I
isidor 已提交
771
		this.updateStateAndEmit(sessionId, debug.State.Initializing);
772
		this.inDebugMode.set(true);
773

J
Joao Moreno 已提交
774
		return this.telemetryService.getTelemetryInfo().then(info => {
775
			const telemetryInfo: { [key: string]: string } = Object.create(null);
776 777
			telemetryInfo['common.vscodemachineid'] = info.machineId;
			telemetryInfo['common.vscodesessionid'] = info.sessionId;
778 779
			return telemetryInfo;
		}).then(data => {
780 781 782
			const adapter = this.configurationManager.getAdapter(configuration.type);
			const { aiKey, type } = adapter;
			const publisher = adapter.extensionDescription.publisher;
783
			this.customTelemetryService = null;
784
			let client: TelemetryClient;
785 786

			if (aiKey) {
787
				client = new TelemetryClient(
788 789 790 791
					uri.parse(require.toUrl('bootstrap')).fsPath,
					{
						serverName: 'Debug Telemetry',
						timeout: 1000 * 60 * 5,
J
Johannes Rieken 已提交
792
						args: [`${publisher}.${type}`, JSON.stringify(data), aiKey],
793
						env: {
B
Benjamin Pasero 已提交
794
							ELECTRON_RUN_AS_NODE: 1,
795 796 797
							PIPE_LOGGING: 'true',
							AMD_ENTRYPOINT: 'vs/workbench/parts/debug/node/telemetryApp'
						}
798
					}
799
				);
800

801 802
				const channel = client.getChannel('telemetryAppender');
				const appender = new TelemetryAppenderClient(channel);
803

804
				this.customTelemetryService = new TelemetryService({ appender }, this.configurationService);
805 806
			}

807
			const session = this.instantiationService.createInstance(RawDebugSession, sessionId, configuration.debugServer, adapter, this.customTelemetryService, root);
808
			const process = this.model.addProcess(configuration, session);
809
			this.allProcesses.set(process.getId(), process);
810

I
isidor 已提交
811
			this.toDisposeOnSessionEnd.set(session.getId(), []);
812
			if (client) {
I
isidor 已提交
813
				this.toDisposeOnSessionEnd.get(session.getId()).push(client);
814
			}
I
isidor 已提交
815
			this.registerSessionListeners(process, session);
J
Joao Moreno 已提交
816

817
			return session.initialize({
818
				clientID: 'vscode',
J
Joao Moreno 已提交
819 820 821
				adapterID: configuration.type,
				pathFormat: 'path',
				linesStartAt1: true,
822
				columnsStartAt1: true,
I
isidor 已提交
823
				supportsVariableType: true, // #8858
824 825
				supportsVariablePaging: true, // #9537
				supportsRunInTerminalRequest: true // #10574
J
Joao Moreno 已提交
826
			}).then((result: DebugProtocol.InitializeResponse) => {
827
				this.model.setExceptionBreakpoints(session.capabilities.exceptionBreakpointFilters);
828
				return configuration.request === 'attach' ? session.attach(configuration) : session.launch(configuration);
J
Joao Moreno 已提交
829
			}).then((result: DebugProtocol.Response) => {
830
				if (session.disconnected) {
831 832
					return TPromise.as(null);
				}
833
				this._onDidNewProcess.fire(process);
I
isidor 已提交
834
				this.focusStackFrameAndEvaluate(null, process);
835 836 837

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

B
Benjamin Pasero 已提交
841
				if (!this.viewModel.changedWorkbenchViewState && (this.partService.isVisible(Parts.SIDEBAR_PART) || !this.contextService.hasWorkspace())) {
J
Joao Moreno 已提交
842 843 844 845
					// 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);
				}
846

J
Joao Moreno 已提交
847
				this.extensionService.activateByEvent(`onDebug:${configuration.type}`).done(null, errors.onUnexpectedError);
I
isidor 已提交
848
				this.debugType.set(configuration.type);
849 850 851
				if (this.model.getProcesses().length > 1) {
					this.viewModel.setMultiProcessView(true);
				}
I
isidor 已提交
852
				this.updateStateAndEmit(session.getId(), debug.State.Running);
J
Joao Moreno 已提交
853

854
				return this.telemetryService.publicLog('debugSessionStart', {
J
Joao Moreno 已提交
855 856 857 858
					type: configuration.type,
					breakpointCount: this.model.getBreakpoints().length,
					exceptionBreakpoints: this.model.getExceptionBreakpoints(),
					watchExpressionsCount: this.model.getWatchExpressions().length,
859
					extensionName: `${adapter.extensionDescription.publisher}.${adapter.extensionDescription.name}`,
860
					isBuiltin: adapter.extensionDescription.isBuiltin,
I
isidor 已提交
861
					launchJsonExists: this.contextService.hasWorkspace() && !!this.configurationService.getConfiguration<debug.IGlobalConfig>('launch', { resource: root })
I
isidor 已提交
862 863
				});
			}).then(() => process, (error: any) => {
864 865 866 867 868
				if (error instanceof Error && error.message === 'Canceled') {
					// Do not show 'canceled' error messages to the user #7906
					return TPromise.as(null);
				}

869 870
				const errorMessage = error instanceof Error ? error.message : error;
				this.telemetryService.publicLog('debugMisconfiguration', { type: configuration ? configuration.type : undefined, error: errorMessage });
I
isidor 已提交
871
				this.updateStateAndEmit(session.getId(), debug.State.Inactive);
872 873
				if (!session.disconnected) {
					session.disconnect().done(null, errors.onUnexpectedError);
J
Joao Moreno 已提交
874
				}
I
isidor 已提交
875 876 877
				if (process) {
					this.model.removeProcess(process.getId());
				}
J
Joao Moreno 已提交
878 879 880
				// 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);
881 882 883
				}
				if (this.model.getReplElements().length === 0) {
					this.inDebugMode.reset();
J
Joao Moreno 已提交
884
				}
I
isidor 已提交
885

J
Joao Moreno 已提交
886 887
				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];
888
				this.messageService.show(severity.Error, { message: errorMessage, actions });
889
				return undefined;
J
Joao Moreno 已提交
890
			});
E
Erich Gamma 已提交
891 892 893
		});
	}

I
isidor 已提交
894
	private runPreLaunchTask(taskName: string): TPromise<ITaskSummary> {
895
		if (!taskName) {
I
isidor 已提交
896
			return TPromise.as(null);
E
Erich Gamma 已提交
897 898
		}

899
		// run a task before starting a debug session
900 901
		return this.taskService.getTask(taskName).then(task => {
			if (!task) {
902
				return TPromise.wrapError(errors.create(nls.localize('DebugTaskNotFound', "Could not find the preLaunchTask \'{0}\'.", taskName)));
E
Erich Gamma 已提交
903 904
			}

I
isidor 已提交
905 906 907 908 909
			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 已提交
910

I
isidor 已提交
911 912 913 914
				const taskPromise = this.taskService.run(task);
				if (task.isBackground) {
					return new TPromise((c, e) => this.taskService.addOneTimeListener(TaskServiceEvents.Inactive, () => c(null)));
				}
E
Erich Gamma 已提交
915

I
isidor 已提交
916
				return taskPromise;
E
Erich Gamma 已提交
917 918 919 920
			});
		});
	}

921 922
	public sourceIsNotAvailable(uri: uri): void {
		this.model.sourceIsNotAvailable(uri);
I
isidor 已提交
923 924
	}

925
	public restartProcess(process: debug.IProcess, restartData?: any): TPromise<any> {
926
		if (process.session.capabilities.supportsRestartRequest) {
927
			return this.textFileService.saveAll().then(() => process.session.custom('restart', null));
928
		}
I
isidor 已提交
929 930
		const focusedProcess = this.viewModel.focusedProcess;
		const preserveFocus = focusedProcess && process.getId() === focusedProcess.getId();
931

932 933
		return process.session.disconnect(true).then(() => {
			if (strings.equalsIgnoreCase(process.configuration.type, 'extensionHost')) {
934
				return this.broadcastService.broadcast({
935 936 937 938 939 940
					channel: EXTENSION_RELOAD_BROADCAST_CHANNEL,
					payload: [process.session.root.fsPath]
				});
			}

			return new TPromise<void>((c, e) => {
E
Erich Gamma 已提交
941
				setTimeout(() => {
942 943
					// Read the configuration again if a launch.json has been changed, if not just use the inmemory configuration
					let config = process.configuration;
944
					if (this.launchJsonChanged && this.configurationManager.selectedLaunch) {
945
						this.launchJsonChanged = false;
946
						config = this.configurationManager.selectedLaunch.getConfiguration(process.configuration.name) || config;
I
isidor 已提交
947 948 949
						// Take the type from the process since the debug extension might overwrite it #21316
						config.type = process.configuration.type;
						config.noDebug = process.configuration.noDebug;
950
					}
I
isidor 已提交
951
					config.__restart = restartData;
952
					this.createProcess(process.session.root, config).then(() => c(null), err => e(err));
E
Erich Gamma 已提交
953
				}, 300);
954 955
			});
		}).then(() => {
I
isidor 已提交
956 957
			if (preserveFocus) {
				// Restart should preserve the focused process
958
				const restartedProcess = this.model.getProcesses().filter(p => p.configuration.name === process.configuration.name).pop();
I
isidor 已提交
959 960 961
				if (restartedProcess && restartedProcess !== this.viewModel.focusedProcess) {
					this.focusStackFrameAndEvaluate(null, restartedProcess);
				}
962 963
			}
		});
E
Erich Gamma 已提交
964 965
	}

966 967 968 969 970 971 972 973 974 975 976 977 978 979 980
	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 已提交
981
	private onSessionEnd(session: RawDebugSession): void {
I
isidor 已提交
982
		const bpsExist = this.model.getBreakpoints().length > 0;
983
		const process = this.model.getProcesses().filter(p => p.getId() === session.getId()).pop();
I
isidor 已提交
984
		this.telemetryService.publicLog('debugSessionStop', {
I
isidor 已提交
985
			type: process && process.configuration.type,
I
isidor 已提交
986 987 988 989 990
			success: session.emittedStopped || !bpsExist,
			sessionLengthInSeconds: session.getLengthInSeconds(),
			breakpointCount: this.model.getBreakpoints().length,
			watchExpressionsCount: this.model.getWatchExpressions().length
		});
991

I
isidor 已提交
992
		this.model.removeProcess(session.getId());
993
		if (process && process.state !== debug.ProcessState.INACTIVE) {
I
isidor 已提交
994 995
			this._onDidEndProcess.fire(process);
		}
I
isidor 已提交
996

I
isidor 已提交
997
		this.toDisposeOnSessionEnd.set(session.getId(), lifecycle.dispose(this.toDisposeOnSessionEnd.get(session.getId())));
I
isidor 已提交
998 999
		const focusedProcess = this.viewModel.focusedProcess;
		if (focusedProcess && focusedProcess.getId() === session.getId()) {
1000 1001
			this.focusStackFrameAndEvaluate(null).done(null, errors.onUnexpectedError);
		}
I
isidor 已提交
1002
		this.updateStateAndEmit(session.getId(), debug.State.Inactive);
1003

1004 1005
		if (this.model.getProcesses().length === 0) {
			// set breakpoints back to unverified since the session ended.
1006
			const data: { [id: string]: { line: number, verified: boolean, column: number, endLine: number, endColumn: number } } = {};
1007
			this.model.getBreakpoints().forEach(bp => {
1008
				data[bp.getId()] = { line: bp.lineNumber, verified: false, column: bp.column, endLine: bp.endLineNumber, endColumn: bp.endColumn };
1009 1010
			});
			this.model.updateBreakpoints(data);
1011

1012
			this.inDebugMode.reset();
I
isidor 已提交
1013
			this.debugType.reset();
1014
			this.viewModel.setMultiProcessView(false);
I
isidor 已提交
1015

1016
			if (this.partService.isVisible(Parts.SIDEBAR_PART) && this.configurationService.getConfiguration<debug.IDebugConfiguration>('debug').openExplorerOnEnd) {
1017 1018
				this.viewletService.openViewlet(EXPLORER_VIEWLET_ID).done(null, errors.onUnexpectedError);
			}
I
isidor 已提交
1019
		}
E
Erich Gamma 已提交
1020 1021 1022 1023 1024 1025 1026 1027 1028 1029
	}

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

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

1030 1031
	public getConfigurationManager(): debug.IConfigurationManager {
		return this.configurationManager;
1032 1033
	}

1034 1035 1036
	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 已提交
1037
			// send exception breakpoints at the end since some debug adapters rely on the order
1038
			.then(() => this.sendExceptionBreakpoints(process));
E
Erich Gamma 已提交
1039 1040
	}

1041 1042 1043 1044
	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 已提交
1045 1046 1047 1048 1049
			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 已提交
1050
				this.breakpointsToSendOnResourceSaved.add(modelUri.toString());
I
isidor 已提交
1051 1052
				return TPromise.as(null);
			}
1053

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

1056
			const source = process.sources.get(modelUri.toString());
I
isidor 已提交
1057
			const rawSource = source ? source.raw : { path: paths.normalize(modelUri.fsPath, true), name: paths.basename(modelUri.fsPath) };
I
isidor 已提交
1058 1059 1060

			return session.setBreakpoints({
				source: rawSource,
1061
				lines: breakpointsToSend.map(bp => bp.lineNumber),
1062
				breakpoints: breakpointsToSend.map(bp => ({ line: bp.lineNumber, column: bp.column, condition: bp.condition, hitCondition: bp.hitCondition })),
I
isidor 已提交
1063 1064 1065 1066 1067
				sourceModified
			}).then(response => {
				if (!response || !response.body) {
					return;
				}
1068

1069
				const data: { [id: string]: DebugProtocol.Breakpoint } = {};
I
isidor 已提交
1070 1071
				for (let i = 0; i < breakpointsToSend.length; i++) {
					data[breakpointsToSend[i].getId()] = response.body.breakpoints[i];
1072 1073 1074 1075
					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 已提交
1076
				}
1077

I
isidor 已提交
1078 1079 1080
				this.model.updateBreakpoints(data);
			});
		};
1081

1082
		return this.sendToOneOrAllProcesses(targetProcess, sendBreakpointsToProcess);
E
Erich Gamma 已提交
1083 1084
	}

1085 1086 1087
	private sendFunctionBreakpoints(targetProcess?: debug.IProcess): TPromise<void> {
		const sendFunctionBreakpointsToProcess = (process: debug.IProcess): TPromise<void> => {
			const session = <RawDebugSession>process.session;
1088
			if (!session.readyForBreakpoints || !session.capabilities.supportsFunctionBreakpoints) {
I
isidor 已提交
1089
				return TPromise.as(null);
1090 1091
			}

I
isidor 已提交
1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106
			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);
			});
		};

1107
		return this.sendToOneOrAllProcesses(targetProcess, sendFunctionBreakpointsToProcess);
I
isidor 已提交
1108 1109
	}

1110 1111 1112 1113
	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 已提交
1114
				return TPromise.as(null);
I
isidor 已提交
1115 1116
			}

I
isidor 已提交
1117 1118 1119 1120
			const enabledExceptionBps = this.model.getExceptionBreakpoints().filter(exb => exb.enabled);
			return session.setExceptionBreakpoints({ filters: enabledExceptionBps.map(exb => exb.filter) });
		};

1121
		return this.sendToOneOrAllProcesses(targetProcess, sendExceptionBreakpointsToProcess);
I
isidor 已提交
1122 1123
	}

1124 1125 1126
	private sendToOneOrAllProcesses(process: debug.IProcess, send: (process: debug.IProcess) => TPromise<void>): TPromise<void> {
		if (process) {
			return send(process);
I
isidor 已提交
1127
		}
I
isidor 已提交
1128

1129
		return TPromise.join(this.model.getProcesses().map(p => send(p))).then(() => void 0);
E
Erich Gamma 已提交
1130 1131 1132
	}

	private onFileChanges(fileChangesEvent: FileChangesEvent): void {
1133
		this.model.removeBreakpoints(this.model.getBreakpoints().filter(bp =>
1134
			fileChangesEvent.contains(bp.uri, FileChangeType.DELETED)));
1135 1136

		fileChangesEvent.getUpdated().forEach(event => {
I
isidor 已提交
1137 1138
			if (this.breakpointsToSendOnResourceSaved.has(event.resource.toString())) {
				this.breakpointsToSendOnResourceSaved.delete(event.resource.toString());
1139 1140
				this.sendBreakpoints(event.resource, true).done(null, errors.onUnexpectedError);
			}
1141 1142 1143
			if (event.resource.toString().indexOf('.vscode/launch.json') >= 0) {
				this.launchJsonChanged = true;
			}
1144
		});
E
Erich Gamma 已提交
1145 1146 1147
	}

	private store(): void {
1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180
		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 已提交
1181 1182 1183
	}

	public dispose(): void {
I
isidor 已提交
1184
		this.toDisposeOnSessionEnd.forEach(toDispose => lifecycle.dispose(toDispose));
I
isidor 已提交
1185
		this.toDispose = lifecycle.dispose(this.toDispose);
E
Erich Gamma 已提交
1186 1187
	}
}