提交 ad8cf43b 编写于 作者: A Alex Dima

Merge remote-tracking branch 'origin/joh/prof-ext' into runtime-extensions

......@@ -18,6 +18,7 @@ import Event, { Emitter } from 'vs/base/common/event';
// --- service instances
class MockExtensionService implements IExtensionService {
public _serviceBrand: any;
private _onDidRegisterExtensions = new Emitter<IExtensionDescription[]>();
......@@ -58,6 +59,10 @@ class MockExtensionService implements IExtensionService {
public stopExtensionHost(): void {
throw new Error('Method not implemented.');
}
public getExtensionHostInformation(): any {
throw new Error('Method not implemented.');
}
}
const extensionService = new MockExtensionService();
......
......@@ -9,7 +9,7 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { CommandService } from 'vs/platform/commands/common/commandService';
import { IExtensionService, ExtensionPointContribution, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { IExtensionService, ExtensionPointContribution, IExtensionDescription, IExtensionHostInformation } from 'vs/platform/extensions/common/extensions';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { IExtensionPoint } from 'vs/platform/extensions/common/extensionsRegistry';
import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService';
......@@ -36,6 +36,9 @@ class SimpleExtensionService implements IExtensionService {
getExtensionsStatus() {
return undefined;
}
getExtensionHostInformation(): IExtensionHostInformation {
return undefined;
}
getExtensions(): TPromise<IExtensionDescription[]> {
return TPromise.wrap([]);
}
......
......@@ -155,16 +155,16 @@ export class EnvironmentService implements IEnvironmentService {
}
export function parseExtensionHostPort(args: ParsedArgs, isBuild: boolean): IExtensionHostDebugParams {
return parseDebugPort(args.debugPluginHost, args.debugBrkPluginHost, 5870, isBuild, args.debugId);
return parseDebugPort(args.debugPluginHost, args.debugBrkPluginHost, 5870, args.debugId);
}
export function parseSearchPort(args: ParsedArgs, isBuild: boolean): IDebugParams {
return parseDebugPort(args.debugSearch, args.debugBrkSearch, 5876, isBuild);
return parseDebugPort(args.debugSearch, args.debugBrkSearch, !isBuild ? 5876 : null);
}
export function parseDebugPort(debugArg: string, debugBrkArg: string, defaultBuildPort: number, isBuild: boolean, debugId?: string): IExtensionHostDebugParams {
export function parseDebugPort(debugArg: string, debugBrkArg: string, defaultBuildPort: number, debugId?: string): IExtensionHostDebugParams {
const portStr = debugBrkArg || debugArg;
const port = Number(portStr) || (!isBuild ? defaultBuildPort : null);
const port = Number(portStr) || defaultBuildPort;
const brk = port ? Boolean(!!debugBrkArg) : false;
return { port, break: brk, debugId };
}
......
......@@ -14,10 +14,10 @@ suite('EnvironmentService', () => {
test('parseExtensionHostPort when built', () => {
const parse = a => parseExtensionHostPort(parseArgs(a), true);
assert.deepEqual(parse([]), { port: null, break: false, debugId: undefined });
assert.deepEqual(parse(['--debugPluginHost']), { port: null, break: false, debugId: undefined });
assert.deepEqual(parse([]), { port: 5870, break: false, debugId: undefined });
assert.deepEqual(parse(['--debugPluginHost']), { port: 5870, break: false, debugId: undefined });
assert.deepEqual(parse(['--debugPluginHost=1234']), { port: 1234, break: false, debugId: undefined });
assert.deepEqual(parse(['--debugBrkPluginHost']), { port: null, break: false, debugId: undefined });
assert.deepEqual(parse(['--debugBrkPluginHost']), { port: 5870, break: false, debugId: undefined });
assert.deepEqual(parse(['--debugBrkPluginHost=5678']), { port: 5678, break: true, debugId: undefined });
assert.deepEqual(parse(['--debugPluginHost=1234', '--debugBrkPluginHost=5678', '--debugId=7']), { port: 5678, break: true, debugId: '7' });
});
......@@ -41,4 +41,4 @@ suite('EnvironmentService', () => {
assert.equal(parse(['--user-data-dir', './dir'], { cwd: () => '/foo', env: { 'VSCODE_CWD': '/bar' } }), path.resolve('/bar/dir'),
'should use VSCODE_CWD as the cwd when --user-data-dir is specified');
});
});
\ No newline at end of file
});
......@@ -66,6 +66,10 @@ export class ExtensionPointContribution<T> {
}
}
export interface IExtensionHostInformation {
inspectPort: number;
}
export interface IExtensionService {
_serviceBrand: any;
......@@ -126,4 +130,9 @@ export interface IExtensionService {
* Stops the extension host.
*/
stopExtensionHost(): void;
/**
*
*/
getExtensionHostInformation(): IExtensionHostInformation;
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IExtensionService, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { TPromise } from 'vs/base/common/winjs.base';
import { localize } from 'vs/nls';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
import { IStatusbarService, StatusbarAlignment } from 'vs/platform/statusbar/common/statusbar';
import { TernarySearchTree } from 'vs/base/common/map';
import { realpathSync } from 'vs/base/node/extfs';
CommandsRegistry.registerCommand('exthost.profile.start', async accessor => {
const statusbarService = accessor.get(IStatusbarService);
const extensionService = accessor.get(IExtensionService);
const searchTree = TernarySearchTree.forPaths<IExtensionDescription>();
for (let extension of await extensionService.getExtensions()) {
searchTree.set(realpathSync(extension.extensionFolderPath), extension);
}
const handle = statusbarService.addEntry({ text: localize('message', "$(zap) Profiling Extension Host...") }, StatusbarAlignment.LEFT);
return TPromise.wrap(import('v8-inspect-profiler')).then(profiler => {
return profiler.startProfiling({ port: extensionService.getExtensionHostInformation().inspectPort }).then(session => {
return session.stop(5000);
}).then(profile => {
distill(profile);
// return profiler.writeProfile(profile, '/Users/jrieken/Code/test.cpuprofile');
}).then(() => {
handle.dispose();
});
});
function distill(profile) {
let nodes = <Node[]>profile.profile.nodes;
let idsToNodes = new Map<number, Node>();
let idsToExt = new Map<number, IExtensionDescription>();
for (let node of nodes) {
idsToNodes.set(node.id, node);
}
function visit(node: Node, extension?: IExtensionDescription) {
if (!extension) {
extension = evaluateExtension(node.callFrame.url);
}
if (extension) {
idsToExt.set(node.id, extension);
}
if (node.children) {
for (let child of node.children) {
visit(idsToNodes.get(child), extension);
}
}
}
visit(nodes[0]);
let extTimes = new Map<IExtensionDescription, number>();
let samples = <number[]>profile.profile.samples;
let timeDeltas = <number[]>profile.profile.timeDeltas;
for (let i = 0; i < samples.length; i++) {
let id = samples[i];
let extension = idsToExt.get(id);
if (extension) {
let time = timeDeltas[i];
extTimes.set(extension, (extTimes.get(extension) || 0) + time);
}
}
extTimes.forEach((val, index) => {
console.log(index.id + ': ' + val + 'm');
});
}
function evaluateExtension(url: string): IExtensionDescription {
if (url) {
return searchTree.findSubstr(url);
}
return null;
}
});
interface Node {
id: number;
children: number[];
callFrame: { url: string };
}
MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: 'exthost.profile.start', title: localize('', "Profile Extension Host for 5 seconds") } });
......@@ -5,93 +5,5 @@
'use strict';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IMessageService } from 'vs/platform/message/common/message';
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { IWindowsService } from 'vs/platform/windows/common/windows';
import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions } from 'vs/workbench/common/contributions';
import { Registry } from 'vs/platform/registry/common/platform';
import { ReportPerformanceIssueAction } from 'vs/workbench/electron-browser/actions';
import { TPromise } from 'vs/base/common/winjs.base';
import { join, dirname } from 'path';
import { localize } from 'vs/nls';
import { readdir, del, readFile } from 'vs/base/node/pfs';
import { basename } from 'vs/base/common/paths';
class StartupProfiler implements IWorkbenchContribution {
constructor(
@IWindowsService private readonly _windowsService: IWindowsService,
@IMessageService private readonly _messageService: IMessageService,
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@ILifecycleService lifecycleService: ILifecycleService,
@IExtensionService extensionService: IExtensionService,
) {
// wait for everything to be ready
Promise.all([
lifecycleService.when(LifecyclePhase.Eventually),
extensionService.whenInstalledExtensionsRegistered()
]).then(() => {
this._stopProfiling();
});
}
private _stopProfiling(): void {
const profileFilenamePrefix = this._environmentService.args['prof-startup-prefix'];
if (!profileFilenamePrefix) {
return;
}
const dir = dirname(profileFilenamePrefix);
const prefix = basename(profileFilenamePrefix);
const removeArgs: string[] = ['--prof-startup'];
const markerFile = readFile(profileFilenamePrefix).then(value => removeArgs.push(...value.toString().split('|')))
.then(() => del(profileFilenamePrefix))
.then(() => TPromise.timeout(1000));
markerFile.then(() => {
return readdir(dir).then(files => files.filter(value => value.indexOf(prefix) === 0));
}).then(files => {
const profileFiles = files.reduce((prev, cur) => `${prev}${join(dir, cur)}\n`, '\n');
const primaryButton = this._messageService.confirmSync({
type: 'info',
message: localize('prof.message', "Successfully created profiles."),
detail: localize('prof.detail', "Please create an issue and manually attach the following files:\n{0}", profileFiles),
primaryButton: localize('prof.restartAndFileIssue', "Create Issue and Restart"),
secondaryButton: localize('prof.restart', "Restart")
});
if (primaryButton) {
const action = this._instantiationService.createInstance(ReportPerformanceIssueAction, ReportPerformanceIssueAction.ID, ReportPerformanceIssueAction.LABEL);
TPromise.join<any>([
this._windowsService.showItemInFolder(join(dir, files[0])),
action.run(`:warning: Make sure to **attach** these files from your *home*-directory: :warning:\n${files.map(file => `-\`${file}\``).join('\n')}`)
]).then(() => {
// keep window stable until restart is selected
this._messageService.confirmSync({
type: 'info',
message: localize('prof.thanks', "Thanks for helping us."),
detail: localize('prof.detail.restart', "A final restart is required to continue to use '{0}'. Again, thank you for your contribution.", this._environmentService.appNameLong),
primaryButton: localize('prof.restart', "Restart"),
secondaryButton: null
});
// now we are ready to restart
this._windowsService.relaunch({ removeArgs });
});
} else {
// simply restart
this._windowsService.relaunch({ removeArgs });
}
});
}
}
const registry = Registry.as<IWorkbenchContributionsRegistry>(Extensions.Workbench);
registry.registerWorkbenchContribution(StartupProfiler, LifecyclePhase.Running);
import './startupProfiler';
import './extHostProfiler';
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IMessageService } from 'vs/platform/message/common/message';
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { IWindowsService } from 'vs/platform/windows/common/windows';
import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions } from 'vs/workbench/common/contributions';
import { Registry } from 'vs/platform/registry/common/platform';
import { ReportPerformanceIssueAction } from 'vs/workbench/electron-browser/actions';
import { TPromise } from 'vs/base/common/winjs.base';
import { join, dirname } from 'path';
import { localize } from 'vs/nls';
import { readdir, del, readFile } from 'vs/base/node/pfs';
import { basename } from 'vs/base/common/paths';
class StartupProfiler implements IWorkbenchContribution {
constructor(
@IWindowsService private readonly _windowsService: IWindowsService,
@IMessageService private readonly _messageService: IMessageService,
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@ILifecycleService lifecycleService: ILifecycleService,
@IExtensionService extensionService: IExtensionService,
) {
// wait for everything to be ready
Promise.all([
lifecycleService.when(LifecyclePhase.Eventually),
extensionService.whenInstalledExtensionsRegistered()
]).then(() => {
this._stopProfiling();
});
}
private _stopProfiling(): void {
const profileFilenamePrefix = this._environmentService.args['prof-startup-prefix'];
if (!profileFilenamePrefix) {
return;
}
const dir = dirname(profileFilenamePrefix);
const prefix = basename(profileFilenamePrefix);
const removeArgs: string[] = ['--prof-startup'];
const markerFile = readFile(profileFilenamePrefix).then(value => removeArgs.push(...value.toString().split('|')))
.then(() => del(profileFilenamePrefix))
.then(() => TPromise.timeout(1000));
markerFile.then(() => {
return readdir(dir).then(files => files.filter(value => value.indexOf(prefix) === 0));
}).then(files => {
const profileFiles = files.reduce((prev, cur) => `${prev}${join(dir, cur)}\n`, '\n');
const primaryButton = this._messageService.confirmSync({
type: 'info',
message: localize('prof.message', "Successfully created profiles."),
detail: localize('prof.detail', "Please create an issue and manually attach the following files:\n{0}", profileFiles),
primaryButton: localize('prof.restartAndFileIssue', "Create Issue and Restart"),
secondaryButton: localize('prof.restart', "Restart")
});
if (primaryButton) {
const action = this._instantiationService.createInstance(ReportPerformanceIssueAction, ReportPerformanceIssueAction.ID, ReportPerformanceIssueAction.LABEL);
TPromise.join<any>([
this._windowsService.showItemInFolder(join(dir, files[0])),
action.run(`:warning: Make sure to **attach** these files from your *home*-directory: :warning:\n${files.map(file => `-\`${file}\``).join('\n')}`)
]).then(() => {
// keep window stable until restart is selected
this._messageService.confirmSync({
type: 'info',
message: localize('prof.thanks', "Thanks for helping us."),
detail: localize('prof.detail.restart', "A final restart is required to continue to use '{0}'. Again, thank you for your contribution.", this._environmentService.appNameLong),
primaryButton: localize('prof.restart', "Restart"),
secondaryButton: null
});
// now we are ready to restart
this._windowsService.relaunch({ removeArgs });
});
} else {
// simply restart
this._windowsService.relaunch({ removeArgs });
}
});
}
}
const registry = Registry.as<IWorkbenchContributionsRegistry>(Extensions.Workbench);
registry.registerWorkbenchContribution(StartupProfiler, LifecyclePhase.Running);
......@@ -55,6 +55,7 @@ export class ExtensionHostProcessWorker {
// Resources, in order they get acquired/created when .start() is called:
private _namedPipeServer: Server;
private _inspectPort: number;
private _extensionHostProcess: ChildProcess;
private _extensionHostConnection: Socket;
private _messageProtocol: TPromise<IMessagePassingProtocol>;
......@@ -216,6 +217,7 @@ export class ExtensionHostProcessWorker {
}
});
}
this._inspectPort = port;
// Help in case we fail to start it
let startupTimeoutHandle: number;
......@@ -426,6 +428,10 @@ export class ExtensionHostProcessWorker {
}
}
public getInspectPort(): number {
return this._inspectPort;
}
public terminate(): void {
if (this._terminating) {
return;
......
......@@ -12,7 +12,7 @@ import pkg from 'vs/platform/node/package';
import * as path from 'path';
import URI from 'vs/base/common/uri';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
import { IMessage, IExtensionDescription, IExtensionsStatus, IExtensionService, ExtensionPointContribution, ActivationTimes } from 'vs/platform/extensions/common/extensions';
import { IMessage, IExtensionDescription, IExtensionsStatus, IExtensionService, ExtensionPointContribution, ActivationTimes, IExtensionHostInformation } from 'vs/platform/extensions/common/extensions';
import { IExtensionEnablementService, IExtensionIdentifier, EnablementState } from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions, BetterMergeId, BetterMergeDisabledNowKey } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionsRegistry, ExtensionPoint, IExtensionPointUser, ExtensionMessageCollector, IExtensionPoint } from 'vs/platform/extensions/common/extensionsRegistry';
......@@ -123,6 +123,15 @@ export class ExtensionService extends Disposable implements IExtensionService {
return this._onDidRegisterExtensions.event;
}
public getExtensionHostInformation(): IExtensionHostInformation {
if (!this._extensionHostProcessWorker) {
throw errors.illegalState();
}
return <IExtensionHostInformation>{
inspectPort: this._extensionHostProcessWorker.getInspectPort()
};
}
public restartExtensionHost(): void {
this._stopExtensionHostProcess();
this._startExtensionHostProcess(Object.keys(this._allRequestedActivateEvents));
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册