Skip to content

Commit f4407b2

Browse files
use execFile() instead of spawn() to simplify logic
1 parent 82ef750 commit f4407b2

File tree

4 files changed

+35
-102
lines changed

4 files changed

+35
-102
lines changed
Lines changed: 20 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
import { ChildProcessWithoutNullStreams } from "child_process";
1+
import * as util from "util";
2+
import * as child_process from "child_process";
23
import { Process, ProcessTree } from ".";
3-
import { Transform } from "stream";
4+
5+
const exec = util.promisify(child_process.execFile);
46

57
/** Parses process information from a given line of process output. */
68
export type ProcessTreeParser = (line: string) => Process | undefined;
@@ -10,93 +12,30 @@ export type ProcessTreeParser = (line: string) => Process | undefined;
1012
*/
1113
export abstract class BaseProcessTree implements ProcessTree {
1214
/**
13-
* Spawn the process responsible for collecting all processes on the system.
15+
* Get the command responsible for collecting all processes on the system.
1416
*/
15-
protected abstract spawnProcess(): ChildProcessWithoutNullStreams;
17+
protected abstract getCommand(): string;
18+
19+
/**
20+
* Get the list of arguments used to launch the command.
21+
*/
22+
protected abstract getCommandArguments(): string[];
1623

1724
/**
1825
* Create a new parser that can read the process information from stdout of the process
1926
* spawned by {@link spawnProcess spawnProcess()}.
2027
*/
2128
protected abstract createParser(): ProcessTreeParser;
2229

23-
listAllProcesses(): Promise<Process[]> {
24-
return new Promise<Process[]>((resolve, reject) => {
25-
const proc = this.spawnProcess();
26-
const parser = this.createParser();
27-
28-
// Capture processes from stdout
29-
const processes: Process[] = [];
30-
proc.stdout.pipe(new LineBasedStream()).on("data", (line) => {
31-
const process = parser(line.toString());
32-
if (process && process.id !== proc.pid) {
33-
processes.push(process);
34-
}
35-
});
36-
37-
// Resolve or reject the promise based on exit code/signal/error
38-
proc.on("error", reject);
39-
proc.on("exit", (code, signal) => {
40-
if (code === 0) {
41-
resolve(processes);
42-
} else if (signal) {
43-
reject(
44-
new Error(
45-
`Unable to list processes: process exited due to signal ${signal}`,
46-
),
47-
);
48-
} else {
49-
reject(
50-
new Error(
51-
`Unable to list processes: process exited with code ${code}`,
52-
),
53-
);
54-
}
55-
});
56-
});
57-
}
58-
}
59-
60-
/**
61-
* A stream that emits each line as a single chunk of data. The end of a line is denoted
62-
* by the newline character '\n'.
63-
*/
64-
export class LineBasedStream extends Transform {
65-
private readonly newline: number = "\n".charCodeAt(0);
66-
private buffer: Buffer = Buffer.alloc(0);
67-
68-
override _transform(
69-
chunk: Buffer,
70-
_encoding: string,
71-
callback: () => void,
72-
): void {
73-
let currentIndex = 0;
74-
while (currentIndex < chunk.length) {
75-
const newlineIndex = chunk.indexOf(this.newline, currentIndex);
76-
if (newlineIndex === -1) {
77-
this.buffer = Buffer.concat([
78-
this.buffer,
79-
chunk.subarray(currentIndex),
80-
]);
81-
break;
30+
async listAllProcesses(): Promise<Process[]> {
31+
const execCommand = exec(this.getCommand(), this.getCommandArguments());
32+
const parser = this.createParser();
33+
return (await execCommand).stdout.split("\n").flatMap((line) => {
34+
const process = parser(line.toString());
35+
if (!process || process.id === execCommand.child.pid) {
36+
return [];
8237
}
83-
84-
const newlineChunk = chunk.subarray(currentIndex, newlineIndex);
85-
const line = Buffer.concat([this.buffer, newlineChunk]);
86-
this.push(line);
87-
this.buffer = Buffer.alloc(0);
88-
89-
currentIndex = newlineIndex + 1;
90-
}
91-
92-
callback();
93-
}
94-
95-
override _flush(callback: () => void): void {
96-
if (this.buffer.length) {
97-
this.push(this.buffer);
98-
}
99-
100-
callback();
38+
return [process];
39+
});
10140
}
10241
}
Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
1-
import { ChildProcessWithoutNullStreams, spawn } from "child_process";
21
import { LinuxProcessTree } from "./linux-process-tree";
32

43
function fill(prefix: string, suffix: string, length: number): string {
54
return prefix + suffix.repeat(length - prefix.length);
65
}
76

87
export class DarwinProcessTree extends LinuxProcessTree {
9-
protected override spawnProcess(): ChildProcessWithoutNullStreams {
10-
return spawn("ps", [
8+
protected override getCommandArguments(): string[] {
9+
return [
1110
"-xo",
1211
// The length of comm must be large enough or data will be truncated.
1312
`pid=PID,lstart=START,comm=${fill("COMMAND", "-", 256)},command=ARGUMENTS`,
14-
]);
13+
];
1514
}
1615
}

lldb/tools/lldb-dap/src-ts/process-tree/platforms/linux-process-tree.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
1-
import { ChildProcessWithoutNullStreams, spawn } from "child_process";
21
import { BaseProcessTree, ProcessTreeParser } from "../base-process-tree";
32

43
export class LinuxProcessTree extends BaseProcessTree {
5-
protected override spawnProcess(): ChildProcessWithoutNullStreams {
6-
return spawn(
7-
"ps",
8-
["-axo", `pid=PID,lstart=START,comm:128=COMMAND,command=ARGUMENTS`],
9-
{
10-
stdio: "pipe",
11-
},
12-
);
4+
protected override getCommand(): string {
5+
return "ps";
6+
}
7+
8+
protected override getCommandArguments(): string[] {
9+
return ["-axo", "pid=PID,lstart=START,comm:128=COMMAND,command=ARGUMENTS"];
1310
}
1411

1512
protected override createParser(): ProcessTreeParser {

lldb/tools/lldb-dap/src-ts/process-tree/platforms/windows-process-tree.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
11
import * as path from "path";
22
import { BaseProcessTree, ProcessTreeParser } from "../base-process-tree";
3-
import { ChildProcessWithoutNullStreams, spawn } from "child_process";
43

54
export class WindowsProcessTree extends BaseProcessTree {
6-
protected override spawnProcess(): ChildProcessWithoutNullStreams {
7-
const wmic = path.join(
5+
protected override getCommand(): string {
6+
return path.join(
87
process.env["WINDIR"] || "C:\\Windows",
98
"System32",
109
"wbem",
1110
"WMIC.exe",
1211
);
13-
return spawn(
14-
wmic,
15-
["process", "get", "CommandLine,CreationDate,ProcessId"],
16-
{ stdio: "pipe" },
17-
);
12+
}
13+
14+
protected override getCommandArguments(): string[] {
15+
return ["process", "get", "CommandLine,CreationDate,ProcessId"];
1816
}
1917

2018
protected override createParser(): ProcessTreeParser {

0 commit comments

Comments
 (0)