Extended remote support by xisui-MSFT · Pull Request #9569 · microsoft/vscode-cpptools · GitHub
Skip to content
Merged
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
39 changes: 24 additions & 15 deletions Extension/package.json
1 change: 1 addition & 0 deletions Extension/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@
"c_cpp.debuggers.miDebuggerPath.description": "The path to the MI debugger (such as gdb). When unspecified, it will search path first for the debugger.",
"c_cpp.debuggers.miDebuggerArgs.description": "Additional arguments for the MI debugger (such as gdb).",
"c_cpp.debuggers.miDebuggerServerAddress.description": "Network address of the MI Debugger Server to connect to (example: localhost:1234).",
"c_cpp.debuggers.useExtendedRemote.description": "Connect to the MI Debugger Server with target extended-remote mode.",
"c_cpp.debuggers.stopAtEntry.description": "Optional parameter. If true, the debugger should stop at the entrypoint of the target. If processId is passed, has no effect.",
"c_cpp.debuggers.debugServerPath.description": "Optional full path to the debug server to launch. Defaults to null. It is used in conjunction with either \"miDebugServerAddress\" or your own server with a \"customSetupCommand\" that runs \"-target-select remote <server:port>\".",
"c_cpp.debuggers.debugServerArgs.description": "Optional debug server args. Defaults to null.",
Expand Down
126 changes: 94 additions & 32 deletions Extension/src/Debugger/attachToProcess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,48 +39,58 @@ export class RemoteAttachPicker {

public async ShowAttachEntries(config: any): Promise<string | undefined> {
this._channel.clear();
let processes: AttachItem[];

const pipeTransport: any = config ? config.pipeTransport : undefined;
const useExtendedRemote: any = config ? config.useExtendedRemote : undefined;
const miDebuggerPath: any = config ? config.miDebuggerPath : undefined;
const miDebuggerServerAddress: any = config ? config.miDebuggerServerAddress : undefined;

if (pipeTransport) {
let pipeProgram: string | undefined;

if (os.platform() === 'win32' &&
pipeTransport.pipeProgram &&
!await util.checkFileExists(pipeTransport.pipeProgram)) {
const pipeProgramStr: string = pipeTransport.pipeProgram.toLowerCase().trim();
const expectedArch: debugUtils.ArchType = debugUtils.ArchType[process.arch as keyof typeof debugUtils.ArchType];

// Check for pipeProgram
if (!await util.checkFileExists(config.pipeTransport.pipeProgram)) {
pipeProgram = debugUtils.ArchitectureReplacer.checkAndReplaceWSLPipeProgram(pipeProgramStr, expectedArch);
}

if (!pipeTransport) {
throw new Error(localize("no.pipetransport", "Chosen debug configuration does not contain {0}", "pipeTransport"));
}

let pipeProgram: string | undefined;

if (os.platform() === 'win32' &&
pipeTransport.pipeProgram &&
!await util.checkFileExists(pipeTransport.pipeProgram)) {
const pipeProgramStr: string = pipeTransport.pipeProgram.toLowerCase().trim();
const expectedArch: debugUtils.ArchType = debugUtils.ArchType[process.arch as keyof typeof debugUtils.ArchType];
// If pipeProgram does not get replaced and there is a pipeCwd, concatenate with pipeProgramStr and attempt to replace.
if (!pipeProgram && config.pipeTransport.pipeCwd) {
const pipeCwdStr: string = config.pipeTransport.pipeCwd.toLowerCase().trim();
const newPipeProgramStr: string = path.join(pipeCwdStr, pipeProgramStr);

// Check for pipeProgram
if (!await util.checkFileExists(config.pipeTransport.pipeProgram)) {
pipeProgram = debugUtils.ArchitectureReplacer.checkAndReplaceWSLPipeProgram(pipeProgramStr, expectedArch);
if (!await util.checkFileExists(newPipeProgramStr)) {
pipeProgram = debugUtils.ArchitectureReplacer.checkAndReplaceWSLPipeProgram(newPipeProgramStr, expectedArch);
}
}
}

// If pipeProgram does not get replaced and there is a pipeCwd, concatenate with pipeProgramStr and attempt to replace.
if (!pipeProgram && config.pipeTransport.pipeCwd) {
const pipeCwdStr: string = config.pipeTransport.pipeCwd.toLowerCase().trim();
const newPipeProgramStr: string = path.join(pipeCwdStr, pipeProgramStr);

if (!await util.checkFileExists(newPipeProgramStr)) {
pipeProgram = debugUtils.ArchitectureReplacer.checkAndReplaceWSLPipeProgram(newPipeProgramStr, expectedArch);
}
if (!pipeProgram) {
pipeProgram = pipeTransport.pipeProgram;
}
}

if (!pipeProgram) {
pipeProgram = pipeTransport.pipeProgram;
}
const pipeArgs: string[] = pipeTransport.pipeArgs;

const pipeArgs: string[] = pipeTransport.pipeArgs;
const argList: string = RemoteAttachPicker.createArgumentList(pipeArgs);

const argList: string = RemoteAttachPicker.createArgumentList(pipeArgs);
const pipeCmd: string = `"${pipeProgram}" ${argList}`;

const pipeCmd: string = `"${pipeProgram}" ${argList}`;
processes = await this.getRemoteOSAndProcesses(pipeCmd);
} else if (!pipeTransport && useExtendedRemote) {
if (!miDebuggerPath || !miDebuggerServerAddress) {
throw new Error(localize("debugger.path.and.server.address.required", "{0} in debug configuration requires {1} and {2}", "useExtendedRemote", "miDebuggerPath", "miDebuggerServerAddress"));
}
processes = await this.getRemoteProcessesExtendedRemote(miDebuggerPath, miDebuggerServerAddress);
} else {
throw new Error(localize("no.pipetransport.useextendedremote", "Chosen debug configuration does not contain {0} or {1}", "pipeTransport", "useExtendedRemote"));
}

const processes: AttachItem[]= await this.getRemoteOSAndProcesses(pipeCmd);
const attachPickOptions: vscode.QuickPickOptions = {
matchOnDetail: true,
matchOnDescription: true,
Expand Down Expand Up @@ -118,8 +128,8 @@ export class RemoteAttachPicker {
}

return `${outerQuote}sh -c ${innerQuote}uname && if [ ${parameterBegin}uname${parameterEnd} = ${escapedQuote}Linux${escapedQuote} ] ; ` +
`then ${PsProcessParser.psLinuxCommand} ; elif [ ${parameterBegin}uname${parameterEnd} = ${escapedQuote}Darwin${escapedQuote} ] ; ` +
`then ${PsProcessParser.psDarwinCommand}; fi${innerQuote}${outerQuote}`;
`then ${PsProcessParser.psLinuxCommand} ; elif [ ${parameterBegin}uname${parameterEnd} = ${escapedQuote}Darwin${escapedQuote} ] ; ` +
`then ${PsProcessParser.psDarwinCommand}; fi${innerQuote}${outerQuote}`;
}

private async getRemoteOSAndProcesses(pipeCmd: string): Promise<AttachItem[]> {
Expand Down Expand Up @@ -167,6 +177,58 @@ export class RemoteAttachPicker {
}
}

private async getRemoteProcessesExtendedRemote(miDebuggerPath: string, miDebuggerServerAddress: string): Promise<AttachItem[]> {
const args: string[] = [`-ex "target extended-remote ${miDebuggerServerAddress}"`, '-ex "info os processes"', '-batch'];
let processListOutput: util.ProcessReturnType = await util.spawnChildProcess(miDebuggerPath, args);
// The device may not be responsive for a while during the restart after image deploy. Retry 5 times.
for (let i: number = 0; i < 5 && !processListOutput.succeeded; i++) {
processListOutput = await util.spawnChildProcess(miDebuggerPath, args);
}

if (!processListOutput.succeeded) {
throw new Error(localize('failed.to.make.gdb.connection', 'Failed to make GDB connection: "{0}".', processListOutput.output));
}
const processes: AttachItem[] = this.parseProcessesFromInfoOsProcesses(processListOutput.output);
if (!processes || processes.length === 0) {
throw new Error(localize('failed.to.parse.processes', 'Failed to parse processes: "{0}".', processListOutput.output));
}
return processes;
}

/**
Format:
pid usr command cores
1 ?
2 ?
3 /usr/bin/sample 0
4 root /usr/bin/gdbserver --multi :6000 0

Returns an AttachItem array, each item contains a label of "<user >command", and a pid.
Unfortunately because the format of each line is not fixed, and everything except pid is optional, it's hard
to get a better label.
*/
private parseProcessesFromInfoOsProcesses(processList: string): AttachItem[] {
const lines: string[] = processList?.split('\n');
if (!lines?.length) {
return [];
}

const processes: AttachItem[] = [];
for (const line of lines) {
const trimmedLine: string = line.trim();
if (!trimmedLine.endsWith('?')) {
const matches: RegExpMatchArray | null = trimmedLine.match(/^(\d+)\s+(.+?)\s+\d+$/);
if (matches?.length === 3) {
const id: string = matches[1];
const userCommand: string = matches[2];
processes.push({ label: userCommand, id });
}
}
}

return processes;
}

private static createArgumentList(args: string[]): string {
let argsString: string = "";

Expand Down
22 changes: 22 additions & 0 deletions Extension/src/Debugger/configurationProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import { expandAllStrings, ExpansionOptions, ExpansionVars } from '../expand';
import { scp, ssh } from '../SSH/commands';
import * as glob from 'glob';
import { promisify } from 'util';
import { AttachItemsProvider, AttachPicker, RemoteAttachPicker } from './attachToProcess';
import { NativeAttachItemsProviderFactory } from './nativeAttach';

nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
const localize: nls.LocalizeFunc = nls.loadMessageBundle();
Expand Down Expand Up @@ -336,6 +338,26 @@ 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);
} else {
const attachItemsProvider: AttachItemsProvider = NativeAttachItemsProviderFactory.Get();
const attacher: AttachPicker = new AttachPicker(attachItemsProvider);
processId = await attacher.ShowAttachEntries();
}

if (processId) {
config.processId = processId;
} else {
logger.getOutputChannelLogger().showErrorMessage("No process was selected.");
return undefined;
}
}

return config;
}

Expand Down
1 change: 0 additions & 1 deletion Extension/src/Debugger/configurations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ function createAttachString(name: string, type: string, executable: string): str
"name": "${name}",
"type": "${type}",
"request": "attach",{0}
"processId": "$\{command:pickProcess\}"
`, [type === "cppdbg" ? `${os.EOL}"program": "${localize("enter.program.name", "enter program name, for example {0}", "$\{workspaceFolder\}" + "/" + executable).replace(/\"/g, "\\\"")}",` : ""]);
}

Expand Down
13 changes: 11 additions & 2 deletions Extension/tools/OptionsSchema.json