Thursday, February 04, 2010

Invoking the PowerShell Debugger from Script

With PowerShell v2, a new and much improved command line debugger was introduced. The old one is still around though. Anyway, more information can be found in the help subject about_Debuggers.

The strange part is that Set-PsDebug –Step invokes the old debugger and there does not seem to be a way of invoking the new one. You can only invoke the new one by setting a breakpoint. Even though, breakpoints are a very useful feature which I use a lot, I would also like to do it from inside a script.

I have played around with some ways of doing this.

First, a self-contained function

Function Invoke-Debugger{
function debug{}
$bp=Set-PSBreakPoint -Command debug
$bp | Remove-PSBreakpoint

function test{
write-host 1
write-host 2


It works great, but has the downside, that the current execution pointer is inside the Invoke-Debugger function -

Entering debug mode. Use h or ? for help.

Hit Command breakpoint on 'debug'

x.ps1:4 debug
7 $docs>>> l

1: Function Invoke-Debugger{
2: function debug{}
3: $bp=Set-PSBreakPoint -Command debug
4:* debug
5: $bp | Remove-PSBreakpoint
6: }
8: function test{
9: write-host 1
10: Invoke-Debugger
11: write-host 2
12: }
14: test

No matter how I try to tweak it, I end up in the same way.

Next, lets try using a two part approach (setting the breakpoint and doing some action to invoke it) -

$null=Set-PSBreakpoint -Variable InvokeDebugger

function test{
write-host 1
write-host 2


This is much better, now the execution pointer is right in the code -

Hit Variable breakpoint on '$InvokeDebugger' (Write access)

x.ps1:5 $InvokeDebugger=1
9 $docs>>> l

1: $null=Set-PSBreakpoint -Variable InvokeDebugger
3: function test{
4: write-host 1
5:* $InvokeDebugger=1
6: write-host 2
7: }
9: test

Eventually, this led me to this piece of code. It is easier to write than the variable assignment and you can also define an easy-writeable alias for it -

function Invoke-Debugger{}
New-Alias id Invoke-Debugger
$null=Set-PSBreakPoint –Command Invoke-Debugger

function test{
write-host 1
write-host 2


The execution pointer is right at the call. If you include any statements in Invoke-Debugger, this will not work as well while ‘step’ will take execution into the function -

Hit Command breakpoint on 'Invoke-Debugger'

x.ps1:9 id
13 $docs>>> l

4: New-Alias id Invoke-Debugger
5: $null=Set-PSBreakPoint –Command Invoke-Debugger
7: function test{
8: write-host 1
9:* id
10: write-host 2
11: }
13: test

This method also enables you to make conditional break using straight, normal code (compared to making the logic in the –action argument of Set-PSBreakPoint) -

filter test{
write-host "got $_"
if ($_ -eq 3) {id}

1..5 | test

and the output -

got 1
got 2
got 3
Hit Command breakpoint on 'Invoke-Debugger'

x.ps1:9 if ($_ -eq 3) {id}
14 $docs>>> l

4: New-Alias id Invoke-Debugger
5: $null=Set-PSBreakPoint –Command Invoke-Debugger
7: filter test{
8: write-host "got $_"
9:* if ($_ -eq 3) {id}
10: }
12: 1..5 | test

You can include the Invoke-Debugger function and the Set-PSBreakPoint in your profile, so they are available in all our scripts.

Happy debugging..

No comments: