Conditional Breakpoints

As long as a breakpoint is enabled, the debugger always stops at that breakpoint. However, sometimes it's useful to tell the debugger to stop at a break point only if some condition is met, like the when a variable has a particularly interesting value.

Putting the toothpaste back into the tube

Figure 2-14. Putting the toothpaste back into the tube


This is similar to how watchpoints work, but with an important distinction. If you have a suspicion about where a variable is getting a bogus value, a conditional breakpoint is preferable to a watchpoint. The watchpoint will break whenever that variable changes value. The conditional breakpoint will only break at the suspected problem code, and then, only when the variable takes on the bogus value. In this sense, watchpoints are good when you haven't a clue where the variable is receiving its bogus value. This is particularly useful for global variables or local variables that are continually passed between functions. But in most other cases, a well placed conditional breakpoint is more useful and convenient.

The syntax for setting a conditional breakpoint is:

break break-args if (condition)

where break-args is any of the arguments you can pass to break to specify a location for a breakpoint, as discussed in Setting Breakpoints in GDB, and condition is a Boolean expression as defined in Expressions. The parentheses around condition are optional. They may make some C programmers feel more at home, but on the other hand, you might prefer the uncluttered look.

For example, here's how you would break at main() if the user had typed some command line arguments to the program:[10]

break main if argc > 1

Conditional breaking is extremely useful, particularly in loop constructs in which something bad happens at a particular value of the index variable. Consider the following code snippet:

for (i=0; i<=75000; ++i) {
   retval = process(i);
   do_something(retval);
}

Suppose you know your program goes haywire when i is 70,000. You want to break at the top of the loop, but you don't want to do next through 69,999 iterations. This is where conditional breaking really shines. You can set a breakpoint at the top of the loop, but only when i equals 70,000 with the following:

break if (i == 70000)

Of course, you could achieve the same effect by typing, say, continue 69999, but that would be less convenient.

Conditional breaking is also extremely flexible. You can do much more than just test a variable for equality or inequality. What kinds of things can you use in a condition Pretty much any expression you can use in a valid C conditional statement. Whatever you use needs to have a Boolean value, that is, true (nonzero) or false (zero). This includes:

Order of precedence rules are in effect, so you may need to use parentheses around constructs like (x \& y) == 0.

Also, if you use a library function in a GDB expression, and the library was not compiled with debugging symbols (which is almost certainly the case), the only return values you can use in your breakpoint conditions are those of type int. In other words, without debugging information, GDB assumes the return value of a function is an int. When this assumption isn't correct, the function's return value will be misinterpreted.

(gdb) print cos(0.0)
$1 = 14368

Unfortunately, typecasting doesn't help, either:

(gdb) print (double) cos(0.0)
$2 = 14336

In case your trigonometry is rusty, the cosine of 0 is 1.

It is possible to set conditions on normal breakpoints to turn them into conditional breakpoints. For example, if you have set breakpoint 3 as unconditional but now wish to add the condition i == 3, simply type

(gdb) cond 3 i == 3

If you later want to remove the condition but keep the breakpoint, simply type

(gdb) cond 3

You can set conditional breakpoints with DDD using GDB semantics with the Console Window. Or, use DDD as follows. Set a normal (i.e., unconditional) breakpoint at the location in your code where you want the conditional breakpoint to be. Right click and hold the red stop sign to bring up a menu and choose Properties. A pop-up window will appear with a text entry box labelled Condition. Type the condition in that box, click Apply, and then click Close. The breakpoint is now a conditional breakpoint.

This is illustrated in Figure 2-16. We see the condition j == 0 on breakpoint 4. By the way, the stop sign on that line will now contain a question mark, to remind us that it is a conditional break.


To make a breakpoint conditional, right-click the breakpoint symbol for that line, select Breakpoint Properties… | Common, and fill in the condition in the dialog box. The dialog box is depicted in Figure 2-17.




[10] Providing you had declared argc and argv as arguments to main(). (Of course, if you declared them with different names, say ac and av, use those.) By the way, note that a program always receives at least one argument—the name of the program itself, which is pointed to by argv[0], which we are not counting as a "user argument" here.