When I run a debug session two things happen:
You can replicate this by running a small script like:
while ($true)
{
Write-Output "I'm In a loop"
Start-Sleep 10
Write-Output "I just woke up"
}
Write-Output Write-OutputWhat will happen is the debugger will break again on the first Write-Output and you won't be able to continue. If you stop execution and type exit in the debug terminal the integrated terminal will crash
EditorServices.log
vscode-powershell.log
) about
capturing and sending logs.
| Name | Version |
| --- | --- |
| Operating System | Windows_NT x64 10.0.16299 |
| VSCode | 1.26.1|
| PowerShell Extension Version | 1.8.3 |
|Name|Value|
|---|---|
|PSVersion|5.1.16299.492|
|PSEdition|Desktop|
|PSCompatibleVersions|1.0 2.0 3.0 4.0 5.0 5.1.16299.492|
|BuildVersion|10.0.16299.492|
|CLRVersion|4.0.30319.42000|
|WSManStackVersion|3.0|
|PSRemotingProtocolVersion|2.3|
|SerializationVersion|1.1.0.1|
Visual Studio Code Extensions(Click to Expand)
|Extension|Author|Version|
|---|---|---|
|aspnet-helper|schneiderpat|0.6.4|
|ecdc|mitchdenny|0.12.0|
|EditorConfig|EditorConfig|0.12.4|
|github-issues-prs|ms-vscode|0.9.0|
|gitlens|eamodio|8.5.6|
|jenkins-declarative-support|jmMeessen|0.1.0|
|markdown-all-in-one|yzhang|1.6.0|
|PowerShell|ms-vscode|1.8.3|
|python|ms-python|2018.7.1|
|rainbow-csv|mechatroner|0.4.2|
|vscode-github|KnisterPeter|0.30.0|
|vscode-icons|robertohuertasm|7.25.0|
|vscode-markdownlint|DavidAnson|0.20.0|
|vscode-mermaid-preview|vstirbu|0.10.1|
|xml|DotJoshJohnson|2.3.2|;
Thanks for opening an issue @GABeech. I can reproduce this on my machine. I've been looking into it but haven't found the cause yet. Will continue investigating.
So I've debugged this a couple of times and I've just hit a nasty hang here:
https://github.com/PowerShell/PowerShellEditorServices/blob/f1ab46289904b77fe5f5bd97cc89e2f5286444db/src/PowerShellEditorServices/Session/PowerShellContext.cs#L352-L355
@rjmholt If you have the PSRL build still, see if you can reproduce it there. I made a lot of changes around the runspace handle queues.
So I did some more investigating on this today.
The breakpoint is set correctly and unset correctly, but trying to set it again does nothing at all. This is almost certainly because PSES is being blocked by the current execution.
I was curious as to where PSES thought it was, so I paused the PSES .NET debugger and on my Windows machine in 1.9.1, it reports itself as being here:
https://github.com/PowerShell/PowerShellEditorServices/blob/f1ab46289904b77fe5f5bd97cc89e2f5286444db/src/PowerShellEditorServices/Console/WindowsConsoleOperations.cs#L22
In v2, I think it reports itself as being here:
https://github.com/PowerShell/PowerShellEditorServices/blob/67af23203c0d7aac52a6c5655d7d7f1202e80dd0/src/PowerShellEditorServices/Session/PowerShellContext.cs#L651
I think it basically comes down to the debug adapter not being able to interrupt the script to add a new breakpoint -- which is obviously a problem.
In 1.9.1 I get no crash (or at least I didn't this time). But with v2 I got a pipe broken error, which I think we've seen can also occur in 1.9.1.
After looking into this and thinking about it a bit, this looks like a deeper issue -- I'll see what we can do to fix it, but I think we will need to do some cleverness with interrupting the execution of the PowerShell pipeline.
We could implement this in 2.0. Taking over script execution between sequence points can be done with events, but that requires all the nested context work done for PSReadLine.
If we can engineer that, it would be useful in a whole bunch of cases for cancellation of the PowerShell executions powering most requests -- so we could finally implement $/cancelRequest properly.
We can already trigger a pipeline stop from any thread, the engine supports that. Just gotta tie a request to a powershell instance
Ok @GABeech I've done some research on this one and talked to @PaulHigin as well, who is our debugger expert.
Because the PowerShell debugger is just PowerShell, setting a breakpoint while the pipeline is running is a bit trickier than for other languages. As far as I know, it's something that neither PowerShell nor the ISE has directly supported.
But, experimenting with your scenario, using the pause (โธ or F6) button will stop pipeline execution. It hooks into the same API that the ISE uses with Ctrl+Break and PowerShell itself (the ConHost) uses with Ctrl+B.
So the current way to set a breakpoint while a script is running like in the scenario above it:
Bear in mind that if you have a single long running command or .NET call, this can't pause that. There's no mechanism for that anywhere, because the PowerShell debugger is PowerShell -- it can't stop in the middle of a native or .NET call.
I'm looking into how we might make this automatic -- so that when you set a breakpoint, it will stop the pipeline, insert the breakpoint and then continue on its way. But it's complicated by needing to sync up VSCode's client state.
@rjmholt We could use events. I think this should be safe from issues like the completion deadlock.
Here's a little sample:
$timer = [System.Timers.Timer]::new(60000)
Register-ObjectEvent -InputObject $timer -EventName Elapsed -Action {
$Event | Unregister-Event
Set-PSBreakpoint -Variable someSpecificVar -Mode Write
}
$timer.Enabled = $true
# After a minute, the debugger will break on the someSpecificVar assignment.
while ($true) {
$someSpecificVar = $true
Start-Sleep 5
}
We would probably want register it on an event on PowerShellContext instead of Timer, that's just for the PoC. The idea is that PowerShell events like those register via Register-ObjectEvent will be processed between sequence points if a script is running. This still won't help in situations where a single sequence point is taking a long time, but we can't really do anything about that.
Does OnIdle execute between pipeline invocations? If so, we could queue up breakpoint requests and execute them then.
@rkeithhill Not sure what the usual timeout is for OnIdle, with PSReadLine it's 200ms (or immediately when we ask it to check for events). I know for sure it won't execute between sequence points of a long running script though.
Same idea though, we would queue up breakpoints by raising an event on an object we control and the subscriber would get to it at the next sequence point.
@rkeithhill I don't think OnIdle events are raised between pipeline invocations, but could be wrong.
The reason I mention our debugService.Break() method, is because I think it actually uses the right API:
PowerShellContext methodIt's what the ISE uses for interrupting a currently running script to break into it.
Is there a reason using that is a bad idea for us?
@rjmholt I could be wrong but I believe that just tells the debugger that it should pause at the next sequence point. I don't think it synchronously stops the debugger.
Right, it just says "please pause the debugger at the next opportunity". Is there a way we think we can/should be doing better than that?
We're not specifically trying to stop the debugger right? If we're just trying to set a breakpoint then I think the event route is cleaner. We could stop the debugger, then in the PowerShellContext.OnDebuggerStop method we could set queued breakpoints and possibly exit early, that seems like a lot of extra state to manage though.
Ah, yes agreed!
It seems like an API we ought to have on PowerShellContext -- like QueueSequencePointCommand or something
Sorry for the late contribution. If I understand the issue correctly, engine events are not needed to manage script breakpoints. But the only way to update script breakpoints on running script is to, as discussed above:
a. Break into the debugger (set to step mode)
b. Add/remove/enable/disable breakpoints
c. Resume script execution
Note that the first step (a.) can be problematic if the script is not running but is stalled during a dotNet API call or while running a native command. The debugger stop event will not occur until the next sequence point is executed.
Breaking into the debugger can be done via the ScriptDebugger.SetDebuggerStepMode() method, and engine events are not needed. The method can be called from any thread. Then as @SeeminglyScience mentions, breakpoints can be managed during the DebuggerStop event callback via the ScriptDebugger.ProcessCommand() method.
Most helpful comment
Ah, yes agreed!
It seems like an API we ought to have on
PowerShellContext-- likeQueueSequencePointCommandor something