syntax highlight

Tuesday 18 November 2014

Gdb hit count: ignoring breakpoints (for a while)

Some times a breakpoint is not enough; if you have a crash inside a loop, the object that makes your code crash might be in the 526th iteration. How can you debug this problem?

One way would be to set up a watch expression. If you can't setup a watch expression, say, because you're using an iterator and it'd be hard to set one, you can also tell gdb to setup a breakpoint, and then ignore it N times.

Let's see how this works with this example:


#include <vector>

int main() {
    std::vector<int> v = {1,2,3,4,5,0,7,8,9};
    int x = 42;

    for (auto i = v.begin(); i != v.end(); ++i) {
        x = x / *i;
    }

    return 0;
}

After compiling we run it to see it crash; let's start it on gdb, then set a brakepoint on the line where it crashes.

(gdb) break foo.cpp:8
Breakpoint 1 at 0x4007bc: file foo.cpp, line 8.

(gdb) info breakpoints Num Type Disp Enb Address What 1 breakpoint keep y 0x00000000004007bc in main() at foo.cpp:8

Typing "info breakpoints" will tell you the breakpoint number; then we can tell gdb to ignore this breakpoint forever (where forever is a very large number, so the program will run until it crashes):

(gdb) ignore 1 99999
Will ignore next 99999 crossings of breakpoint 1.
(gdb) run
Starting program: /home/nico/src/a.out

Program received signal SIGFPE, Arithmetic exception.
0x00000000004007d5 in main () at foo.cpp:8
8            x = x / *i;
(gdb) info breakpoints
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x00000000004007bc in main() at foo.cpp:8
    breakpoint already hit 6 times
    ignore next 99993 hits
(gdb)

By doing this now we know the program crashes the sixth time it goes through that breakpoint. Now we can actually debug the problem:

(gdb) ignore 1 5
Will ignore next 5 crossings of breakpoint 1.
(gdb) run
Starting program: /home/nico/src/a.out

Breakpoint 1, main () at foo.cpp:8
8            x = x / *i;
(gdb) p *i
$1 = (int &) @0x603024: 0

This time gdb will break exactly on the spot we want.

Tuesday 7 January 2014

Extending the life of a temp variable in C++

Take a look at this code: what does it do?

struct X {
    X() { cout << "X"; }
    ~X() { cout << "~X"; }
};

void foo() {
    X x;
}

It's not hard to see this code will print "X", then "~X" immediately after it: X() is created as a temporary variable which gets constructed and then immediately destructed. Any side effects this object may have should happen in the constructor or the destructor.

Now that we know a bit more about the lifetime of temp objects, is this valid C++?

struct X {
    int y;
    X(int y) : y(y) {}
};

int foo() {
    const X &ref = X(42);
    return ref.y;
}

It looks a bit strange: ref is a reference to a temporary object. Temporary objects get destroyed as soon as they are created, so ref.y should be an undefined data access. Right? Not quite, the C++ standard has a special consideration for const references using a temporary object: according to 12.2.3 this is a valid read, as long as ref is a "const X&". Even more interesting, in this case the lifetime of the temporary object "X(42)" gets extended until ref goes out of scope: only when the reference is gone the destructor for X will be run!