Skip to content

Fix debugger step through on Unix platforms #592

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

Merged

Conversation

SeeminglyScience
Copy link
Collaborator

This change resolves an issue where the debugger would appear hung until a key was pressed on Unix platforms.

This issue was caused by a quirk with the Unix implementation for System.Console. The way cursor position is retrieved is by writing the cursor position escape sequence query and reading it from stdin. To ensure those escape sequences don't trigger Console.ReadKey, the internal stdin buffer is locked while ReadKey is running. This means that while ReadKey is running, any attempts to query cursor position will block the thread until the pending ReadKey finishes.

The pipeline appears to hang on a debugger stop because there is a pending ReadKey call in another thread and various methods in both the internal PowerShell engine and in our host implementation obtain the cursor position between command executions to determine prompt location.

The best solution to this problem (credit goes to @rkeithhill) is to create an alternative ReadKey implementation that waits for KeyAvailable before blocking. However, if ReadKey is not currently running then any input is echoed to the console. Aside from being generally annoying, this presents a huge problem while trying to do something like Read-Host -AsSecureString.

To get around this, I stripped out the native code from corefx that disables console input echo, created a separate package that this project can consume, and implemented a WaitForKeyAvailable method that utilizes it.

This fix should be viewed as a temporary solution until either ReadKey acts like this by default or a ReadKeyAsync method is implemented into corefx.

@tylerl0706 I've tested on Windows and Linux, but it still needs to be tested on Mac. Thanks again for your help with building! :)

Resolves PowerShell/vscode-powershell#987
Resolves PowerShell/vscode-powershell#1107
Resolves #554

This change resolves an issue where the debugger would appear hung
until a key was pressed on Unix platforms.

This issue was caused by a quirk with the Unix implementation for
System.Console.  The way cursor position is retreived is by writing
the cursor position escape sequence query and reading it from stdin.
To ensure those escape sequences don't trigger `Console.ReadKey`,
the internal stdin buffer is locked while `ReadKey` is running.
This means that while `ReadKey` is running, any attempts to query
cursor position will block the thread until the pending `ReadKey`
finishes.

The pipeline appears to hang on a debugger stop because there is
a pending `ReadKey` call in another thread and various methods in
both the internal PowerShell engine and in our host implementation
obtain the cursor position between command executions to determine
prompt location.

The best solution to this problem (credit goes to @rkeithhill) is to
create an alternative `ReadKey` implementation that waits for
`KeyAvailable` before blocking. However, if `ReadKey` is not currently
running then any input is echoed to the console. Aside from being
generally annoying, this presents a huge problem while trying to do
something like `Read-Host -AsSecureString`.

To get around this, I stripped out the native code from corefx
that disables console input echo, created a separate package that this
project can consume, and implemented a `WaitForKeyAvailable` method
that utilizes it.

This fix should be viewed as a temporary solution until either `ReadKey`
acts like this by default or a `ReadKeyAsync` method is implemented into
corefx.
@SeeminglyScience
Copy link
Collaborator Author

@tylerl0706 Could you restart the AppVeyor build when you get a minute? I believe that's the test issue that happens intermittently.

@TylerLeonhardt
Copy link
Member

Awesome! I'll test this on Mac later today. Let me restart that build for you...

@TylerLeonhardt
Copy link
Member

Should I just go through the steps in each of those issues? Or do you have a specific set of steps you want me try?

@SeeminglyScience
Copy link
Collaborator Author

SeeminglyScience commented Dec 30, 2017

Good question, I would say the important things to try would be

  1. Start debugging a file with break points, make sure you can
    • Press F5 to continue to the next breakpoint
    • Press F11 to step to the next sequence point
    • Never have to hit enter in the integrated console to do any of that
  2. Spam input to see if any input is lost or just feels bad (timeout between checking for a available key may be need to be adjusted)
  3. Run $Host.UI.ReadLineAsSecureString() and ensure plain text characters aren't echoed to the screen

Edit: Also for anyone patiently awaiting this fix, if you want to try it early you can grab the AppVeyor build here.

@TylerLeonhardt
Copy link
Member

TylerLeonhardt commented Jan 1, 2018

NYE got in the way! I'll take care of this... next year 😄 (tomorrow or Tuesday). Happy New Year!

@realonedet
Copy link

realonedet commented Jan 2, 2018

Can anyone validate if this fix works with function breakpoints ? i'm crashing everytime i set a function breakpoint (for example, when using the sample scripts provided with the powershell plug in like in DebugTest.ps1)

@TylerLeonhardt
Copy link
Member

@SeeminglyScience

1: Didn't have to hit ENTER a single time, nor did it crash 😄 @realonedet, I used the contents of DebugTest.ps1. This PR should help you.
screen shot 2018-01-02 at 10 51 14 am

2: I spammed the Integrated Console pretty hard and didn't see any hiccups!

3: 😄

/Users/tylerleonhardt/Desktop > $Host.UI.ReadLineAsSecureString()
********
System.Security.SecureString
/Users/tylerleonhardt/Desktop >

The code is good for macOS 🎉

Copy link
Member

@TylerLeonhardt TylerLeonhardt left a comment

Choose a reason for hiding this comment

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

LGTM but I think @rkeithhill and @daviwil should take a look at this.

@@ -129,6 +129,10 @@ task TestPowerShellApi -If { !$script:IsUnix } {
task Build {
exec { & $script:dotnetExe build -c $Configuration .\src\PowerShellEditorServices.Host\PowerShellEditorServices.Host.csproj $script:TargetFrameworksParam }
exec { & $script:dotnetExe build -c $Configuration .\src\PowerShellEditorServices.VSCode\PowerShellEditorServices.VSCode.csproj $script:TargetFrameworksParam }
exec { & $script:dotnetExe publish -c $Configuration .\src\PowerShellEditorServices\PowerShellEditorServices.csproj -f netstandard1.6 }
Copy link
Member

Choose a reason for hiding this comment

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

Just curious - why are you are calling dotnet publish? Does a normal build not have what you're looking for?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, dependencies aren't copied on build when you target standard or core. publish is supposed to the the final product with everything needed to run including packages, runtimes and framework assemblies. Most of that is already provided by being hosted in PowerShell though, so I just pick out what's needed.

@realonedet
Copy link

(newb question) i'm running version 1.5.1 of the VSCode Powershell plugin, will there be another release of the plugin added ? or if i want to test this i will need to rebuild the plugin myself ?

@SeeminglyScience
Copy link
Collaborator Author

@realonedet it would be in the next release. But if you want to try it out early, you can grab the built version from appveyor (here's a direct link). Extra testing before release is very helpful and always welcome :)

exec { & $script:dotnetExe publish -c $Configuration .\src\PowerShellEditorServices\PowerShellEditorServices.csproj -f netstandard1.6 }
Copy-Item $PSScriptRoot\src\PowerShellEditorServices\bin\$Configuration\netstandard1.6\publish\UnixConsoleEcho.dll -Destination $PSScriptRoot\src\PowerShellEditorServices.Host\bin\$Configuration\netstandard1.6
Copy-Item $PSScriptRoot\src\PowerShellEditorServices\bin\$Configuration\netstandard1.6\publish\runtimes\osx-64\native\libdisablekeyecho.dylib -Destination $PSScriptRoot\src\PowerShellEditorServices.Host\bin\$Configuration\netstandard1.6
Copy-Item $PSScriptRoot\src\PowerShellEditorServices\bin\$Configuration\netstandard1.6\publish\runtimes\linux-64\native\libdisablekeyecho.so -Destination $PSScriptRoot\src\PowerShellEditorServices.Host\bin\$Configuration\netstandard1.6
Copy link
Contributor

Choose a reason for hiding this comment

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

This is not my wheelhouse but are we comfortable that this .so (and the above .dylib) will work on all OS distributions supported by VSCode?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@rkeithhill Good question, I initially only tested on Ubuntu. I just pulled the microsoft/powershell docker images for centos7, opensuse42.1, and opensuse13.2 to make sure they at least can load the lib and turn off echo. But that's not a full test, and I don't know enough about docker and Linux to know if what I did is even an accurate test. I'm pretty comfortable because of how small the code is, but I definitely can't say with absolute certainly that it will work everywhere.

Copy link
Member

Choose a reason for hiding this comment

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

I think you could maybe be a little bit more certain on docker by installing vscode in the image along with some vnc server. Then you should be able to rdp/vnc into the container and mess around with VSCode to verify the fix.

It's possibly a bit lengthly, but if you want to be more certain, it will certainly help.

I'm not actually 100% certain if this is possible though - haven't tried it

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@tylerl0706 Yeah for sure, though it sounds like about the same time and effort as spinning up a vm for each distro. Either way that's a bit more involved than I have the bandwidth for, but if someone wants to take that up I'm all for it.

Copy link
Member

Choose a reason for hiding this comment

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

I'll make some Dockerfiles one of these days that can grab the distros, install PowerShell, VNC, VSCode, and the extension so we have them for next time.

@rkeithhill
Copy link
Contributor

So the extension supports feature flags via the setting:

"powershell.developer.featureFlags": [],

Those get passed down to PSES. Would it be reasonable to firewall the new code behind a feature flag and tell folks how to enable the feature on Linux/macOS? Not sure how hard that would be with this change. We used to firewall features like this on a cross-platform project I worked on back in the 90's - yeah dating myself a bit here. OTOH the current debugging situation on Linux/macOS is so horribly broken it is hard to imagine this could make it worse. :-)

@TylerLeonhardt
Copy link
Member

IMO, I'm fine with just shipping it 🚢

Copy link
Contributor

@rkeithhill rkeithhill left a comment

Choose a reason for hiding this comment

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

LGTM

@TylerLeonhardt
Copy link
Member

Should we merge this, or wait for @daviwil? Your call @SeeminglyScience!

@TylerLeonhardt
Copy link
Member

Just one question to the room, is there an opportunity to add tests for this PR at all?

@SeeminglyScience
Copy link
Collaborator Author

@tylerl0706 Let's go ahead and merge. Though, you're right, tests would be good. Not sure exactly when I'll get around to that. We can either keep this open until then, or merge it now and open an issue to track the need for tests. I'd prefer the latter, but I'll leave that up to you.

@SeeminglyScience
Copy link
Collaborator Author

Oh also, I'm open to putting this behind a feature flag. If only because it'd be nice to have that capability nailed down before PSReadLine, which will definitely need to be behind a feature flag.

Copy link
Contributor

@daviwil daviwil left a comment

Choose a reason for hiding this comment

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

@SeeminglyScience is a superhero. Thanks a ton for working so hard to get this fixed!

@@ -466,41 +487,6 @@ public Task<string> ReadSimpleLine(CancellationToken cancellationToken)
return null;
}

private async Task<ConsoleKeyInfo> ReadKeyAsync(CancellationToken cancellationToken)
Copy link
Contributor

Choose a reason for hiding this comment

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

Really happy to get rid of this horrible piece of code :)

@@ -185,9 +189,12 @@ task LayoutModule -After Build {
New-Item -Force $PSScriptRoot\module\PowerShellEditorServices\bin\Core -Type Directory | Out-Null

Copy-Item -Force -Path $PSScriptRoot\src\PowerShellEditorServices.Host\bin\$Configuration\netstandard1.6\* -Filter Microsoft.PowerShell.EditorServices*.dll -Destination $PSScriptRoot\module\PowerShellEditorServices\bin\Core\
Copy-Item -Force -Path $PSScriptRoot\src\PowerShellEditorServices.Host\bin\$Configuration\netstandard1.6\UnixConsoleEcho.dll -Destination $PSScriptRoot\module\PowerShellEditorServices\bin\Core\
Copy link
Contributor

Choose a reason for hiding this comment

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

@tylerl0706 before you can ship these binaries with PSES and the PowerShell extension, you'll need to request OSS approval for Patrick's library. You may also need to include his copyright in a Third Party Notices file like the one in the PS extension. I highly doubt they will reject the request to use this code but better to follow the necessary process anyway :)

@TylerLeonhardt
Copy link
Member

TylerLeonhardt commented Jan 6, 2018

@SeeminglyScience I got approval to use your library 🎉

Let's merge this in and I'll add the Third Party Notices.

@TylerLeonhardt TylerLeonhardt merged commit 8b12d75 into PowerShell:master Jan 6, 2018
@SeeminglyScience SeeminglyScience deleted the unix-stepping-fix branch January 6, 2018 19:46
@realonedet
Copy link

@SeeminglyScience I tried the code from the appveyor link (replacing the directories in the zip in the powershell modules folders), the behavior did not change, i'm still crashing as soon as i debug code with breakpoints set (i'm just trying to debug the sample code that comes with the Powershell AddIn). Any ideas ? #StillFrustrated

@SeeminglyScience
Copy link
Collaborator Author

@realonedet Hmm that may be a different issue then. Could you open an issue and include verbose logs from right after a crash? (Capturing verbose logs)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
5 participants