****************************** GDB - Navigating your program ****************************** .. topic:: Learning Outcome Able to control the execution of a program using commands *step*, *next* and *continue*. .. topic:: Introduction Breakpoints halt execution at the point specified, however the specified point is usually only our best guess as to where the problem might be. Once halted, we need to continue execution a small amount at a time so we can observe what the problem might be. The GDB commands *step*, *next* and *continue* can be used execution the program in small amounts to investigate how variables change as the program runs. .. topic:: Applicable subjects COMP1521, COMP2521, COMP3231 ---- .. note:: The next line of code to execute is the one displayed beside the displayed line number when the program halts at a breakpoint or after using a step or a next command. This line of code has **not** run yet. step ===== Execute the next statement. If the next statement is a function call, step into the function (i.e. start debugging inside the function itself). :: (gdb) step next ====== Execute the next statement. If the next statement is a function call, execute the entire function and return to the next line of code. :: (gdb) next continue ========= Contine the execution of the program until it reaches a breakpoint, a fatal error or finishes execution normally. :: (gdb) continue where ====== Print out the function call stack including the current line number. If you are not familiar with the function call stack, just look at the top line for the current file and line number. Not an execution related command but helpful if you get lost in navigating the program. :: (gdb) where list ====== Print out 5 lines of code above and below the current statement. Like *where*, this is also not execution related but helpful to display of surrounding context of where execution has stopped. :: (gdb) list ---- Example ======= In the previous modules we learned how to set a breakpoint, run our program in GDB and inspect the state of the program. However, to find the bug in factorial.c we must move through the execution of our program. In this example, we will be using *step* and *next* to move through factorial.c and inspect variables of interest. :download:`factorial.c` .. literalinclude:: factorial.c :language: c :linenos: :caption: factorial.c When we run the above code without gdb, the following output is given: :: The factorial of 5 is 120. The factorial of 17 is -288522240. Recall, we know that the value of 17! should be 355,687,428,096,000 not -288522240, so something has gone wrong. .. topic:: Recap In the previous modules we compiled the code for use with GDB, started a gdb session and set a breakpoint on line 15. :: $ gcc -Wall -g -o factorial factorial.c $ gdb factorial (gdb) break 15 (gdb) run Starting program: /code/factorial The factorial of 5 is 120. Breakpoint 1, main () at factorial.c:15 15 f = factorial(n); We want to debug inside the factorial function so we use step to **step into** it: :: (gdb) step factorial (n=17) at factorial.c:24 24 int f = 1; We are now on the first line of the factorial function. We want to look at the variables in this function, especially the result of f and the corresponding value of i after the multiplication (i.e. we'd like to inspect line 26). To get to line 26 we use the next command: :: (gdb) next 25 int i = 1; (gdb) next 26 while (i <= n) { (gdb) next 27 f = f * i; (gdb) next 28 i++; .. note:: To repeat the previous command, just press enter. Now we are at a place in the code where we want to check if the values are what we expect them to be. We have three variables available on line 28, n, f and i. For each of the iterations, f should be equal to i! after the execution of the multiplication. We can compare the values of f against known factorial values of i by filling out the third column of the table below. On the first iteration of the loop we expect i == 1 and f == 1. Let's check this is true using gdb. :: (gdb) print i $2 = 1 (gdb) print f $3 = 1 The values of f and i are correct for the first iteration of the loop. If we forget where we are, we can use the where command. :: (gdb) where =0 factorial (n=17) at factorial.c:28 =1 0x000000000040056a in main () at factorial.c:15 From this stack trace, we know that we are at line 28 in factorial.c in the function factorial. This was called at line 15 in factorial.c in the function main If we want to see the code around where we are we can use the list command. :: (gdb) list 23 int factorial(int n) { 24 int f = 1; 25 int i = 1; 26 while (i <= n) { 27 f = f * i; 28 i++; 29 } Now let's check the values of f against i for the rest of the iterations. It is quite tedious stepping through line by line. To speed things up we can use a second breakpoint on line 28. :: (gdb) break 28 Breakpoint 2 at 0x4005ac: file factorial.c, line 28. We can then use continue to execute until we reach line 28 again. :: (gdb) continue Continuing. Breakpoint 2, factorial (n=17) at factorial.c:28 28 i++; And we can again print out f and i. :: (gdb) info locals f = 2 i = 2 This is again what we expect. We repeat this process of continuing and printing until we notice that f does not equal i!. At i = 13 we notice that the value of f is 1,932,053,504 but it should be 6,227,020,800. 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) | +------------+---------------------+---------------------+ .. moduleauthor:: Liz Willer :Date: 2020-01-15