Stopping at and Resuming from a Breakpoint
Learning to use the debugger is one of the most important things that you can do to up your C++ game. What follows are the steps necessary to set a breakpoint, stop your program, examine a few variables, and resume the program.
As of Spring 2014, Code::Blocks does not integrate with the debugger for the Mac, so if you’re a Mac user these examples may not work for you.
What is a debugger?
Before going any further, it may help if you understand what a debugger is. A debugger is a separate program that allows you to control your executing program. Specifically it allows you to stop execution of the program at specific lines of code, to examine the value of variables, to change the value of variables, and to resume execution of the program.
The GNU debugger has a command line interface that’s both difficult to remember and hard to use (that’s a winning combination if you’ve ever heard one). Fortunately, Code::Blocks provides an almost seamless graphical interface to the GNU debugger. While you click on the editor windows and activate drop-down menus, Code::Blocks sends cryptic commands to the debugger in the background and then interprets and graphically displays the results. Isn’t it great when two programs chit-chat in the background to save you work?
The best way to learn about the debugger is to just follow these examples.
Setting up your program to use the debugger
You need to build your program using special compiler settings if you intend to run it under control of the debugger. The easiest way to do this is right at the beginning, when you first create your project. After you have specified that you want to create a Console Application and provided the name of the program, Code::Blocks will ask you whether you want to build your program in Debug configuration, Release configuration, or both, as shown in the Figure 1. Be sure that Debug configuration is enabled.
A program in Debug configuration includes a lot of extra information that the debugger needs in order to do its magic. This information includes
The address of each line of C++ source code
The address of each variable
The names and makeup of each class in your program
All of this debugger information adds a lot of bulk to the executable program — hundreds of kilobytes or more. This space is totally unnecessary if you don’t intend to run the program under control of the debugger, so Code::Blocks gives you the option of building executables without this information in what is known as Release configuration. Release programs are smaller, load faster, and even execute quicker.
If you enable both Debug and Release configurations in your project, then you will get to choose at build time which type of executable you are trying to build.
There are two differences between the Debug and Release configurations:
The Debug configuration includes the -g compiler option.
This option tells the compiler to include symbol information in the final executable. This is all that stuff mentioned above — the address of every line of code and of every variable, as well as a description of every class used in your program.
The code optimizer is turned off in Debug configuration and turned on in the Release configuration.
The GNU code optimizer generates programs that execute faster, but it does so by moving instructions around in ways that can be quite confusing when you’re debugging your program. The release configuration does not include the -g switch and enables some level of optimization through the inclusion of the -O switch.
Make sure that you are currently building the Debug release by selecting Build→Select Target→Debug, as shown in the Figure 2.
Rebuild your program by selecting Build→Rebuild.
Stopping at a breakpoint
To set a breakpoint, simply click in the area just to the right of the line number — this area is known in Code::Blocks parlance as the trough. Not only does the line appear a different color, indicating that the editor has selected it, but a small stop sign appears in the trough, as shown in Figure 3. This is an indication that a breakpoint has been set at line 11.
You can set as many breakpoints as you want to at one time, but this one breakpoint will be fine for this example.
Here, you can start the program using the Debug→Start/Continue option shown in Figure 4.
The program begins execution like normal but stops almost immediately with a small yellow arrow pointing at Line 11 as shown in Figure 5. In addition, a blank window which would normally contain program output appears. Nothing appears in the program output because line 11 has yet to execute.
Wow! That’s cool.
Resuming execution of a program
You could set a breakpoint on the next line and select Debug→Start/Continue to continue executing the program to the next line if you wanted; however, since this is such a common thing to do, Code::Blocks provides the Debug→Next Line option, which means, “Execute the program to the next line.”
Doing so causes the program to momentarily flash as control passes to the program just long enough to execute line 11 before coming to a stop again, as shown in Figure 6.
You can repeat this process as often as you like, effectively single-stepping my way through the program.
When you come to a function call, Next Line stays in the current function, executing the entire function as if it were just a single line (which it is at this level). If you want to pass control to the function call, select Debug→Step Into instead. This works only for functions for which you have the source code — this doesn’t work very well for C++ library functions.
Any time you get tired of single-stepping around, you can terminate the program by selecting Debug→Stop Debugger. Not only does this stop the debugger, but it stops your program as well.
Single-stepping through your program is a great way to get a feel for where your program is going, but it may be difficult to understand why unless you can examine the variables it’s using to make decisions. For example, when you single-step the example program, you may notice that control doesn’t enter the for loop at all. Why is that?
You stop the program by selecting Debug→Stop Debugger or pressing Shift+F8 and restart by selecting Debug→Start/Continue or pressing F8. Single-step by selecting Debug→Next Line or pressing F7 until execution reaches the beginning of the for loop.
This time, make sure that the Watch window is visible by selecting Debug→Debugging windows→Watches. A window like the one in Figure 7 appears showing the values of the arguments and any local variables. Notice that nNumberofArgs is 1, just the name of the program itself. Since i starts with a value of 1, the loop is never going to be executed. Add the watch variables pszArgs and pszArgs to see what they contain: the name of the program and a null. Aha! The program has no arguments to iterate over.
You can change the arguments to the program by selecting Project→Set Programs’ Arguments. In this window, you can put whatever arguments you want.
Notice that the Watch window shows me the name of the variable, its type, and its value. You can’t change its type, but you can change its value from this window, if you want. Changing the value of nNumberofArgs isn’t very useful, but sometimes changing the value of a variable allows me to force execution down a different path just to see what might happen.
Continuing a stopped program
One trick to remember is that it can sometimes get confusing as to why a program is stopped. Not all programs that are stopped are under the control of the debugger. Sometimes they’re waiting for input from the user.
To see this effect, single-step the PrintArgs program until control reaches line 26. When you select Next Line, the cursor disappears as shown in Figure 8. Gone is the pleasant yellow arrow that reminds you of where you am in the program. What happened?
What happened is that the program is waiting for me to press Enter at the keyboard. First, you have to select the program window. Then you can press Enter (you may need to do this more than once). Having done that, nothing seems to happen in the program window because control has now passed back to the debugger in the Code::Blocks window. When you select that window again, you see the reassuring yellow arrow telling me that Code::Blocks is back in control.
When you’re executing your program under the debugger and control appears to just go away, the program may be waiting for input from the keyboard. Switch over to the program window to see if that’s the case. If so, as soon as you enter something, control will return back to the debugger.