Chapter 12. Debugging Code

A bug is a programming error that makes a program fail to produce the correct result. The program might crash, display incorrect data, or do something completely unexpected such as delete the wrong file.

In this lesson you learn how to use the excellent debugging tools provided by Visual Studio's IDE to find bugs in Visual Basic. You learn about different kinds of bugs and you get to practice debugging techniques on some buggy examples that you can download from the book's web site.

DEFERRED TECHNIQUES

Unfortunately, at this point in the book you don't know enough about writing code to be able to understand and fix certain kinds of bugs. For example, a program crashes if it tries to access an array entry that is outside of the array, but you won't learn about arrays until Lesson 16.

So why does this lesson cover debugging when you don't even know all of what can cause bugs or the techniques that you need to fix certain kinds of bugs? It makes sense for two reasons.

First, the previous lesson was the first part of the book in which you were likely to encounter bugs. Whenever I teach beginning programming, students start seeing bugs as soon as they write code that performs calculations like those covered in Lesson 11. These kinds of bugs are easy to fix if you know just a little bit about debugging but can be extremely frustrating if you don't.

Second, it turns out that you don't need to know more advanced techniques to learn simple debugging. Once you learn how to track down simple bugs, you can use the same techniques to find more advanced bugs. (If you learn to swim in 3 feet of water, you can use the same techniques to swim in 10 feet or 100 feet of water.)

Later, when you know more about Visual Basic programming and deal with more advanced bugs, that same knowledge will help you fix those bugs. When you know enough to have array indexing errors, you'll also know enough to fix them.

DEBUGGING THEN AND NOW

Back in the bad old days, programmers often fixed bugs by staring hard at the code, making a few test changes, and then running the program again to see what happened. This trial-and-error approach was extremely slow because the programmer didn't really know exactly what was going on inside the code. If the programmer didn't have a good understanding of what was really happening, the test changes often didn't help and sometimes made the problem worse.

Visual Studio's IDE provides excellent tools for debugging code. In particular, it lets you stop a program while it is running and see what it's doing. It lets you follow the program as it executes its code one line at a time, look at variable values, and even change those values while the program is still running.

The following sections describe some of Visual Studio's most useful debugging tools.

SETTING BREAKPOINTS

A breakpoint stops code execution at a particular piece of code. To set a breakpoint, open the Code Editor and click the gray margin to the left of the code where you want to stop. Alternatively, you can place the cursor on the line and press F9. The IDE displays a red circle to show that the line has a breakpoint.

Figure 12-1 shows a breakpoint set on the following line of code:

Dim grandTotal As Decimal = subTotal + salesTax + shipping
Figure 12-1

Figure 12.1. Figure 12-1

If you run the program now, execution stops when it reaches that line. You can then study the code as described in the following sections.

The debugger provides an edit-and-continue feature that lets you modify a stopped program's code. You can add new statements, remove existing statements, declare new variables, and so forth. Unfortunately, some changes confuse the debugger, and you'll have to restart your program. But sometimes you can make small changes without restarting.

To remove a breakpoint, click the red breakpoint circle or press F9 again.

Note

Note that edit-and-continue isn't supported when you're building a 64-bit program, but you can test a 32-bit program built on a 64-bit system. In Solution Explorer, right-click on the solution and select Properties. Expand the Configuration Properties folder, select the Configuration tab, and set Platform to "x86." After you finish testing, you can switch the program back to 64 bits.

READING VARIABLES

It's easy to read a variable's value while execution is stopped. Simply hover the mouse over a variable and its value appears in a popup window.

For example, consider the TaxCalculator program shown in Figure 12-2. The program is supposed to add a subtotal, 9% sales tax, and shipping costs to get a grand total. It doesn't take Stephen Hawking to realize that something's wrong. If you're really paying $204.50 for a $19.95 purchase, you need to find a new place to shop.

Figure 12-2

Figure 12.2. Figure 12-2

To debug this program, you could place a breakpoint on a line of code near where you know the bug occurs. For example, the line of code containing the breakpoint in Figure 12-1 calculates the grand total. Because the total displayed in Figure 12-2 is wrong, this seems like a good place to begin the bug hunt. (You can download the TaxCalculator program from the book's web site and follow along if you like.)

When the code is stopped, you can hover the mouse over a variable to learn its value. If you hover the mouse over the variables in that line of code, you'll find that subTotal is 19.95 (correct), shipping is 5 (correct), and salesTax is 179.55 (very much incorrect). Figure 12-3 shows the mouse hovering over the salesTax variable to display its value.

Figure 12-3

Figure 12.3. Figure 12-3

Now that you know the bug is lurking in the variable salesTax, you can hover the mouse over other variables to see how that value was calculated. If you hover the mouse over the variables in the previous line of code, you'll find that subTotal is 19.95 (still correct), and taxRate is 9.

You may need to think about that for a bit to realize what's going wrong. To apply a tax rate such as 9%, you divide by 100 and then multiply. In this case, taxRate should be 0.09, not 9.

Having figured out the problem, you can stop the program by opening the Debug menu and selecting the Stop Debugging command or by clicking the Stop Debugging button on the toolbar (see Figure 12-4).

Now you can fix the code and run the program again to see if it works. The following line shows the incorrect line of code (I scrolled it out of view in Figure 12-3 so it wouldn't be a complete giveaway):

Const taxRate As Decimal = 9D

If you change this to 0.09D and run the program again, you should get the correct a sales tax $1.80 and the grand total $26.75. In a more complicated program, you would need to perform a lot more tests to ensure that the program behaved properly for different inputs, including weird ones such as when the user enters "ten dollars" for the subtotal or leaves the shipping cost blank. This example isn't robust enough to handle those problems.

STEPPING THROUGH CODE

Once you've stopped the code at a breakpoint, you can step through the execution one statement at a time to see what happens. The Debug menu provides four commands that control execution:

  • Continue (F5) — Makes the program continue running until it finishes or reaches another breakpoint. Use this to run the program normally after you're done looking at the code.

  • Step Into (F8) — Makes the program execute the current statement. If that statement contains a call to a subroutine, function, or other executable piece of code (these are covered in Lesson 20), execution stops inside that code so you can see how it works.

  • Step Over ([Shift]+F8) — Makes the program execute the current statement. If that statement contains a call to another piece of executable code, the program runs that code and returns without stopping inside that code (unless it contains a breakpoint).

  • Step Out ([Ctrl]+[Shift]+F8) — Makes the program run the current routine until it finishes and returns to the calling routine (unless it hits another breakpoint first).

Note

When it is stopped, the debugger highlights the next line of code that it will execute in yellow.

In addition to using the Debug menu or shortcut keys, you can invoke these commands from the toolbar. Figure 12-4 shows the debugging-related buttons in the Standard toolbar.

Figure 12-4

Figure 12.4. Figure 12-4

Normally the program steps through its statements in order, but there is a way to jump to a particular line of code if you feel the need. Right-click the line that you want the code to execute next and select Set Next Statement from the context menu. Alternatively you can place the cursor on the line and press [Ctrl]+F9. When you let the program continue, it starts executing from this line.

Setting the next statement to execute is useful for replaying history to see where an error occurred, re-executing a line after you change a variable's value (described in the "Using the Immediate Window" section later in this lesson), or jumping forward to skip some code.

Note that you can jump only to certain lines of code. For example, you can't jump to a comment or other line of code that doesn't actually do anything (you can't set a breakpoint there either), you can't jump to a different method, you can't jump at all if an error has just occurred, you can't jump to a variable declaration unless it also initializes the variable, and so forth. Visual Basic does its best, but it has its limits.

USING WATCHES

Sometimes you may want to check a variable's value frequently as you step through the code one line at a time. In that case, pausing between steps to hover over a variable could slow you down, particularly if you have a lot of code to work through.

To make monitoring a variable easier, the debugger provides watches. A watch displays a variable's value whenever the program stops.

To create a watch, break execution, right-click a variable, and select Add Watch from the context menu. Figure 12-5 shows watches set on the variables salesTax and subTotal. Each time the program executes a line of code and stops, the watches update to display the variables' current values.

Figure 12-5

Figure 12.5. Figure 12-5

The Watch window also highlights, in red, any variables that have just changed. If you're tracking a lot of watches, this makes it easy to find the values that have changed.

Note

The Locals window is similar to the Watch window except it shows the values of all the local variables (and constants). This window is handy if you want to view many of the variables at once. It also highlights recently changed values in red so you can see what's changing.

USING THE IMMEDIATE WINDOW

While the program is stopped, the Immediate window lets you execute simple commands. The four most useful commands that this window supports let you view variable values, evaluate expressions, set variable values, and call subroutines.

Note

To find the Immediate window if it's missing, open the Debug menu, expand the Windows submenu, and select Immediate.

To view a variable's value, type a question mark followed by the variable's name and press [Enter].

The following code shows the contents of the Immediate window after I asked for the value of the salesTax variable:

?salesTax
179.55D

To evaluate an expression, type a question mark followed by the expression and press [Enter]. You can include literal values, variables, properties, constants, and just about anything else that you can normally include inside an expression in the code.

The following text shows the Immediate window after I typed an expression and pressed [Enter]:

?taxRate * subTotal
179.55D

To set a variable's value, simply type the variable's name, an equals sign, and the value that you want to give it. The new value can be a literal value or it can be the result of an expression. After you press [Enter], the Immediate window evaluates whatever is on the right of the equals sign and saves it in the variable.

Note

The same technique lets you set new values for properties. For example, you can change a control's Location, Text, Visible, BackColor, and other properties on-the-fly.

Finally, to call a subroutine, simply type the subroutine call into the Immediate window and press [Enter]. Don't forget to add parentheses and parameters if they are required. If the method has a return value, start with a question mark to make the Immediate window display the result.

TRY IT

If you look closely at Figure 12-6, you'll see that this program has a serious problem. One tofu dinner at $13.95 each doesn't add up to $142.65. If you look a little more closely, you'll also see that the grand total doesn't add up properly.

Figure 12-6

Figure 12.6. Figure 12-6

In this Try It, you debug this program. You set breakpoints and use the debugger to figure out where the code is going wrong.

Note

You can download the code and resources for this Try It from the book's web page at www.wrox.com or www.vb-helper.com/24hourvb.html. You can find them in the Lesson12 folder in the download. Note that the download contains both the version of the program that isn't working right and the corrected version (named "TryIt12" and "TryIt12Solution," respectively).

Lesson Requirements

In this lesson:

  • Use the debugger to fix this program. To follow along in the debugger, download this lesson's material from the book's web site and open the TryIt12 project.

  • Run the program and experiment with it for a bit to see what seems to work and what seems to be broken. This should give you an idea of where the problem may lie.

  • Set a breakpoint in the code near where you think there might be a problem. In this case, the tofu dinner cost calculation is wrong so you might set a breakpoint on this line:

    Dim priceTofu As Decimal = tofuCost * numTofu
  • Run the program so it stops at that breakpoint. Hover the mouse over different variables to see whether they look like they make sense.

  • Step through the code watching each line closely to see what's wrong.

  • Fix the error.

  • Run the program again and test it to make sure the change you made works. Try setting two of the quantities to 0 and the third to 1 to see if the program can correctly calculate the nonzero value.

  • If the program still has problems, run through these steps again.

Step-by-Step

The first two lesson requirements for this Try It are fairly straightforward so they aren't repeated here. The following paragraphs discuss the solution to the mystery, so if you want to try to debug the program yourself, do so before you read any further.

Ready? Let's go.

The following code shows how the program works. The bold line is where I set my breakpoint. If you stare at the code long enough, you'll probably find the bug so don't look too closely. Remember, the point is to practice using the debugger (which will be your only hope in more complicated programs), not to simply fix the program by staring at the code.

' Calculate the prices for each entree and the total price.
Private Sub btnCalculate_Click() Handles btnCalculate.Click
    Const costChicken As Decimal = 15.95D
    Const costSteak As Decimal = 18.95D
    Const costTofu As Decimal = 13.95D

    ' Get inputs.
    Dim numChicken As Integer = Integer.Parse(txtNumChicken.Text)
    Dim numSteak As Integer = Integer.Parse(txtNumSteak.Text)
    Dim numTofu As Integer = Integer.Parse(txtNumTofu.Text)

    ' Calculate results.
    Dim total As Decimal = 0

    Dim priceChicken As Decimal = costChicken * numChicken
    total += priceChicken

    Dim priceSteak As Decimal = costSteak * numSteak
    total += priceSteak

    Dim priceTofu As Decimal = costTofu * numTofu
    total += priceTofu

    ' Display results.
    txtPriceChicken.Text = priceChicken.ToString("C")
    txtPriceSteak.Text = priceSteak.ToString("C")
    txtPriceTofu.Text = priceTofu.ToString("C")
    txtTotal.Text = total.ToString("C")
End Sub
  • Run the program so it stops at that breakpoint. Hover the mouse over different variables to see whether they look like they make sense.

    1. If you run to the breakpoint and hover the mouse over the variables, you'll find that most of them make sense: the values numChicken = 9, priceChicken = 142.65, and so forth.

  • Step through the code watching each line closely to see what's wrong.

    1. While the program is stopped on the breakpoint, the variable priceTofu has the value 0 because the code hasn't yet executed the line that sets its value. Press [f8] to step over that line and you'll see that priceTofu is 13.95 as it should be. So far, you haven't found the bug.

If you continue stepping through the code, watching each line carefully, you'll eventually see the problem in this line:

txtPriceTofu.Text = priceChicken.ToString("C")

Here the code is making the tofu price TextBox display the value of priceChicken!

Note

This is a fairly typical copy-and-paste error. The programmer wrote one line of code, copied and pasted it several times to perform similar tasks (displaying the values in the TextBoxes), but then didn't update each pasted line correctly.

  • Fix the error.

    1. This bug is easy to fix. Simply change the offending line to this:

      txtPriceTofu.Text = priceTofu.ToString("C")
  • Run the program again and test it to make sure the change you made works. Try setting two of the quantities to 0 and the third to 1 to see if the program can correctly calculate the nonzero value.

    1. If you run the program again, everything should initially look okay. If you reproduce some calculations by hand, however, you may find a small discrepancy in the chicken prices.

    2. You can see the problem more easily if you set the quantities of steak and tofu to 0 and the quantity of chicken to 1. Then the program calculates that the price of one chicken dinner (at $15.95 each) is $15.85.

  • If the program still has problems, run through these steps again.

    1. Having found another bug, run through the debugging process again. Set a breakpoint on the line that calculates priceChicken and hover over the variables to see if their values make sense.

      If you're paying attention, you'll see that the value of the constant costChicken is 15.85, not 15.95 as it should be.

    2. Fix the constant declaration and test the program again.

Note

Please select Lesson 12 on the DVD to view the video that accompanies this lesson.

Note

It's extremely common for a program to contain more than one bug. In fact, it's an axiom of software development that any nontrivial program contains at least one bug.

A consequence of this axiom is that even after you fix the program's "last" bug, it still contains another bug. Sometimes fixing the bug introduces a new bug. (That's not as uncommon as you might think in a complex program.) Other times more bugs are hiding; you just haven't found them yet.

In complex projects, the goal is still to eradicate every single bug; but the reality is that often the best you can do is to fix bugs until the odds of the user finding one in everyday use are extremely small.

EXERCISES

Note

Putting debugging exercises in a book can be a bit strange. If the code is included in the book, you can stare at that code until you see the bugs without using the debugger, which defeats the purpose.

For that reason, this section only describes the programs containing the bugs; you'll have to download the broken programs from the book's web site at www.wrox.com or www.vb-helper.com/24hourvb.html. You can find the programs and corrected versions in the Lesson12 folder. The corrected versions are named after their exercises — for example, Ex12-1Solution. Modified lines are marked with comments.

  1. Debug this CelsiusToFahrenheit conversion program that produces incorrect results. (Hint: 0°C = 32°F and 100°C = 212°F.)

  2. Debug this FeetToMiles program that produces incorrect results. (After you fix this one, notice that using constants instead of magic numbers and the approach taken by Exercises 5 and 6 in Lesson 11 would make fixing these bugs easier and might have avoided them from the start. Also note again that the duplicated code is a bad thing, which you'll learn how to fix in Lesson 20.)

  3. The ResizePicture program is supposed to zoom in on a picture when you adjust its TrackBar. Unfortunately, when you move the TrackBar, the picture seems to shrink and move to a new location. Debug the program.

  4. Debug the TaxForm program, which performs a fictitious tax calculation based on a real one. It's an ugly little program, but it's probably the most realistic one in this lesson. (Hint: For the program's initial inputs, the tax due should be $290.00.)

EXERCISES
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset