Debug Mode and Tracing


As the previous debugging example illustrated, it's critical to understand what your script is doing and with what data it's working. The contents of variables change as your script jumps in and out of functions. So, knowing exactly what's going on allows you to mentally follow the script's progress and spot the problems that are causing bugs. Unfortunately, PowerShell v1 lacks a full debugger. In fact, v1 lacks any means for a third-party debugging to "plug in" and help you. That means you're more or less on your own when it comes to figuring out what your scripts are doing.

Fortunately, PowerShell does include a "debug mode" that gives you some ability to see what's going on inside your script. Your primary tool is the Set-PSDebug cmdlet that allows you to trace the execution of your script. This means that by writing status information throughout the script you can see what's happening as your script executes.

Set-PSDebug is a fairly complicated cmdlet that allows you to control the trace level of script execution. It also allows you to turn on line-by-line script execution, pausing execution after each line so you can examine the contents of variables to see what your script is doing. We'll use the DebugTest.ps1 script in the following example to see how this works:

DebugTest.ps1

image from book
 function F1 {   param ($n, $a)   if (F2($a)) {     "$n is old enough to vote"   } else {     "$n is too young to vote"   } } function F2 {   param ($var)   if ($var -gt 17) {     $true   } else {     $false   } } [string]$name = "Joe" [int]$age = 25 F1 $name, $age 
image from book

Looking at this script, you'd expect it to display "Joe is old enough to vote." Walk through the script in your head to make sure you agree with that before you proceed.

However, running the script produces this output:

 PS C:\> test\debugtest Joe 25 is too young to vote 

Clearly this output is not correct. So what went wrong? The best way to find out is to start debugging in order to find out what's in the variables and what execution path the script is taking.

We'll start by running Set-PSDebug and specifying a trace level. We'll also turn on step-by-step execution. There are three possible trace levels:

  • 0: No tracing.

  • 1: Trace script lines as they execute.

  • 2: Also trace variable assignments, function calls, and scripts.

Level 2 is the most detailed, which is what we want. Actually, specifying the -step argument implies -trace 1, so we'll need to explicitly specify -trace 2 to get the detail we want. After running Set-PSDebug, we'll run our script.

As you can see, PowerShell now asks on a line-by-line basis if we're ready to execute that line:

 PS C:\> set-psdebug -trace 2 -step PS C:\> test\debugtest Continue with this operation?    1+ test\debugtest [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): 

If you're following along, hit "Y" for each of the following lines as we move through this script one line at a time:

  • You'll notice that the first three lines are merely asking for permission to execute the script itself, and to recognize the two functions. Press Enter three times to move to line 19 of the script.

  • After setting a variable, PowerShell confirms the value that's gone into the variable. This is shown by default in yellow text after the line of script is run:

     Continue with this operation?   19+ [string]$name = "Joe" [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): DEBUG:   19+ [string]$name = "Joe" DEBUG:     ! SET $name = 'Joe'. Continue with this operation?   20+ [int]$age = 25 [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): 

  • We then set the $age variable, which is confirmed.

  • Next we call the F1 function.

  • Now we're on line 3 inside of function F1, which calls function F2.

  • This is a good point to see what actually got passed into F1 for input arguments. Press "S" and hit Enter to suspend the script. Notice that PowerShell drops to a special prompt so we can examine the values of the $n and $a variables. Notice that $n contains "Joe," a carriage return, and "25," which is not what we expected. Also notice that $a does not contain anything, which is why our script isn't working properly.

     Continue with this operation?    3+   if (F2($a)) { [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):S PS C:\>>> $n Joe 25 PS C:\>>> $a PS C:\>>> 

  • Since we've spotted a problem, there's no point in continuing until this problem is fixed. We'll enter EXIT to return to the script, and then reply with "L" to abandon execution of further lines of code:

     PS C:\>>> exit Continue with this operation?    3+   if (F2($a)) { [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):l WriteDebug stopped because the DebugPreference was 'Stop'. At C:\test\debugtest.ps1:22 char:1 + F <<<< 1 $name, $age PS C:\> 

So our problem is that both the name and age are being passed into the $n argument of function F1, while $a isn't getting a value at all. The problem? Our initial call to F1:

 F1 $name, $age 

PowerShell doesn't use a comma to separate arguments. This line should be:

 F1 $name $age 

After making this modification, let's debug the script again. To begin, hit Enter at the debug prompts until you get to line 3 again. Then, hit S to suspend the script and check the values in $n and $a:

 PS C:\> test\debugtest Continue with this operation?    1+ test\debugtest [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): DEBUG:    1+ test\debugtest DEBUG:     ! CALL script 'debugtest.ps1' Continue with this operation?    1+ function F1 { [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): DEBUG:    1+ function F1 { Continue with this operation?   10+ function F2 { [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): DEBUG:   10+ function F2 { Continue with this operation?   19+ [string]$name = "Joe" [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): DEBUG:   19+ [string]$name = "Joe" DEBUG:     ! SET $name = 'Joe'. Continue with this operation?   20+ [int]$age = 25 [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): DEBUG:   20+ [int]$age = 25 DEBUG:     ! SET $age = '25'. Continue with this operation?   22+ F1 $name $age [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): DEBUG:   22+ F1 $name $age DEBUG:     ! CALL function 'F1' (defined in file 'C:\test\debugtest.ps1') Continue with this operation?    3+   if (F2($a)) { [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):s PS C:\>>> $n Joe PS C:\>>> $a 25 PS C:\>>> 

Now we can see that $n contains "Joe" and $a contains "25", which is what we want. We'll type EXIT to return to the script, and hit A to execute all remaining lines without stopping one line at a time.

 PS C:\>>> exit Continue with this operation?    3+   if (F2($a)) { [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):a DEBUG:   3+    if (F2($a)) { DEBUG:    ! CALL function 'F2'  (defined in file 'C:\test\debugtest.ps1') DEBUG:  12+   if ($var -gt 17) { DEBUG:  13+     $true DEBUG:   4+     "$n is old enough to vote" Joe is old enough to vote PS C:\> 

You can see that trace messages are still output for each line of code that executes, but we're not prompted to run each line. Line 3 calls function F2, which starts on line 12. We can see that the if construct was true because it executed line 13, which returned the value $true. That resulted in a true comparison for line 3, which resulted in line 4 executing, producing our script's output. We'll disable tracing by running:

 Set-PSDebug -off 

Tracing Your Work

PowerShell also provides the Trace-Command cmdlet, which is pretty complicated but provides insight into PowerShell's internal workings. Because it provides such a deep view of PowerShell, it requires significant programming experience to use and understand. Therefore, it will not be covered since we consider it a bit beyond the scope of this book.

One good technique for using Set-PSDebug is to either have a printed copy of your script or have your script up in a script development environment such as SAPIEN PrimalScript as you execute the script line-by-line. That way you can see each line of script code in the context of the full script, which allows you to follow PowerShell's execution of your script one line at a time. As you debug, keep a piece of scratch paper handy so you can jot down variables' contents. You'll also frequently suspend the script to check variables' contents. When PowerShell displays each line of code prior to executing it, ask yourself what should happen. That way, when you hit Enter to execute that line, you'll either get the result you expected indicating all is well, or you won't, meaning you'll know where the problem occurred.



Windows PowerShell. TFM
Internet Forensics
ISBN: 982131445
EAN: 2147483647
Year: 2004
Pages: 289

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net