Wednesday, March 26, 2008 10:18 PM
bart
Windows PowerShell 2.0 Feature Focus - Script Debugging
Two weeks ago I did a little tour through Europe spreading the word on a couple of our technologies including Windows PowerShell 2.0. In this blog series I'll dive into a few features of Windows PowerShell 2.0. Keep in mind though it's still very early and things might change towards RTW - all samples presented in this series are based on the CTP which is available over here.
Introduction
Previously in this series we covered new scripting capabilities with script cmdlets, script internationalization and a few language enhancements in Windows PowerShell 2.0. But writing scripts is just one piece of the puzzle, how to debug those when something isn't quite right? To answer that question, Windows PowerShell 2.0 introduces script debugging capabilities.
Set-PsDebug
The Windows PowerShell debugging story embodies a series of features that cooperate with each other. First we have some configuration options using Set-PsDebug. This is the cmdlet you'll use to configure the debugging options of the system. There are a few configuration options:
- -off: turns script debugging off
- -trace: specifies a trace level, where 0 is like -off, 1 traces script execution on a line-per-line basis, 2 does the same but also traces variable assignment and function calls
- -strict: like VB's Option Strict, makes the debugger throw an exception is a variable is used before being assigned to
Below is a run showing some Set-PsDebug options:
Notice all debugging output triggered by the trace level set through Set-PsDebug is prefixed with DEBUG. In order to write to the debug output yourself, there's Write-Debug which I'll leave as an exploration for the reader.
Working with breakpoints
Where it really gets interesting is the concept of breakpoints which are "points" where execution is "broken". In PowerShell that corresponds to the following:
- A line (and column) number in a certain script;
- Calls to a specified function;
- Invocations of a specified command;
- Variable access.
Once we have specified where we need to focus on what to do when the breakpoint is hit. When no action is specified, the shell will spawn a sub-shell that has access to the current state of the execution so that variables can be inspected and other actions can be taken during debugging. Alternatively, one can specify a script-block as an action.
Enough theory, let's map those concepts on cmdlets. Breakpoint in PowerShell are called PSBreakpoints, so let get-command be our guide:
It obviously all starts with New-PSBreakpoint and all other cmdlets are self-explanatory. Time to show a few uses of breakpoints. First, create a simple script called debug.ps1:
function bar {
$a = 123
if ($a -gt 100)
{
$a
foo
}
}
function foo {
$a = 321
Write-Host $a
}
"Welcome to PowerShell 2.0 Script Debugging!"
bar
Invoking it should produce no surprises:
First we'll set a breakpoint on a specific line of the script using New-PSBreakpoint -Script debug.ps1 -Line 16 and re-run the script. Notice - with tracing on to show line info of the script executing - we're breaking on the call to bar:
Also notice the two additional > signs to the prompt below. This indicates we've entered a nested debugging prompt. Now we need to control the debugger to indicate what we want to do. For that purpose there are a few Step-* cmdlets as shown below:
With Step-Into you simple go to the next statement, possibly entering a function call. With Step-Over you do the same, but you "step over" function calls straight to the line below the call. Step-Out is used to exit from a breakpoint and let the script continue to run till the next breakpoint is hit (or till it completes). A quick run:
So far we've been stepping through the code line-by-line. Notice the line numbers being shown next the DEBUG: word when tracing is enabled. The second DEBUG: line shows the output of the Step-Into command, showing where we'd end up next (preview of the next line). Now we're inside the foo function call, but you might wonder how we got there and which functions have been called before: enter Get-PsCallstack:
From the original prompt (0), we executed debug.ps1, which called into bar and foo subsequently to end up in the nested debugger prompt. While debugging you'll obviously want to investigate the system, for example to see what $a contains, so you can simple print the variable. Finally, we continue to step and exit the nested prompt because the script has stopped:
Time for some bookkeeping: let's get rid of this breakpoint. Easy once more, using Remove-PSBreakpoint:
So illustrate a few other concepts, we'll set a breakpoint on a function, on a command invocation and on variable access:
Re-run the script and watch out. Here's the output - we break four times: two variable $a assignments, one foo call and one call to Write-Host:

Notice the use of exit to escape from the nested prompt and to make the script execution continue to the next breakpoint. An alternative would be to use Step-Out. Especially the variable assignment debugger breakpoint option is very attractive because in lots of cases you see state of a variable being changed and you simple want to trace back where changes are happening.
Other stuff you might want to take a look into includes the -Action parameter to New-PSBreakpoint, the ability to clone breakpoints using -Clone, enabling/disabling breakpoints and the HitCount property of breakpoints.
For more information on debugging, simply take a look at get-help about_debug.
Happy debugging!
Del.icio.us |
Digg It |
Technorati |
Blinklist |
Furl |
reddit |
DotNetKicks
Filed under: Windows PowerShell