GDB's Conditional Breakpoints

Conditional Breakpoints for Scalar Types

Let’s assume that you, the brilliant hacker, has coded up some really uber-cool stuffs, like this piece of code below:

1: for ( int i = 0; i < gazillion; i++ ) {
2:   doSlightlyBuggyButUberCoolStuffs(i)
3: }
4:
5: void doSlightlyBuggyButUberCoolStuffs(int i) {
6:   // your code here that needs some
7:   // fixing before it becomes uber-cool
8: }

It is doing all the cool stuffs as intended, but somehow something always goes wrong when the code executes up to 2147483648, which is kind of puzzling.

So what to do?

You may be tempted to breakpoint at line 5, at the start of the doSlightlyBuggyButUberCoolStuffs():

(gdb) br doSlightlyBuggyButUberCoolStuffs

And gdb dutifully does what it’s told; every single time doSlightlyBuggyButUberCoolStuffs() gets executed, it stops and waits for you to act on it:

Breakpoint 1, doBuggyButUberCoolStuffs (i=1) at test.cpp:6
6:        // start of your uber-cool code
(gdb) c

Breakpoint 1, doBuggyButUberCoolStuffs (i=2) at test.cpp:6
6:        // start of your uber-cool code
(gdb) c

.....

Breakpoint 1, doBuggyButUberCoolStuffs (i=100) at test.cpp:6
6:        // start of your uber-cool code
(gdb) c

After 100 iterations, you think you’ve had enough! So it’s time to do it the smart way, by setting a conditional:

(gdb) br test.cpp:2
Breakpoint 1 at 0x1234: file test.cpp, line 2.
(gdb) cond 1 i==2147483648
(gdb) run

After the breakpoint is set, gdb only notifies you when the loop is at its 2147483648th iteration:

Breakpoint 1 at 0x5678: file test.cpp:2
2:   doBuggyButUberCoolStuffs(i)
(gdb) s
6:         // start of your uber-cool code
(gdb) p i
$1 = 2147483648

Jackpot! You’re now at the 2147483648th iteration! And very soon after, you found the offending piece of code, caused by a numerical overflow of a signed integer. Another bug trampled, and peace returns to your realm once more.

Conditional Breakpoints for char* Strings
But very soon after, you run into another irritating problem which is happening within another section of your uber-cool code. This time, the conditional depends on parsing a huge portion of text that comes from, um…, /dev/random :P

1: while ( true ) {
2:   char* c = getStringFromDevRandom();
3:   launchNuclearMissileIfCodeMatch(c);
4:}

Somehow, you are absolutely convinced that /dev/random will eventually provide correct codes to launch the nuclear missile, but given that launchNuclearMissileIfCodeMatch() is a really top-secret and highly obfuscated code residing in an external library called libtopsecret.so, it isn’t such a good idea to debug into the call unless you want the NSA bursting through your front doors…

But since you do know the launch code (it’s one of those things that you’ll have to kill your friends if you ever told them), you can perform a conditional check on the string, and breakpoint at it to tell you if the secret code is ever generated by /dev/random to find out if launchNuclearMissileIfCodeMatch() is really a hoax:

(gdb) br test.cpp:3
Breakpoint 1 at 0xdeadbabe: file test.cpp, line 3.
(gdb) set $secret_code = "MyUberSecretivePassword"
(gdb) cond 1 strcmp ( $secret_code, c ) == 0
(gdb) run

And then, you let your code run… (!)

Well, unfortunately, you get sick of sitting around and waiting for it to happen after a whole day. It seems like /dev/random doesn’t really generate your uber-secret nuclear launch codes as frequently as you would like to think. In the meantime, the world thanks their lucky stars that you haven’t caused a nuclear winter to materialise just yet… :)