GDB - watch and display¶
Learning Outcome
Able to set a watchpoint on a variable in order to break a program when a variable changes.
Use display to automatically print how variables change throughout the program’s execution.
Introduction
watch allows us to stop the execution every time the value of a variable changes.
display prints variables every time the program’s execution stops (i.e. at a watchpoint, breakpoint, etc…)
Using both allows us to automatically stop at various points throughout a loop, and print all the relevant variables. Therefore, the only command needed to move through a loop is continue.
Applicable subjects
COMP1521, COMP2521, COMP3231
watch¶
Set a watchpoint To set a watchpoint every time the value of a particular variable changes set a watchpoint on that variable using:
(gdb) watch <variable_name>
Remove a watchpoint¶
To remove a watchpoint, use:
(gdb) disable <watchpoint_number>
Note
Watchpoints don’t work on WSL
display¶
Set an Automatic Display¶
Display an expression every time the program stops:
(gdb) display expression
View all Automatic Displays¶
Print a list of the automatically displayed expressions including the display number:
(gdb) info display
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!. Doing this with next and print is tedious, so we can use watchpoints, continue and display to simplify the process.
$ ./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 want to skip the first call to the function.
(gdb) br factorial
Breakpoint 1 at 0x11a5: file factorial.c, line 24.
(gdb) r
Starting program: ~/factorial
Breakpoint 1, factorial (n=5) at factorial.c:24
24 int f = 1;
(gdb) c
Continuing.
The factorial of 5 is 120.
Breakpoint 1, factorial (n=17) at factorial.c:24
24 int f = 1;
We are now in the second call of the factorial function, but we want to set our watchpoints and displays once the variables have been initialised (i.e. line 26):
(gdb) n
25 int i = 1;
(gdb) n
26 while (i <= n) {
We want to print out both f and i when f changes within the loop. So we set a watchpoint on f and display i:
(gdb) watch f
Hardware watchpoint 2: f
(gdb) display i
1: i = 1
Now all we need to do is type continue until we notice that f != i! See the table below for a comparison of the f and i!:
(gdb) c
Continuing.
Hardware watchpoint 2: f
Old value = 1
New value = 2
factorial (n=17) at factorial.c:28
28 i++;
1: i = 2
(gdb) c
Continuing.
Hardware watchpoint 2: f
Old value = 2
New value = 6
factorial (n=17) at factorial.c:28
28 i++;
1: i = 3
(gdb) c
Continuing.
Hardware watchpoint 2: f
Old value = 6
New value = 24
factorial (n=17) at factorial.c:28
28 i++;
1: i = 4
(gdb) c
Continuing.
Hardware watchpoint 2: f
Old value = 24
New value = 120
factorial (n=17) at factorial.c:28
28 i++;
1: i = 5
(gdb) c
Continuing.
Hardware watchpoint 2: f
Old value = 120
New value = 720
factorial (n=17) at factorial.c:28
28 i++;
1: i = 6
(gdb) c
Continuing.
Hardware watchpoint 2: f
Old value = 720
New value = 5040
factorial (n=17) at factorial.c:28
28 i++;
1: i = 7
(gdb) c
Continuing.
Hardware watchpoint 2: f
Old value = 5040
New value = 40320
factorial (n=17) at factorial.c:28
28 i++;
1: i = 8
(gdb) c
Continuing.
Hardware watchpoint 2: f
Old value = 40320
New value = 362880
factorial (n=17) at factorial.c:28
28 i++;
1: i = 9
(gdb) c
Continuing.
Hardware watchpoint 2: f
Old value = 362880
New value = 3628800
factorial (n=17) at factorial.c:28
28 i++;
1: i = 10
(gdb) c
Continuing.
Hardware watchpoint 2: f
Old value = 3628800
New value = 39916800
factorial (n=17) at factorial.c:28
28 i++;
1: i = 11
(gdb) c
Continuing.
Hardware watchpoint 2: f
Old value = 39916800
New value = 479001600
factorial (n=17) at factorial.c:28
28 i++;
1: i = 12
(gdb) c
Continuing.
Hardware watchpoint 2: f
Old value = 479001600
New value = 1932053504
factorial (n=17) at factorial.c:28
28 i++;
1: i = 13
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.
We found out the same information we could have using next and print, except it was a lot faster and easier.
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