GDB - Conditional Breakpoints¶
Learning Outcome
Use conditional breakpoints to conditionally stop program execution.
Introduction
Breakpoints normally stop the execution every time a certain line or function is reached. However, using the condition keyword, a breakpoint will only be activated if a certain condition is true. This can avoid stopping at breakpoints until something of interest is true.
Applicable subjects
COMP1521, COMP2521, COMP3231
Set a breakpoint¶
The first step in setting a conditional breakpoint is to set a breakpoint as you normally would. I.e.
(gdb) break <file_name> : <line_number>
(gdb) break <function_name>
This will set a breakpoint and output the breakpoint number
Check breakpoints¶
If you forget which breakpoints you can add a condition to, you can list the breakpoints using:
(gdb) info breakpoints
Set a condition for a breakpoint¶
Set an existing breakpoint to only break if a certain condition is true:
(gdb) condition <breakpoint_number> condition
The condition is written in syntax similar to c using operators such as == != and <.
Remove a condition from a breakpoint¶
If no condition is required on a breakpoint anymore (i.e. the breakpoint should always break upon being reached), the breakpoint can be removed using:
(gdb) condition <breakpoint_number>
Example¶
We have a factorial program which calculates the factorials of 5 and 17. When we run the program the factoiral of 5 is correct but the factorial of 17 is incorrect. Our factorial function is iterative, it is helpful to view the values of f and i as they change and compare the two, since with our algorithm, f = i!. Since we know that the value is correct up until 5! we can skip checking the values up until i = 6.
$ ./factorial The factorial of 5 is 120. The factorial of 17 is -288522240.
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 | //This program calculates and prints out the factorials of 5 and 17
#include <stdio.h>
#include <stdlib.h>
int factorial(int n);
int main(void) {
int n = 5;
int f = factorial(n);
printf("The factorial of %d is %d.\n", n, f);
n = 17;
f = factorial(n);
printf("The factorial of %d is %d.\n", n, f);
return 0;
}
//A factorial is calculated by n! = n * (n - 1) * (n - 2) * ... * 1
//E.g. 5! = 5 * 4 * 3 * 2 * 1 = 120
int factorial(int n) {
int f = 1;
int i = 1;
while (i <= n) {
f = f * i;
i++;
}
return f;
}
|
We can compile with the debug flag and run our program with gdb:
$ gcc -g -o factorial factorial.c
$ gdb factorial
Reading symbols from factorial...done.
The output of the factorial function is correct when n = 5, so we don’t need to check the values of f for i less than 5.
So we want to put a breakpoint on line 28 (after the calculation of f for the corresponding value of i), that only is active for i > 5.
$ gdb factorial
Reading symbols from factorial...done.
(gdb) br 28
Breakpoint 1 at 0x11bf: file factorial.c, line 28.
(gdb) condition 1 i > 5
Now we need continue, and list the local variables until we notice that f != i! See the table below for a comparison of the f and i!:
(gdb) r
Starting program: ~/factorial
The factorial of 5 is 120.
Breakpoint 1, factorial (n=17) at factorial.c:28
28 i++;
(gdb) info locals
f = 720
i = 6
(gdb) c
Continuing.
Breakpoint 1, factorial (n=17) at factorial.c:28
28 i++;
(gdb) info locals
f = 5040
i = 7
(gdb) c
Continuing.
Breakpoint 1, factorial (n=17) at factorial.c:28
28 i++;
(gdb) info locals
f = 40320
i = 8
(gdb) c
Continuing.
Breakpoint 1, factorial (n=17) at factorial.c:28
28 i++;
(gdb) info locals
f = 362880
i = 9
(gdb) c
Continuing.
Breakpoint 1, factorial (n=17) at factorial.c:28
28 i++;
(gdb) info locals
f = 3628800
i = 10
(gdb) c
Continuing.
Breakpoint 1, factorial (n=17) at factorial.c:28
28 i++;
(gdb) info locals
f = 39916800
i = 11
(gdb) c
Continuing.
Breakpoint 1, factorial (n=17) at factorial.c:28
28 i++;
(gdb) info locals
f = 479001600
i = 12
(gdb) c
Continuing.
Breakpoint 1, factorial (n=17) at factorial.c:28
28 i++;
(gdb) info locals
f = 1932053504
i = 13
(gdb)
When i = 13, f should be 6,227,020,800, but it is not.
Instead of increasing, f has decreased. Therefore, we know that our function works up until f reaches a certain very large number. It then decreases and later even becomes negative. This seems consistent with integer overflow.
A quick google reveals that depending on the computer, the range of an integer is either -32,768 to 32,767 or -2,147,483,647 to 2,147,483,647. The function stops working when f is between 479,001,600 and 6,227,020,800 which is conistent with the 2,147,483,647 integer limit. The variable can’t store a number larger than the maximum integer size.
Several options are available to fix this issue:
A larger data type such as a long long could be used
If the program should never be used with a value of n larger than 12, error handling can be added and the result can be left as an integer.
i |
i! |
f |
---|---|---|
0 |
1 |
1 |
1 |
1 |
1 |
2 |
2 |
2 |
3 |
6 |
6 |
4 |
24 |
24 |
5 |
120 |
120 |
6 |
720 |
720 |
7 |
5,040 |
5,040 |
8 |
40,320 |
40,320 |
9 |
362,880 |
362,880 |
10 |
3,628,800 |
3,628,800 |
11 |
39,916,800 |
39,916,800 |
12 |
479,001,600 |
479,001,600 |
13 |
6,227,020,800 |
1932053504 (wrong) |
14 |
87,178,291,200 |
don’t care yet |
15 |
1,307,674,368,000 |
don’t care yet |
16 |
20,922,789,888,000 |
don’t care yet |
17 |
355,687,428,096,000 |
-288522240 (wrong) |
Module author: Liz Willer <e.willer@unsw.edu.au>
- Date
2020-01-30