src/vs/base/node/ps.ts

256 LOC · 15 covered · 241 uncovered · 2 ranges · 1 concepts · 1 introducers · 9 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the fileps.ts ×2 · 15 introduced LOCps.ts ×2ps.test|title=Process Utils JS file regex should match .js at end of command|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/base/test/node/ps.test|title=Process Utils JS file regex should match .js at end of command|occurrence=1ps.test|title=Process Ut…ps.test|title=Process Utils JS file regex should match .js but not .json in same command|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/base/test/node/ps.test|title=Process Utils JS file regex should match .js but not .json in same command|occurrence=1ps.test|title=Process Ut…ps.test|title=Process Utils JS file regex should match .js files with hyphens|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/base/test/node/ps.test|title=Process Utils JS file regex should match .js files with hyphens|occurrence=1ps.test|title=Process Ut…ps.test|title=Process Utils JS file regex should match multiple .js files|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/base/test/node/ps.test|title=Process Utils JS file regex should match multiple .js files|occurrence=1ps.test|title=Process Ut…ps.test|title=Process Utils JS file regex should match simple .js files|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/base/test/node/ps.test|title=Process Utils JS file regex should match simple .js files|occurrence=1ps.test|title=Process Ut…ps.test|title=Process Utils JS file regex should not match .js prefix in .json extension (regression test for \b fix)|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/base/test/node/ps.test|title=Process Utils JS file regex should not match .js prefix in .json extension (regression test for \b fix)|occurrence=1ps.test|title=Process Ut…ps.test|title=Process Utils JS file regex should not match .json files|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/base/test/node/ps.test|title=Process Utils JS file regex should not match .json files|occurrence=1ps.test|title=Process Ut…ps.test|title=Process Utils JS file regex should not match .jsx files|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/base/test/node/ps.test|title=Process Utils JS file regex should not match .jsx files|occurrence=1ps.test|title=Process Ut…ps.test|title=Process Utils JS file regex should not match partial matches inside other extensions|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/base/test/node/ps.test|title=Process Utils JS file regex should not match partial matches inside other extensions|occurrence=1ps.test|title=Process Ut…Focused file · src/vs/base/node/ps.ts · 256 LOCnode/ps.ts

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- ps.ts ×2
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 > ps.ts ×2
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 }