Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { TernarySearchTree } from '../../../../base/common/ternarySearchTree.js';
7
import { IExtensionHostProfile, IExtensionService, ProfileSegmentId, ProfileSession } from '../common/extensions.js';
8
import { IExtensionDescription } from '../../../../platform/extensions/common/extensions.js';
9
import { Schemas } from '../../../../base/common/network.js';
10
import { URI } from '../../../../base/common/uri.js';
11
import { IV8InspectProfilingService, IV8Profile, IV8ProfileNode } from '../../../../platform/profiling/common/profiling.js';
12
import { createSingleCallFunction } from '../../../../base/common/functional.js';
13
14
export class ExtensionHostProfiler {
15
16
constructor(
17
private readonly _host: string,
18
private readonly _port: number,
19
@IExtensionService private readonly _extensionService: IExtensionService,
20
@IV8InspectProfilingService private readonly _profilingService: IV8InspectProfilingService,
21
) {
22
}
23
24
public async start(): Promise<ProfileSession> {
25
26
const id = await this._profilingService.startProfiling({ host: this._host, port: this._port });
27
28
return {
29
stop: createSingleCallFunction(async () => {
30
const profile = await this._profilingService.stopProfiling(id);
31
await this._extensionService.whenInstalledExtensionsRegistered();
32
const extensions = this._extensionService.extensions;
33
return this._distill(profile, extensions);
34
})
35
};
36
}
37
38
private _distill(profile: IV8Profile, extensions: readonly IExtensionDescription[]): IExtensionHostProfile {
39
const searchTree = TernarySearchTree.forUris<IExtensionDescription>();
40
for (const extension of extensions) {
41
if (extension.extensionLocation.scheme === Schemas.file) {
42
searchTree.set(URI.file(extension.extensionLocation.fsPath), extension);
43
}
44
}
45
46
const nodes = profile.nodes;
47
const idsToNodes = new Map<number, IV8ProfileNode>();
48
const idsToSegmentId = new Map<number, ProfileSegmentId | null>();
49
for (const node of nodes) {
50
idsToNodes.set(node.id, node);
51
}
52
53
function visit(node: IV8ProfileNode, segmentId: ProfileSegmentId | null) {
54
if (!segmentId) {
55
switch (node.callFrame.functionName) {
56
case '(root)':
57
break;
58
case '(program)':
59
segmentId = 'program';
60
break;
61
case '(garbage collector)':
62
segmentId = 'gc';
63
break;
64
default:
65
segmentId = 'self';
66
break;
67
}
68
} else if (segmentId === 'self' && node.callFrame.url) {
69
let extension: IExtensionDescription | undefined;
70
try {
71
extension = searchTree.findSubstr(URI.parse(node.callFrame.url));
72
} catch {
73
// ignore
74
}
75
if (extension) {
76
segmentId = extension.identifier.value;
77
}
78
}
79
idsToSegmentId.set(node.id, segmentId);
80
81
if (node.children) {
82
for (const child of node.children) {
83
const childNode = idsToNodes.get(child);
84
if (childNode) {
85
visit(childNode, segmentId);
86
}
87
}
88
}
89
}
90
visit(nodes[0], null);
91
92
const samples = profile.samples || [];
93
const timeDeltas = profile.timeDeltas || [];
94
const distilledDeltas: number[] = [];
95
const distilledIds: ProfileSegmentId[] = [];
96
97
let currSegmentTime = 0;
98
let currSegmentId: string | undefined;
99
for (let i = 0; i < samples.length; i++) {
100
const id = samples[i];
101
const segmentId = idsToSegmentId.get(id);
102
if (segmentId !== currSegmentId) {
103
if (currSegmentId) {
104
distilledIds.push(currSegmentId);
105
distilledDeltas.push(currSegmentTime);
106
}
107
currSegmentId = segmentId ?? undefined;
108
currSegmentTime = 0;
109
}
110
currSegmentTime += timeDeltas[i];
111
}
112
if (currSegmentId) {
113
distilledIds.push(currSegmentId);
114
distilledDeltas.push(currSegmentTime);
115
}
116
117
return {
118
startTime: profile.startTime,
119
endTime: profile.endTime,
120
deltas: distilledDeltas,
121
ids: distilledIds,
122
data: profile,
123
getAggregatedTimes: () => {
124
const segmentsToTime = new Map<ProfileSegmentId, number>();
125
for (let i = 0; i < distilledIds.length; i++) {
126
const id = distilledIds[i];
127
segmentsToTime.set(id, (segmentsToTime.get(id) || 0) + distilledDeltas[i]);
128
}
129
return segmentsToTime;
130
}
131
};
132
}
133
}
134
135