diff --git a/src/vs/workbench/node/extensionHostProcess.ts b/src/vs/workbench/node/extensionHostProcess.ts index 0e68ea4e1a7..d44ebcc379c 100644 --- a/src/vs/workbench/node/extensionHostProcess.ts +++ b/src/vs/workbench/node/extensionHostProcess.ts @@ -120,6 +120,8 @@ function connectToRenderer(protocol: IMessagePassingProtocol): TPromise { // connect to main side return connectToRenderer(protocol); @@ -129,3 +131,18 @@ createExtHostProtocol().then(protocol => { onTerminate = () => extensionHostMain.terminate(); return extensionHostMain.start(); }).done(null, err => console.error(err)); + + + +function patchExecArgv() { + // when encountering the prevent-inspect flag we delete this + // and the prior flag + if (process.env.VSCODE_PREVENT_FOREIGN_INSPECT) { + for (let i = 0; i < process.execArgv.length; i++) { + if (process.execArgv[i].match(/--inspect-brk=\d+|--inspect=\d+/)) { + process.execArgv.splice(i, 1); + break; + } + } + } +} diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index 9dafd98bf5d..4e881ab0b2f 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -131,10 +131,9 @@ export class ExtensionHostProcessWorker { } if (!this._messageProtocol) { - this._messageProtocol = TPromise.join([this._tryListenOnPipe(), this._tryFindDebugPort()]).then((data: [string, number]) => { + this._messageProtocol = TPromise.join([this._tryListenOnPipe(), this._tryFindDebugPort()]).then(data => { const pipeName = data[0]; - // The port will be 0 if there's no need to debug or if a free port was not found - const port = data[1]; + const portData = data[1]; const opts = { env: objects.mixin(objects.deepClone(process.env), { @@ -152,12 +151,22 @@ export class ExtensionHostProcessWorker { // We detach because we have noticed that when the renderer exits, its child processes // (i.e. extension host) are taken down in a brutal fashion by the OS detached: !!isWindows, - execArgv: port - ? ['--nolazy', (this._isExtensionDevDebugBrk ? '--inspect-brk=' : '--inspect=') + port] - : undefined, + execArgv: undefined, silent: true }; + if (portData.actual) { + opts.execArgv = [ + '--nolazy', + (this._isExtensionDevDebugBrk ? '--inspect-brk=' : '--inspect=') + portData.actual + ]; + if (!portData.expected) { + // No one asked for 'inspect' or 'inspect-brk', only us. We add another + // option such that the extension host can manipulate the execArgv array + opts.env.VSCODE_PREVENT_FOREIGN_INSPECT = true; + } + } + const crashReporterOptions = this._crashReporterService.getChildProcessStartOptions('extensionHost'); if (crashReporterOptions) { opts.env.CRASH_REPORTER_START_OPTIONS = JSON.stringify(crashReporterOptions); @@ -208,16 +217,16 @@ export class ExtensionHostProcessWorker { this._extensionHostProcess.on('exit', (code: number, signal: string) => this._onExtHostProcessExit(code, signal)); // Notify debugger that we are ready to attach to the process if we run a development extension - if (this._isExtensionDevHost && port) { + if (this._isExtensionDevHost && portData.actual) { this._broadcastService.broadcast({ channel: EXTENSION_ATTACH_BROADCAST_CHANNEL, payload: { debugId: this._environmentService.debugExtensionHost.debugId, - port + port: portData.actual } }); } - this._inspectPort = port; + this._inspectPort = portData.actual; // Help in case we fail to start it let startupTimeoutHandle: number; @@ -261,26 +270,27 @@ export class ExtensionHostProcessWorker { /** * Find a free port if extension host debugging is enabled. */ - private _tryFindDebugPort(): TPromise { - const extensionHostPort = this._environmentService.debugExtensionHost.port; - if (typeof extensionHostPort !== 'number') { - return TPromise.wrap(0); + private _tryFindDebugPort(): TPromise<{ expected: number; actual: number }> { + let expected: number; + let startPort = 9333; + if (typeof this._environmentService.debugExtensionHost.port === 'number') { + startPort = expected = this._environmentService.debugExtensionHost.port; } - return new TPromise((c, e) => { - return findFreePort(extensionHostPort, 10 /* try 10 ports */, 5000 /* try up to 5 seconds */).then(port => { + return new TPromise((c, e) => { + return findFreePort(startPort, 10 /* try 10 ports */, 5000 /* try up to 5 seconds */).then(port => { if (!port) { console.warn('%c[Extension Host] %cCould not find a free port for debugging', 'color: blue', 'color: black'); - return c(void 0); - } - if (port !== extensionHostPort) { - console.warn(`%c[Extension Host] %cProvided debugging port ${extensionHostPort} is not free, using ${port} instead.`, 'color: blue', 'color: black'); - } - if (this._isExtensionDevDebugBrk) { - console.warn(`%c[Extension Host] %cSTOPPED on first line for debugging on port ${port}`, 'color: blue', 'color: black'); } else { - console.info(`%c[Extension Host] %cdebugger listening on port ${port}`, 'color: blue', 'color: black'); + if (expected && port !== expected) { + console.warn(`%c[Extension Host] %cProvided debugging port ${expected} is not free, using ${port} instead.`, 'color: blue', 'color: black'); + } + if (this._isExtensionDevDebugBrk) { + console.warn(`%c[Extension Host] %cSTOPPED on first line for debugging on port ${port}`, 'color: blue', 'color: black'); + } else { + console.info(`%c[Extension Host] %cdebugger listening on port ${port}`, 'color: blue', 'color: black'); + } } - return c(port); + return c({ expected, actual: port }); }); }); }