simplify extension host profiling

This commit is contained in:
Johannes Rieken 2019-04-12 12:06:53 +02:00
parent efd5704d8d
commit 4baa90bfde
4 changed files with 33 additions and 44 deletions

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IExtensionService, IResponsiveStateChangeEvent, ICpuProfilerTarget, IExtensionHostProfile, ProfileSession } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionService, IResponsiveStateChangeEvent, IExtensionHostProfile, ProfileSession } from 'vs/workbench/services/extensions/common/extensions';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { Disposable } from 'vs/base/common/lifecycle';
import { ILogService } from 'vs/platform/log/common/log';
@ -24,8 +24,8 @@ import { createSlowExtensionAction } from 'vs/workbench/contrib/extensions/elect
export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchContribution {
private readonly _session = new Map<ICpuProfilerTarget, CancellationTokenSource>();
private readonly _blame = new Set<string>();
private _session: CancellationTokenSource | undefined;
constructor(
@IExtensionService private readonly _extensionService: IExtensionService,
@ -41,26 +41,27 @@ export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchCont
}
private async _onDidChangeResponsiveChange(event: IResponsiveStateChangeEvent): Promise<void> {
const { target } = event;
const target = this._extensionService;
if (!target.canProfileExtensionHost()) {
return;
}
if (event.isResponsive && this._session.has(target)) {
if (event.isResponsive && this._session) {
// stop profiling when responsive again
this._session.get(target)!.cancel();
this._session.cancel();
} else if (!event.isResponsive && !this._session.has(target)) {
} else if (!event.isResponsive && !this._session) {
// start profiling if not yet profiling
const token = new CancellationTokenSource();
this._session.set(target, token);
const cts = new CancellationTokenSource();
this._session = cts;
let session: ProfileSession;
try {
session = await target.startExtensionHostProfile();
} catch (err) {
this._session.delete(target);
this._session = undefined;
// fail silent as this is often
// caused by another party being
// connected already
@ -69,7 +70,7 @@ export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchCont
// wait 5 seconds or until responsive again
await new Promise(resolve => {
token.token.onCancellationRequested(resolve);
cts.token.onCancellationRequested(resolve);
setTimeout(resolve, 5e3);
});
@ -79,7 +80,7 @@ export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchCont
} catch (err) {
onUnexpectedError(err);
} finally {
this._session.delete(target);
this._session = undefined;
}
}
}

View file

@ -116,11 +116,10 @@ export interface IWillActivateEvent {
}
export interface IResponsiveStateChangeEvent {
target: ICpuProfilerTarget;
isResponsive: boolean;
}
export interface IExtensionService extends ICpuProfilerTarget {
export interface IExtensionService {
_serviceBrand: any;
/**
@ -204,6 +203,16 @@ export interface IExtensionService extends ICpuProfilerTarget {
*/
getInspectPort(): number;
/**
* Can the extension host be profiled.
*/
canProfileExtensionHost(): boolean;
/**
*
*/
startExtensionHostProfile(): Promise<ProfileSession>;
/**
* Restarts the extension host.
*/
@ -227,19 +236,6 @@ export interface IExtensionService extends ICpuProfilerTarget {
_onExtensionHostExit(code: number): void;
}
export interface ICpuProfilerTarget {
/**
* Check if the extension host can be profiled.
*/
canProfileExtensionHost(): boolean;
/**
* Begin an extension host process profile session.
*/
startExtensionHostProfile(): Promise<ProfileSession>;
}
export interface ProfileSession {
stop(): Promise<IExtensionHostProfile>;
}

View file

@ -12,9 +12,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ExtHostCustomersRegistry } from 'vs/workbench/api/common/extHostCustomers';
import { ExtHostContext, ExtHostExtensionServiceShape, IExtHostContext, MainContext } from 'vs/workbench/api/common/extHost.protocol';
import { ProfileSession } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionHostStarter } from 'vs/workbench/services/extensions/electron-browser/extensionHost';
import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler';
import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier';
import { IRPCProtocolLogger, RPCProtocol, RequestInitiator, ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol';
import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver';
@ -169,10 +167,6 @@ export class ExtensionHostProcessManager extends Disposable {
return ExtensionHostProcessManager._convert(SIZE, sw.elapsed());
}
public canProfileExtensionHost(): boolean {
return this._extensionHostProcessWorker && Boolean(this._extensionHostProcessWorker.getInspectPort());
}
private _createExtensionHostCustomers(protocol: IMessagePassingProtocol): ExtHostExtensionServiceShape {
let logger: IRPCProtocolLogger | null = null;
@ -236,16 +230,6 @@ export class ExtensionHostProcessManager extends Disposable {
});
}
public startExtensionHostProfile(): Promise<ProfileSession> {
if (this._extensionHostProcessWorker) {
let port = this._extensionHostProcessWorker.getInspectPort();
if (port) {
return this._instantiationService.createInstance(ExtensionHostProfiler, port).start();
}
}
throw new Error('Extension host not running or no inspect port available');
}
public getInspectPort(): number {
if (this._extensionHostProcessWorker) {
let port = this._extensionHostProcessWorker.getInspectPort();
@ -256,6 +240,10 @@ export class ExtensionHostProcessManager extends Disposable {
return 0;
}
public canProfileExtensionHost(): boolean {
return this._extensionHostProcessWorker && Boolean(this._extensionHostProcessWorker.getInspectPort());
}
public async resolveAuthority(remoteAuthority: string): Promise<ResolvedAuthority> {
const authorityPlusIndex = remoteAuthority.indexOf('+');
if (authorityPlusIndex === -1) {

View file

@ -35,6 +35,7 @@ import { Schemas } from 'vs/base/common/network';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IFileService } from 'vs/platform/files/common/files';
import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions';
import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler';
const hasOwnProperty = Object.hasOwnProperty;
const NO_OP_VOID_PROMISE = Promise.resolve<void>(undefined);
@ -442,7 +443,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
const extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, autoStart, extensions, this._extensionHostLogsLocation);
const extHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, extHostProcessWorker, null, initialActivationEvents);
extHostProcessManager.onDidCrash(([code, signal]) => this._onExtensionHostCrashed(code, signal));
extHostProcessManager.onDidChangeResponsiveState((responsiveState) => { this._onDidChangeResponsiveChange.fire({ target: extHostProcessManager, isResponsive: responsiveState === ResponsiveState.Responsive }); });
extHostProcessManager.onDidChangeResponsiveState((responsiveState) => { this._onDidChangeResponsiveChange.fire({ isResponsive: responsiveState === ResponsiveState.Responsive }); });
this._extensionHostProcessManagers.push(extHostProcessManager);
}
@ -583,7 +584,10 @@ export class ExtensionService extends Disposable implements IExtensionService {
for (let i = 0, len = this._extensionHostProcessManagers.length; i < len; i++) {
const extHostProcessManager = this._extensionHostProcessManagers[i];
if (extHostProcessManager.canProfileExtensionHost()) {
return extHostProcessManager.startExtensionHostProfile();
const port = extHostProcessManager.getInspectPort();
if (port) {
return this._instantiationService.createInstance(ExtensionHostProfiler, port).start();
}
}
}
throw new Error('Extension host not running or no inspect port available');