Skip to content

Commit 5da105e

Browse files
committed
waitFor Attach mode naive support
1 parent 799f0b3 commit 5da105e

File tree

5 files changed

+131
-15
lines changed

5 files changed

+131
-15
lines changed

Extension/package.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4673,6 +4673,28 @@
46734673
}
46744674
]
46754675
},
4676+
"waitFor": {
4677+
"description": "%c_cpp.debuggers.waitFor.description%",
4678+
"type": "object",
4679+
"default": {},
4680+
"properties": {
4681+
"enabled": {
4682+
"type": "boolean",
4683+
"description": "%c_cpp.debuggers.waitFor.enabled.description%",
4684+
"default": false
4685+
},
4686+
"pattern": {
4687+
"type": "string",
4688+
"description": "%c_cpp.debuggers.waitFor.pattern.description%",
4689+
"default": ""
4690+
},
4691+
"timeout": {
4692+
"type": "number",
4693+
"description": "%c_cpp.debuggers.waitFor.timeout.description%",
4694+
"default": 30000
4695+
}
4696+
}
4697+
},
46764698
"filterStdout": {
46774699
"type": "boolean",
46784700
"description": "%c_cpp.debuggers.filterStdout.description%",
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
2+
import * as vscode from 'vscode';
3+
import * as util from '../common';
4+
import { sleep } from '../Utility/Async/sleep';
5+
import { PsProcessParser } from './nativeAttach';
6+
7+
export class AttachWaitFor {
8+
9+
constructor() {
10+
this._channel = vscode.window.createOutputChannel('waitfor-attach');
11+
this.timeout = 30000
12+
}
13+
14+
private _channel: vscode.OutputChannel;
15+
private timeout: number;
16+
17+
public async WaitForProcess(program: string, timeout: number): Promise<string | undefined> {
18+
if (timeout) {
19+
this.timeout = timeout
20+
}
21+
22+
return await this.poll(program)
23+
}
24+
25+
//Naive poll mechanism, parses /proc for a while till a match is found
26+
private async poll(program: string): Promise<string | undefined> {
27+
const startTime = Date.now(); // Get the current time in milliseconds
28+
let seen = new Set<string>();
29+
let process: string | undefined;
30+
31+
while (true) {
32+
const elapsedTime = Date.now() - startTime;
33+
34+
if (elapsedTime >= this.timeout) {
35+
console.log('Timeout reached. No process matched pattern.');
36+
return undefined
37+
}
38+
39+
const output: string = await util.execChildProcess(PsProcessParser.psLinuxCommand, undefined, this._channel)
40+
const lines: string[] = output.split(/\r?\n/);
41+
const processes: string[] = lines.slice(1);
42+
const processAttach = PsProcessParser.ParseProcessFromPsArray(processes)
43+
.sort((a, b) => {
44+
if (a.name === undefined) {
45+
if (b.name === undefined) {
46+
return 0;
47+
}
48+
return 1;
49+
}
50+
if (b.name === undefined) {
51+
return -1;
52+
}
53+
const aLower: string = a.name.toLowerCase();
54+
const bLower: string = b.name.toLowerCase();
55+
if (aLower === bLower) {
56+
return 0;
57+
}
58+
return aLower < bLower ? -1 : 1;
59+
})
60+
.map(p => p.toAttachItem());
61+
processAttach.forEach(p => {
62+
if (!process && p.detail!.includes(program)) {
63+
console.log("Found program waiting for with pid %s - info %s", p.id!, p.detail!)
64+
process = p.id!
65+
return
66+
}
67+
68+
if (seen.has(p.id!) == false && p.label != "ps" && !p.detail!.includes("ps")) {
69+
seen.add(p.id!)
70+
}
71+
})
72+
73+
if (process) {
74+
return process
75+
}
76+
77+
sleep(200)
78+
}
79+
}
80+
81+
}

Extension/src/Debugger/configurationProvider.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { PlatformInformation } from '../platform';
2222
import { rsync, scp, ssh } from '../SSH/commands';
2323
import * as Telemetry from '../telemetry';
2424
import { AttachItemsProvider, AttachPicker, RemoteAttachPicker } from './attachToProcess';
25+
import { AttachWaitFor } from './attachWaitFor';
2526
import { ConfigMenu, ConfigMode, ConfigSource, CppDebugConfiguration, DebuggerEvent, DebuggerType, DebugType, IConfiguration, IConfigurationSnippet, isDebugLaunchStr, MIConfigurations, PipeTransportConfigurations, TaskStatus, WindowsConfigurations, WSLConfigurations } from './configurations';
2627
import { NativeAttachItemsProviderFactory } from './nativeAttach';
2728
import { Environment, ParsedEnvironmentFile } from './ParsedEnvironmentFile';
@@ -347,16 +348,20 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
347348
}
348349
}
349350

350-
// Pick process if process id is empty
351351
if (config.request === "attach" && !config.processId) {
352352
let processId: string | undefined;
353-
if (config.pipeTransport || config.useExtendedRemote) {
354-
const remoteAttachPicker: RemoteAttachPicker = new RemoteAttachPicker();
355-
processId = await remoteAttachPicker.ShowAttachEntries(config);
353+
if (config.waitFor.enabled) {
354+
const waitForAttach: AttachWaitFor = new AttachWaitFor()
355+
processId = await waitForAttach.WaitForProcess(config.waitFor.pattern, config.waitFor.timeout)
356356
} else {
357-
const attachItemsProvider: AttachItemsProvider = NativeAttachItemsProviderFactory.Get();
358-
const attacher: AttachPicker = new AttachPicker(attachItemsProvider);
359-
processId = await attacher.ShowAttachEntries(token);
357+
if (config.pipeTransport || config.useExtendedRemote) {
358+
const remoteAttachPicker: RemoteAttachPicker = new RemoteAttachPicker();
359+
processId = await remoteAttachPicker.ShowAttachEntries(config);
360+
} else {
361+
const attachItemsProvider: AttachItemsProvider = NativeAttachItemsProviderFactory.Get();
362+
const attacher: AttachPicker = new AttachPicker(attachItemsProvider);
363+
processId = await attacher.ShowAttachEntries(token);
364+
}
360365
}
361366

362367
if (processId) {

Extension/src/Debugger/configurations.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ function createLaunchString(name: string, type: string, executable: string): str
9797
"stopAtEntry": false,
9898
"cwd": "$\{fileDirname\}",
9999
"environment": [],
100-
${ type === "cppdbg" ? `"externalConsole": false` : `"console": "externalTerminal"` }
100+
${type === "cppdbg" ? `"externalConsole": false` : `"console": "externalTerminal"`}
101101
`;
102102
}
103103

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

@@ -114,11 +115,13 @@ function createRemoteAttachString(name: string, type: string, executable: string
114115
"name": "${name}",
115116
"type": "${type}",
116117
"request": "attach",
118+
"waitFor": false,
117119
"program": "${localize("enter.program.name", "enter program name, for example {0}", "$\{workspaceFolder\}" + "/" + executable).replace(/"/g, '')}",
118120
"processId": "$\{command:pickRemoteProcess\}"
119121
`;
120122
}
121123

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

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

187190
return {
188191
"label": configPrefix + name,

Extension/src/common.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -734,13 +734,18 @@ export function execChildProcess(process: string, workingDirectory?: string, cha
734734
}
735735
}
736736

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

742-
if (stderr && stderr.length > 0) {
743-
reject(new Error(stderr));
747+
if (error) {
748+
reject(error);
744749
return;
745750
}
746751

0 commit comments

Comments
 (0)