@lvce-editor/shared-process 0.15.26 → 0.15.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/builtinCommands.json +4 -0
- package/extensions/builtin.language-basics-typescript/src/tokenizeTypeScript.js +47 -0
- package/mocks/windows-process-tree.js +1 -0
- package/package.json +5 -5
- package/src/parts/CreatePidMap/CreatePidMap.js +6 -0
- package/src/parts/ErrorCodes/ErrorCodes.js +1 -0
- package/src/parts/ErrorType/ErrorType.js +4 -0
- package/src/parts/GetAccurateMemoryUsage/GetAccurateMemoryUsage.js +38 -0
- package/src/parts/GetElectronRebuildPath/GetElectronRebuildPath.js +6 -0
- package/src/parts/GetErrorConstructor/GetErrorConstructor.js +28 -0
- package/src/parts/GetPsOutput/GetPsOutput.js +20 -0
- package/src/parts/JsonRpc/JsonRpc.js +9 -4
- package/src/parts/ListProcessGetName/ListProcessGetName.js +54 -0
- package/src/parts/ListProcessesWithMemoryUsage/ListProcessesWithMemoryUsage.ipc.js +7 -0
- package/src/parts/ListProcessesWithMemoryUsage/ListProcessesWithMemoryUsage.js +13 -0
- package/src/parts/ListProcessesWithMemoryUsageUnix/ListProcessesWithMemoryUsageUnix.js +32 -0
- package/src/parts/ListProcessesWithMemoryUsageWindows/ListProcessesWithMemoryUsageWindows.js +51 -0
- package/src/parts/LoadWindowsProcessTree/LoadWindowsProcessTree.js +16 -0
- package/src/parts/Module/Module.js +2 -0
- package/src/parts/ModuleId/ModuleId.js +1 -0
- package/src/parts/ModuleMap/ModuleMap.js +3 -0
- package/src/parts/ParentIpc/ParentIpc.js +8 -0
- package/src/parts/ParsePsOutput/ParsePsOutput.js +50 -0
- package/src/parts/Process/Process.ipc.js +1 -0
- package/src/parts/RebuildNodePty/RebuildNodePty.js +11 -10
- package/src/parts/RestoreJsonRpcError/RestoreJsonRpcError.js +79 -0
- package/src/parts/UnwrapJsonRpcResult/UnwrapJsonRpcResult.js +13 -0
- package/src/parts/WindowsProcessTree/WindowsProcessTree.js +26 -0
- package/src/parts/WindowsProcessTreeDataFlag/WindowsProcessTreeDataFlag.js +3 -0
|
@@ -37,6 +37,7 @@ const State = {
|
|
|
37
37
|
InsideBacktickString: 34,
|
|
38
38
|
AfterInterfaceName: 35,
|
|
39
39
|
AfterKeywordImport: 36,
|
|
40
|
+
AfterKeywordFunction: 37,
|
|
40
41
|
// AfterKeywordImport: 27,
|
|
41
42
|
}
|
|
42
43
|
|
|
@@ -246,6 +247,7 @@ export const tokenizeLine = (line, lineState) => {
|
|
|
246
247
|
break
|
|
247
248
|
case 'let':
|
|
248
249
|
case 'const':
|
|
250
|
+
case 'var':
|
|
249
251
|
token = TokenType.Keyword
|
|
250
252
|
state = State.AfterKeywordDeclaration
|
|
251
253
|
break
|
|
@@ -281,6 +283,10 @@ export const tokenizeLine = (line, lineState) => {
|
|
|
281
283
|
token = TokenType.KeywordOperator
|
|
282
284
|
state = State.TopLevelContent
|
|
283
285
|
break
|
|
286
|
+
case 'function':
|
|
287
|
+
token = TokenType.Keyword
|
|
288
|
+
state = State.AfterKeywordFunction
|
|
289
|
+
break
|
|
284
290
|
default:
|
|
285
291
|
token = TokenType.Keyword
|
|
286
292
|
state = State.TopLevelContent
|
|
@@ -398,6 +404,15 @@ export const tokenizeLine = (line, lineState) => {
|
|
|
398
404
|
} else if ((next = part.match(RE_SEMICOLON))) {
|
|
399
405
|
token = TokenType.Punctuation
|
|
400
406
|
state = State.TopLevelContent
|
|
407
|
+
} else if ((next = part.match(RE_COMMA))) {
|
|
408
|
+
token = TokenType.Punctuation
|
|
409
|
+
state = State.AfterKeywordDeclaration
|
|
410
|
+
} else if ((next = part.match(RE_PUNCTUATION))) {
|
|
411
|
+
token = TokenType.Punctuation
|
|
412
|
+
state = State.AfterKeywordDeclaration
|
|
413
|
+
} else if ((next = part.match(RE_QUOTE_DOUBLE))) {
|
|
414
|
+
token = TokenType.Punctuation
|
|
415
|
+
state = State.InsideDoubleQuoteString
|
|
401
416
|
} else {
|
|
402
417
|
line
|
|
403
418
|
part
|
|
@@ -745,6 +760,9 @@ export const tokenizeLine = (line, lineState) => {
|
|
|
745
760
|
} else if ((next = part.match(RE_VARIABLE_NAME))) {
|
|
746
761
|
token = TokenType.VariableName
|
|
747
762
|
state = State.InsideObjectDestructuringAfterValue
|
|
763
|
+
} else if ((next = part.match(RE_QUOTE_DOUBLE))) {
|
|
764
|
+
token = TokenType.Punctuation
|
|
765
|
+
state = State.InsideDoubleQuoteString
|
|
748
766
|
} else {
|
|
749
767
|
throw new Error('no')
|
|
750
768
|
}
|
|
@@ -756,6 +774,21 @@ export const tokenizeLine = (line, lineState) => {
|
|
|
756
774
|
} else if ((next = part.match(RE_CURLY_CLOSE))) {
|
|
757
775
|
token = TokenType.Punctuation
|
|
758
776
|
state = State.AfterKeywordDeclaration
|
|
777
|
+
} else if ((next = part.match(RE_COLON))) {
|
|
778
|
+
token = TokenType.Punctuation
|
|
779
|
+
state = State.InsideObjectDestructuringAfterValue
|
|
780
|
+
} else if ((next = part.match(RE_VARIABLE_NAME))) {
|
|
781
|
+
token = TokenType.VariableName
|
|
782
|
+
state = State.InsideObjectDestructuringAfterValue
|
|
783
|
+
} else if ((next = part.match(RE_PUNCTUATION))) {
|
|
784
|
+
token = TokenType.Punctuation
|
|
785
|
+
state = State.TopLevelContent
|
|
786
|
+
} else if ((next = part.match(RE_QUOTE_DOUBLE))) {
|
|
787
|
+
token = TokenType.Punctuation
|
|
788
|
+
state = State.InsideDoubleQuoteString
|
|
789
|
+
} else if ((next = part.match(RE_QUOTE_SINGLE))) {
|
|
790
|
+
token = TokenType.Punctuation
|
|
791
|
+
state = State.InsideSingleQuoteString
|
|
759
792
|
} else {
|
|
760
793
|
throw new Error('no')
|
|
761
794
|
}
|
|
@@ -959,6 +992,20 @@ export const tokenizeLine = (line, lineState) => {
|
|
|
959
992
|
throw new Error('no')
|
|
960
993
|
}
|
|
961
994
|
break
|
|
995
|
+
case State.AfterKeywordFunction:
|
|
996
|
+
if ((next = part.match(RE_WHITESPACE))) {
|
|
997
|
+
token = TokenType.Whitespace
|
|
998
|
+
state = State.AfterKeywordFunction
|
|
999
|
+
} else if ((next = part.match(RE_VARIABLE_NAME))) {
|
|
1000
|
+
token = TokenType.FunctionName
|
|
1001
|
+
state = State.TopLevelContent
|
|
1002
|
+
} else if ((next = part.match(RE_PUNCTUATION))) {
|
|
1003
|
+
token = TokenType.Punctuation
|
|
1004
|
+
state = State.TopLevelContent
|
|
1005
|
+
} else {
|
|
1006
|
+
throw new Error('no')
|
|
1007
|
+
}
|
|
1008
|
+
break
|
|
962
1009
|
default:
|
|
963
1010
|
state
|
|
964
1011
|
throw new Error('no')
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lvce-editor/shared-process",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.28",
|
|
4
4
|
"description": "Utility package for @lvce-editor/server",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
"node": ">=16"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@babel/code-frame": "^7.
|
|
20
|
-
"@lvce-editor/extension-host": "0.15.
|
|
21
|
-
"@lvce-editor/extension-host-helper-process": "0.15.
|
|
22
|
-
"@lvce-editor/pty-host": "0.15.
|
|
19
|
+
"@babel/code-frame": "^7.22.5",
|
|
20
|
+
"@lvce-editor/extension-host": "0.15.28",
|
|
21
|
+
"@lvce-editor/extension-host-helper-process": "0.15.28",
|
|
22
|
+
"@lvce-editor/pty-host": "0.15.28",
|
|
23
23
|
"debug": "^4.3.4",
|
|
24
24
|
"execa": "^7.1.1",
|
|
25
25
|
"exit-hook": "^3.2.0",
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { join } from 'node:path'
|
|
2
|
+
import { readFile } from 'node:fs/promises'
|
|
3
|
+
import { VError } from '../VError/VError.js'
|
|
4
|
+
import * as Assert from '../Assert/Assert.js'
|
|
5
|
+
import * as EncodingType from '../EncodingType/EncodingType.js'
|
|
6
|
+
import * as ErrorCodes from '../ErrorCodes/ErrorCodes.js'
|
|
7
|
+
|
|
8
|
+
export const getAccurateMemoryUsage = async (pid) => {
|
|
9
|
+
Assert.number(pid)
|
|
10
|
+
try {
|
|
11
|
+
const filePath = join('/proc', `${pid}`, 'statm')
|
|
12
|
+
let content
|
|
13
|
+
try {
|
|
14
|
+
content = await readFile(filePath, EncodingType.Utf8)
|
|
15
|
+
} catch (error) {
|
|
16
|
+
if (
|
|
17
|
+
error &&
|
|
18
|
+
// @ts-ignore
|
|
19
|
+
(error.code === ErrorCodes.ENOENT ||
|
|
20
|
+
// @ts-ignore
|
|
21
|
+
error.code === ErrorCodes.ESRCH)
|
|
22
|
+
) {
|
|
23
|
+
return -1
|
|
24
|
+
}
|
|
25
|
+
throw error
|
|
26
|
+
}
|
|
27
|
+
const trimmedContent = content.trim()
|
|
28
|
+
const numberBlocks = trimmedContent.split(' ')
|
|
29
|
+
const pageSize = 4096
|
|
30
|
+
const rss = Number.parseInt(numberBlocks[1]) * pageSize
|
|
31
|
+
const shared = Number.parseInt(numberBlocks[2]) * pageSize
|
|
32
|
+
const memory = rss - shared
|
|
33
|
+
return memory
|
|
34
|
+
} catch (error) {
|
|
35
|
+
// @ts-ignore
|
|
36
|
+
throw new VError(error, 'Failed to get accurate memory usage')
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as ErrorType from '../ErrorType/ErrorType.js'
|
|
2
|
+
|
|
3
|
+
export const getErrorConstructor = (message, type) => {
|
|
4
|
+
if (type) {
|
|
5
|
+
switch (type) {
|
|
6
|
+
case ErrorType.DomException:
|
|
7
|
+
return DOMException
|
|
8
|
+
case ErrorType.TypeError:
|
|
9
|
+
return TypeError
|
|
10
|
+
case ErrorType.SyntaxError:
|
|
11
|
+
return SyntaxError
|
|
12
|
+
case ErrorType.ReferenceError:
|
|
13
|
+
return ReferenceError
|
|
14
|
+
default:
|
|
15
|
+
return Error
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (message.startsWith('TypeError: ')) {
|
|
19
|
+
return TypeError
|
|
20
|
+
}
|
|
21
|
+
if (message.startsWith('SyntaxError: ')) {
|
|
22
|
+
return SyntaxError
|
|
23
|
+
}
|
|
24
|
+
if (message.startsWith('ReferenceError: ')) {
|
|
25
|
+
return ReferenceError
|
|
26
|
+
}
|
|
27
|
+
return Error
|
|
28
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { VError } from '../VError/VError.js'
|
|
2
|
+
import { execFile as _execFile } from 'node:child_process'
|
|
3
|
+
import * as Signal from '../Signal/Signal.js'
|
|
4
|
+
import { promisify } from 'node:util'
|
|
5
|
+
|
|
6
|
+
const execFile = promisify(_execFile)
|
|
7
|
+
|
|
8
|
+
export const getPsOutput = async () => {
|
|
9
|
+
try {
|
|
10
|
+
const { stdout } = await execFile('ps', ['-ax', '-o', 'pid=,ppid=,pcpu=,pmem=,command='])
|
|
11
|
+
return stdout.trim()
|
|
12
|
+
} catch (error) {
|
|
13
|
+
// @ts-ignore
|
|
14
|
+
if (error && error.signal === Signal.SIGINT) {
|
|
15
|
+
return ''
|
|
16
|
+
}
|
|
17
|
+
// @ts-ignore
|
|
18
|
+
throw new VError(error, `Failed to execute ps`)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as Callback from '../Callback/Callback.js'
|
|
2
2
|
import * as JsonRpcVersion from '../JsonRpcVersion/JsonRpcVersion.js'
|
|
3
|
+
import * as UnwrapJsonRpcResult from '../UnwrapJsonRpcResult/UnwrapJsonRpcResult.js'
|
|
3
4
|
|
|
4
5
|
export const send = (transport, method, ...params) => {
|
|
5
6
|
transport.send({
|
|
@@ -9,7 +10,7 @@ export const send = (transport, method, ...params) => {
|
|
|
9
10
|
})
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
export const invoke = (ipc, method, ...params) => {
|
|
13
|
+
export const invoke = async (ipc, method, ...params) => {
|
|
13
14
|
const { id, promise } = Callback.registerPromise()
|
|
14
15
|
ipc.send({
|
|
15
16
|
jsonrpc: JsonRpcVersion.Two,
|
|
@@ -17,10 +18,12 @@ export const invoke = (ipc, method, ...params) => {
|
|
|
17
18
|
params,
|
|
18
19
|
id,
|
|
19
20
|
})
|
|
20
|
-
|
|
21
|
+
const responseMessage = await promise
|
|
22
|
+
const result = UnwrapJsonRpcResult.unwrapJsonRpcResult(responseMessage)
|
|
23
|
+
return result
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
export const invokeAndTransfer = (ipc, handle, method, ...params) => {
|
|
26
|
+
export const invokeAndTransfer = async (ipc, handle, method, ...params) => {
|
|
24
27
|
const { id, promise } = Callback.registerPromise()
|
|
25
28
|
ipc.sendAndTransfer(
|
|
26
29
|
{
|
|
@@ -31,5 +34,7 @@ export const invokeAndTransfer = (ipc, handle, method, ...params) => {
|
|
|
31
34
|
},
|
|
32
35
|
handle
|
|
33
36
|
)
|
|
34
|
-
|
|
37
|
+
const responseMessage = await promise
|
|
38
|
+
const result = UnwrapJsonRpcResult.unwrapJsonRpcResult(responseMessage)
|
|
39
|
+
return result
|
|
35
40
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import * as Assert from '../Assert/Assert.js'
|
|
2
|
+
|
|
3
|
+
export const getName = (pid, cmd, rootPid, pidMap) => {
|
|
4
|
+
Assert.number(pid)
|
|
5
|
+
Assert.string(cmd)
|
|
6
|
+
Assert.number(rootPid)
|
|
7
|
+
Assert.object(pidMap)
|
|
8
|
+
if (pid === rootPid) {
|
|
9
|
+
return 'main'
|
|
10
|
+
}
|
|
11
|
+
if (cmd.includes('--type=zygote')) {
|
|
12
|
+
return 'zygote'
|
|
13
|
+
}
|
|
14
|
+
if (cmd.includes('--type=gpu-process')) {
|
|
15
|
+
return 'gpu-process'
|
|
16
|
+
}
|
|
17
|
+
if (cmd.includes('extensionHostMain.js')) {
|
|
18
|
+
return 'extension-host'
|
|
19
|
+
}
|
|
20
|
+
if (cmd.includes('ptyHostMain.js')) {
|
|
21
|
+
return 'pty-host'
|
|
22
|
+
}
|
|
23
|
+
if (cmd.includes('--lvce-window-kind=process-explorer')) {
|
|
24
|
+
return 'process-explorer'
|
|
25
|
+
}
|
|
26
|
+
if (pid in pidMap) {
|
|
27
|
+
return pidMap[pid] || `<unknown>`
|
|
28
|
+
}
|
|
29
|
+
if (cmd.includes('--type=renderer')) {
|
|
30
|
+
return `renderer`
|
|
31
|
+
}
|
|
32
|
+
if (cmd.includes('--type=utility')) {
|
|
33
|
+
return 'utility'
|
|
34
|
+
}
|
|
35
|
+
if (cmd.includes('typescript/lib/tsserver.js')) {
|
|
36
|
+
return 'tsserver.js'
|
|
37
|
+
}
|
|
38
|
+
if (cmd.includes('typescript/lib/typingsInstaller.js')) {
|
|
39
|
+
return 'typingsInstaller.js'
|
|
40
|
+
}
|
|
41
|
+
if (cmd.includes('extensionHostHelperProcessMain.js')) {
|
|
42
|
+
return 'extension-host-helper-process'
|
|
43
|
+
}
|
|
44
|
+
if (cmd.includes('/bin/rg')) {
|
|
45
|
+
return 'ripgrep'
|
|
46
|
+
}
|
|
47
|
+
if (cmd.startsWith('bash')) {
|
|
48
|
+
return 'bash'
|
|
49
|
+
}
|
|
50
|
+
if (cmd.startsWith(`/opt/sublime_text/sublime_text `)) {
|
|
51
|
+
return 'sublime-text'
|
|
52
|
+
}
|
|
53
|
+
return `${cmd}`
|
|
54
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as Platform from '../Platform/Platform.js'
|
|
2
|
+
|
|
3
|
+
const getModule = () => {
|
|
4
|
+
if (Platform.isWindows) {
|
|
5
|
+
return import('../ListProcessesWithMemoryUsageWindows/ListProcessesWithMemoryUsageWindows.js')
|
|
6
|
+
}
|
|
7
|
+
return import('../ListProcessesWithMemoryUsageUnix/ListProcessesWithMemoryUsageUnix.js')
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const listProcessesWithMemoryUsage = async (rootPid) => {
|
|
11
|
+
const module = await getModule()
|
|
12
|
+
return module.listProcessesWithMemoryUsage(rootPid)
|
|
13
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as CreatePidMap from '../CreatePidMap/CreatePidMap.js'
|
|
2
|
+
import * as GetAccurateMemoryUsage from '../GetAccurateMemoryUsage/GetAccurateMemoryUsage.js'
|
|
3
|
+
import * as GetPsOutput from '../GetPsOutput/GetPsOutput.js'
|
|
4
|
+
import * as ParsePsOutput from '../ParsePsOutput/ParsePsOutput.js'
|
|
5
|
+
|
|
6
|
+
const addAccurateMemoryUsage = async (process) => {
|
|
7
|
+
const accurateMemoryUsage = await GetAccurateMemoryUsage.getAccurateMemoryUsage(process.pid)
|
|
8
|
+
return {
|
|
9
|
+
...process,
|
|
10
|
+
memory: accurateMemoryUsage,
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const hasPositiveMemoryUsage = (process) => {
|
|
15
|
+
return process.memory >= 0
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const listProcessesWithMemoryUsage = async (rootPid) => {
|
|
19
|
+
// console.time('getPsOutput')
|
|
20
|
+
const stdout = await GetPsOutput.getPsOutput()
|
|
21
|
+
const pidMap = await CreatePidMap.createPidMap()
|
|
22
|
+
// console.log({ stdout })
|
|
23
|
+
// console.timeEnd('getPsOutput')
|
|
24
|
+
// console.time('parsePsOutput')
|
|
25
|
+
const parsed = ParsePsOutput.parsePsOutput(stdout, rootPid, pidMap)
|
|
26
|
+
// console.timeEnd('parsePsOutput')
|
|
27
|
+
// console.time('addAccurateMemoryUsage')
|
|
28
|
+
const parsedWithAccurateMemoryUsage = await Promise.all(parsed.map(addAccurateMemoryUsage))
|
|
29
|
+
// console.timeEnd('addAccurateMemoryUsage')
|
|
30
|
+
const filtered = parsedWithAccurateMemoryUsage.filter(hasPositiveMemoryUsage)
|
|
31
|
+
return filtered
|
|
32
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// listProcesses windows implementation based on https://github.com/microsoft/vscode/blob/c0769274fa136b45799edeccc0d0a2f645b75caf/src/vs/base/node/ps.ts (License MIT)
|
|
2
|
+
|
|
3
|
+
import { VError } from '../VError/VError.js'
|
|
4
|
+
import * as ListProcessGetName from '../ListProcessGetName/ListProcessGetName.js'
|
|
5
|
+
import * as WindowsProcessTree from '../WindowsProcessTree/WindowsProcessTree.js'
|
|
6
|
+
import * as WindowsProcessTreeDataFlag from '../WindowsProcessTreeDataFlag/WindowsProcessTreeDataFlag.js'
|
|
7
|
+
import * as CreatePidMap from '../CreatePidMap/CreatePidMap.js'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {import('@vscode/windows-process-tree').IProcessCpuInfo} item
|
|
11
|
+
* @param {number} rootPid
|
|
12
|
+
* @param {object} pidMap
|
|
13
|
+
*/
|
|
14
|
+
const toResultItem = (item, rootPid, pidMap) => {
|
|
15
|
+
return {
|
|
16
|
+
name: ListProcessGetName.getName(item.pid, item.commandLine, rootPid, pidMap),
|
|
17
|
+
pid: item.pid,
|
|
18
|
+
ppid: item.ppid,
|
|
19
|
+
memory: item.memory,
|
|
20
|
+
cmd: item.commandLine,
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
*
|
|
26
|
+
* @param {import('@vscode/windows-process-tree').IProcessCpuInfo[]} completeProcessList
|
|
27
|
+
* @param {number} rootPid
|
|
28
|
+
*/
|
|
29
|
+
const toResult = (completeProcessList, rootPid, pidMap) => {
|
|
30
|
+
const results = []
|
|
31
|
+
for (const item of completeProcessList) {
|
|
32
|
+
results.push(toResultItem(item, rootPid, pidMap))
|
|
33
|
+
}
|
|
34
|
+
return results
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const listProcessesWithMemoryUsage = async (rootPid) => {
|
|
38
|
+
try {
|
|
39
|
+
const processList = await WindowsProcessTree.getProcessList(rootPid, WindowsProcessTreeDataFlag.CommandLine | WindowsProcessTreeDataFlag.Memory)
|
|
40
|
+
if (!processList) {
|
|
41
|
+
throw new VError(`Root process ${rootPid} not found`)
|
|
42
|
+
}
|
|
43
|
+
const pidMap = await CreatePidMap.createPidMap()
|
|
44
|
+
const completeProcessList = await WindowsProcessTree.addCpuUsage(processList)
|
|
45
|
+
const result = toResult(completeProcessList, rootPid, pidMap)
|
|
46
|
+
return result
|
|
47
|
+
} catch (error) {
|
|
48
|
+
// @ts-ignore
|
|
49
|
+
throw new VError(error, `Failed to list processes`)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { VError } from '../VError/VError.js'
|
|
2
|
+
import * as ErrorCodes from '../ErrorCodes/ErrorCodes.js'
|
|
3
|
+
|
|
4
|
+
export const loadWindowProcessTree = async () => {
|
|
5
|
+
try {
|
|
6
|
+
// @ts-ignore
|
|
7
|
+
return await import('@vscode/windows-process-tree')
|
|
8
|
+
} catch (error) {
|
|
9
|
+
if (error && error instanceof Error && 'code' in error && error.code === ErrorCodes.ERR_DLOPEN_FAILED) {
|
|
10
|
+
throw new VError(
|
|
11
|
+
`Failed to load windows process tree: The native module "@vscode/windows-process-tree" is not compatible with this node version and must be compiled against a matching electron version using electron-rebuild`
|
|
12
|
+
)
|
|
13
|
+
}
|
|
14
|
+
throw new VError(error, `Failed to load windows process tree`)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -70,6 +70,8 @@ export const load = (moduleId) => {
|
|
|
70
70
|
return import('../GetTerminalSpawnOptions/GetTerminalSpawnOptions.ipc.js')
|
|
71
71
|
case ModuleId.HandleCliArgs:
|
|
72
72
|
return import('../HandleCliArgs/HandleCliArgs.ipc.js')
|
|
73
|
+
case ModuleId.ListProcessesWithMemoryUsage:
|
|
74
|
+
return import('../ListProcessesWithMemoryUsage/ListProcessesWithMemoryUsage.ipc.js')
|
|
73
75
|
default:
|
|
74
76
|
throw new Error(`module ${moduleId} not found`)
|
|
75
77
|
}
|
|
@@ -168,6 +168,7 @@ export const getModuleId = (commandId) => {
|
|
|
168
168
|
case 'RebuildNodePty.rebuildNodePty':
|
|
169
169
|
return ModuleId.RebuildNodePty
|
|
170
170
|
case 'Process.getPid':
|
|
171
|
+
case 'Process.kill':
|
|
171
172
|
return ModuleId.Process
|
|
172
173
|
case 'HandleNodeMessagePort.handleNodeMessagePort':
|
|
173
174
|
return ModuleId.HandleNodeMessagePort
|
|
@@ -177,6 +178,8 @@ export const getModuleId = (commandId) => {
|
|
|
177
178
|
return ModuleId.GetTerminalSpawnOptions
|
|
178
179
|
case 'HandleCliArgs.handleCliArgs':
|
|
179
180
|
return ModuleId.HandleCliArgs
|
|
181
|
+
case 'ListProcessesWithMemoryUsage.listProcessesWithMemoryUsage':
|
|
182
|
+
return ModuleId.ListProcessesWithMemoryUsage
|
|
180
183
|
default:
|
|
181
184
|
throw new CommandNotFoundError(commandId)
|
|
182
185
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as HandleIpc from '../HandleIpc/HandleIpc.js'
|
|
2
2
|
import * as IpcChild from '../IpcChild/IpcChild.js'
|
|
3
3
|
import * as IpcChildType from '../IpcChildType/IpcChildType.js'
|
|
4
|
+
import * as JsonRpc from '../JsonRpc/JsonRpc.js'
|
|
4
5
|
|
|
5
6
|
// TODO add tests for this
|
|
6
7
|
|
|
@@ -10,6 +11,7 @@ import * as IpcChildType from '../IpcChildType/IpcChildType.js'
|
|
|
10
11
|
|
|
11
12
|
export const state = {
|
|
12
13
|
electronPortMap: new Map(),
|
|
14
|
+
ipc: undefined,
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
// TODO maybe rename to hydrate
|
|
@@ -19,4 +21,10 @@ export const listen = async () => {
|
|
|
19
21
|
method,
|
|
20
22
|
})
|
|
21
23
|
HandleIpc.handleIpc(ipc)
|
|
24
|
+
// @ts-ignore
|
|
25
|
+
state.ipc = ipc
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const invoke = (method, ...params) => {
|
|
29
|
+
return JsonRpc.invoke(state.ipc, method, ...params)
|
|
22
30
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// parse ps output based on vscode https://github.com/microsoft/vscode/blob/c0769274fa136b45799edeccc0d0a2f645b75caf/src/vs/base/node/ps.ts (License MIT)
|
|
2
|
+
|
|
3
|
+
import * as Assert from '../Assert/Assert.js'
|
|
4
|
+
import * as ListProcessGetName from '../ListProcessGetName/ListProcessGetName.js'
|
|
5
|
+
import * as SplitLines from '../SplitLines/SplitLines.js'
|
|
6
|
+
|
|
7
|
+
const PID_CMD = /^\s*(\d+)\s+(\d+)\s+([\d.]+)\s+([\d.]+)\s+(.+)$/s
|
|
8
|
+
|
|
9
|
+
const parsePsOutputLine = (line) => {
|
|
10
|
+
Assert.string(line)
|
|
11
|
+
const matches = PID_CMD.exec(line.trim())
|
|
12
|
+
if (matches && matches.length === 6) {
|
|
13
|
+
return {
|
|
14
|
+
pid: Number.parseInt(matches[1]),
|
|
15
|
+
ppid: Number.parseInt(matches[2]),
|
|
16
|
+
cmd: matches[5],
|
|
17
|
+
// load: parseInt(matches[3]),
|
|
18
|
+
// mem: parseInt(matches[4]),
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
throw new Error(`line could not be parsed: ${line}`)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const parsePsOutput = (stdout, rootPid, pidMap) => {
|
|
25
|
+
Assert.string(stdout)
|
|
26
|
+
Assert.number(rootPid)
|
|
27
|
+
Assert.object(pidMap)
|
|
28
|
+
if (stdout === '') {
|
|
29
|
+
return []
|
|
30
|
+
}
|
|
31
|
+
const lines = SplitLines.splitLines(stdout)
|
|
32
|
+
const result = []
|
|
33
|
+
const depthMap = Object.create(null)
|
|
34
|
+
depthMap[rootPid] = 1
|
|
35
|
+
const parsedLines = lines.map(parsePsOutputLine)
|
|
36
|
+
for (const parsedLine of parsedLines) {
|
|
37
|
+
const { pid, ppid, cmd } = parsedLine
|
|
38
|
+
const depth = pid === rootPid ? 1 : depthMap[ppid]
|
|
39
|
+
if (!depth) {
|
|
40
|
+
continue
|
|
41
|
+
}
|
|
42
|
+
result.push({
|
|
43
|
+
...parsedLine,
|
|
44
|
+
depth,
|
|
45
|
+
name: ListProcessGetName.getName(pid, cmd, rootPid, pidMap),
|
|
46
|
+
})
|
|
47
|
+
depthMap[pid] = depth + 1
|
|
48
|
+
}
|
|
49
|
+
return result
|
|
50
|
+
}
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process'
|
|
2
|
+
import * as GetElectronRebuildPath from '../GetElectronRebuildPath/GetElectronRebuildPath.js'
|
|
2
3
|
import * as IsElectron from '../IsElectron/IsElectron.js'
|
|
3
4
|
import * as Path from '../Path/Path.js'
|
|
4
5
|
import * as Root from '../Root/Root.js'
|
|
5
6
|
import { VError } from '../VError/VError.js'
|
|
6
7
|
|
|
7
|
-
const getElectronRebuildPath = () => {
|
|
8
|
-
return Path.join(Root.root, 'packages', 'main-process', 'node_modules', '.bin', 'electron-rebuild')
|
|
9
|
-
}
|
|
10
|
-
|
|
11
8
|
const getPtyHostPath = () => {
|
|
12
9
|
return Path.join(Root.root, 'packages', 'pty-host')
|
|
13
10
|
}
|
|
@@ -16,7 +13,7 @@ const getPtyHostPath = () => {
|
|
|
16
13
|
* @param {string} cwd
|
|
17
14
|
*/
|
|
18
15
|
const rebuildNodePtyElectron = async (cwd) => {
|
|
19
|
-
const electronRebuildPath = getElectronRebuildPath()
|
|
16
|
+
const electronRebuildPath = GetElectronRebuildPath.getElectronRebuildPath()
|
|
20
17
|
const childProcess = spawn(electronRebuildPath, [], {
|
|
21
18
|
cwd,
|
|
22
19
|
stdio: 'inherit',
|
|
@@ -34,14 +31,18 @@ const rebuildNodePtyNode = async (cwd) => {
|
|
|
34
31
|
})
|
|
35
32
|
}
|
|
36
33
|
|
|
34
|
+
const getFn = () => {
|
|
35
|
+
if (IsElectron.isElectron()) {
|
|
36
|
+
return rebuildNodePtyElectron
|
|
37
|
+
}
|
|
38
|
+
return rebuildNodePtyNode
|
|
39
|
+
}
|
|
40
|
+
|
|
37
41
|
export const rebuildNodePty = async () => {
|
|
38
42
|
try {
|
|
39
43
|
const ptyHostPath = getPtyHostPath()
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
} else {
|
|
43
|
-
await rebuildNodePtyNode(ptyHostPath)
|
|
44
|
-
}
|
|
44
|
+
const rebuild = getFn()
|
|
45
|
+
await rebuild(ptyHostPath)
|
|
45
46
|
} catch (error) {
|
|
46
47
|
throw new VError(error, `Failed to rebuild node-pty`)
|
|
47
48
|
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import * as Character from '../Character/Character.js'
|
|
2
|
+
import * as GetErrorConstructor from '../GetErrorConstructor/GetErrorConstructor.js'
|
|
3
|
+
import * as JoinLines from '../JoinLines/JoinLines.js'
|
|
4
|
+
import { JsonRpcError } from '../JsonRpcError/JsonRpcError.js'
|
|
5
|
+
import * as JsonRpcErrorCode from '../JsonRpcErrorCode/JsonRpcErrorCode.js'
|
|
6
|
+
import * as SplitLines from '../SplitLines/SplitLines.js'
|
|
7
|
+
|
|
8
|
+
const constructError = (message, type, name) => {
|
|
9
|
+
const ErrorConstructor = GetErrorConstructor.getErrorConstructor(message, type)
|
|
10
|
+
// @ts-ignore
|
|
11
|
+
if (ErrorConstructor === DOMException && name) {
|
|
12
|
+
return new ErrorConstructor(message, name)
|
|
13
|
+
}
|
|
14
|
+
if (ErrorConstructor === Error) {
|
|
15
|
+
const error = new Error(message)
|
|
16
|
+
if (name && name !== 'VError') {
|
|
17
|
+
error.name = name
|
|
18
|
+
}
|
|
19
|
+
return error
|
|
20
|
+
}
|
|
21
|
+
return new ErrorConstructor(message)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const recreateStack = (message, stack) => {
|
|
25
|
+
if (message && !stack.includes(message)) {
|
|
26
|
+
return message + Character.NewLine + stack
|
|
27
|
+
}
|
|
28
|
+
return stack
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const getErrorType = (error) => {
|
|
32
|
+
if (error && error.type) {
|
|
33
|
+
return error.type
|
|
34
|
+
}
|
|
35
|
+
if (error && error.data && error.data.type) {
|
|
36
|
+
return error.data.type
|
|
37
|
+
}
|
|
38
|
+
return ''
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const restoreJsonRpcError = (error) => {
|
|
42
|
+
if (error && error instanceof Error) {
|
|
43
|
+
return error
|
|
44
|
+
}
|
|
45
|
+
if (error && error.code && error.code === JsonRpcErrorCode.MethodNotFound) {
|
|
46
|
+
const restoredError = new JsonRpcError(error.message)
|
|
47
|
+
restoredError.stack = error.stack || error.data || ''
|
|
48
|
+
return restoredError
|
|
49
|
+
}
|
|
50
|
+
if (error && error.message) {
|
|
51
|
+
const type = getErrorType(error)
|
|
52
|
+
const restoredError = constructError(error.message, type, error.name)
|
|
53
|
+
const currentStack = JoinLines.joinLines(SplitLines.splitLines(new Error().stack).slice(1))
|
|
54
|
+
if (error.data) {
|
|
55
|
+
if (error.data.stack && type && error.message) {
|
|
56
|
+
restoredError.stack = type + ': ' + error.message + Character.NewLine + error.data.stack + Character.NewLine + currentStack
|
|
57
|
+
} else if (error.data.stack) {
|
|
58
|
+
restoredError.stack = recreateStack(error.message, error.data.stack) + Character.NewLine + currentStack
|
|
59
|
+
}
|
|
60
|
+
if (error.data.codeFrame) {
|
|
61
|
+
// @ts-ignore
|
|
62
|
+
restoredError.codeFrame = error.data.codeFrame
|
|
63
|
+
}
|
|
64
|
+
if (error.data.code) {
|
|
65
|
+
// @ts-ignore
|
|
66
|
+
restoredError.code = error.data.code
|
|
67
|
+
}
|
|
68
|
+
} else if (error.stack) {
|
|
69
|
+
// @ts-ignore
|
|
70
|
+
restoredError.stack = recreateStack(error.message, error.stack) + Character.NewLine + currentStack
|
|
71
|
+
}
|
|
72
|
+
return restoredError
|
|
73
|
+
}
|
|
74
|
+
if (typeof error === 'string') {
|
|
75
|
+
return new Error(`JsonRpc Error: ${error}`)
|
|
76
|
+
}
|
|
77
|
+
console.log({ error })
|
|
78
|
+
return new Error(`JsonRpc Error: Unknown Error`)
|
|
79
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { JsonRpcError } from '../JsonRpcError/JsonRpcError.js'
|
|
2
|
+
import * as RestoreJsonRpcError from '../RestoreJsonRpcError/RestoreJsonRpcError.js'
|
|
3
|
+
|
|
4
|
+
export const unwrapJsonRpcResult = (responseMessage) => {
|
|
5
|
+
if ('error' in responseMessage) {
|
|
6
|
+
const restoredError = RestoreJsonRpcError.restoreJsonRpcError(responseMessage.error)
|
|
7
|
+
throw restoredError
|
|
8
|
+
}
|
|
9
|
+
if ('result' in responseMessage) {
|
|
10
|
+
return responseMessage.result
|
|
11
|
+
}
|
|
12
|
+
throw new JsonRpcError('unexpected response message')
|
|
13
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as LoadWindowsProcessTree from '../LoadWindowsProcessTree/LoadWindowsProcessTree.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @param {number} rootPid
|
|
6
|
+
* @param {WindowsProcessTree.ProcessDataFlag} flags
|
|
7
|
+
* @returns {Promise<WindowsProcessTree.IProcessInfo[] | undefined>}
|
|
8
|
+
*/
|
|
9
|
+
export const getProcessList = async (rootPid, flags) => {
|
|
10
|
+
const WindowsProcessTree = await LoadWindowsProcessTree.loadWindowProcessTree()
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
WindowsProcessTree.getProcessList(rootPid, resolve, flags)
|
|
13
|
+
})
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
*
|
|
18
|
+
* @param {WindowsProcessTree.IProcessInfo[]} processList
|
|
19
|
+
* @returns Promise< WindowsProcessTree.IProcessCpuInfo[]>
|
|
20
|
+
*/
|
|
21
|
+
export const addCpuUsage = async (processList) => {
|
|
22
|
+
const WindowsProcessTree = await LoadWindowsProcessTree.loadWindowProcessTree()
|
|
23
|
+
return new Promise((resolve) => {
|
|
24
|
+
WindowsProcessTree.getProcessCpuUsage(processList, resolve)
|
|
25
|
+
})
|
|
26
|
+
}
|