mirror of
https://github.com/Microsoft/vscode
synced 2024-10-30 01:37:20 +00:00
perf - log startup timings in web same as desktop (#171310)
This commit is contained in:
parent
5e4c5c7812
commit
8be8d96ab8
4 changed files with 160 additions and 112 deletions
|
@ -6,78 +6,16 @@
|
|||
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Extensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { posix } from 'vs/base/common/path';
|
||||
import { hash } from 'vs/base/common/hash';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { ITimerService } from 'vs/workbench/services/timer/browser/timerService';
|
||||
import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { BrowserResourcePerformanceMarks, BrowserStartupTimings } from 'vs/workbench/contrib/performance/browser/startupTimings';
|
||||
|
||||
class ResourcePerformanceMarks {
|
||||
|
||||
constructor(
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IEnvironmentService environmentService: IEnvironmentService
|
||||
) {
|
||||
|
||||
type Entry = {
|
||||
hosthash: string;
|
||||
name: string;
|
||||
duration: number;
|
||||
};
|
||||
type EntryClassifify = {
|
||||
owner: 'jrieken';
|
||||
comment: 'Resource performance numbers';
|
||||
hosthash: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Hash of the hostname' };
|
||||
name: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Resource basename' };
|
||||
duration: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'Resource duration' };
|
||||
};
|
||||
for (const item of performance.getEntriesByType('resource')) {
|
||||
|
||||
try {
|
||||
const url = new URL(item.name);
|
||||
const name = posix.basename(url.pathname);
|
||||
|
||||
telemetryService.publicLog2<Entry, EntryClassifify>('startup.resource.perf', {
|
||||
hosthash: `H${hash(url.host).toString(16)}`,
|
||||
name,
|
||||
duration: item.duration
|
||||
});
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class StartupTimings {
|
||||
constructor(
|
||||
@ITimerService private readonly timerService: ITimerService,
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@IBrowserWorkbenchEnvironmentService private readonly environmentService: IBrowserWorkbenchEnvironmentService
|
||||
) {
|
||||
this.logPerfMarks();
|
||||
}
|
||||
|
||||
private async logPerfMarks(): Promise<void> {
|
||||
if (!this.environmentService.profDurationMarkers) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.timerService.whenReady();
|
||||
|
||||
const [from, to] = this.environmentService.profDurationMarkers;
|
||||
this.logService.info(`[perf] from '${from}' to '${to}': ${this.timerService.getDuration(from, to)}ms`);
|
||||
}
|
||||
}
|
||||
// -- startup timings
|
||||
|
||||
Registry.as<IWorkbenchContributionsRegistry>(Extensions.Workbench).registerWorkbenchContribution(
|
||||
ResourcePerformanceMarks,
|
||||
BrowserResourcePerformanceMarks,
|
||||
LifecyclePhase.Eventually
|
||||
);
|
||||
|
||||
Registry.as<IWorkbenchContributionsRegistry>(Extensions.Workbench).registerWorkbenchContribution(
|
||||
StartupTimings,
|
||||
BrowserStartupTimings,
|
||||
LifecyclePhase.Eventually
|
||||
);
|
||||
|
|
140
src/vs/workbench/contrib/performance/browser/startupTimings.ts
Normal file
140
src/vs/workbench/contrib/performance/browser/startupTimings.ts
Normal file
|
@ -0,0 +1,140 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ILifecycleService, StartupKind, StartupKindToString } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
import { IUpdateService } from 'vs/platform/update/common/update';
|
||||
import * as files from 'vs/workbench/contrib/files/common/files';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite';
|
||||
import { ViewContainerLocation } from 'vs/workbench/common/views';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
|
||||
import { ITimerService } from 'vs/workbench/services/timer/browser/timerService';
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { posix } from 'vs/base/common/path';
|
||||
import { hash } from 'vs/base/common/hash';
|
||||
|
||||
export abstract class StartupTimings {
|
||||
|
||||
constructor(
|
||||
@IEditorService private readonly _editorService: IEditorService,
|
||||
@IPaneCompositePartService private readonly _paneCompositeService: IPaneCompositePartService,
|
||||
@ILifecycleService private readonly _lifecycleService: ILifecycleService,
|
||||
@IUpdateService private readonly _updateService: IUpdateService,
|
||||
@IWorkspaceTrustManagementService private readonly _workspaceTrustService: IWorkspaceTrustManagementService
|
||||
) {
|
||||
}
|
||||
|
||||
protected async _isStandardStartup(): Promise<string | undefined> {
|
||||
// check for standard startup:
|
||||
// * new window (no reload)
|
||||
// * workspace is trusted
|
||||
// * just one window
|
||||
// * explorer viewlet visible
|
||||
// * one text editor (not multiple, not webview, welcome etc...)
|
||||
// * cached data present (not rejected, not created)
|
||||
if (this._lifecycleService.startupKind !== StartupKind.NewWindow) {
|
||||
return StartupKindToString(this._lifecycleService.startupKind);
|
||||
}
|
||||
if (!this._workspaceTrustService.isWorkspaceTrusted()) {
|
||||
return 'Workspace not trusted';
|
||||
}
|
||||
const activeViewlet = this._paneCompositeService.getActivePaneComposite(ViewContainerLocation.Sidebar);
|
||||
if (!activeViewlet || activeViewlet.getId() !== files.VIEWLET_ID) {
|
||||
return 'Explorer viewlet not visible';
|
||||
}
|
||||
const visibleEditorPanes = this._editorService.visibleEditorPanes;
|
||||
if (visibleEditorPanes.length !== 1) {
|
||||
return `Expected text editor count : 1, Actual : ${visibleEditorPanes.length}`;
|
||||
}
|
||||
if (!isCodeEditor(visibleEditorPanes[0].getControl())) {
|
||||
return 'Active editor is not a text editor';
|
||||
}
|
||||
const activePanel = this._paneCompositeService.getActivePaneComposite(ViewContainerLocation.Panel);
|
||||
if (activePanel) {
|
||||
return `Current active panel : ${this._paneCompositeService.getPaneComposite(activePanel.getId(), ViewContainerLocation.Panel)?.name}`;
|
||||
}
|
||||
if (!await this._updateService.isLatestVersion()) {
|
||||
return 'Not on latest version, updates available';
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export class BrowserStartupTimings extends StartupTimings implements IWorkbenchContribution {
|
||||
|
||||
constructor(
|
||||
@IEditorService editorService: IEditorService,
|
||||
@IPaneCompositePartService paneCompositeService: IPaneCompositePartService,
|
||||
@ILifecycleService lifecycleService: ILifecycleService,
|
||||
@IUpdateService updateService: IUpdateService,
|
||||
@IWorkspaceTrustManagementService workspaceTrustService: IWorkspaceTrustManagementService,
|
||||
@ITimerService private readonly timerService: ITimerService,
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@IBrowserWorkbenchEnvironmentService private readonly environmentService: IBrowserWorkbenchEnvironmentService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
@IProductService private readonly productService: IProductService
|
||||
) {
|
||||
super(editorService, paneCompositeService, lifecycleService, updateService, workspaceTrustService);
|
||||
|
||||
this.logPerfMarks();
|
||||
}
|
||||
|
||||
private async logPerfMarks(): Promise<void> {
|
||||
if (!this.environmentService.profDurationMarkers) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.timerService.whenReady();
|
||||
|
||||
const standardStartupError = await this._isStandardStartup();
|
||||
const perfBaseline = await this.timerService.perfBaseline;
|
||||
const { sessionId } = await this.telemetryService.getTelemetryInfo();
|
||||
const [from, to] = this.environmentService.profDurationMarkers;
|
||||
const content = `${this.timerService.getDuration(from, to)}\t${this.productService.nameShort}\t${(this.productService.commit || '').slice(0, 10) || '0000000000'}\t${sessionId}\t${standardStartupError === undefined ? 'standard_start' : 'NO_standard_start : ' + standardStartupError}\t${String(perfBaseline).padStart(4, '0')}ms\n`;
|
||||
|
||||
this.logService.info(`[prof-timers] ${content}`);
|
||||
}
|
||||
}
|
||||
|
||||
export class BrowserResourcePerformanceMarks {
|
||||
|
||||
constructor(
|
||||
@ITelemetryService telemetryService: ITelemetryService
|
||||
) {
|
||||
|
||||
type Entry = {
|
||||
hosthash: string;
|
||||
name: string;
|
||||
duration: number;
|
||||
};
|
||||
type EntryClassifify = {
|
||||
owner: 'jrieken';
|
||||
comment: 'Resource performance numbers';
|
||||
hosthash: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Hash of the hostname' };
|
||||
name: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Resource basename' };
|
||||
duration: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'Resource duration' };
|
||||
};
|
||||
for (const item of performance.getEntriesByType('resource')) {
|
||||
|
||||
try {
|
||||
const url = new URL(item.name);
|
||||
const name = posix.basename(url.pathname);
|
||||
|
||||
telemetryService.publicLog2<Entry, EntryClassifify>('startup.resource.perf', {
|
||||
hosthash: `H${hash(url.host).toString(16)}`,
|
||||
name,
|
||||
duration: item.duration
|
||||
});
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,12 +7,13 @@ import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle
|
|||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Extensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
|
||||
import { StartupProfiler } from './startupProfiler';
|
||||
import { StartupTimings } from './startupTimings';
|
||||
import { NativeStartupTimings } from './startupTimings';
|
||||
import { RendererProfiling } from 'vs/workbench/contrib/performance/electron-sandbox/rendererAutoProfiler';
|
||||
import { IConfigurationRegistry, Extensions as ConfigExt } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { localize } from 'vs/nls';
|
||||
import { applicationConfigurationNodeBase } from 'vs/workbench/common/configuration';
|
||||
|
||||
// -- auto profiler
|
||||
|
||||
Registry.as<IWorkbenchContributionsRegistry>(Extensions.Workbench).registerWorkbenchContribution(
|
||||
RendererProfiling,
|
||||
|
@ -29,7 +30,7 @@ Registry.as<IWorkbenchContributionsRegistry>(Extensions.Workbench).registerWorkb
|
|||
// -- startup timings
|
||||
|
||||
Registry.as<IWorkbenchContributionsRegistry>(Extensions.Workbench).registerWorkbenchContribution(
|
||||
StartupTimings,
|
||||
NativeStartupTimings,
|
||||
LifecyclePhase.Eventually
|
||||
);
|
||||
|
||||
|
|
|
@ -6,14 +6,12 @@
|
|||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService';
|
||||
import { ILifecycleService, StartupKind, StartupKindToString } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IUpdateService } from 'vs/platform/update/common/update';
|
||||
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
|
||||
import * as files from 'vs/workbench/contrib/files/common/files';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { ITimerService } from 'vs/workbench/services/timer/browser/timerService';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
|
@ -21,23 +19,25 @@ import { URI } from 'vs/base/common/uri';
|
|||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite';
|
||||
import { ViewContainerLocation } from 'vs/workbench/common/views';
|
||||
import { StartupTimings } from 'vs/workbench/contrib/performance/browser/startupTimings';
|
||||
|
||||
export class StartupTimings implements IWorkbenchContribution {
|
||||
export class NativeStartupTimings extends StartupTimings implements IWorkbenchContribution {
|
||||
|
||||
constructor(
|
||||
@IFileService private readonly _fileService: IFileService,
|
||||
@ITimerService private readonly _timerService: ITimerService,
|
||||
@INativeHostService private readonly _nativeHostService: INativeHostService,
|
||||
@IEditorService private readonly _editorService: IEditorService,
|
||||
@IPaneCompositePartService private readonly _paneCompositeService: IPaneCompositePartService,
|
||||
@IEditorService editorService: IEditorService,
|
||||
@IPaneCompositePartService paneCompositeService: IPaneCompositePartService,
|
||||
@ITelemetryService private readonly _telemetryService: ITelemetryService,
|
||||
@ILifecycleService private readonly _lifecycleService: ILifecycleService,
|
||||
@IUpdateService private readonly _updateService: IUpdateService,
|
||||
@ILifecycleService lifecycleService: ILifecycleService,
|
||||
@IUpdateService updateService: IUpdateService,
|
||||
@INativeWorkbenchEnvironmentService private readonly _environmentService: INativeWorkbenchEnvironmentService,
|
||||
@IProductService private readonly _productService: IProductService,
|
||||
@IWorkspaceTrustManagementService private readonly _workspaceTrustService: IWorkspaceTrustManagementService,
|
||||
@IWorkspaceTrustManagementService workspaceTrustService: IWorkspaceTrustManagementService,
|
||||
) {
|
||||
super(editorService, paneCompositeService, lifecycleService, updateService, workspaceTrustService);
|
||||
|
||||
this._report().catch(onUnexpectedError);
|
||||
}
|
||||
|
||||
|
@ -102,43 +102,12 @@ export class StartupTimings implements IWorkbenchContribution {
|
|||
}
|
||||
}
|
||||
|
||||
private async _isStandardStartup(): Promise<string | undefined> {
|
||||
// check for standard startup:
|
||||
// * new window (no reload)
|
||||
// * workspace is trusted
|
||||
// * just one window
|
||||
// * explorer viewlet visible
|
||||
// * one text editor (not multiple, not webview, welcome etc...)
|
||||
// * cached data present (not rejected, not created)
|
||||
if (this._lifecycleService.startupKind !== StartupKind.NewWindow) {
|
||||
return StartupKindToString(this._lifecycleService.startupKind);
|
||||
}
|
||||
if (!this._workspaceTrustService.isWorkspaceTrusted()) {
|
||||
return 'Workspace not trusted';
|
||||
}
|
||||
protected override async _isStandardStartup(): Promise<string | undefined> {
|
||||
const windowCount = await this._nativeHostService.getWindowCount();
|
||||
if (windowCount !== 1) {
|
||||
return 'Expected window count : 1, Actual : ' + windowCount;
|
||||
return `Expected window count : 1, Actual : ${windowCount}`;
|
||||
}
|
||||
const activeViewlet = this._paneCompositeService.getActivePaneComposite(ViewContainerLocation.Sidebar);
|
||||
if (!activeViewlet || activeViewlet.getId() !== files.VIEWLET_ID) {
|
||||
return 'Explorer viewlet not visible';
|
||||
}
|
||||
const visibleEditorPanes = this._editorService.visibleEditorPanes;
|
||||
if (visibleEditorPanes.length !== 1) {
|
||||
return 'Expected text editor count : 1, Actual : ' + visibleEditorPanes.length;
|
||||
}
|
||||
if (!isCodeEditor(visibleEditorPanes[0].getControl())) {
|
||||
return 'Active editor is not a text editor';
|
||||
}
|
||||
const activePanel = this._paneCompositeService.getActivePaneComposite(ViewContainerLocation.Panel);
|
||||
if (activePanel) {
|
||||
return 'Current active panel : ' + this._paneCompositeService.getPaneComposite(activePanel.getId(), ViewContainerLocation.Panel)?.name;
|
||||
}
|
||||
if (!await this._updateService.isLatestVersion()) {
|
||||
return 'Not on latest version, updates available';
|
||||
}
|
||||
return undefined;
|
||||
return super._isStandardStartup();
|
||||
}
|
||||
|
||||
private async appendContent(file: URI, content: string): Promise<void> {
|
||||
|
|
Loading…
Reference in a new issue