-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[lldb-dap] Add process picker command to VS Code extension #128943
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
matthewbastien
wants to merge
23
commits into
llvm:main
Choose a base branch
from
matthewbastien:lldb-dap-process-picker
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
b9083ea
add a process picker for attaching by PID
matthewbastien b423842
convert pid to a number so that lldb-dap can properly consume it
matthewbastien ee7b00e
update extension README
matthewbastien 82ef750
allow matching on the full command line arguments in the process picker
matthewbastien f4407b2
use execFile() instead of spawn() to simplify logic
matthewbastien daf2618
use camel case for ${command:pickProcess}
matthewbastien b2c0382
allow filtering processes by program
matthewbastien 18cba4b
fix Linux process tree to include the full executable path
matthewbastien 85a19e4
minor fixes to Darwin process tree
matthewbastien f84f5cc
remove program property from attach to process ID example
matthewbastien 0a615f2
add `lldb-dap.attachToProcess` command
matthewbastien 6f40eb3
use Get-CimInstance on Windows because WMIC is deprecated
matthewbastien d4c81e1
update README with more info about the 'program' property
matthewbastien 6deb671
add code comment to LinuxProcessTree's parser
matthewbastien 582cd9b
use the pickProcess command in attach requests by default
matthewbastien 548ac79
filter out zombie, trace, and debug state processes on macOS and Linux
matthewbastien f967f6d
allow searching by pid in the process picker
matthewbastien 2492e1e
match program by basename like the docs say
matthewbastien e9b4e6a
use the process picker even if `program` is set
matthewbastien a18e5cc
update documentation for attaching to a process
matthewbastien 55e0c95
update attach parameter table in the README
matthewbastien 6d01d9a
show an error message if no program is provided for waitFor
matthewbastien e4155ec
update description of waitFor in the package.json to indicate that th…
matthewbastien File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import * as path from "path"; | ||
import * as vscode from "vscode"; | ||
import { createProcessTree } from "../process-tree"; | ||
|
||
interface ProcessQuickPick extends vscode.QuickPickItem { | ||
processId: number; | ||
} | ||
|
||
/** | ||
* Prompts the user to select a running process. | ||
* | ||
* @returns The pid of the process as a string or undefined if cancelled. | ||
*/ | ||
export async function pickProcess(): Promise<string | undefined> { | ||
vogelsgesang marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const processTree = createProcessTree(); | ||
const selectedProcess = await vscode.window.showQuickPick<ProcessQuickPick>( | ||
processTree.listAllProcesses().then((processes): ProcessQuickPick[] => { | ||
return processes | ||
.sort((a, b) => b.start - a.start) // Sort by start date in descending order | ||
.map((proc) => { | ||
return { | ||
processId: proc.id, | ||
label: path.basename(proc.command), | ||
description: proc.id.toString(), | ||
detail: proc.arguments, | ||
} satisfies ProcessQuickPick; | ||
}); | ||
}), | ||
{ | ||
placeHolder: "Select a process to attach the debugger to", | ||
}, | ||
); | ||
if (!selectedProcess) { | ||
return; | ||
} | ||
return selectedProcess.processId.toString(); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
102 changes: 102 additions & 0 deletions
102
lldb/tools/lldb-dap/src-ts/process-tree/base-process-tree.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import { ChildProcessWithoutNullStreams } from "child_process"; | ||
import { Process, ProcessTree } from "."; | ||
import { Transform } from "stream"; | ||
|
||
/** Parses process information from a given line of process output. */ | ||
export type ProcessTreeParser = (line: string) => Process | undefined; | ||
|
||
/** | ||
* Implements common behavior between the different {@link ProcessTree} implementations. | ||
*/ | ||
export abstract class BaseProcessTree implements ProcessTree { | ||
/** | ||
* Spawn the process responsible for collecting all processes on the system. | ||
*/ | ||
protected abstract spawnProcess(): ChildProcessWithoutNullStreams; | ||
matthewbastien marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/** | ||
* Create a new parser that can read the process information from stdout of the process | ||
* spawned by {@link spawnProcess spawnProcess()}. | ||
*/ | ||
protected abstract createParser(): ProcessTreeParser; | ||
|
||
listAllProcesses(): Promise<Process[]> { | ||
return new Promise<Process[]>((resolve, reject) => { | ||
const proc = this.spawnProcess(); | ||
const parser = this.createParser(); | ||
|
||
// Capture processes from stdout | ||
const processes: Process[] = []; | ||
proc.stdout.pipe(new LineBasedStream()).on("data", (line) => { | ||
const process = parser(line.toString()); | ||
if (process && process.id !== proc.pid) { | ||
processes.push(process); | ||
} | ||
}); | ||
|
||
// Resolve or reject the promise based on exit code/signal/error | ||
proc.on("error", reject); | ||
proc.on("exit", (code, signal) => { | ||
if (code === 0) { | ||
resolve(processes); | ||
} else if (signal) { | ||
reject( | ||
new Error( | ||
`Unable to list processes: process exited due to signal ${signal}`, | ||
), | ||
); | ||
} else { | ||
reject( | ||
new Error( | ||
`Unable to list processes: process exited with code ${code}`, | ||
), | ||
); | ||
} | ||
}); | ||
}); | ||
} | ||
} | ||
|
||
/** | ||
* A stream that emits each line as a single chunk of data. The end of a line is denoted | ||
* by the newline character '\n'. | ||
*/ | ||
export class LineBasedStream extends Transform { | ||
private readonly newline: number = "\n".charCodeAt(0); | ||
private buffer: Buffer = Buffer.alloc(0); | ||
|
||
override _transform( | ||
chunk: Buffer, | ||
_encoding: string, | ||
callback: () => void, | ||
): void { | ||
let currentIndex = 0; | ||
while (currentIndex < chunk.length) { | ||
const newlineIndex = chunk.indexOf(this.newline, currentIndex); | ||
if (newlineIndex === -1) { | ||
this.buffer = Buffer.concat([ | ||
this.buffer, | ||
chunk.subarray(currentIndex), | ||
]); | ||
break; | ||
} | ||
|
||
const newlineChunk = chunk.subarray(currentIndex, newlineIndex); | ||
const line = Buffer.concat([this.buffer, newlineChunk]); | ||
this.push(line); | ||
this.buffer = Buffer.alloc(0); | ||
|
||
currentIndex = newlineIndex + 1; | ||
} | ||
|
||
callback(); | ||
} | ||
|
||
override _flush(callback: () => void): void { | ||
if (this.buffer.length) { | ||
this.push(this.buffer); | ||
} | ||
|
||
callback(); | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { DarwinProcessTree } from "./platforms/darwin-process-tree"; | ||
import { LinuxProcessTree } from "./platforms/linux-process-tree"; | ||
import { WindowsProcessTree } from "./platforms/windows-process-tree"; | ||
|
||
/** | ||
* Represents a single process running on the system. | ||
*/ | ||
export interface Process { | ||
/** Process ID */ | ||
id: number; | ||
|
||
/** Command that was used to start the process */ | ||
command: string; | ||
|
||
/** The full command including arguments that was used to start the process */ | ||
arguments: string; | ||
|
||
/** The date when the process was started */ | ||
start: number; | ||
} | ||
|
||
export interface ProcessTree { | ||
listAllProcesses(): Promise<Process[]>; | ||
} | ||
|
||
/** Returns a {@link ProcessTree} based on the current platform. */ | ||
export function createProcessTree(): ProcessTree { | ||
switch (process.platform) { | ||
case "darwin": | ||
return new DarwinProcessTree(); | ||
case "win32": | ||
return new WindowsProcessTree(); | ||
default: | ||
return new LinuxProcessTree(); | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
lldb/tools/lldb-dap/src-ts/process-tree/platforms/darwin-process-tree.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { ChildProcessWithoutNullStreams, spawn } from "child_process"; | ||
import { LinuxProcessTree } from "./linux-process-tree"; | ||
|
||
function fill(prefix: string, suffix: string, length: number): string { | ||
matthewbastien marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return prefix + suffix.repeat(length - prefix.length); | ||
} | ||
|
||
export class DarwinProcessTree extends LinuxProcessTree { | ||
protected override spawnProcess(): ChildProcessWithoutNullStreams { | ||
return spawn("ps", [ | ||
"-xo", | ||
matthewbastien marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// The length of comm must be large enough or data will be truncated. | ||
`pid=PID,lstart=START,comm=${fill("COMMAND", "-", 256)},command=ARGUMENTS`, | ||
vogelsgesang marked this conversation as resolved.
Show resolved
Hide resolved
|
||
]); | ||
} | ||
} |
38 changes: 38 additions & 0 deletions
38
lldb/tools/lldb-dap/src-ts/process-tree/platforms/linux-process-tree.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { ChildProcessWithoutNullStreams, spawn } from "child_process"; | ||
import { BaseProcessTree, ProcessTreeParser } from "../base-process-tree"; | ||
|
||
export class LinuxProcessTree extends BaseProcessTree { | ||
protected override spawnProcess(): ChildProcessWithoutNullStreams { | ||
return spawn( | ||
"ps", | ||
["-axo", `pid=PID,lstart=START,comm:128=COMMAND,command=ARGUMENTS`], | ||
matthewbastien marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
stdio: "pipe", | ||
}, | ||
matthewbastien marked this conversation as resolved.
Show resolved
Hide resolved
|
||
); | ||
} | ||
|
||
protected override createParser(): ProcessTreeParser { | ||
let commandOffset: number | undefined; | ||
let argumentsOffset: number | undefined; | ||
return (line) => { | ||
if (!commandOffset || !argumentsOffset) { | ||
commandOffset = line.indexOf("COMMAND"); | ||
argumentsOffset = line.indexOf("ARGUMENTS"); | ||
return; | ||
} | ||
|
||
const pid = /^\s*([0-9]+)\s*/.exec(line); | ||
if (!pid) { | ||
return; | ||
} | ||
|
||
return { | ||
id: Number(pid[1]), | ||
command: line.slice(commandOffset, argumentsOffset).trim(), | ||
arguments: line.slice(argumentsOffset).trim(), | ||
start: Date.parse(line.slice(pid[0].length, commandOffset).trim()), | ||
}; | ||
}; | ||
} | ||
} |
52 changes: 52 additions & 0 deletions
52
lldb/tools/lldb-dap/src-ts/process-tree/platforms/windows-process-tree.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import * as path from "path"; | ||
import { BaseProcessTree, ProcessTreeParser } from "../base-process-tree"; | ||
import { ChildProcessWithoutNullStreams, spawn } from "child_process"; | ||
|
||
export class WindowsProcessTree extends BaseProcessTree { | ||
protected override spawnProcess(): ChildProcessWithoutNullStreams { | ||
const wmic = path.join( | ||
process.env["WINDIR"] || "C:\\Windows", | ||
"System32", | ||
"wbem", | ||
"WMIC.exe", | ||
matthewbastien marked this conversation as resolved.
Show resolved
Hide resolved
|
||
); | ||
return spawn( | ||
wmic, | ||
["process", "get", "CommandLine,CreationDate,ProcessId"], | ||
{ stdio: "pipe" }, | ||
); | ||
} | ||
|
||
protected override createParser(): ProcessTreeParser { | ||
const lineRegex = /^(.*)\s+([0-9]+)\.[0-9]+[+-][0-9]+\s+([0-9]+)$/; | ||
|
||
return (line) => { | ||
const matches = lineRegex.exec(line.trim()); | ||
if (!matches || matches.length !== 4) { | ||
return; | ||
} | ||
|
||
const id = Number(matches[3]); | ||
const start = Number(matches[2]); | ||
let fullCommandLine = matches[1].trim(); | ||
if (isNaN(id) || !fullCommandLine) { | ||
return; | ||
} | ||
// Extract the command from the full command line | ||
let command = fullCommandLine; | ||
if (fullCommandLine[0] === '"') { | ||
const end = fullCommandLine.indexOf('"', 1); | ||
if (end > 0) { | ||
command = fullCommandLine.slice(1, end - 1); | ||
} | ||
} else { | ||
const end = fullCommandLine.indexOf(" "); | ||
if (end > 0) { | ||
command = fullCommandLine.slice(0, end); | ||
} | ||
} | ||
|
||
return { id, command, arguments: fullCommandLine, start }; | ||
}; | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.