Resuming Execution

Knowing how to instruct the debugger where or when to pause execution of your program is important, but knowing how to instruct it to resume execution is just as important. After all, inspecting your variables may not be enough. Sometimes you need to know how the variables' values interact with the rest of the code.

Recall the Principle of Confirmation in Chapter 1: You continue to confirm that certain variables have the values you think they do, until you encounter one that fails to match your expectation. That failure will then be a clue as to the likely location of your bug. But typically the failure will not occur until you have paused and resumed execution at a number of breakpoints (or multiple times at the same breakpoint). Thus, resuming execution at a breakpoint is just as important as setting the breakpoint itself, which is why a debugging tool will typically have a fairly rich set of methods for resuming execution.

There are three classes of methods for resuming execution. The first involves "single stepping" through your program with step and next, executing only the next line of code and then pausing again. The second consists of using continue, which makes GDB unconditionally resume execution of the program until it hits another breakpoint or the program finishes. The last class of methods involves conditions: resuming with the finish or Until commands. In this case, GDB will resume execution and the program will run until either some predetermined condition is met (e.g., the end of a function is reached), another breakpoint is reached, or the program finishes.

We'll consider each method of resuming execution in turn for GDB, and then show how to perform such operations in DDD and Eclipse.

We'll start this section by discussing the various ways you can resume execution once GDB is paused at a breakpoint.

Once GDB stops at a breakpoint, the next (abbreviated as n) and step (abbreviated as s) commands are used to single-step through your code. After a breakpoint is triggered and GDB pauses, you can use next and step to execute just the very next line of code. After the line is executed, GDB will again pause and give a command prompt. Let's take a look at this in action. Consider the program swapflaw.c:


We'll set a breakpoint at the entry to main() and run the program in GDB.

$ gcc -g3 -Wall -Wextra -o swapflaw swapflaw.c
$ gdb swapflaw
(gdb) break main
Breakpoint 1 at 0x80483f6: file swapflaw.c, line 7.
(gdb) run
Starting program: swapflaw
Breakpoint 1, main () at swapflaw.c:7
7               int i = 4;

GDB is now at line 7 of the program, meaning that line 7 has not been executed yet. We can use the next command to execute just this line of code, leaving us just before line 8:

(gdb) next
8               int j = 6;

We'll use step to execute the next line of code, line 8, which moves us to line 10.

(gdb) step
10              printf("i: %d, j: %d\n", i, j);

We see that both next and step execute the next line of code. So the big question is: "How are these commands different?" They both appear to execute the next line of code. The difference between these two commands is how they handle function calls: next will execute the function, without pausing within it, and then pause at the first statement following the call. step, on the other hand, will pause at the first statement within the function.

A call to swap() is coming up at line 11. Let's look at the effect of next and Step side by side.

Using step:

(gdb) step
i: 4, j: 6
11      swap(i, j);
(gdb) step
swap (a=4, b=6) at swapflaw.c:19
19      int c = a;
(gdb) step
20      a = b;
(gdb) step
21      b = c;
(gdb) step
22      }
(gdb) step
main () at swapflaw.c:12
12      printf("i: %d, j: %d\n", i, j);
(gdb) step
i: 4, j: 6
14      return 0;

Using next:

(gdb) next
i: 4, j: 6
11      swap(i, j);
(gdb) next
12      printf("i: %d, j: %d\n", i, j);
(gdb) next
i: 4, j: 6
14      return 0;

The step command works the way you might expect it to. It executed the printf() on line 10, then the the call to swap() on line 11,[6] and then it began executing lines of code within swap(). This is called stepping into a function. Once we step through all the lines of swap, step brings us back to main().

In contrast, it appears that next never left main(). This is the main difference between the two commands. next considers the function call to be a single line of code, and executes the entire function in one operation, which is called stepping over the function.

However, don't be fooled; it may look like next skipped over the body of swap(), but it didn't really step "over" anything. GDB silently executed each line of swap() without showing us the details (although it shows any screen output that swap() may print) and without prompting us to execute individual lines from the function.

The difference between stepping into a function (what step does) and stepping over a function (what next does) is such an important concept that, at the risk of belaboring the point, we'll demonstrate the difference between next and step with a diagram that shows the program execution using arrows.

Figure 2-8 illustrates the behavior of the step command. Imagine that the program is paused at the first printf() statement. The figure shows where each step statement will take us:


Figure 2-9 illustrates the same thing, but using next instead.


Whether you use next or step is really a matter of what you are trying to do. If you're in a part of the code with no function calls, it doesn't matter which one you use. In this case, the two commands are completely equivalent.

However, if you're debugging a program and find yourself about to step into a function that you know is free of bugs (or irrelevent to the bug you're trying to track down), clearly you'd want to use next to save yourself from stepping through each line of a function you're not interested in.

One of the general debugging principles laid out in Chapter 1 was to take a top-down approach to debugging. If you're stepping through source code and encounter a function call, it is typically better to use next instead of Step. Immediately after using next in this situation, you would check if the result of the call was correct. If so, the bug is likely not in the function, meaning the use of next instead of step saved you the time and effort of stepping through every line of the function. On the other hand, if the result of the function is incorrect, you can re-run the program with a temporary breakpoint set at the function call, and then use step to enter the function.

Both the next and step commands take an optional numerical argument which indicates the number of extra lines to next or step through. In other words, next 3 is the same as typing next three times in a row (or typing next once followed by hitting the ENTER key twice).[7] Figure 2-10 shows an illustration of what next 3 does:


Once a breakpoint is triggered, the next and step commands are used to execute the program line by line. Sometimes this can be a painful endeavor. For example, suppose GDB reached a breakpoint within a function. You've inspected a few variables and have gathered all the information you had intended to get. At this point, you're not interested in single-stepping through the remainder of the function. You'd like to return back to the calling function where GDB was before you stepped into the called function. However, setting an extraneous breakpoint and using continue seems wasteful if all you want to do is skip the remainder of the function. That's where finish comes in.

The finish command (abbreviated fin) instructs GDB to resume execution until just after the current stack frame finishes. In English, this means that if you're in a function other than main(), the finish command will cause GDB to resume execution until just after the function returns. Figure 2-11 illustrates the use of finish.

Although you could type next 3 instead of finish, it is easier to type the latter, which involves counting lines (anything more than a half dozen would be a needless nuisance).

It may not look like finish executes each line of code as it takes you to the bottom of the function, but it does. GDB executes each line without pausing[8] except to show the program's output.

Another common use of finish is when you've accidentally stepped into a function that you meant to step over (in other words, you used step when you meant to use next). In this case, using finish places you right back where you would've been had you used next.


If you're within a recursive function, finish will only take you one level up in the recursion. This is because each call is considered a function call in its own right, since each one has its own stack frame. If you want to get completely out of a recursive function when the recursive level is high, a temporary breakpoint along with Continue, or using the until command, is more appropriate. We will discuss until next.

Recall that the finish command completes execution of the current function without further pauses within the function (except at any intervening breakpoints). Similarly, the Until command (abbreviated simply as u) is typically used to complete an executing loop, without further pauses within the loop, except at any intervening breakpoints within the loop. Consider the following code snippet.

...previous code...

int i = 9999;
while (i--) {
   printf("i is %d\n", i);
   ... lots of code ...
}
...future code...

Suppose GDB is stopped at a breakpoint at the while statement, you've inspected a few variables, and now you'd like to leave the loop to debug "future code."

The problem is that i is so large, it would take forever to use next to complete the loop. You can't use finish because that command will pass right over "future code" and take us out of the function. You could set a temporary breakpoint at the future code and use continue; however, this is exactly the situation that until was meant to address.

Using until will execute the rest of the loop, leaving GDB paused at the first line of code following the loop. Figure 2-12 shows an illustration of what using until will do:


Of course if GDB encounters a breakpoint before leaving the loop, it will still pause there: If there were a breakpoint at the printf() statement in Figure2-12, you'd certainly want to disable it.

The GDB User's Guide gives the official definition of until as:

Execute until the program reaches a source line greater than the current [one].

However, the documentation also warns that this can be a little counterintuitive. To demonstrate why, consider the following program:


We'll set a breakpoint at the entry of main(), run the program, and use until to reach the return statement.

$ gdb until-anomaly
Using host libthread_db library "/lib/tls/libthread_db.so.1".
(gdb) break main
Breakpoint 1 at 0x80483b4: file until-anomaly.c, line 7.
(gdb) run
Starting program: until-anomaly

Breakpoint 1, main () at until-anomaly.c:7
7          for (i=0; i<10; ++i)
(gdb) until
8             printf("hello world!");
(gdb) until
7          for (i=0; i<10; ++i)
(gdb) until
10         return 0;
(gdb)

Whoa! Using until, GDB went from line 7 to line 8 and then back to line 7. Surely this doesn't mean that source line 7 is greater than source line 8? Actually, it does. Perhaps you can guess the answer by now, since it seems to be a common theme. GDB ultimately works with machine instructions. Although the for construct is written with the loop test at the top of the body, GCC compiled the program with the conditional at the bottom of the loop body. Since the conditional is associated with line 7 of the source code, it appears that GDB went backwards in the source code. In fact, what until really does is execute until it reaches a machine instruction that has a higher memory address than the current one, rather than until it reaches a larger line number in the source code.

In practice, this sort of thing may not arise too often, but it's nice to understand it when it does occur. Additionally, by looking at some of GDB's odd behavior, you can glean information about how compilers turn source code into machine instructions—not a bad bit of knowledge to have.

If you are curious and you know the assembly language of your machine, you can take a quick look at the machine code, using GDB's disassemble command, followed by p/x $pc to print out the current location.[9] This will show you what Until will do. But it's just a quirk, and in practical terms, it is not an issue. If you are at the end of a loop and issuing the until command causes a jump back to the top of the loop, simply issue until a second time, and you will leave the loop as desired.

The until command can also take an argument of a location within your source code. In fact, it takes the same arguments as the break command that was discussed in Setting Breakpoints in GDB. Returning to Example Listing 2-1, if GDB triggered a breakpoint on the entry of main(), these are all equivalent ways of conveniently executing the program until the the entry to swap():

  • until 17

  • until swap

  • until swapflaw.c:17

  • until swapflaw.c:swap

We'll start this section by discussing the various ways you can resume execution once DDD is paused at a breakpoint.

As noted in Undoing/Redoing Breakpoint Actions in DDD, DDD has an invaluable Undo/Redo feature. In that section, we showed how to undo an accidental breakpoint deletion. It can also be used on actions such as Run, Next, Step, and so on.

Consider, for example, the situation depicted in Figure 2-13. We had reached the breakpoint at the call to swap() and had intended to do a Step operation, but accidentally clicked Next. But by clicking Undo, you can roll back time, as shown in Figure 2-14. DDD reminds you that you have undone something by displaying its current-line cursor as an outline, instead of the solid green it normally uses.


Eclipse's analogs of step and next are the Step Into and Step Over icons. The Step Into icon is visible in the Debug view (upper left) in Figure 2-15; the mouse pointer has temporarily caused the icon label to appear. The Step Over icon is just to the right of it. Note that the next statement you will execute will be the call to get_args(), so clicking Step Into would result in the next pause of execution occurring at the first statement within that function, while selecting Step Over would mean the next pause would be at the call to process_data().

Eclipse has a Step Return icon (next to Step Over) that performs finish. It has nothing exactly corresponding to Until. Its Run to Line (invoked by clicking the target line and then right-clicking in the source code window) will typically accomplish what you would want with until.



[6] You may wonder why step did not take you to the first line of the function printf(). The reason is that GDB does not stop within code for which it does not have debugging information (i.e., the symbol table). The function printf(), being linked in from the C library, is an example of such code.

[7] Vim users should feel right at home with the concept of specifying a count for a given command.

[8] If there are any intervening breakpoints, finish will pause at them.

[9] You can also view the machine code by using the -S option of GCC. This will produce an assembly language file with suffix .s, showing the code that was produced by the compiler. Note that this produces the assembly language file only, not an executable.