perf - log startup timings in web same as desktop (#171310)

This commit is contained in:
Benjamin Pasero 2023-01-15 15:26:36 +01:00 committed by GitHub
parent 5e4c5c7812
commit 8be8d96ab8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 160 additions and 112 deletions

View file

@ -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
);

View 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
}
}
}
}

View file

@ -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
);

View file

@ -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> {