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