Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/base/node/profiling.ts
13389 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 type { Client } from 'chrome-remote-interface';
7
import { timeout } from '../common/async.js';
8
9
export interface ProfileResult {
10
profile: Profile;
11
}
12
13
export interface Profile {
14
nodes: ProfileNode[];
15
samples?: number[];
16
timeDeltas?: number[];
17
startTime: number;
18
endTime: number;
19
}
20
21
export interface ProfileNode {
22
id: number;
23
hitCount?: number;
24
children?: number[];
25
callFrame: {
26
url: string;
27
scriptId: string;
28
functionName: string;
29
lineNumber: number;
30
columnNumber: number;
31
};
32
deoptReason?: string;
33
positionTicks?: { line: number; ticks: number }[];
34
}
35
36
export interface ProfilingSession {
37
stop(afterDelay?: number): Promise<ProfileResult>;
38
}
39
40
export interface Target {
41
description: string;
42
devtoolsFrontendUrl: string;
43
id: string;
44
title: string;
45
type: string;
46
url: string;
47
webSocketDebuggerUrl: string;
48
}
49
50
export interface StartOptions {
51
host?: string;
52
port: number;
53
tries?: number;
54
retryWait?: number;
55
checkForPaused?: boolean;
56
target?: (targets: Target[]) => Target;
57
}
58
59
60
async function connectWithRetry(host: string | undefined, port: number, tries: number = 10, retryWait: number = 50, errors: Error[] = [], target?: (targets: Target[]) => Target): Promise<Client> {
61
if (typeof target === 'undefined') {
62
target = function (targets: Target[]) {
63
const target = targets.find(target => {
64
if (target.webSocketDebuggerUrl) {
65
if (target.type === 'page') {
66
return target.url.indexOf('bootstrap/index.html') > 0;
67
} else {
68
return true;
69
}
70
}
71
return false;
72
});
73
if (!target) {
74
throw new class extends Error {
75
code: string;
76
constructor() {
77
super('no target');
78
this.code = 'ECONNREFUSED';
79
}
80
};
81
}
82
return target;
83
};
84
}
85
86
const { default: cdp } = await import('chrome-remote-interface');
87
88
try {
89
return await cdp({
90
host,
91
port,
92
target,
93
local: true,
94
});
95
} catch (e) {
96
errors.push(e);
97
if (tries <= 1) {
98
throw new class extends Error {
99
errors: Error[];
100
constructor() {
101
super('failed to connect');
102
this.errors = errors;
103
}
104
};
105
}
106
await timeout(retryWait);
107
return connectWithRetry(host, port, tries - 1, retryWait, errors, target);
108
}
109
}
110
111
export async function startProfiling(options: StartOptions): Promise<ProfilingSession> {
112
113
const client = await connectWithRetry(options.host, options.port, options.tries, options.retryWait, [], options.target);
114
const { Runtime, Profiler } = client;
115
116
if (options.checkForPaused) {
117
// ensure the runtime isn't being debugged
118
const { Debugger } = client;
119
let isPaused = false;
120
client.on('event', (message) => {
121
if (message.method === 'Debugger.paused') {
122
isPaused = true;
123
}
124
});
125
await Debugger.enable();
126
if (isPaused) {
127
// client.close();
128
// ^ this leaks the connection but there is an issue in
129
// chrome that it will resume the runtime whenever a client
130
// disconnects. Because things are relatively short-lived
131
// we trade the leakage for being able to debug
132
throw new Error('runtime is paused');
133
}
134
} else {
135
// resume from inspect-brk
136
await Runtime.runIfWaitingForDebugger();
137
}
138
139
// now start profiling
140
await Profiler.enable();
141
await Profiler.setSamplingInterval({ interval: 100 });
142
await Profiler.start();
143
144
return {
145
stop: async function (n: number = 0) {
146
if (n > 0) {
147
await timeout(n);
148
}
149
const data = await Profiler.stop();
150
await client.close();
151
return data as ProfileResult;
152
}
153
};
154
}
155
156