Skip to content

Conversation

@WardenGnaw
Copy link
Contributor

Feature:
package.json adds listRemoteProcesses as a command, including debuggerPath into pipeTransport. The
command is registered into commands.ts. processPicker.ts has its process listing parser refactored
and now includes calling process listing to remote processes. Only works on Linux and OSX. Windows
remote attach will result in a rejected promise.

Fix:
Fixing tsc error TS1252 in platform.ts.
Moved the getValue function within getCurrentPlatform function outside of function scope. Added string array parameter to getValue function because lines was originally a local variable within the getCurrentPlatform function.

Moved the getValue function within getCurrentPlatform function outside of function scope. Added string array
parameter to getValue function because lines was originally a local variable within the getCurrentPlatform
function.
package.json adds listRemoteProcesses as a command, including debuggerPath into pipeTransport. The
command is registered into commands.ts. processPicker.ts has its process listing parser refactored
and now includes calling process listing to remote processes. Only works on Linux and OSX. Windows
remote attach will result in a rejected promise.
@dnfclas
Copy link

dnfclas commented Oct 3, 2016

Hi @WardenGnaw, I'm your friendly neighborhood .NET Foundation Pull Request Bot (You can call me DNFBOT). Thanks for your contribution!

In order for us to evaluate and accept your PR, we ask that you sign a contribution license agreement. It's all electronic and will take just minutes. I promise there's no faxing. https://cla2.dotnetfoundation.org.

TTYL, DNFBOT;

@dnfclas
Copy link

dnfclas commented Oct 3, 2016

@WardenGnaw, Thanks for signing the contribution license agreement so quickly! Actual humans will now validate the agreement and then evaluate the PR.

Thanks, DNFBOT;

@rajkumar42
Copy link
Contributor

@edmunoz is added to the review. #Closed

Added checking for platformSpecificPipeTransportOptions and added more
error checks. Required debuggerPath for attach.pipeTransport.
{
"command" : "csharp.listRemoteProcess",
"title" : "List processes on remote connection for attach",
"category": "CSharp"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I notice that this command and "csharp.listProcess" have a category of "CSharp", but chsarp.downloadDebugger" has a category of "Debug". Is that intentional? Also, should the category be "C#"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This I am not to sure of, @rajkumar42 or @gregg-miskelly

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know what category is supposed to be for. You can see if the schema will tell you. @chuckries created the downladDebugger command, so he might know. Otherwise you can look at the VS Code source to see if that has the answer (http://github.com/Microsoft/VSCode)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

category appears in the Command Palette:

image

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at https://github.com/Microsoft/vscode-node-debug/search?utf8=%E2%9C%93&q=pickNodeProcess, I wonder if there is a way to avoid declaring this command entirely since it shouldn't actually show up in the command well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing it from the commands JSON prevents it from showing up in the command well but functionality is kept.

let d9 = vscode.commands.registerCommand('csharp.listRemoteProcess', (args) => RemoteAttachPicker.ShowAttachEntries(args));

return vscode.Disposable.from(d1, d2, d3, d4, d5, d6, d7, d8);
return vscode.Disposable.from(d1, d2, d3, d4, d5, d6, d7, d8, d9);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will conflict with #795. First one merged wins! 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I lost. 😢

// Config name not found.
return new Promise<string>((resolve, reject) => {
reject(new Error("Name not defined in current configuration."));
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. This could be written simpler as
return Promise.reject<string>(new Error("Name not defined in current configuration"));

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Should the error message mention the name that was passed in?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the case where name is null.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah of course. I was reading quickly. 😄

if (configIdx == json.configurations.length) {
// Name not found in list of given configurations.
return new Promise<string>((resolve, reject) => {
reject(new Error("Could not find configuration that matches given name"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar feedback to my comments above.

return new Promise<string>((resolve, reject) => {
reject(new Error("Configuration \"" + args.name + "\" in launch.json does not have a " +
"pipeTransport argument with debuggerPath for pickRemoteProcess. Use pickProcess for local attach."));
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here too.

if (remoteOS != "Linux" && remoteOS != "Darwin") {
return new Promise<AttachItem[]>((resolve, reject) => {
reject(new Error("Could not determine operating system or operating system not supported. " + remoteOS));
});
Copy link
Member

@DustinCampbell DustinCampbell Oct 10, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return Promise.reject<AttackItem[]>(new Error(`Could not determine operating system or operating system not supported. ${remoteOS}`));

if (lines.length == 1) {
return new Promise<AttachItem[]>((resolve, reject) => {
reject(new Error("Transport attach could not obtain processes list."));
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above. You can just return Promise.reject.


public static getRemoteProcesses(pipeCmd: string, os: string) : Promise<AttachItem[]> {
const commColumnTitle = Array(PsOutputParser.secondColumnCharacters).join("a");
const psCommand = `ps -axww -o pid=,comm=${commColumnTitle},args=` + (os === 'darwin' ? ' -c' : '');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also could be:

const psCommand = `ps -axww -o pid=,comm=${commColumnTitle},args=${(os === 'darwin' ? ' -c' : '')}`;

if (lines.length == 0) {
return new Promise<AttachItem[]>((resolve, reject) => {
reject(new Error("Pipe transport failed to get OS and processes."));
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another spot to return Promise.reject.

// Note that comm on Linux systems is truncated to 16 characters:
// https://bugzilla.redhat.com/show_bug.cgi?id=429565
// Since 'args' contains the full path to the executable, even if truncated, searching will work as desired.
const psCommand = `ps -axww -o pid=,comm=${commColumnTitle},args=` + (os.platform() === 'darwin' ? ' -c' : '');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this code copied and pasted? Could it be shared?

Instead of creating Promise objects, used Promise.reject.
Used template strings properly and formatting.
Copy link
Contributor

@gregg-miskelly gregg-miskelly left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are almost there :). I tried to delete all the stale comments.

pipeArgs = platformSpecificPipeTransportOptions.pipeArgs || pipeArgs;
}

let pipeCmd: string = pipeProgram + " " + pipeArgs.join(" ");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If pipeProgram contains spaces I believe you will find you need to quote it. If so, you should probably should do the same for pipeArgs also.

const command = `uname && if [ $(uname) == "Linux" ] ; then ${RemoteAttachPicker.linuxPsCommand} ; elif [ $(uname) == "Darwin" ] ; ` +
`then ${RemoteAttachPicker.osxPsCommand}; fi`;

return execChildProcess(`${pipeCmd} "${command}"`, null).then(output => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this comment earlier, but to a spot where it got lost -- currently we will just reject the promise if the command returns stderror text. This might be confusing now that we are using execChildProcess for the pipe program. I would suggest doing something like putting in the wrong ip address in your plink or ssh command line and see if the experience is compressible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After 15 seconds, it results in a "Command failed" error with the important bits cut off. Most of the output is the pipe command and arguments.

let remoteOS = lines[0].replace(/[\r\n]+/g, '');

if (remoteOS != "Linux" && remoteOS != "Darwin") {
return Promise.reject<AttachItem[]>(new Error("Could not determine operating system " +
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this always been the OS is unsupported?

Adding output channel for pipe transport errors.
Fixing error messages and escaping pipe transport commands.
// Create remote attach output channel for errors.
if (!RemoteAttachPicker._channel) {
RemoteAttachPicker._channel = vscode.window.createOutputChannel('remote-attach');
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should probably clear the channel if it already exists

}

function execChildProcessAndOutputErrorToChannel(process: string, workingDirectory: string, channel: vscode.OutputChannel): Promise<string> {
return new Promise<string>((resolve, reject) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might want to write out the command be executed to the channel for easy debugging.

return;
}

if (stderr && stderr.length > 0) {
Copy link
Contributor

@gregg-miskelly gregg-miskelly Oct 11, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would probably suggest keeping all the stdout and stderr text in a buffer so that on an error you can dump it all.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like this? Using the channel as the buffer.

            if (stdout && stdout.length > 0) {
                channel.appendLine('stdout:');
                channel.append(stdout);
            }

            if (stderr && stderr.length > 0) {
                channel.appendLine('stderr:');
                channel.append(stderr);
            }

            if (error) {
                channel.appendLine('Error message:');
                channel.append(error.message);
            }


            if (error || (stderr && stderr.length > 0)) {
                reject(new Error("See remote-attach output"));
                channel.show();
                return;
            }

            resolve(stdout);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, except that you probably don't want to send it to the channel in the success case. So I would suggest keeping an internal string buffer until there is an error. You probably don't need the stderr/stdout prefix -- if something is an error or normal output is usually apparent to humans by reading the text.

}

if (stderr && stderr.length > 0) {
reject(new Error("See remote-attach output"));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to wait till the command finishes in case there are multiple error lines.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused about how to do this. I thought this would be part of the callback function after it finishes the exec.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you just keep state as to if you saw an error and then you can reject the promise at the end if there was an error.

@gregg-miskelly
Copy link
Contributor

This PR has been replaced with #918

@asyschikov
Copy link

@WardenGnaw Is there any documentation on remote debugging, including how to use pipeTransport?

@gregg-miskelly
Copy link
Contributor

@asyschikov I wrote up https://github.com/OmniSharp/omnisharp-vscode/wiki/Attaching-to-remote-processes. Let me know if you need more information/find problems.

@asyschikov
Copy link

@gregg-miskelly looks good thanks. Is debugging (locally) over SSH advantageous in any way?

@gregg-miskelly
Copy link
Contributor

@asyschikov no - I wouldn't expect it to make a big difference, but it would add a bit of extra latency in every interaction between the UI and the backend.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants