Path: blob/main/src/vs/workbench/contrib/performance/electron-browser/rendererAutoProfiler.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { timeout } from '../../../../base/common/async.js';6import { VSBuffer } from '../../../../base/common/buffer.js';7import { joinPath } from '../../../../base/common/resources.js';8import { generateUuid } from '../../../../base/common/uuid.js';9import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';10import { IFileService } from '../../../../platform/files/common/files.js';11import { ILogService } from '../../../../platform/log/common/log.js';12import { INativeHostService } from '../../../../platform/native/common/native.js';13import { IV8Profile } from '../../../../platform/profiling/common/profiling.js';14import { IProfileAnalysisWorkerService, ProfilingOutput } from '../../../../platform/profiling/electron-browser/profileAnalysisWorkerService.js';15import { INativeWorkbenchEnvironmentService } from '../../../services/environment/electron-browser/environmentService.js';16import { parseExtensionDevOptions } from '../../../services/extensions/common/extensionDevOptions.js';17import { ITimerService } from '../../../services/timer/browser/timerService.js';1819export class RendererProfiling {2021private _observer?: PerformanceObserver;2223constructor(24@INativeWorkbenchEnvironmentService private readonly _environmentService: INativeWorkbenchEnvironmentService,25@IFileService private readonly _fileService: IFileService,26@ILogService private readonly _logService: ILogService,27@INativeHostService nativeHostService: INativeHostService,28@ITimerService timerService: ITimerService,29@IConfigurationService configService: IConfigurationService,30@IProfileAnalysisWorkerService profileAnalysisService: IProfileAnalysisWorkerService31) {3233const devOpts = parseExtensionDevOptions(_environmentService);34if (devOpts.isExtensionDevTestFromCli) {35// disabled when running extension tests36return;37}3839timerService.perfBaseline.then(perfBaseline => {40(_environmentService.isBuilt ? _logService.info : _logService.trace).apply(_logService, [`[perf] Render performance baseline is ${perfBaseline}ms`]);4142if (perfBaseline < 0) {43// too slow44return;45}4647// SLOW threshold48const slowThreshold = perfBaseline * 10; // ~10 frames at 64fps on MY machine4950const obs = new PerformanceObserver(async list => {5152obs.takeRecords();53const maxDuration = list.getEntries()54.map(e => e.duration)55.reduce((p, c) => Math.max(p, c), 0);5657if (maxDuration < slowThreshold) {58return;59}6061if (!configService.getValue('application.experimental.rendererProfiling')) {62_logService.debug(`[perf] SLOW task detected (${maxDuration}ms) but renderer profiling is disabled via 'application.experimental.rendererProfiling'`);63return;64}6566const sessionId = generateUuid();6768_logService.warn(`[perf] Renderer reported VERY LONG TASK (${maxDuration}ms), starting profiling session '${sessionId}'`);6970// pause observation, we'll take a detailed look71obs.disconnect();7273// profile renderer for 5secs, analyse, and take action depending on the result74for (let i = 0; i < 3; i++) {7576try {77const profile = await nativeHostService.profileRenderer(sessionId, 5000);78const output = await profileAnalysisService.analyseBottomUp(profile, _url => '<<renderer>>', perfBaseline, true);79if (output === ProfilingOutput.Interesting) {80this._store(profile, sessionId);81break;82}8384timeout(15000); // wait 15s8586} catch (err) {87_logService.error(err);88break;89}90}9192// reconnect the observer93obs.observe({ entryTypes: ['longtask'] });94});9596obs.observe({ entryTypes: ['longtask'] });97this._observer = obs;9899});100}101102dispose(): void {103this._observer?.disconnect();104}105106107private async _store(profile: IV8Profile, sessionId: string): Promise<void> {108const path = joinPath(this._environmentService.tmpDir, `renderer-${Math.random().toString(16).slice(2, 8)}.cpuprofile.json`);109await this._fileService.writeFile(path, VSBuffer.fromString(JSON.stringify(profile)));110this._logService.info(`[perf] stored profile to DISK '${path}'`, sessionId);111}112}113114115