Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/test/automation/src/profiler.ts
3520 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
const { decode_bytes } = require('@vscode/v8-heap-parser');
7
import { Code } from './code';
8
import { PlaywrightDriver } from './playwrightDriver';
9
10
export class Profiler {
11
constructor(private readonly code: Code) {
12
}
13
14
async checkObjectLeaks(classNames: string | string[], fn: () => Promise<void>): Promise<void> {
15
await this.code.driver.startCDP();
16
17
const classNamesArray = Array.isArray(classNames) ? classNames : [classNames];
18
const countsBefore = await getInstances(this.code.driver, classNamesArray);
19
20
await fn();
21
22
const countAfter = await getInstances(this.code.driver, classNamesArray);
23
const leaks: string[] = [];
24
for (const className of classNamesArray) {
25
const count = countAfter[className] ?? 0;
26
const countBefore = countsBefore[className] ?? 0;
27
if (count !== countBefore) {
28
leaks.push(`Leaked ${count - countBefore} ${className}`);
29
}
30
}
31
32
if (leaks.length > 0) {
33
throw new Error(leaks.join('\n'));
34
}
35
}
36
37
async checkHeapLeaks(classNames: string | string[], fn: () => Promise<void>): Promise<void> {
38
await this.code.driver.startCDP();
39
await fn();
40
41
const heapSnapshotAfter = await this.code.driver.takeHeapSnapshot();
42
const buff = Buffer.from(heapSnapshotAfter);
43
const graph = await decode_bytes(buff);
44
const counts: number[] = Array.from(graph.get_class_counts(classNames));
45
const leaks: string[] = [];
46
for (let i = 0; i < classNames.length; i++) {
47
if (counts[i] > 0) {
48
leaks.push(`Leaked ${counts[i]} ${classNames[i]}`);
49
}
50
}
51
52
if (leaks.length > 0) {
53
throw new Error(leaks.join('\n'));
54
}
55
}
56
}
57
58
/**
59
* Copied from src/vs/base/common/uuid.ts
60
*/
61
export function generateUuid(): string {
62
// use `randomUUID` if possible
63
if (typeof crypto.randomUUID === 'function') {
64
// see https://developer.mozilla.org/en-US/docs/Web/API/Window/crypto
65
// > Although crypto is available on all windows, the returned Crypto object only has one
66
// > usable feature in insecure contexts: the getRandomValues() method.
67
// > In general, you should use this API only in secure contexts.
68
69
return crypto.randomUUID.bind(crypto)();
70
}
71
72
// prep-work
73
const _data = new Uint8Array(16);
74
const _hex: string[] = [];
75
for (let i = 0; i < 256; i++) {
76
_hex.push(i.toString(16).padStart(2, '0'));
77
}
78
79
// get data
80
crypto.getRandomValues(_data);
81
82
// set version bits
83
_data[6] = (_data[6] & 0x0f) | 0x40;
84
_data[8] = (_data[8] & 0x3f) | 0x80;
85
86
// print as string
87
let i = 0;
88
let result = '';
89
result += _hex[_data[i++]];
90
result += _hex[_data[i++]];
91
result += _hex[_data[i++]];
92
result += _hex[_data[i++]];
93
result += '-';
94
result += _hex[_data[i++]];
95
result += _hex[_data[i++]];
96
result += '-';
97
result += _hex[_data[i++]];
98
result += _hex[_data[i++]];
99
result += '-';
100
result += _hex[_data[i++]];
101
result += _hex[_data[i++]];
102
result += '-';
103
result += _hex[_data[i++]];
104
result += _hex[_data[i++]];
105
result += _hex[_data[i++]];
106
result += _hex[_data[i++]];
107
result += _hex[_data[i++]];
108
result += _hex[_data[i++]];
109
return result;
110
}
111
112
113
114
/*---------------------------------------------------------------------------------------------
115
* The MIT License (MIT)
116
* Copyright (c) 2023-present, Simon Siefke
117
*
118
* This code is derived from https://github.com/SimonSiefke/vscode-memory-leak-finder
119
*--------------------------------------------------------------------------------------------*/
120
121
const getInstances = async (driver: PlaywrightDriver, classNames: string[]): Promise<{ [key: string]: number }> => {
122
await driver.collectGarbage();
123
const objectGroup = `og:${generateUuid()}`;
124
const prototypeDescriptor = await driver.evaluate({
125
expression: 'Object.prototype',
126
returnByValue: false,
127
objectGroup,
128
});
129
const objects = await driver.queryObjects({
130
prototypeObjectId: prototypeDescriptor.result.objectId!,
131
objectGroup,
132
});
133
const fnResult1 = await driver.callFunctionOn({
134
functionDeclaration: `function(){
135
const objects = this
136
const classNames = ${JSON.stringify(classNames)}
137
138
const nativeConstructors = [
139
Object,
140
Array,
141
Function,
142
Set,
143
Map,
144
WeakMap,
145
WeakSet,
146
RegExp,
147
Node,
148
HTMLScriptElement,
149
DOMRectReadOnly,
150
DOMRect,
151
HTMLHtmlElement,
152
Node,
153
DOMTokenList,
154
HTMLUListElement,
155
HTMLStyleElement,
156
HTMLDivElement,
157
HTMLCollection,
158
FocusEvent,
159
Promise,
160
HTMLLinkElement,
161
HTMLLIElement,
162
HTMLAnchorElement,
163
HTMLSpanElement,
164
ArrayBuffer,
165
Uint16Array,
166
HTMLLabelElement,
167
TrustedTypePolicy,
168
Uint8Array,
169
Uint32Array,
170
HTMLHeadingElement,
171
MediaQueryList,
172
HTMLDocument,
173
TextDecoder,
174
TextEncoder,
175
HTMLInputElement,
176
HTMLCanvasElement,
177
HTMLIFrameElement,
178
Int32Array,
179
CSSStyleDeclaration
180
]
181
182
const isNativeConstructor = object => {
183
return nativeConstructors.includes(object.constructor) ||
184
object.constructor.name === 'AsyncFunction' ||
185
object.constructor.name === 'GeneratorFunction' ||
186
object.constructor.name === 'AsyncGeneratorFunction'
187
}
188
189
const isInstance = (object) => {
190
return object && !isNativeConstructor(object)
191
}
192
193
const instances = objects.filter(isInstance)
194
195
const counts = Object.create(null)
196
for(const instance of instances){
197
const name=instance.constructor.name
198
if(classNames.includes(name)){
199
counts[name]||= 0
200
counts[name]++
201
}
202
}
203
return counts
204
}`,
205
objectId: objects.objects.objectId,
206
returnByValue: true,
207
objectGroup,
208
});
209
210
const returnObject = fnResult1.result.value;
211
await driver.releaseObjectGroup({ objectGroup: objectGroup });
212
return returnObject;
213
};
214
215