Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/base/node/ps.ts
5230 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 { exec } from 'child_process';
7
import { totalmem } from 'os';
8
import { FileAccess } from '../common/network.js';
9
import { ProcessItem } from '../common/processes.js';
10
import { isWindows } from '../common/platform.js';
11
12
export const JS_FILENAME_PATTERN = /[a-zA-Z-]+\.js\b/g;
13
14
export function listProcesses(rootPid: number): Promise<ProcessItem> {
15
return new Promise((resolve, reject) => {
16
let rootItem: ProcessItem | undefined;
17
const map = new Map<number, ProcessItem>();
18
const totalMemory = totalmem();
19
20
function addToTree(pid: number, ppid: number, cmd: string, load: number, mem: number) {
21
const parent = map.get(ppid);
22
if (pid === rootPid || parent) {
23
const item: ProcessItem = {
24
name: findName(cmd),
25
cmd,
26
pid,
27
ppid,
28
load,
29
mem: isWindows ? mem : (totalMemory * (mem / 100))
30
};
31
map.set(pid, item);
32
33
if (pid === rootPid) {
34
rootItem = item;
35
}
36
37
if (parent) {
38
if (!parent.children) {
39
parent.children = [];
40
}
41
parent.children.push(item);
42
if (parent.children.length > 1) {
43
parent.children = parent.children.sort((a, b) => a.pid - b.pid);
44
}
45
}
46
}
47
}
48
49
function findName(cmd: string): string {
50
const UTILITY_NETWORK_HINT = /--utility-sub-type=network/i;
51
const WINDOWS_CRASH_REPORTER = /--crashes-directory/i;
52
const CONPTY = /conhost\.exe.+--headless/i;
53
const TYPE = /--type=([a-zA-Z-]+)/;
54
55
// find windows crash reporter
56
if (WINDOWS_CRASH_REPORTER.exec(cmd)) {
57
return 'electron-crash-reporter';
58
}
59
60
// find conpty process
61
if (CONPTY.exec(cmd)) {
62
return 'conpty-agent';
63
}
64
65
// find "--type=xxxx"
66
let matches = TYPE.exec(cmd);
67
if (matches && matches.length === 2) {
68
if (matches[1] === 'renderer') {
69
return `window`;
70
} else if (matches[1] === 'utility') {
71
if (UTILITY_NETWORK_HINT.exec(cmd)) {
72
return 'utility-network-service';
73
}
74
75
return 'utility-process';
76
} else if (matches[1] === 'extensionHost') {
77
return 'extension-host'; // normalize remote extension host type
78
}
79
return matches[1];
80
}
81
82
if (cmd.indexOf('node ') < 0 && cmd.indexOf('node.exe') < 0) {
83
let result = ''; // find all xyz.js
84
do {
85
matches = JS_FILENAME_PATTERN.exec(cmd);
86
if (matches) {
87
result += matches + ' ';
88
}
89
} while (matches);
90
91
if (result) {
92
return `electron-nodejs (${result.trim()})`;
93
}
94
}
95
96
return cmd;
97
}
98
99
if (process.platform === 'win32') {
100
const cleanUNCPrefix = (value: string): string => {
101
if (value.indexOf('\\\\?\\') === 0) {
102
return value.substring(4);
103
} else if (value.indexOf('\\??\\') === 0) {
104
return value.substring(4);
105
} else if (value.indexOf('"\\\\?\\') === 0) {
106
return '"' + value.substring(5);
107
} else if (value.indexOf('"\\??\\') === 0) {
108
return '"' + value.substring(5);
109
} else {
110
return value;
111
}
112
};
113
114
(import('@vscode/windows-process-tree')).then(windowsProcessTree => {
115
windowsProcessTree.getProcessList(rootPid, (processList) => {
116
if (!processList) {
117
reject(new Error(`Root process ${rootPid} not found`));
118
return;
119
}
120
windowsProcessTree.getProcessCpuUsage(processList, (completeProcessList) => {
121
const processItems: Map<number, ProcessItem> = new Map();
122
completeProcessList.forEach(process => {
123
const commandLine = cleanUNCPrefix(process.commandLine || '');
124
processItems.set(process.pid, {
125
name: findName(commandLine),
126
cmd: commandLine,
127
pid: process.pid,
128
ppid: process.ppid,
129
load: process.cpu || 0,
130
mem: process.memory || 0
131
});
132
});
133
134
rootItem = processItems.get(rootPid);
135
if (rootItem) {
136
processItems.forEach(item => {
137
const parent = processItems.get(item.ppid);
138
if (parent) {
139
if (!parent.children) {
140
parent.children = [];
141
}
142
parent.children.push(item);
143
}
144
});
145
146
processItems.forEach(item => {
147
if (item.children) {
148
item.children = item.children.sort((a, b) => a.pid - b.pid);
149
}
150
});
151
resolve(rootItem);
152
} else {
153
reject(new Error(`Root process ${rootPid} not found`));
154
}
155
});
156
}, windowsProcessTree.ProcessDataFlag.CommandLine | windowsProcessTree.ProcessDataFlag.Memory);
157
});
158
}
159
160
// OS X & Linux
161
else {
162
function calculateLinuxCpuUsage() {
163
164
// Flatten rootItem to get a list of all VSCode processes
165
let processes = [rootItem];
166
const pids: number[] = [];
167
while (processes.length) {
168
const process = processes.shift();
169
if (process) {
170
pids.push(process.pid);
171
if (process.children) {
172
processes = processes.concat(process.children);
173
}
174
}
175
}
176
177
// The cpu usage value reported on Linux is the average over the process lifetime,
178
// recalculate the usage over a one second interval
179
// JSON.stringify is needed to escape spaces, https://github.com/nodejs/node/issues/6803
180
let cmd = JSON.stringify(FileAccess.asFileUri('vs/base/node/cpuUsage.sh').fsPath);
181
cmd += ' ' + pids.join(' ');
182
183
exec(cmd, {}, (err, stdout, stderr) => {
184
if (err || stderr) {
185
reject(err || new Error(stderr.toString()));
186
} else {
187
const cpuUsage = stdout.toString().split('\n');
188
for (let i = 0; i < pids.length; i++) {
189
const processInfo = map.get(pids[i])!;
190
processInfo.load = parseFloat(cpuUsage[i]);
191
}
192
193
if (!rootItem) {
194
reject(new Error(`Root process ${rootPid} not found`));
195
return;
196
}
197
198
resolve(rootItem);
199
}
200
});
201
}
202
203
exec('which ps', {}, (err, stdout, stderr) => {
204
if (err || stderr) {
205
if (process.platform !== 'linux') {
206
reject(err || new Error(stderr.toString()));
207
} else {
208
const cmd = JSON.stringify(FileAccess.asFileUri('vs/base/node/ps.sh').fsPath);
209
exec(cmd, {}, (err, stdout, stderr) => {
210
if (err || stderr) {
211
reject(err || new Error(stderr.toString()));
212
} else {
213
parsePsOutput(stdout, addToTree);
214
calculateLinuxCpuUsage();
215
}
216
});
217
}
218
} else {
219
const ps = stdout.toString().trim();
220
const args = '-ax -o pid=,ppid=,pcpu=,pmem=,command=';
221
222
// Set numeric locale to ensure '.' is used as the decimal separator
223
exec(`${ps} ${args}`, { maxBuffer: 1000 * 1024, env: { LC_NUMERIC: 'en_US.UTF-8' } }, (err, stdout, stderr) => {
224
// Silently ignoring the screen size is bogus error. See https://github.com/microsoft/vscode/issues/98590
225
if (err || (stderr && !stderr.includes('screen size is bogus'))) {
226
reject(err || new Error(stderr.toString()));
227
} else {
228
parsePsOutput(stdout, addToTree);
229
230
if (process.platform === 'linux') {
231
calculateLinuxCpuUsage();
232
} else {
233
if (!rootItem) {
234
reject(new Error(`Root process ${rootPid} not found`));
235
} else {
236
resolve(rootItem);
237
}
238
}
239
}
240
});
241
}
242
});
243
}
244
});
245
}
246
247
function parsePsOutput(stdout: string, addToTree: (pid: number, ppid: number, cmd: string, load: number, mem: number) => void): void {
248
const PID_CMD = /^\s*([0-9]+)\s+([0-9]+)\s+([0-9]+\.[0-9]+)\s+([0-9]+\.[0-9]+)\s+(.+)$/;
249
const lines = stdout.toString().split('\n');
250
for (const line of lines) {
251
const matches = PID_CMD.exec(line.trim());
252
if (matches && matches.length === 6) {
253
addToTree(parseInt(matches[1]), parseInt(matches[2]), matches[5], parseFloat(matches[3]), parseFloat(matches[4]));
254
}
255
}
256
}
257
258