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
debug
$bp | Remove-PSBreakpoint
}

function test{
write-host 1
Invoke-Debugger
write-host 2
}

test





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




1
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: }
7:
8: function test{
9: write-host 1
10: Invoke-Debugger
11: write-host 2
12: }
13:
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
$InvokeDebugger=1
write-host 2
}

test



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



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

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

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





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
id
write-host 2
}

test







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 -















1
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
6:
7: function test{
8: write-host 1
9:* id
10: write-host 2
11: }
12:
13: test
14:







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
6:
7: filter test{
8: write-host "got $_"
9:* if ($_ -eq 3) {id}
10: }
11:
12: 1..5 | test
13:





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: