Monday, January 24, 2011

APPENDIX-4 Debugging your program (stepping and breakpoints)

An introduction to debugging
Programming is difficult, and there are a lot of ways to make mistakes. As you learned in the section on handling errors, there are two primary types of errors: syntax errors and semantic errors.
A syntax error occurs when you write a statement that is not valid according to the grammar of the C++ language. This happens a lot through typos or accidental omission of keywords or symbols that C++ is expecting. Fortunately, the compiler will generally catch syntax errors and generate warnings or errors so you know what the problem is.
Once your program is compiling correctly, getting it to actually produce the result(s) you want can be tricky. A semantic error occurs when a statement is syntactically valid, but does not do what the programmer intended. Unfortunately, the compiler will not be able to catch these types of problems, because it only knows what you wrote, not what you intended.
Fortunately, that’s where the debugger comes in. A debugger is a computer program that allows the programmer to control how a program executes and watch what happens as it runs. For example, the programmer can use a debugger to execute a program line by line, examining the value of variables along the way. By comparing the actual value of variables to what is expected, or watching the path of execution through the code, the debugger can help immensely in tracking down semantic errors.
Early debuggers, such as gdb, had command-line interfaces, where the programmer had to type arcane commands to make them work. Later debuggers (such as Borland’s turbo debugger) came with their own “graphical” front ends to make working with them easier. Almost all modern IDEs available these days have integrated debuggers — that is, the debugger is built-in to the editor, so you can debug using the same environment that you use to write your code (rather than having to switch programs).
Nearly all modern debuggers contain the same standard set of basic features — however, there is little consistency in terms of how the menus to access these features are arranged, and even less consistency in the keyboard shortcuts. Although our examples will be from Microsoft Visual Studio 2005 Express, you should have little trouble figuring out how to access each feature we discuss no matter which development environment you are using.
Stepping
Stepping is a debugger feature that lets you execute (step through) your code line by line. This allows you to examine each line of code in isolation to determine whether it is behaving as intended.
There are actually 3 different stepping commands: step into, step over, and step out. We will cover each one in turn.
Step into
The step into command executes the next line of code. If this line is a function call, step into enters the function and returns control at the top of the function.
Let’s take a look at a very simple program:
01#include <iostream>
02 
03void PrintValue(int nValue)
04{
05    std::cout << nValue;
06}
07 
08int main()
09{
10    PrintValue(5);
11    return 0;
12}
As you know, when running a program, execution begins with a call to main(). Because we want to debug main(), let’s begin by using the “step into” command.
In Visual Studio 2005 Express, go to the debug menu and choose “Step Into”, or press F11.
If you are using a different IDE, find the “Step Into” command in the menus and choose it.
When you do this, two things should happen. First, a console output window should open. It will be empty because we haven’t output anything to it yet. Second, you should see some kind of marker appear to the left of the opening brace of main. In Visual Studio 2005 Express, this marker is a yellow arrow. If you are using a different IDE, you should see something that serves the same purpose.

This arrow marker indicates that the line being pointed to will be executed next. In this case, the debugger is telling us that the next line that will be executed is the opening brace of main(). Choose “step into” again to execute the opening brace, and the arrow will move to the function call to PrintValue().

This means the next line that will be executed is the call to PrintValue(). Choose “step into” again. Because PrintValue() was a function call, we “stepped into” the function, and the arrow should be at the top of the PrintValue() code.

Choose “step into” to execute the opening brace of PrintValue().
At this point, the arrow should be pointing to std::cout << nValue;.
Choose "step into" again, and you should see that the value 5 appears in the output window.
Choose "step into" again to execute the closing brace of PrintValue(). At this point, PrintValue() has finished executing and control is returned to main().
You will note that the arrow is again pointing to PrintValue()!

While you might think that the debugger intends to call PrintValue() again, in actuality the debugger is just letting you know that it is returning from the function call.
Choose "step into" twice more. At this point, we have executed all the lines in our program, so we are done. Some debuggers will terminate the debugging session automatically. Visual Studio 2005 Express does not, so choose "Stop Debugging" from the debug menu. This will terminate your debugging session (and can be used at any point in the debugging process to do so).
Step over
Like "step into", The step over command executes the next line of code. If this line is a function call, step over silently executes the function and returns control after the function has been executed.
Let's take a look at an example of this using the same program as above:
01#include <iostream>
02 
03void PrintValue(int nValue)
04{
05    std::cout << nValue;
06}
07 
08int main()
09{
10    PrintValue(5);
11    return 0;
12}
Step into the program until the next statement to be executed is the call to PrintValue().

Instead of stepping into PrintValue(), choose "step over" instead. The debugger will execute the function (which prints the value 5 in the output window) and then return control to you when it is finished.
Step over provides a convenient way to skip functions when you are sure they already work or do not need to be debugged.
Step out
Unlike the other two stepping commands, step out does not execute the next line of code. Instead, it executes the rest of the function you are currently in, and returns control to you when the function has finished executing.
Let's take a look at an example of this using the same program as above:
01#include <iostream>
02 
03void PrintValue(int nValue)
04{
05    std::cout << nValue;
06}
07 
08int main()
09{
10    PrintValue(5);
11    return 0;
12}
Step into the program until you are inside PrintValue().

Then choose "step out". You will notice the value 5 appears in the output window, and the debugger returns control to you after the function has terminated.
Run to cursor
While stepping is useful for examining each individual line of your code in isolation, in a large program, it can take a long time to step through your code just to get to the point where you want to examine in more detail.
Fortunately, modern debuggers provide a few more tools to help us efficiently debug our programs.
The first useful command is commonly called run to cursor. This command executes the program like normal until it gets to the line of code selected by your cursor. Then it returns control to you so you can debug starting at that point. Let's try it using the same program we've been using:
01#include <iostream>
02 
03void PrintValue(int nValue)
04{
05    std::cout << nValue;
06}
07 
08int main()
09{
10    PrintValue(5);
11    return 0;
12}
First, choose "step into" to enter debugging mode. Second, put your cursor on the std::cout << nValue; line inside of PrintValue(). Third, choose the "run to cursor" debug command. In Visual Studio 2005 Express,, you can do this by right clicking and choosing "run to cursor".
You will notice the arrow indicating the line that will be executed next moves to the line you just selected. Your program executed up to this point and is now waiting for your further debugging commands.
Run
It is also possible to tell the debugger to run until it hits the end of the program. In Visual Studio 2005 Express, this command is called "Continue". In other debuggers, it may be called "Run" or "Go".
If you have been following along with the examples, you should now be inside the PrintValue() function. Choose the run command, and your program will finish executing and then terminate.
Breakpoints
The last topic we are going to talk in this section is breakpoints. A breakpoint is a special marker that tells the debugger to stop execution of the program at the breakpoint when running in debug mode.
To set a breakpoint in Visual Studio 2005 Express, go to the Debug menu and choose "Toggle Breakpoint". You will see a new type of icon appear:

Start a new debugging session and let's see what the breakpoint does.
First, choose "Step into" to start your debugging session. Then choose the run command (may be called "Continue" or "Go").
You will notice that instead of running all the way to the end of the program, the debugger stopped at the breakpoint!

Breakpoints are extremely useful if you want to examine a particular section of code. Simply set a breakpoint at the top of that section of code, tell the debugger to run, and the debugger will automatically stop every time it hits that breakpoint. Then you can use the stepping commands from there to watch your program run line by line.
One last note: Up until now, we've been using "step into" to start debugging sessions. However, it is possible to tell the debugger to just start running to the end of the program immediately. In Visual Studio 2005 Express, this is done by picking "Start debugging" from the Debug menu. Other debuggers will have similar commands. This, when used in conjunction with breakpoints, can reduce the number of commands you need to use to get to the spot you want to debug in more detail using the stepping commands.
Conclusion
Congratulations, you now know all of the major ways to make the debugger move through your code. However, this is only half of what makes debuggers useful. The next lesson will talk about how to examine the value of variables while we are debugging, as well as a couple of additional windows of information we can make use of to help debug our code.
Return to the C++ tutorial index page

No comments: