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… :)