-
Notifications
You must be signed in to change notification settings - Fork 728
Adding support for Remote .NET Attach and tsc fix on Windows #798
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
Conversation
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.
|
Hi @WardenGnaw, I'm your friendly neighborhood .NET Foundation Pull Request Bot (You can call me DNFBOT). Thanks for your contribution! TTYL, DNFBOT; |
|
@WardenGnaw, Thanks for signing the contribution license agreement so quickly! Actual humans will now validate the agreement and then evaluate the PR. |
|
@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" |
There was a problem hiding this comment.
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#"?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
src/features/commands.ts
Outdated
| 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); |
There was a problem hiding this comment.
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! 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I lost. 😢
src/features/processPicker.ts
Outdated
| // Config name not found. | ||
| return new Promise<string>((resolve, reject) => { | ||
| reject(new Error("Name not defined in current configuration.")); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- This could be written simpler as
return Promise.reject<string>(new Error("Name not defined in current configuration"));There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Should the error message mention the name that was passed in?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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. 😄
src/features/processPicker.ts
Outdated
| 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")); |
There was a problem hiding this comment.
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.
src/features/processPicker.ts
Outdated
| 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.")); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here too.
src/features/processPicker.ts
Outdated
| 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)); | ||
| }); |
There was a problem hiding this comment.
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}`));
src/features/processPicker.ts
Outdated
| if (lines.length == 1) { | ||
| return new Promise<AttachItem[]>((resolve, reject) => { | ||
| reject(new Error("Transport attach could not obtain processes list.")); | ||
| }); |
There was a problem hiding this comment.
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.
src/features/processPicker.ts
Outdated
|
|
||
| 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' : ''); |
There was a problem hiding this comment.
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' : '')}`;
src/features/processPicker.ts
Outdated
| if (lines.length == 0) { | ||
| return new Promise<AttachItem[]>((resolve, reject) => { | ||
| reject(new Error("Pipe transport failed to get OS and processes.")); | ||
| }); |
There was a problem hiding this comment.
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.
src/features/processPicker.ts
Outdated
| // 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' : ''); |
There was a problem hiding this comment.
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.
gregg-miskelly
left a comment
There was a problem hiding this 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.
src/features/processPicker.ts
Outdated
| pipeArgs = platformSpecificPipeTransportOptions.pipeArgs || pipeArgs; | ||
| } | ||
|
|
||
| let pipeCmd: string = pipeProgram + " " + pipeArgs.join(" "); |
There was a problem hiding this comment.
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.
src/features/processPicker.ts
Outdated
| const command = `uname && if [ $(uname) == "Linux" ] ; then ${RemoteAttachPicker.linuxPsCommand} ; elif [ $(uname) == "Darwin" ] ; ` + | ||
| `then ${RemoteAttachPicker.osxPsCommand}; fi`; | ||
|
|
||
| return execChildProcess(`${pipeCmd} "${command}"`, null).then(output => { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
src/features/processPicker.ts
Outdated
| let remoteOS = lines[0].replace(/[\r\n]+/g, ''); | ||
|
|
||
| if (remoteOS != "Linux" && remoteOS != "Darwin") { | ||
| return Promise.reject<AttachItem[]>(new Error("Could not determine operating system " + |
There was a problem hiding this comment.
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'); | ||
| } |
There was a problem hiding this comment.
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) => { |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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);
There was a problem hiding this comment.
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.
src/features/processPicker.ts
Outdated
| } | ||
|
|
||
| if (stderr && stderr.length > 0) { | ||
| reject(new Error("See remote-attach output")); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
Fixing error handling on exec calls, and clearing channel on remoteProcessPicker startup.
|
This PR has been replaced with #918 |
|
@WardenGnaw Is there any documentation on remote debugging, including how to use pipeTransport? |
|
@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. |
|
@gregg-miskelly looks good thanks. Is debugging (locally) over SSH advantageous in any way? |
|
@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. |

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.