The Tornado debugger (CrossWind) combines the best features of graphical and command-line debugging interfaces.
The most common debugging activities, such as setting breakpoints and controlling program execution, are available through convenient point-and-click interfaces. Similarly, program listings and data-inspection windows provide an immediate visual context for the crucial portions of your application.
For complex or unpredictable debugging needs, the command-line interface gives you full access to a wealth of specialized debugging commands.You can extend or automate command-line debugger interactions by using the Tcl scripting interface that allows you to develop custom debugger commands (see 10.1 Introduction).
The underlying debugging engine is an enhanced version of GDB, the portable symbolic debugger from the Free Software Foundation (FSF). For full documentation of the GDB commands, see GDB User's Guide.
Figure 10-1 illustrates the GUI elements you can use to interact with the debugger.
The Debug menu provides the complete list of Tornado GUI debugger commands, as well as their keyboard shortcuts (10.2.1 Debugger Toolbar, Buttons, and Menu Commands).
The Debug toolbar provides buttons for the most common debugger commands, as well as for opening and closing windows that display data, memory, and stack information (see 10.2.1 Debugger Toolbar, Buttons, and Menu Commands).
You use the editor window to keep track of the code you are debugging. You can click in this window to specify information for debugger commands (such as symbol names, or lines of code). The debugger in turn uses the attribute panel, in the left margin of the editor window, to show breakpoints and the execution context. (See 10.3 Using the Debugger.)
The debugger command line window for CrossWind provides a command-line interface to the debugger (see 10.5 Using the Debugger Command Line). The window is not needed for standard debugger operations; the graphical interface provide simpler controls.
The Debug toolbar has buttons for the most common debugging commands, as well as for displaying auxiliary debugger windows. The toolbar shown as a floating palette in Figure 10-2.
The commands in the Debug menu include alternatives to the buttons in the Debug toolbar, as well as additional debugger functions. Keyboard shortcuts are also available for all graphical debugger commands
The debugger buttons and menu commands are described in Table 10-1.
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
Identify source search paths for the debugger. Opens the Debugger Source Search Path dialog box. See 10.3.2 Setting the Search Path.
|
|||||||||||||||||||
|
Run a routine on the target as a new task under debugger control. Opens the Run Task dialog box, which allows you to choose the routine. See 10.3.7 Continuing Through a Program.
|
|||||||||||||||||||
|
Detach from the task currently under debugger control, leaving it in the state present (suspended or running) when you give the command. See 10.3.10 Detaching from a Running Task.
|
|||||||||||||||||||
|
Detach from the task currently under debugger control, first ensuring that it is running. See 10.3.10 Detaching from a Running Task
|
|||||||||||||||||||
|
Attach the debugger to a task. See 10.3.8 Attaching to a Running Task.
|
|||||||||||||||||||
|
Interrupt program execution. See 10.3.5 Interrupting a Program.
|
|||||||||||||||||||
|
Stop the debugger. See 10.3.1 Starting and Stopping the Debugger.
|
|||||||||||||||||||
|
Open the Breakpoints window. See 10.3.6 Setting Breakpoints.
|
|||||||||||||||||||
|
Set or remove a task-level breakpoint on the current line of the editor window. To delete a breakpoint, click this button on a line that is already marked with the breakpoint icon. See 10.3.6 Setting Breakpoints.
|
|||||||||||||||||||
|
Set or remove a temporary breakpoint. See 10.3.6 Setting Breakpoints.
|
|||||||||||||||||||
|
Step to the next line of code, in order of execution (not necessarily the next line displayed in the editor). See 10.3.7 Continuing Through a Program.
|
|||||||||||||||||||
|
Step to the next line displayed on the screen. If there is a subroutine call on the current line, the button executes that subroutine in its entirety, then stops at the line after the subroutine call. See 10.3.7 Continuing Through a Program.
|
|||||||||||||||||||
|
Continue program execution. The task you are debugging continues until the next breakpoint, exception, or until you use Interrupt Debugger to halt it. See 10.3.7 Continuing Through a Program.
|
|||||||||||||||||||
|
Finish the current subroutine. Execution continues until the current subroutine returns to its caller. See 10.3.7 Continuing Through a Program.
|
|||||||||||||||||||
|
Open and close the Watch window, which displays the values of specified variables throughout the execution of the program. See Watch Window.
|
|||||||||||||||||||
|
Open and close the Variables window, which displays the values of local variables. See Variables Window.
|
|||||||||||||||||||
|
Open and close the Registers window, which displays values of the target registers. See Registers Window.
|
|||||||||||||||||||
|
Open and close the Back Trace window, which displays stack information. See Back Trace Window.
|
|||||||||||||||||||
|
Open and close the Memory window, which displays target memory information. See Memory Window.
|
|||||||||||||||||||
|
|
|||||||||||||||||||
The debugger allows you to download object modules, to start routines under debugger control, and to take over existing tasks in the target. Tasks under debugger control execute normally until they terminate, unless they encounter a breakpoint, or you interrupt them, or some other event sends them an interrupt or a signal.
|
|
|||||||||||||||||||
button. This starts a debugging session for the currently selected target server (see Tornado Launch Toolbar, p.98).
When the debugger is running, you can interact with it through the editor window, through the debugger command line window, and through the Debug menu and toolbar. (See 10.2 Debugger GUI.)
You can end the debugging session in any of the following ways:
The debugger maintains a list of directories where it searches for source code, and for the host-resident image of the VxWorks run-time (the debugger uses the latter to load debugging symbol information). This list is called the source search path.
Normally you will not need to set the source search path because the debugger can derive the path from the object file. If GDB finds the object but cannot find the source, the GUI prompts you for the source file location and remembers it.
If necessary, click on Source Search Path to add directories to, remove directories from, or change the order of this list. Figure 10-4 illustrates the Debugger Source Search Path dialog box.
|
|
|||||||||||||||||||
Normally you will not need to unload a module. If you update and download a module with the same name, it replaces the module loaded earlier. In the unusual case where you need to unload a module without replacing it, use the unload option from the context menu in the project Workspace window (see 4.3.5 Downloading an Application).
You can also use the debugger command line to remove any dynamically-linked module from the target. Open the debug command-line window and use the GDB unload command. See 10.5 Using the Debugger Command Line for more information about command line usage.
Specify the argument list after the routine name, with the arguments separated by spaces. The argument list must contain integers or addresses only; it may not contain floating-point or double-precision values, function calls, or user-defined C++ operations. (See GDB User's Guide for other commands which allow arbitrary arguments to be passed.)
Figure 10-5 shows the Run Task dialog box with a routine name (required) and an argument list (optional). The default for required arguments that you do not supply is zero. To set a temporary breakpoint where the routine begins execution, check the Break at Entrypoint box.
Once a task stops under debugger control (most often, at a breakpoint), you can single-step through the code, jump over subroutine calls, or resume execution. Figure 10-10 shows the debugger stopped at the entry point of the routine BigBang( ). The context pointer þ indicates what statement will execute if you allow the program to resume.
To set multiple breakpoints, select Breakpoints in either the Debug or the pop-up menu (right-click in the editor window). The Breakpoints dialog box appears. (See Figure 10-7.) Enter a file name and line number in the Location box, select a scope (local task or all tasks), and click Add. The new breakpoint appears in the Breakpoints list. If Externally managed is checked, it indicates that the breakpoint was set by means other than the debugger (by the Tornado shell, for example).
|
|
|||||||||||||||||||
The Advanced button opens the Advanced Breakpoint dialog box (Figure 10-8).
dialog lets you attach conditions to the break point as well as deleting or disabling it instead of keeping it when it is hit. Enter a file name and line number in the Location box. A conditional expression of type int can be used, which will be evaluated as true or false (all non-zero values being true), or to check if a value in memory has changed. The On Break options have the following meanings with regard to handling a breakpoint:
To set a breakpoint on an individual line of code, place the text cursor in the line where you want the program to stop. Then click the
button, select Toggle Breakpoint from the Debug menu, or right-click on the line of code and select Toggle Breakpoint from the pop-up menu. The
symbol appears in the left margin of the editor window to indicate the breakpoint location, when the attribute pane is turned on for the editor (12.3.2 Editor Preferences). Otherwise, the entire line is highlighted to indicate the breakpoint location.
If you click Toggle Breakpoint on a line that produces no object code (such as a comment line or a declaration), the breakpoint appears on the next line that does produce object code.
You can also set temporary breakpoints by using Toggle Temp. Breakpoint from the Debug menu. A temporary breakpoint stops the program only once. The debugger deletes it automatically as soon as the program stops there. A hollow breakpoint symbol (
) marks temporary breakpoints in the editor's left margin, so that you can readily distinguish the two kinds of breakpoints there.
To remove either kind of breakpoint, click either of these commands with a line selected that already has a breakpoint or use the Breakpoints dialog box.
|
|
CAUTION:
If your application was compiled without debugging information, the debugger displays an error when you try to set a breakpoint using these commands. If you are forced to work on an object module without debugging information, you can still break at the start of any subroutine in either of the following ways: (1) Check the Break at Entrypoint check box in the Run Task dialog box when you start the task (10.3.7 Continuing Through a Program). (2) Use the Breakpoints dialog box (see 10.3.6 Setting Breakpoints). In either case, when the debugger stops, it displays a Disassembly window, as it does whenever no debugging information is available for the program context.
|
||||||||||||||||||
|
|
|||||||||||||||||||
Figure 10-9 shows the editor window with two breakpoints set: a temporary breakpoint þ (hollow) and a persistent breakpoint À (filled in).
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
Once a task has been stopped under debugger control (most often, at a breakpoint), you can single-step through the code, jump over subroutine calls, or resume execution. Figure 10-10 shows the debugger stopped at the entry point of the routine graphInit( ). The context pointer þ indicates what statement will execute if you allow the program to resume.
When the program is stopped, you can resume operation with the Continue command from the Debug menu. If there are no remaining breakpoints, interrupts, or signals, the task runs to completion. A common example of using Continue is to set a breakpoint at the end of a loop, then use Continue repeatedly to stop once in each iteration through the loop, while monitoring a variable used within that loop.
To step through the code one line at a time, click Step Into. If you have auxiliary debugger windows open (10.3.11 Examining Data, Memory, and the Stack), they are updated with current values as you step through the code. If there is a subroutine call in the current line, Step Into takes you to the first line of that subroutine, not to the next line currently displayed on your screen. The only exception is for calls to system subroutines and application subroutines that are compiled without debugging information; Step Into cannot step into these.
The effect of Step Into is somewhat different if the current view in the editor shows assembly instructions (when either Disassembly or Mixed is selected from the View menu, or the current routine has no debugging symbols). In this case, Step Into advances execution to the next instruction rather than to the next line of source.
To single-step without going into other subroutines, click Step Over instead of Step Into. The Step Over command is almost the same as Step Into, but instead of stepping to the very next statement executed (which, in the case of a subroutine call, is typically not the next statement displayed), Step Over steps to the next line on the screen. If there is no intervening subroutine call, this is the same thing as Step Into. But if there is an intervening subroutine call, Step Over executes that subroutine in its entirety, then stops at the line after the subroutine call.
The display style has the same effect on Step Over as on Step Into. Step Over steps to either the next machine instruction or the next source statement, and if necessary completes a subroutine call first.
While stepping through a program, you may conclude that the problem you are interested in lies in the current subroutine's caller, rather than at the stack level where your task is suspended. Use the Step Out command from the Debug menu in that situation: execution continues until the current subroutine completes, then the debugger regains control in the calling statement.
To continue a stopped task to a specific point without setting a breakpoint, place the cursor on the desired line of code, right-click to open the pop-up menu, and select Run to Cursor.
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
Click Attach to attach the debugging session to a task that is already running. If you were already debugging another task, the previous task is released from debugger control, remaining in its current state (running or stopped). Attach displays a scrolling list of the tasks running on the target (Figure 10-11). You can either select a task in the list, or type the name (or task ID) of a task in the Attach to: box. By default, the debugger attaches without stopping the task. The task will stop when it hits a breakpoint.
Usually, a newly-attached task stops in a system routine; thus, the debugger displays an assembly listing in the editor window. Open the backtrace window using the
button. Change the frame by double-clicking on the frame you want to jump to.
The first entry in the Attach dialog box is always System. Select this entry to enter system mode, as described in 10.6 System-Mode Debugging. An error display appears if your BSP is not configured to support system mode.
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
When a program stops under debugger control, you can use auxiliary windows to examine local and global program variables, arguments, registers, target memory, and the execution stack. The windows can be displayed docked or free-floating (Figure 10-12). When they are docked (the default), the split bars at their edges can be used to change their size.
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
The Watch window displays the current values of symbols, throughout the execution of the program. The Watch window has four pages, which allows you to group and display sets of symbols in any manner you find useful.
To select a symbol for display in the watch window, click on or highlight the symbol name in the editor, display the pop-up menu (with the right-mouse button), and select Add to Watch. If you highlighted the symbols name, the Watch window opens, and lists the symbol and its current value. If you simply clicked on the symbol, the Add to Watch dialog box opens (Figure 10-13).
Enter the name of the symbol and click OK to display the symbol and its current value in the Watch window (Figure 10-14). Use the pop-up menu or the Delete key to remove symbols from the window.
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
Click the Variables command in the Debug Windows sub-menu to open a window that shows the values of local variables. Figure 10-15 shows an example of the Variables window.
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
Click the Registers command in the Debug menu to open a window that shows the values in the target registers. Figure 10-16 illustrates the Registers window. The contents of the window depend on the architecture of the target, and the title displayed when the window is not docked includes information about machine architecture.
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
To inspect the calling sequence leading to the current routine, click Back Trace in the Debug Windows sub-menu. The Back Trace window allows you to monitor the stack. You can double-click on any routine in the window to move the context pointer to that stack level in the editor window (Figure 10-17).
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
Click the Memory command to open a window that displays a range of target memory starting at the address specified in the Start Address control field. The debugger saves each address you type in the field; you can select a previously displayed address from the drop-down list associated with this box. To update the memory display, press the
button. Figure 10-18 shows the window docked and maximized.
While the debugger is running, you can display your program code in the editor as source code, as symbolic disassembly of object code, and as mixed source and disassembly.
The Source, Disassembly, and Mixed Source and Disassembly commands in the View menu control the display of code in the editor:
Figure 10-19 shows a mixed-mode code display.
Source display (in either the Source or the Mixed Source and Disassembly view) requires that your application modules be compiled with debugging information which is the default for compilations with the Tornado project facility (see 4Projects).
The debugger is fully operational no matter what view you select. For example, you can set breakpoints in a line of assembly code, and you can use the Step and Next commands in either assembly or source. (In views that show assembly, these commands step by instructions rather than by source lines; see 10.3.7 Continuing Through a Program.)
The editor, however, works only on source code. Thus, when you display a view with disassembled instructions, the editor display goes into read-only mode until you either stop debugging or switch to the Source view.
The Tornado graphical interface is usually the most convenient way to run the debugger. However, you can also use the GDB command-line interface, which in some cases is the best way to perform a particular action (see Figure 10-1). The debugger command-line window provides full access to GDB commands, as well as to Tornado extensions to those commands. The command line can be displayed with Debug>Debug Windows>Debug Command Line.
|
|
|||||||||||||||||||
One use of the debugger window is to experiment with text-based commands for actions that you might want to perform automatically each time you start debugging.
When the debugger first executes GDB, it looks for a file named .gdbinit. It first looks in the directory named by the environment variable HOME (if it is defined), then in your current working directory.1 If it finds the file in either directory, the debugger commands in it are executed; if it finds the file in both directories, the commands in both are executed.
A related initialization file, called gdb.tcl, is specifically intended for Tcl code to customize GDB with your own extensions. The Tcl code in this file executes before .gdbinit. The debugger searches for gdb.tcl in two places: first in installDir\.wind, then (if the environment variable HOME is defined) in homeDir\.wind. See 10.7 Tcl: Debugger Automation for a discussion of extending GDB through Tcl. See also 10.8.1 Tcl: Debugger Initialization Files for a discussion of how the Tornado debugger initialization files interact.
Just as with the Tornado shell, you can execute any subroutine in your application from the debugger. Use the following commands:
When you run a routine from the debugger using one of these commands, the routine runs until it encounters a breakpoint or a signal, or until it completes execution. The normal practice is to set one or more breakpoints in contexts of interest before starting a routine. However, you can interrupt the running task by clicking Interrupt Debugger in the Debug menu or by pressing CTRL+BREAK from the Debugger window.
By default, any tasks you start with the run command use the standard I/O channels assigned globally in VxWorks. However, the debugger has the following mechanisms to specify input and output channels:
Each time you use the run command, you can redirect I/O explicitly for that particular task by using < to redirect input and > to redirect output. For output, ordinary path names refer to files on the host where the debugger is running, and path names preceded by an @ character refer to files or devices on the target. Input cannot be redirected to host files, but input redirection to target files or devices is supported with the same @ convention for consistency. For example, the following command starts the routine appMain( ) in a task that gets input from target device /tyCo/0 and writes output to host file grab.it:
(gdb) run appMain > grab.it < @/tyCo/0
The debugger command tty sets a new default input and output device for all future run commands in the debugging session. The same convention used with explicit redirection on the run line allow you to specify target files or devices for I/O. For example, the following command sets the default input and output channels to target device /tyCo/0:
(gdb) tty @/tyCo/0
Tcl extensions are available within the debugger's Tcl interpreter to redirect either all target I/O, or the I/O channels of any running task. See 10.7.3 Tcl: Invoking GDB Facilities for details.
Instead of using the target server list in the Tornado Launch toolbar, you can select a target from the Debugger window with the target wtx command. The two methods of selecting a target are interchangeable. However, it may sometimes be more convenient to use the GDB command language--for example, you might specify a target this way in your .gdbinit initialization file or in other debugger scripts.
The debugger also provides two kinds of extended commands:
The Tornado tools use a protocol called WTX to communicate with the target server. You can send WTX protocol requests directly from the GDB command area as well, by using a family of commands beginning with the prefix "wtx". See Tornado API Programmer's Guide: WTX Protocol for descriptions of WTX protocol requests. Convert protocol message names to lower case, and use hyphens in place of underbars. For example, issue the message WTX_CONSOLE_CREATE as the command wtx-console-create.
You can run any of the Tornado shell's primitive facilities described in 7.2.4 Invoking Built-In Shell Routines in the Debugger window, by inserting the prefix "wind-" before the shell command name. For example, to run the shell td( ) command from the debugger, enter wind-td in the Debugger window.3 Because of GDB naming conventions, mixed-case command names cannot be used; if the shell command you need has upper-case characters, use lower case and insert a hyphen before the upper-case letter. For example, to run the semShow( ) command, enter wind-sem-show.
You can change many details of the debugger's behavior by using the set command to establish alternative values for internal parameters. (The show command displays these values; you can list the full set interactively with help set.)
The following additional set/show parameters are available in the Tornado debugger (CrossWind) in addition to those described in GDB User's Guide:
(gdb) set wtx-load-timeout 120
By default, in Tornado you debug only one task at a time. The task is selected either by using the run dialog box to create a new task, or by using the Attach dialog box (10.3.8 Attaching to a Running Task) to debug an existing task. When the debugger is attached to a task, debugger commands affect only that particular task. For example, breakpoints apply only to that task. When the task reaches a breakpoint, only that task stops, not the entire system. This form of debugging is called task mode debugging. (All the material in 10.3 Using the Debugger applies to task mode debugging).
Tornado also supports an alternate form of debugging, where you can switch among multiple tasks (also called threads) and even examine execution in system routines such as ISRs. This alternative mode is called system mode debugging; it is also sometimes called external mode. In system mode, you can use global breakpoints to stop the entire system whenever any task hits the breakpoint.
Most of the debugger features described elsewhere in this manual, and the debugging commands described in GDB User's Guide, are available regardless of which debugging mode you select. However, certain debugging commands (discussed below in 10.6.3 Thread Facilities in System Mode) are useful only in system mode.
|
|
CAUTION:
The run command is not available in system mode, because its use of a new subordinate task is more intrusive in that mode. In system mode, use the shell to start new tasks as discussed in 7.2.7 Using the Shell for System Mode Debugging, then attach to them with the thread command.
|
||||||||||||||||||
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
To debug in system mode, click on the System entry displayed in the Attach dialog box (use the Attach command in the Debug menu to display the dialog box).
You can also switch to system mode from the debugger window or an initialization file, but first make sure your debugger session is not attached to any task (type detach). Then issue the following command:
The response to a successful attach system is output similar to the following:
(gdb) attach system Attaching to system. 0x5b58 in wdbSuspendSystemHere ()
Once in system mode, the entire target system stops. In the example above, the system stopped in wdbSuspendSystemHere( ), the normal suspension point after attach system.
|
|
WARNING:
Not all targets support system mode, because the BSP must include a special driver for that purpose (see 4.7 Configuring the Target-Host Communication Interface). If your target does not support system mode, attempting to use attach system produces an error.
|
||||||||||||||||||
In system mode, the GDB thread-debugging facilities become useful. Thread is the general term for processes with some independent context, but a shared address space. In VxWorks, each task is a thread. The system context (including ISRs and drivers) is also a thread. GDB identifies each thread with a thread ID, a single arbitrary number internal to the debugger.
The command info threads shows what thread ID corresponds to which VxWorks task. For example, immediately after attaching to the system, the info threads display resembles the following:
(gdb) info threads 4 task 0x4fc868 tExcTask 0x444f58 in ?? () 3 task 0x4f9f40 tLogTask 0x444f58 in ?? () 2 task 0x4c7380 + tNetTask 0x4151e0 in ?? () 1 task 0x4b0a24 tWdbTask 0x4184fe in ?? () (gdb)
In the info threads output, the left-most number on each line is the debugger's thread ID. The single asterisk (*) at the left margin indicates which thread is the current thread. The current thread identifies the "most local" perspective: debugger commands that report task-specific information, such as bt and info regs (as well as the corresponding debugger displays) apply only to the current thread.
The next two columns in the thread list show the VxWorks task ID and the task name. If the system context is shown, the single word system replaces both of these columns. The thread (either a task, or the system context) currently scheduled by the kernel is marked with a + to the right of the task identification.
|
|
|||||||||||||||||||
To switch to a different thread (making that thread the current one for debugging, but without affecting kernel task scheduling), use the thread command. For example:
(gdb) thread 2 [Switching to task 0x3a4bd8 tShell ] #0 0x66454 in semBTake () (gdb) bt #0 0x66454 in semBTake () #1 0x66980 in semTake () #2 0x63a50 in tyRead () #3 0x5b07c in iosRead () #4 0x5a050 in read () #5 0x997a8 in ledRead () #6 0x4a144 in execShell () #7 0x49fe4 in shell () (gdb) thread 3 [Switching to task 0x3aa9d8 tFtpdTask ] #0 0x66454 in semBTake () (gdb) print/x $i0 $3 = 0x3bdb50
As in the display shown above, each time you switch threads the debugger exhibits the newly current thread's task ID and task name.
In system mode, unqualified breakpoints (set with graphical controls on the editor window, or in the Debugger window with the break command and a single argument) apply globally: any thread stops when it reaches such a breakpoint. You can also set thread-specific breakpoints, so that only one thread stops there.
To set a thread-specific breakpoint, append the word thread followed by a thread ID to the break command. For example:
(gdb) break printf thread 2 Breakpoint 1 at 0x568b8 (gdb) cont Continuing. [Switching to task 0x3a4bd8 + tShell ] Breakpoint 1, 0x568b8 in printf ()
(gdb) i th 8 task 0x3b8ef0 tExcTask 0x9bfd0 in qJobGet () 7 task 0x3b6580 tLogTask 0x9bfd0 in qJobGet () 6 task 0x3b15b8 tNetTask 0x66454 in semBTake () 5 task 0x3ade80 tRlogind 0x66454 in semBTake () 4 task 0x3abf60 tTelnetd 0x66454 in semBTake () 3 task 0x3aa9d8 tFtpdTask 0x66454 in semBTake () * 2 task 0x3a4bd8 + tShell 0x568b8 in printf () 1 task 0x398688 tWdbTask 0x66454 in semBTake () (gdb) bt #0 0x568b8 in printf () #1 0x4a108 in execShell () #2 0x49fe4 in shell ()
Internally, the debugger still gets control every time any thread encounters the breakpoint; but if the thread ID is not the one you specified with the break command, the debugger silently continues program execution without prompting you.
|
|
|||||||||||||||||||
Your program may not always suspend in the thread you expect. If any breakpoint or other event (such as an exception) occurs while in system mode, in any thread, the debugger gets control. Whenever the target system is stopped, the debugger switches to the thread that was executing. If the new current thread is different from the previous value, a message beginning with "Switching to" shows which thread is suspended:
(gdb) thread 2 (gdb) cont Continuing. Interrupt... Program received signal SIGINT, Interrupt. [Switching to system +] 0x5b58 in wdbSuspendSystemHere ()
Whenever the debugger does not have control, you can interrupt the target system by clicking Interrupt Debugger in the Debug menu, or by keying CTRL+BREAK. This usually suspends the target in the system thread rather than in any task.
When you step program execution (with any of the commands step, stepi, next, or nexti, or the equivalent buttons
or
), the target resumes execution where it left off, which is in the thread marked + in the info threads display. However, in the course of stepping that thread, other threads may begin executing. The debugger may stop in another thread before the stepping command completes, due to an event in that other thread.
|
|
NOTE:
For information about using Tcl to customize the Tornado GUI, see 10.8 Tcl: Debugger Customization. This section is mainly of interest when you need complex debugger macros; thus, you might want to skip it on first reading.
|
||||||||||||||||||
Tcl has two major advantages over the other GDB macro facility (the define command). First, Tcl provides control and looping (such as for, foreach, while, and case). Second, Tcl procedures can take parameters. Tcl, building on the command interface, extends the scripting facility of GDB to allow you to create new commands.
To submit commands to the Tcl interpreter within GDB from the Tornado Debugger window, use the tcl command. For example:
(gdb) tcl info tclversion
This command reports which version of Tcl is integrated with GDB. All the text passed as arguments to the tcl command (in this example, info tclversion) is provided to the Tcl interpreter exactly as typed. Convenience variables (described in GDB User's Guide: Convenience Variables) are not expanded by GDB. However, Tcl scripts can force GDB to evaluate their arguments; see 10.7.3 Tcl: Invoking GDB Facilities.
You can also define Tcl procedures from the GDB command line. The following example procedure, mload, calls the load command for each file in a list:
(gdb) tcl proc mload args {foreach obj $args {gdb load $obj}}
You can run the new procedure from the GDB command line; for example:
(gdb) tcl mload vxColor.o priTst.o
To avoid typing tcl every time, use the tclproc command to assign a new GDB command name to the Tcl procedure. For example:
(gdb) tclproc mld mload
This command creates a new GDB command, mld. Now, instead of typing tcl mload, you can run mld as follows:
(gdb) mld vxColor.o priTst.o
You can collect Tcl procedures in a file, and load them into the GDB Tcl interpreter with this command:
(gdb) tcl source tclFile
If you develop a collection of Tcl procedures that you want to make available automatically in all your debugging sessions, write them in the file gdb.tcl. The GDB Tcl interpreter reads this file when it begins executing. (See 10.8.1 Tcl: Debugger Initialization Files for a discussion of where you can put this file, and of how all the Tornado debugger and GDB initialization files interact.)
The Tornado debugger (CrossWind) includes four commands to help you use Tcl. The first two were discussed in the previous section. The commands are:
|
|
NOTE:
To execute tclproc commands automatically when GDB begins executing, you can place them in .gdbinit directly (see 10.5.1 GDB Initialization Files), because tclproc is a GDB command rather than a Tcl command. However, if you want to keep the tclproc definition together with supporting Tcl code, you can exploit the gdb Tcl extension described in 10.7.3 Tcl: Invoking GDB Facilities to call gdb tclproc in gdb.tcl.
|
||||||||||||||||||
(gdb) tcl puts stdout [expr $x+2] can't read "x": no such variable
(gdb) tclerror TCL verbose error reporting is ON.
(gdb) tcl puts stdout [expr $x+2] can't read "x": no such variable while executing "expr $x..." invoked from within "puts stdout [expr $x..."
Tcl also stores the error stack in a global variable, errorInfo. To see the error stack when Tcl verbose error mode is OFF, examine this variable as follows:
(gdb) tcl $errorInfo
For more information about error handling in Tcl, see C.2.9 Tcl Error Handling.
execute: command
evaluate: expression
(gdb) tcl gdbFileAddrInfo vxColor.c
{239 1058 0x39e2d0 0x39fbfc}
(gdb) tcl gdbFileLineInfo vxColor.c
{239 0x39e2d0 0x39e2d4} {244 0x39e2d4 0x39e2ec} ...
(gdb) tcl gdbIORedirect - grab.it
symbolName [ + Offset ]
(gdb) tcl puts stdout [gdbSymbol 0x20000]
floatInit+2276
symbol: value
This section shows a Tcl procedure to traverse a linked list, printing information about each node.5 The example is tailored to a list where each node has the following structure:
struct node
{
int data;
struct node *next;
}
for (pNode = pHead; pNode; pNode = pNode->next)
...
We imitate this code in Tcl, with the important difference that all Tcl data is in strings, not pointers.
The argument to the Tcl procedure will be an expression (called head in our procedure) representing the first node of the list.
Use gdbEvalScalar to convert the GDB expression for a pointer into a Tcl string:
set pNode [gdbEvalScalar "$head"]
set pNode [gdbEvalScalar "( (struct node *) $pNode)->next"]
Putting these lines together into a Tcl for loop, the procedure (in a form suitable for a Tcl script file) looks like the following:
proc traverse head {
for {set pNode [gdbEvalScalar "$head"] } \
{$pNode} \
{set pNode [gdbEvalScalar "( (struct node *)$pNode)->next"]} \
{puts stdout $pNode}
}
In the body of the loop, the Tcl command puts prints the address of the node.
To type the procedure directly into the Debugger window would require prefacing the text above with the tcl command, and would require a backslash at the end of every line.
If pList is a variable of type (struct *node), you can execute:
(gdb) tcl traverse pList
The procedure displays the address of each node in the list. For a list with two elements, the output would look something like the following:
0xffeb00 0xffea2c
It might be more useful to redefine the procedure body to print out the integer member data, instead. For example, replace the last line with the following:
{puts stdout [format "data = %d" \
[gdbEvalScalar "((struct node *) $pNode)->data"]]}
You can bind a new GDB command to this Tcl procedure by using tclproc (typically, in the same Tcl script file as the procedure definition):
tclproc traverse traverse
Like every other Tornado tool, the debugger's graphical user interface is "soft" (amenable to customizing) because it is written in Tcl, which is an interpreted language. The Tornado API Reference describes the graphical building blocks available. You can also study the Tcl implementation of the debugger graphical interface itself, in host\resource\tcl\CrossWind.win32.tcl.
You can write Tcl code to customize the debugger's graphical presentation in a file called crosswind.tcl. The debugger looks for this file in two places: first under c:\tornado (or whatever directory you specify with the environment variable WIND_BASE), and then in the directory specified by the HOME environment variable (if that environment variable is defined). Use this file to collect your custom modifications, or to incorporate shared modifications from a central repository of Tcl extensions at your site.
Recall that the debugger uses two separate Tcl interpreters. Previous sections described the .gdbinit and gdb.tcl initialization files for the debugger command language (see 10.7 Tcl: Debugger Automation). The following outline summarizes the role of all the Tornado debugger customization files. The files are listed in the order in which they execute:
You can prevent the Tornado debugger from looking for the two .gdbinit files, if you choose, by setting the internal GDB parameter inhibit-gdbinit to yes. Because the initialization files execute in the order they are listed above, you have the opportunity to set this parameter before the debugger reads either .gdbinit file. To do this, insert the following in gdb.tcl:
gdb set inhibit-gdbinit yes
You can use the following specialized Tcl commands to pass control between the two Tornado debugger (CrossWind) Tcl interpreters:
The major use of uptcl is to experiment with customizing or extending the graphical interface. For example, if you have a file myXWind containing experimental Tcl code for extending the interface, you can try it out by entering the following in the Debugger window:
(gdb) tcl upctl source myXWind
By contrast, downtcl and writeDebugInput are likely to be embedded in Tcl procedures, because (in conjunction with the commands discussed in 10.7.3 Tcl: Invoking GDB Facilities) they are the path to debugger functionality from the graphical front end.
Most of the examples in 10.8.3 Tcl: Experimenting with Tornado Debugger Extensions, below, center around calls to downtcl.
The examples in this section use the Tcl extensions summarized in Table 10-3. For detailed descriptions of these and other Tornado graphical building blocks in Tcl, see Tornado API Programmer's Guide or the online Tornado API Reference.
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
As explained in 10.5.2 Selecting Modules to Debug, you sometimes need to tell the debugger explicitly to load symbols for modules that were downloaded to the target using other programs (such as the shell).
Example 10-1 illustrates a Debug menu command Add Symbols to handle this through the graphical user interface, instead of typing the add-symbol-file command.
Example 10-1: Add-Symbols Command
# MENU COMMAND: "Add Symbols", additional entry under "Debug"
menuItemInsert -after {"&Debug" "Do&wnload...\tShift+F6"} \
{"Add Symbols..."} {windAddSyms}
##############################################################################
#
# windAddSyms - called from Debug menu to add symbols from chosen object file
#
# This routine implements the "Add Symbols" command in the Debug menu.
# It prompts the user for a filename; if the user selects one, it tells
# GDB to load symbols from that file.
#
# SYNOPSIS:
# xwindAddSyms
#
# RETURNS: N/A
#
# ERRORS: N/A
#proc windAddSyms {} {
set result [fileDialogCreate -title "Symbols from file" -filemustexist \
-filefilters "Object Files (*.o;*.out)|*.o;*.out|
All Files (*.*)|*.*||" \
-okbuttontitle "&Add"]
if {$result != ""} {
# we violate good taste here by not capturing or testing the result
# of catch, because an error notification appears in the command panel.
catch {downtcl gdb add-symbol-file [lindex $result 0]}
}
}
In C++ programs, one particular named value has special interest: this, which is a pointer to the object where the currently executing routine is a member.
Example 10-2 defines a Debug menu command to launch an inspection window for the value where this points. The Tcl primitive catch is used in the definition in order to avoid propagating error conditions (for instance, if the buttons are pressed with no code loaded) from GDB back to the controlling Tornado debugger session.
Example 10-2: Command to Display C++ this Value
# Insert a separator line after default menu commands
menuItemAppend -separator {&Debug}
# Debug Menu Command: *this - Inspect window on current C++ class (*this)
menuItemAppend {"&Debug"} {"*this"} {
catch {downtcl gdb display/W *this}
}
1: The graphical interface to the debugger has a separate initialization file crosswind.tcl, which runs after .gdbinit.
2: See also the description of wtx-load-path-qualify in 10.5.8 Extended Debugger Variables for another way of managing how the debugger reports load path names to the target server.
3: You can exploit command completion to see a list of all the shell primitives as they appear in the debugger: type "wind-" followed by two tabs.
4: A more restricted form of this command, called gdbEvalAddress, can only evaluate a single expression (constructed by concatenating all its arguments). gdbEvalAddress is only supported to provide compatibility with Tcl debugger extensions written for an older debugger, VxGDB. Use the more general gdbEvalScalar in new Tcl extensions.
5: Keep in mind that for interactive exploration of a list the window (Figure 10-14) described in Watch Window is probably more convenient.