This chapter describes how you can control the execution of your program as you debug it. This involves the following menus and windows:
This chapter also describes how to interrupt a running program.
The Run menu controls how your program executes. It contains the following items:
In the dialog, you can click the Symbols... button as a shortcut. You can type a partial symbol name like foo, and the Symbol button shows you a list of symbols that start with foo. You can then choose one of these symbols by clicking on it, or by pressing Enter. Note that the first time you use Symbols... in a debugging session, it may take a while, as the debugger sorts the symbol table for the program.
If your program encounters a breakpoint, or an error occurs before the specified address is executed, the debugger ignores your request to stop at the given address.
When using this command, be sure that the execution path will eventually execute the next statement or instruction. If execution fails to reach this point, then the program may continue to execute until completion. This situation is like setting a breakpoint at a statement or assembly instruction that will never be executed, and then issuing a GO command. In this situation, the application executes until an error occurrs or another breakpoint is encountered. |
Use this menu item with caution. If you skip to an instruction that isn't in the current function, or skip to code that expects a different program state, your program could crash. |
All breakpoints in your program are preserved, but those in DLLs aren't.
The debugger keeps an execution history as you debug your program. This history is accessible using the Undo menu. The effects of program statements as you single step through your program are recorded. So are all interactions that allow you to modify the state of your program, including modifying variable values, memory and registers. Undo and Redo let you browse backward and forward through this execution history. As you use these menu items, all recorded effects are undone or redone, and each of the debugger's windows is updated accordingly.
You can resume program execution at any previous point in the history. The program history has no size restrictions, aside from the amount of memory available to the debugger, so theoretically you could single-step through your entire program and then execute it in reverse.
There are several practical problems that get in
the way of this. When you single-step over a call or interrupt
instruction, or let the program run normally, the debugger has no
way of knowing what kind of side effects occurred. No attempt is
made to discover and record these side effects, but the fact that
you did step over a call is recorded.
If you try to resume program execution from a point prior to a side effect, the debugger gives you the option of continuing or backing out of the operation. Use caution if you choose to continue. If an important side effect is duplicated, your program could crash. Of course, reversing execution over functions with no side effects is harmless and can be a useful debugging technique. If you have accidentally stepped over a call that does have a side effect, you can use Replay to restore your program state. |
Unwind and Rewind move the debugger's state up and down the call stack. Like Undo, all windows are updated as you browse up and down the stack, and you can resume execution from a point up the call stack. If you try resuming from a point up the call stack, the debugger issues a warning, since it can't completely undo the effects of the call.
Unwind is particularly useful when your program crashes in a routine that doesn't contain debugging information. The strcpy() routine is a good example of this. You can use Unwind to find the call site and inspect the parameters that caused the problem.
The runtime library detects certain classes of errors and diagnoses them as fatal runtime errors. If this occurs when you're debugging, the debugger is activated and the error message is displayed. For example, throwing an exception in C++ without having a catch in place is a fatal runtime error. In C, the abort() and assert() functions are fatal errors. When this happens, you're positioned in an internal C library call. You can use Unwind to find the point in your source code that initiated the error condition.
When Unwind and Undo are used in conjunction, Undo is the primary operation and Unwind is secondary. You can Undo to a previous point in the history and then Unwind the stack. If you try to Unwind the stack first and then use Undo, the Unwind has no effect.
If you modify the machine state in any way when you're browsing backward through the execution history, all forward information from that point is discarded. If you have browsed backward over a side effect the debugger gives you the option of canceling any such operation.
The Undo menu contains the following items:
Choose Replay from the Code menu to open the Replay window. This window displays each of the steps that you have performed during this debugging session that might have affected program flow. Three items are displayed for each step in the replay window:
The most common use for Replay is when you accidentally step over a function call, or the program unexpectedly runs to completion. If this happens, you can open the Replay window and replay your debugging session up to any point prior to the last action you took.
There are special cases where Replay won't perform as expected. Since replay is essentially the same as playing your keystrokes and mouse interactions back to the debugger, your program must behave identically on a subsequent run:
To replay program execution to any point, double-click on that line, or cursor to it and press Enter. Select any line and press the right mouse button to see the following popup menu items:
Choose Calls from the Code menu to display the Calls window. This window displays the program's call stack. Each line contains the name of the function that was executing, and the source or assembly code at the call site. You can use Unwind and Rewind to obtain this information, but the Calls windows shows you the entire call stack.
To Unwind to any point in the call stack, double-click on a line, or cursor to it and press Enter. Select a line and press the right mouse button to access the following popup menu items:
Choose Threads from the Code menu to display the Threads window. This window displays the system ID of each thread, the state of the thread, and under some operating systems, system specific information about the thread, including its name and scheduling priority. The state of each thread can be:
To make any thread current, double-click on it, or cursor to it and press Enter. All other debugger windows update accordingly. Press the right mouse button to access the following popup menu items:
It isn't unusual for your code to contain an endless loop that results in the program's getting stuck in one spot. You then want to interrupt the program so that you can see where it's getting stuck.
To do this, switch focus to the debugger console and type Ctrl-Break or Ctrl-C. Alternatively, you may send any unhandled signal to the application being debugged. Consult your QNX system documentation for details.