aboutsummaryrefslogtreecommitdiff
path: root/README.md
blob: eb40d790734692b501a19d84eb242a13e364527a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125

bpb4crash
=========

(BreakPoint Before Crash)

This little script allows a buggy program to be stopped just before it
crashes, at the start of the function that crashes (or optionally, in
any other function in a call stack).

This is useful in the following circumstances:

* When you have a crashing program, and
* it crashes on a large input, that makes reverse-execution too slow, and
* the crashing function is called multiple times (so you can just simply
  set a plain breakpoint on it).

NOTE: It only works for programs that are both deterministic and
non-interactive!

Dependencies
------------

* gdb
* mkfifo
* POSIX shell  (e.g. Dash, Bash, ...)

How it works
------------

It first runs the program under gdb, using the GDB/MI interface, to analyze
how many times the failing function runs in total (before the crash happens).

Then starts gdb normally, but with a pre-set breakpoint with an ignore count
such that it stops just before the crash.

From there you can inspect the function arguments, step through the function,
etc.

Usage
-----

By default, it stops at start of the function where the crash or assertion
failure happens:

    $ ./bpb4crash.sh ./test_crash

You can also override which function in should stop in:

    $ TRACKED_FUNC=crash_loop ./bpb4crash.sh ./test_crash

If you want to stop not a the last call before the crash, but at an earlier
call, then you can overrides this too:

    $ STEPS_BEFORE=2 ./bpb4crash.sh ./test_crash

Example use case
----------------

The test_crash.c file has an assertion failure, but it can happen
in several cases:

    void check_number()
    {
        if (num == -90) goto fail;
        if (num == 0)   goto fail;
        if (num == 200) goto fail;
        return;
      fail:
        num = -1;
        assert(0);
    }

When running it, we can't know which number triggered the assertion failure:

    $ ./test_crash
    test_crash: test_crash.c:35: check_number: Assertion `0' failed.
    Aborted

And gdb isn't very helpful either:

    $ gdb ./test_crash
    Reading symbols from ./test_crash...
    (gdb) run
    Starting program: /home/privmisc/code/beforecrash/test_crash 
    [Thread debugging using libthread_db enabled]
    Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
    test_crash: test_crash.c:35: check_number: Assertion `0' failed.

    Program received signal SIGABRT, Aborted.
    __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, 
        no_tid=no_tid@entry=0) at ./nptl/pthread_kill.c:44
    44	./nptl/pthread_kill.c: Filen eller katalogen finns inte.
    (gdb) f 6
    #6  0x0000555555555197 in check_number () at test_crash.c:35
    35	    assert(0);
    (gdb) p num
    $1 = -1

That doesn't tell anything at all :(

But we can use the bpb4crash to get a breakpoint at the last call to
this function, just before the assertion failure happened.

    $ ./bpb4crash.sh ./test_crash
    First run (analysis)...
    test_crash: test_crash.c:35: check_number: Assertion `0' failed.
    #6  0x0000555555555197 in check_number () at test_crash.c:35
    #7  0x00005555555551ba in crash_loop (i=123) at test_crash.c:42
    #8  0x00005555555551ed in intermediate_func (i=123) at test_crash.c:49
    #9  0x0000555555555209 in main (argc=1, argv=0x7fffffffe298) at test_crash.c:55
    Second run (to breakpoint)...
    test_crash: test_crash.c:35: check_number: Assertion `0' failed.
    Reading symbols from ./test_crash...
    Breakpoint 1 at 0x113d: file test_crash.c, line 29.
    Will ignore next 122 crossings of breakpoint 1.
    Starting program: /home/privmisc/code/beforecrash/test_crash 
    [Thread debugging using libthread_db enabled]
    Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
    Breakpoint 1, check_number () at test_crash.c:29
    29	    if (num == -90) goto fail;

From here we can see the state at the beginning of the function call:

    (gdb) p num
    $1 = 0