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

View file

@ -116,11 +116,10 @@ export interface IWillActivateEvent {
} }
export interface IResponsiveStateChangeEvent { export interface IResponsiveStateChangeEvent {
target: ICpuProfilerTarget;
isResponsive: boolean; isResponsive: boolean;
} }
export interface IExtensionService extends ICpuProfilerTarget { export interface IExtensionService {
_serviceBrand: any; _serviceBrand: any;
/** /**
@ -204,6 +203,16 @@ export interface IExtensionService extends ICpuProfilerTarget {
*/ */
getInspectPort(): number; getInspectPort(): number;
/**
* Can the extension host be profiled.
*/
canProfileExtensionHost(): boolean;
/**
*
*/
startExtensionHostProfile(): Promise<ProfileSession>;
/** /**
* Restarts the extension host. * Restarts the extension host.
*/ */
@ -227,19 +236,6 @@ export interface IExtensionService extends ICpuProfilerTarget {
_onExtensionHostExit(code: number): void; _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 { export interface ProfileSession {
stop(): Promise<IExtensionHostProfile>; 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 { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ExtHostCustomersRegistry } from 'vs/workbench/api/common/extHostCustomers'; import { ExtHostCustomersRegistry } from 'vs/workbench/api/common/extHostCustomers';
import { ExtHostContext, ExtHostExtensionServiceShape, IExtHostContext, MainContext } from 'vs/workbench/api/common/extHost.protocol'; 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 { 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 { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier';
import { IRPCProtocolLogger, RPCProtocol, RequestInitiator, ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol'; import { IRPCProtocolLogger, RPCProtocol, RequestInitiator, ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol';
import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver';
@ -169,10 +167,6 @@ export class ExtensionHostProcessManager extends Disposable {
return ExtensionHostProcessManager._convert(SIZE, sw.elapsed()); return ExtensionHostProcessManager._convert(SIZE, sw.elapsed());
} }
public canProfileExtensionHost(): boolean {
return this._extensionHostProcessWorker && Boolean(this._extensionHostProcessWorker.getInspectPort());
}
private _createExtensionHostCustomers(protocol: IMessagePassingProtocol): ExtHostExtensionServiceShape { private _createExtensionHostCustomers(protocol: IMessagePassingProtocol): ExtHostExtensionServiceShape {
let logger: IRPCProtocolLogger | null = null; 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 { public getInspectPort(): number {
if (this._extensionHostProcessWorker) { if (this._extensionHostProcessWorker) {
let port = this._extensionHostProcessWorker.getInspectPort(); let port = this._extensionHostProcessWorker.getInspectPort();
@ -256,6 +240,10 @@ export class ExtensionHostProcessManager extends Disposable {
return 0; return 0;
} }
public canProfileExtensionHost(): boolean {
return this._extensionHostProcessWorker && Boolean(this._extensionHostProcessWorker.getInspectPort());
}
public async resolveAuthority(remoteAuthority: string): Promise<ResolvedAuthority> { public async resolveAuthority(remoteAuthority: string): Promise<ResolvedAuthority> {
const authorityPlusIndex = remoteAuthority.indexOf('+'); const authorityPlusIndex = remoteAuthority.indexOf('+');
if (authorityPlusIndex === -1) { 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 { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IFileService } from 'vs/platform/files/common/files'; import { IFileService } from 'vs/platform/files/common/files';
import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions'; import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions';
import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler';
const hasOwnProperty = Object.hasOwnProperty; const hasOwnProperty = Object.hasOwnProperty;
const NO_OP_VOID_PROMISE = Promise.resolve<void>(undefined); 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 extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, autoStart, extensions, this._extensionHostLogsLocation);
const extHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, extHostProcessWorker, null, initialActivationEvents); const extHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, extHostProcessWorker, null, initialActivationEvents);
extHostProcessManager.onDidCrash(([code, signal]) => this._onExtensionHostCrashed(code, signal)); 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); 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++) { for (let i = 0, len = this._extensionHostProcessManagers.length; i < len; i++) {
const extHostProcessManager = this._extensionHostProcessManagers[i]; const extHostProcessManager = this._extensionHostProcessManagers[i];
if (extHostProcessManager.canProfileExtensionHost()) { 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'); throw new Error('Extension host not running or no inspect port available');