Path: blob/main/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.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 { TernarySearchTree } from '../../../../base/common/ternarySearchTree.js';6import { IExtensionHostProfile, IExtensionService, ProfileSegmentId, ProfileSession } from '../common/extensions.js';7import { IExtensionDescription } from '../../../../platform/extensions/common/extensions.js';8import { Schemas } from '../../../../base/common/network.js';9import { URI } from '../../../../base/common/uri.js';10import { IV8InspectProfilingService, IV8Profile, IV8ProfileNode } from '../../../../platform/profiling/common/profiling.js';11import { createSingleCallFunction } from '../../../../base/common/functional.js';1213export class ExtensionHostProfiler {1415constructor(16private readonly _host: string,17private readonly _port: number,18@IExtensionService private readonly _extensionService: IExtensionService,19@IV8InspectProfilingService private readonly _profilingService: IV8InspectProfilingService,20) {21}2223public async start(): Promise<ProfileSession> {2425const id = await this._profilingService.startProfiling({ host: this._host, port: this._port });2627return {28stop: createSingleCallFunction(async () => {29const profile = await this._profilingService.stopProfiling(id);30await this._extensionService.whenInstalledExtensionsRegistered();31const extensions = this._extensionService.extensions;32return this._distill(profile, extensions);33})34};35}3637private _distill(profile: IV8Profile, extensions: readonly IExtensionDescription[]): IExtensionHostProfile {38const searchTree = TernarySearchTree.forUris<IExtensionDescription>();39for (const extension of extensions) {40if (extension.extensionLocation.scheme === Schemas.file) {41searchTree.set(URI.file(extension.extensionLocation.fsPath), extension);42}43}4445const nodes = profile.nodes;46const idsToNodes = new Map<number, IV8ProfileNode>();47const idsToSegmentId = new Map<number, ProfileSegmentId | null>();48for (const node of nodes) {49idsToNodes.set(node.id, node);50}5152function visit(node: IV8ProfileNode, segmentId: ProfileSegmentId | null) {53if (!segmentId) {54switch (node.callFrame.functionName) {55case '(root)':56break;57case '(program)':58segmentId = 'program';59break;60case '(garbage collector)':61segmentId = 'gc';62break;63default:64segmentId = 'self';65break;66}67} else if (segmentId === 'self' && node.callFrame.url) {68let extension: IExtensionDescription | undefined;69try {70extension = searchTree.findSubstr(URI.parse(node.callFrame.url));71} catch {72// ignore73}74if (extension) {75segmentId = extension.identifier.value;76}77}78idsToSegmentId.set(node.id, segmentId);7980if (node.children) {81for (const child of node.children) {82const childNode = idsToNodes.get(child);83if (childNode) {84visit(childNode, segmentId);85}86}87}88}89visit(nodes[0], null);9091const samples = profile.samples || [];92const timeDeltas = profile.timeDeltas || [];93const distilledDeltas: number[] = [];94const distilledIds: ProfileSegmentId[] = [];9596let currSegmentTime = 0;97let currSegmentId: string | undefined;98for (let i = 0; i < samples.length; i++) {99const id = samples[i];100const segmentId = idsToSegmentId.get(id);101if (segmentId !== currSegmentId) {102if (currSegmentId) {103distilledIds.push(currSegmentId);104distilledDeltas.push(currSegmentTime);105}106currSegmentId = segmentId ?? undefined;107currSegmentTime = 0;108}109currSegmentTime += timeDeltas[i];110}111if (currSegmentId) {112distilledIds.push(currSegmentId);113distilledDeltas.push(currSegmentTime);114}115116return {117startTime: profile.startTime,118endTime: profile.endTime,119deltas: distilledDeltas,120ids: distilledIds,121data: profile,122getAggregatedTimes: () => {123const segmentsToTime = new Map<ProfileSegmentId, number>();124for (let i = 0; i < distilledIds.length; i++) {125const id = distilledIds[i];126segmentsToTime.set(id, (segmentsToTime.get(id) || 0) + distilledDeltas[i]);127}128return segmentsToTime;129}130};131}132}133134135