Skip to content

Feat: naive waitFor preattach/attach support #13464

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
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions Extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4673,6 +4673,28 @@
}
]
},
"waitFor": {
"description": "%c_cpp.debuggers.waitFor.description%",
"type": "object",
"default": {},
"properties": {
"enabled": {
"type": "boolean",
"description": "%c_cpp.debuggers.waitFor.enabled.description%",
"default": false
},
"pattern": {
"type": "string",
"description": "%c_cpp.debuggers.waitFor.pattern.description%",
"default": ""
},
"timeout": {
"type": "number",
"description": "%c_cpp.debuggers.waitFor.timeout.description%",
"default": 30000
}
}
},
"filterStdout": {
"type": "boolean",
"description": "%c_cpp.debuggers.filterStdout.description%",
Expand Down
84 changes: 84 additions & 0 deletions Extension/src/Debugger/attachWaitFor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@

Check failure on line 1 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

Too many blank lines at the beginning of file. Max of 0 allowed

Check failure on line 1 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

Too many blank lines at the beginning of file. Max of 0 allowed

Check failure on line 1 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

Too many blank lines at the beginning of file. Max of 0 allowed
import * as vscode from 'vscode';

Check warning on line 2 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

missing header

Check warning on line 2 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

missing header

Check warning on line 2 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

missing header
import * as util from '../common';
import { sleep } from '../Utility/Async/sleep';
import { PsProcessParser } from './nativeAttach';

export class AttachWaitFor {

constructor() {
this._channel = vscode.window.createOutputChannel('waitfor-attach');
this.timeout = 30000

Check failure on line 11 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

Missing semicolon

Check failure on line 11 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

Missing semicolon

Check failure on line 11 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

Missing semicolon
}

private _channel: vscode.OutputChannel;
private timeout: number;

public async WaitForProcess(program: string, timeout: number): Promise<string | undefined> {
if (timeout) {
this.timeout = timeout

Check failure on line 19 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

Missing semicolon

Check failure on line 19 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

Missing semicolon

Check failure on line 19 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

Missing semicolon
}

return await this.poll(program)

Check failure on line 22 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

Redundant use of `await` on a return value

Check failure on line 22 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

Missing semicolon

Check failure on line 22 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

Redundant use of `await` on a return value

Check failure on line 22 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

Missing semicolon

Check failure on line 22 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

Redundant use of `await` on a return value

Check failure on line 22 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

Missing semicolon
}

//Naive poll mechanism, parses /proc for a while till a match is found
private async poll(program: string): Promise<string | undefined> {
this._channel.clear()

Check failure on line 27 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

Missing semicolon

Check failure on line 27 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

Missing semicolon

Check failure on line 27 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

Missing semicolon
const startTime = Date.now(); // Get the current time in milliseconds

Check failure on line 28 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

Multiple spaces found before '// Get the cur...'

Check failure on line 28 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

Multiple spaces found before '// Get the cur...'

Check failure on line 28 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

Multiple spaces found before '// Get the cur...'
let seen = new Set<string>();

Check failure on line 29 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

'seen' is never reassigned. Use 'const' instead

Check failure on line 29 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

'seen' is never reassigned. Use 'const' instead

Check failure on line 29 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

'seen' is never reassigned. Use 'const' instead
let process: string | undefined;
while (true) {

Check failure on line 31 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

Unexpected constant condition

Check failure on line 31 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

Unexpected constant condition

Check failure on line 31 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

Unexpected constant condition
const elapsedTime = Date.now() - startTime;

if (elapsedTime >= this.timeout) {
console.log('Timeout reached. No process matched pattern.');
return undefined

Check failure on line 36 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

Missing semicolon

Check failure on line 36 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

Missing semicolon

Check failure on line 36 in Extension/src/Debugger/attachWaitFor.ts

View workflow job for this annotation

GitHub Actions / job / build

Missing semicolon
}

const output: string = await util.execChildProcess(PsProcessParser.psLinuxCommand, undefined, this._channel)
const lines: string[] = output.split(/\r?\n/);
const processes: string[] = lines.slice(1);
const processAttach = PsProcessParser.ParseProcessFromPsArray(processes)
.sort((a, b) => {
if (a.name === undefined) {
if (b.name === undefined) {
return 0;
}
return 1;
}
if (b.name === undefined) {
return -1;
}
const aLower: string = a.name.toLowerCase();
const bLower: string = b.name.toLowerCase();
if (aLower === bLower) {
return 0;
}
return aLower < bLower ? -1 : 1;
})
.map(p => p.toAttachItem());
processAttach.forEach(p => {
if (!process && p.detail!.includes(program)) {
console.log("Found program waiting for with pid %s - info %s", p.id!, p.detail!)
process = p.id!

// Send sigstop by default?
util.execChildProcess(`kill -STOP ${process}`, undefined, this._channel)
return
}

if (seen.has(p.id!) == false && p.label != "ps" && !p.detail!.includes("ps")) {
seen.add(p.id!)
}
})

if (process) {
return process
}

sleep(200)
}
}

}
19 changes: 12 additions & 7 deletions Extension/src/Debugger/configurationProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { PlatformInformation } from '../platform';
import { rsync, scp, ssh } from '../SSH/commands';
import * as Telemetry from '../telemetry';
import { AttachItemsProvider, AttachPicker, RemoteAttachPicker } from './attachToProcess';
import { AttachWaitFor } from './attachWaitFor';
import { ConfigMenu, ConfigMode, ConfigSource, CppDebugConfiguration, DebuggerEvent, DebuggerType, DebugType, IConfiguration, IConfigurationSnippet, isDebugLaunchStr, MIConfigurations, PipeTransportConfigurations, TaskStatus, WindowsConfigurations, WSLConfigurations } from './configurations';
import { NativeAttachItemsProviderFactory } from './nativeAttach';
import { Environment, ParsedEnvironmentFile } from './ParsedEnvironmentFile';
Expand Down Expand Up @@ -347,16 +348,20 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
}
}

// Pick process if process id is empty
if (config.request === "attach" && !config.processId) {
let processId: string | undefined;
if (config.pipeTransport || config.useExtendedRemote) {
const remoteAttachPicker: RemoteAttachPicker = new RemoteAttachPicker();
processId = await remoteAttachPicker.ShowAttachEntries(config);
if (config.waitFor.enabled) {
const waitForAttach: AttachWaitFor = new AttachWaitFor()
processId = await waitForAttach.WaitForProcess(config.waitFor.pattern, config.waitFor.timeout)
} else {
const attachItemsProvider: AttachItemsProvider = NativeAttachItemsProviderFactory.Get();
const attacher: AttachPicker = new AttachPicker(attachItemsProvider);
processId = await attacher.ShowAttachEntries(token);
if (config.pipeTransport || config.useExtendedRemote) {
const remoteAttachPicker: RemoteAttachPicker = new RemoteAttachPicker();
processId = await remoteAttachPicker.ShowAttachEntries(config);
} else {
const attachItemsProvider: AttachItemsProvider = NativeAttachItemsProviderFactory.Get();
const attacher: AttachPicker = new AttachPicker(attachItemsProvider);
processId = await attacher.ShowAttachEntries(token);
}
}

if (processId) {
Expand Down
9 changes: 6 additions & 3 deletions Extension/src/Debugger/configurations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ function createLaunchString(name: string, type: string, executable: string): str
"stopAtEntry": false,
"cwd": "$\{fileDirname\}",
"environment": [],
${ type === "cppdbg" ? `"externalConsole": false` : `"console": "externalTerminal"` }
${type === "cppdbg" ? `"externalConsole": false` : `"console": "externalTerminal"`}
`;
}

Expand All @@ -106,6 +106,7 @@ function createAttachString(name: string, type: string, executable: string): str
"name": "${name}",
"type": "${type}",
"request": "attach",{0}
"waitFor": false,
`, [type === "cppdbg" ? `${os.EOL}"program": "${localize("enter.program.name", "enter program name, for example {0}", "$\{workspaceFolder\}" + "/" + executable).replace(/"/g, '')}",` : ""]);
}

Expand All @@ -114,11 +115,13 @@ function createRemoteAttachString(name: string, type: string, executable: string
"name": "${name}",
"type": "${type}",
"request": "attach",
"waitFor": false,
"program": "${localize("enter.program.name", "enter program name, for example {0}", "$\{workspaceFolder\}" + "/" + executable).replace(/"/g, '')}",
"processId": "$\{command:pickRemoteProcess\}"
`;
}


function createPipeTransportString(pipeProgram: string, debuggerProgram: string, pipeArgs: string[] = []): string {
return `
"pipeTransport": {
Expand Down Expand Up @@ -164,7 +167,7 @@ export class MIConfigurations extends Configuration {
\t${indentJsonString(createLaunchString(name, this.miDebugger, this.executable))},
\t"MIMode": "${this.MIMode}"{0}{1}
}`, [this.miDebugger === "cppdbg" && os.platform() === "win32" ? `,${os.EOL}\t"miDebuggerPath": "/path/to/gdb"` : "",
this.additionalProperties ? `,${os.EOL}\t${indentJsonString(this.additionalProperties)}` : ""]);
this.additionalProperties ? `,${os.EOL}\t${indentJsonString(this.additionalProperties)}` : ""]);

return {
"label": configPrefix + name,
Expand All @@ -182,7 +185,7 @@ export class MIConfigurations extends Configuration {
\t${indentJsonString(createAttachString(name, this.miDebugger, this.executable))}
\t"MIMode": "${this.MIMode}"{0}{1}
}`, [this.miDebugger === "cppdbg" && os.platform() === "win32" ? `,${os.EOL}\t"miDebuggerPath": "/path/to/gdb"` : "",
this.additionalProperties ? `,${os.EOL}\t${indentJsonString(this.additionalProperties)}` : ""]);
this.additionalProperties ? `,${os.EOL}\t${indentJsonString(this.additionalProperties)}` : ""]);

return {
"label": configPrefix + name,
Expand Down
15 changes: 10 additions & 5 deletions Extension/src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -734,13 +734,18 @@ export function execChildProcess(process: string, workingDirectory?: string, cha
}
}

if (error) {
reject(error);
return;
if (stderr && stderr.length > 0) {
if (stderr.indexOf('screen size is bogus') >= 0) {
// ignore this error silently; see https://github.com/microsoft/vscode/issues/75932
// see similar fix for the Node - Debug (Legacy) Extension at https://github.com/microsoft/vscode-node-debug/commit/5298920
} else {
reject(error);
return;
}
}

if (stderr && stderr.length > 0) {
reject(new Error(stderr));
if (error) {
reject(error);
return;
}

Expand Down