![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Machine Level Debugging The Apex environment lets you to perform machine-level debugging by displaying assembly level code, and by displaying and modifying registers and memory locations.
The following topics are covered in this chapter:
- Displaying Assembly Language
- Setting Breakpoints at Instructions
- Stepping Machine-Code
- Register Operations
- Memory Location Operations
Displaying Assembly LanguageThere are a number of ways to display the assembly language code of your program.
If Show Source mode is active and your code is being displayed in the Source pane of the Debugger window, it is possible to display disassembled machine instructions interspersed with source code, if available. Do this by either selecting the Disassembly hot button in the Toolbar, selecting the Debug > Visit Assembly in an editor window, or by positioning your cursor anywhere in the Source pane and entering I (capital I). To return to the source display, deselect the Disassembly button or enter I again in the Source pane. An example of a Source pane in disassembly mode is illustrated in Figure 5.
Figure 5 Disassembly Mode
![]()
Note that in Disassembly mode, all Source pane icons such as breakpoint stop signs are displayed. Breakpoint commands entered while this mode is active cause the breakpoints to be set at the highlighted instruction.
If you are using the non-GUI debugger, viewing of assembly code can be done in several ways. If you are using screen-mode, the debugger supports two sub-modes, instruction and source. The I (uppercase i) command toggles the sub-mode, switching from one to the other. In source mode, the source window displays program source code and the s, i, and o commands single-step at the source statement level.
Source mode is the default. In instruction mode, the source window contains disassembled machine instructions, interspersed with source code, if available. Although the source window contains machine instructions, control it with all regular debugger commands. In this mode, the s, i, and o debugger commands are interpreted as their machine instruction counterparts, the si, ii, and oi commands. The b command is interpreted as bi, setting a breakpoint at the machine instruction under the cursor. All searching and window control commands are available.
If screen mode is not being used, the li and wi commands display assembly code.
li
List disassembled instructions
Syntax
li [expression
|decimal_number
] [,number
]
Arguments
- expression
Expression defining the instruction address where listing starts.
- decimal_number
Decimal number indicating the line number at which listing is to start.
- number
Description
li lists the specified number of lines including disassembled machine instructions interspersed with source lines. This listing is displayed in the Command/Log Window. If a decimal line number is given, disassembly starts with the first line of code generated by or after that line. If expression is specified, the listing starts with the instruction at that address.
If * appears next to the instruction address, that instruction is the home position for the current frame. Use * as the expression|decimal_number to start disassembly at the home position instruction.
If an equals sign appears after the address, a breakpoint is at that instruction.
If [expression|decimal_number] is omitted, the listing begins with the line or instruction following the most recently l'd or li'd line or instruction or the home line or instruction if no l or li command has been given for this file.
The display contains program source lines interspersed among disassembled machine instructions. The first instruction is preceded by the source line that generated it except when no source is available or disassembly begins mid-statement.
The debugger does not, in one li command, disassemble across a source file boundary, no matter how many lines it is instructed to print. It stops the display at the source file boundary. The next li command with no parameters starts at the beginning of the next source file.
In Screen Mode
Precede this command with : and follow with Return. You can show disassembly in the upper window with Instruction and Source Sub-modes.
In screen mode, toggle the upper window (source window) to display either source code or disassembled machine instructions. The screen mode command, I (uppercase i), performs this toggling.
wi
List window of disassembled and original code
Syntax
wi [expression
|decimal_number
] [,number
]
Arguments
- expression
Expression defining the instruction address at the center of the listing.
- decimal_number
Decimal number indicating the line number at the center of the listing.
- number
Description
The term "window" is used here to mean a section of source text. Do not confuse it with the windows of the debugger.
wi prints a window containing the specified number of lines including disassembled machine instructions interspersed with source lines. This window appears in the Command/Log Window. If a decimal line number is given (decimal_number), this line is at the center of the window. If expression is specified, the instruction at the address given by expression is at the center of the window.
If an equals sign appears after the address, a breakpoint is at that instruction.
If [expression|decimal_number] is omitted, the display is centered on the line or instruction following the most recent w'd or wi'd line or instruction or the home line or instruction if no w or wi command is given for this file. The home position is centered in the window if [expression|decimal_number] is *.
The display contains program source lines interspersed among disassembled machine instructions. The first instruction is preceded by the source line that generated it except when no source is available or disassembly begins mid statement.
The debugger does not, in one wi command, disassemble across a source file boundary, no matter how many lines it is instructed to print. It stops the display at the source file boundary.
In Screen Mode
Precede this command with : and follow with Return.
In screen mode, the upper window (source window) toggles to display either source code or disassembled machine instructions. The screen mode command, I, performs this toggling.
Setting Breakpoints at InstructionsIf assembly code is displayed, entering the standard breakpoint commands have the effect of setting the breakpoint at the instruction level. You can also use the bi command in the Command Pane or non-GUI debugger to set a breakpoint at an instruction.
Stepping Machine-CodeAll stepping commands available in source mode are also available in Assembly mode. These are as follows:
Register OperationsApex offers the capability of displaying and modifying the contents of regular and floating point registers.
Obtaining Information from Registers
There are several ways to obtain information from registers. You can:
- View the values of all registers by displaying the Registers or the Floating Point Registers window.
- Entering the reg command in the Command Pane or the non-GUI debugger. Using the -f option to the reg command displays the floating point registers.
- View the contents of a register by executing the Debug > Show... command and specifying the name of the register preceded by a dollar sign (for example, $g0, $14).
- View the contents of a register by executing the p command and specifying the name of register preceded by a dollar sign (for example, p $g0).
Displaying the Registers Window
The Registers window displays the current values of all the registers in the current context. When you change the context to a different task or frame, the registers for the specified frame are displayed. Each register name is followed by its hexadecimal value.
The Registers window, shown in Figure 6 , is platform-specific. This example is from a Sun SPARC. The entries shown in blue are registers whose values have changed with the last execution command given to the program.
Figure 6 Registers Window
![]()
There are several ways to display the Registers window. You can:
- Execute the Windows > Registers command from any debugger window
- Execute the Debug > Window > Registers command from an editor window
Displaying the Floating Point Registers Window
The Floating Point Registers window displays the current values of all the floating point registers in the current context. When you change the context to a different task or frame, the registers for the specified frame are displayed. Each register name is followed by its decimal value.
The Floating Point Registers window is platform-specific. The example shown is from a Sun SPARC
![]()
There are several ways to display the Floating Point Registers window. You can:
- Execute the Windows > Floating Point Registers command from any debugger window
- Execute the Debug > Window > Floating Point Registers command from an editor window
- To display the contents of the floating point registers in the Log Area or with the non-GUI debugger, use the reg f command.
Displaying the Contents of a Single Register
In the Debugger window or the non-GUI debugger, you can display the contents of a register directly by entering p $register_name command.
You can also use the following steps to display the value of any register:
- 1 . Execute the Debug > Show... command from any debugger window or Control-Debug > Show from an editor window.
- 2 . When the Show Data dialog box appears, enter the name of the register you want to display. Note that register names must begin with a dollar sign ($).
- 3 . Click OK to close the Show Data dialog box.
reg
List the current machine register contents
Syntax
reg [all|f|s] reg [all|f|d|s] (MIPS only)
Arguments
- all
Display the contents of all registers.
- d
Display the contents of the floating point registers as 64 bit floats. (MIPS only)
- f
Display the contents of the floating point registers as 32 bit floats. (MIPS only)
- f
Display the contents of the floating point registers. (except MIPS)
- s
Description
reg lists the contents of registers as they are when the program stops.
reg f
lists the floating point coprocessor registers if the implementation supports a coprocessor.
On the SPARC, display floating point registers as single-precision floats using p $fnumber ($f2), as double-precision floats using p $dnumber and as extended reals using p $enumber.
Display individual registers using the p command with the name of the register preceded by a dollar sign ($).
For MIPS processors, single precision registers are displayed using p $fnumber ($f0), Double-precision registers are displayed using p $dnumber ($d0).
In Screen Mode
Precede this command with : and follow with Return.
Modifying the Value of a Register
To modify the value of a register, execute the Debug > Modify Register command from any debugger window. Apex opens the Modify Register dialog box.
After you execute this command, the name of the register and its new value are displayed in the Log area of the main window.
After you execute the Modify Register command, the old and new values are displayed in the log area. Note that the new value is listed first. The new value is also displayed in blue in the Registers window.
![]()
In the non-GUI debugger, or command pane, change the value of a register using an assignment statement described in Modifying Data Using Assignment Statements.
Memory Location OperationsDisplaying Memory Locations
You can display the contents of memory information in several ways:
- To view the actual contents of range of memory locations, display the Memory window.
- To view the contents of memory locations in which an object is stored, set the Show Location option in the Show Data dialog box and request information for an object that is not stored in a register. The Memory window displays the current values of the selected memory locations.
- Use the Command Pane or non-GUI debugger. Memory display commands have the following syntax:
[p]hexadecimal_address
[:display
][number
] [p]decimal_address
:display
[number
] [p]name
:display
[number
]
These commands are discussed in detail in Display Memory - Command Line Interface.
Displaying the Memory Window
To display the Memory window, execute the Windows > Memory command from any debugger window or execute the Debug > Window > Memory command from an editor window. When the Memory dialog box opens, no data is displayed because the debugger has not been instructed to display any particular range of memory locations.
When an empty Memory dialog box opens because no memory-display instructions have been issued, Apex also opens the Memory Bounds dialog box. You can then enter the beginning location, the range of the memory locations you want to display, and the bytes per unit to display.
The Memory window displays the contents of specific memory locations. On each line, the first entry is the memory address in hexadecimal. This entry is followed by the contents of memory starting at that address. The memory contents are displayed in hexadecimal notation. By default, 128 bytes are displayed, 16 bytes per line. An ASCII string is appended at the end of each line.
This display can be modified by using the View > Bounds command from the Memory window.
The Memory window displayed after entering a starting address is illustrated below.
![]()
Display Memory - Command Line Interface
The following syntaxes can be used to display the value of a memory location. If the debugger GUI is being used, enter these in the Command Pane.
Syntax
[p]hexadecimal_address
[:display
][number
] [p]decimal_address
:display
[number
] [p]name
:display
[number
]
Arguments
- decimal_address
Memory address in decimal notation.
- display
One- or two-character code indicating how to display the contents of the memory address. See the Description section for a listing of these codes.
- hexadecimal_address
Memory address in hexadecimal notation (begins with a leading 0).
- name
Ada or C/C++ object to display. This can be a complex expression. name can be a register name of the form $register_name in which case the contents of the register are displayed.
- number
Description
Use the p command to display memory. In this form of the p command, you supply an address on the left side of the : and a format on the right side. The optional number field is used to repeat that display at successive memory locations.
The address you supply can be a simple number (0123:LX), a variable name (name:m), or an expression ($pc + 4). The format can be one of those described below under display, or a typedef defined in your program. If you use a name, it is evaluated and the address of the named object is used.
The precedence of operators is that the C unary operators * and & have the highest precedence, followed by the : in display memory and then the binary operators. For example, if the source is C, the debugger evaluates p *address:display as p (*address):display. Use parentheses to establish a different precedence, p *(address:display).
For binary operators, p $pc + 4:m is evaluated, by default, as p $pc + (4:m). Use parentheses within binary expressions preceding the : to avoid ambiguity, p ($pc + 4):m.
display consists of a length character alone, a length character and a format character or a format character alone. The default is shown in brackets.
You can enter number in either decimal or in hexadecimal with a leading 0. If the leading digit of a number is a zero, the debugger assumes it is a hexadecimal number (0123 or 0F2).
number determines how many values are displayed. The address is advanced by a number corresponding to the length specified. For example, to display 8 16-bit hexadecimal values starting at address 01A9A, type the following command:
p 01A9A:Wx 8
Note: This is just an example address and may not be valid for your processor. It can be replaced by an address applicable to your system.
The debugger responds with this output:
01A9A: 2074 6865 204E 2071 7565 656E 7320 7072
The default length is 32 bits. The default format is either decimal, octal or hexadecimal, depending on the setting of the set obase number command. The default count is 1. Specifying an address of the form hexadecimal_number displays 32 bits in the current output base. The following command example displays two lines each, beginning at address 01A9A. Each line is displayed in hexadecimal format followed by an ASCII string:
p 01a9a:m 2
The debugger responds with this output:
01A9A: 20 74 68 65 20 4E 20 71 75 65 65 6E 73 20 70 72 "the N queens pr" 01AAA: 6F 62 6C 65 6D A 0 0 0 1 0 0 0 1 0 0 "oblem.........."Even though they cannot be typed in directly by the user, the debugger can display bit addresses using the notation hex_byte_addr.bit_offset. For example, given the following declarations:
type boo is array(1..8) of boolean; pragma pack (boo); abc: boo := (true, true, false, false, true, true, false, false);
the following can be displayed:
>abc:b 07fffc620: 11001100 01000001 10011011 10000100 00010000 00000101 >abc(1):b 07fffc620.01: .1001100 01000001 10011011 10000100 00010000 00000101 >abc(2):b 07fffc620.02: ..001100 01000001 10011011 10000100 00010000 00000101 >abc(3):b 07fffc620.03: ...01100 01000001 10011011 10000100 00010000 00000101
typedef struct { unsigned int a : 1; unsigned int b : 1; unsigned int c : 1; unsigned int d : 1; unsigned int e : 1; unsigned int f : 1; unsigned int g : 1; } flag_type; flag_type flags = {0,1,1,0,1,1,0};
the following can be displayed:
>p flags STRUCT flag_type a :00 b: 01 c: 01 d: 00 e: 01 f: 01 g: 00 >flags:b 020be70L 01101100 00000000 00000000 00000000 00000000 00000000 >flags.b:b 020be70.01: .1101100 00000000 00000000 00000000 00000000 00000000 >flags.c:b 020be70.02: ..101100 00000000 00000000 00000000 00000000 00000000
The :a format is useful for finding the address of any name expression. For example, given the array My_Array, the command
p My_Array:a
displays the address of the first element. This is equivalent to typing p &My_Array. You can use the :a format for procedure names, simple variables, fields of a structure, etc
Note: Typing a plain decimal number moves the current position to that line number. Memory displays require the number to be followed by a colon and a display letter. The p command symbolically displays the value of variables or name expressions; a command consisting of only a name expression is a syntax error.
In addition to the lengths and formats of display listed above, the debugger can display memory at a given address using a type declared in the user Ada or C/C++ program. For example, if Task_Block is a complex structure, p 080040:TASK_BLOCK displays the entire structure at address 080040.
You can display the structure with a:
p (080040:TASK_BLOCK).NEXT_TASK
As another example, consider these Ada type declarations:
type task_block_ptr is access task_block;
type queue; type queue_ptr is access queue;
type queue is record next: queue_ptr node: task_block_ptr; end record
If the user is breakpointed in a subprogram where the register with the name rn holds a Queue_Ptr value, the following expressions can be typed:
After an object is created using the p address:type syntax, use it in any expression where such an object is legal.
Note: First, the debugger checks the specified type against the debugger basic display values. If a match exists, it uses the basic value, even if the program contains a declared type of the same name. The debugger display values are listed above and are displayed as part of the diagnostic if an unrecognized value is used.
Applying a Type to an Address
The debugger also has the ability to display an object in memory when no visible variable points to that object.
There are times when debugging that you either have only the address of an object because you are debugging a machine code routine, or are in a location in your program where the debugger cannot determine with certainty where an object is. In the latter case, you can often determine the address of the object by examining the machine instructions and/or register contents. In addition, there is a convenient way for you to display your object using just its address and type mark.
The syntax of the debugger command is an extension of the syntax for examining memory. Instead of supplying a format specifier such as L or B, you supply the type mark (Ada) or name (C/C++), which must be visible according to the standard visibility rules. Consider the following Ada example:
package dashboard is ... type speedometer_t is record speed: speed_t; trip_counter: miles_t; odometer: miles_t; end record;
Suppose you know that the address of an object of this type is 16#100A48#. To display the object you would simply enter the command:
>0100a48:dashboard.speedometer_t
Further, if the object is located at an offset of 16 off of register s0 (This is just an example register and may not be valid for your system but the process is the same on all systems), you could either get the address in register s0, add 16 then use the method above, or more simply type:
>*($s0+16):dashboard.speedometer_t
This expression is evaluated as follows:
the * means "indirect", that is, read the memory whose address is $s0+16. The 32 bits of memory at that location are the address that is used to display dashboard.speedometer_t.
To display an object as another type, use the same command as above only substitute the name of the object for the address on the left. For example, if there is an object declared in the program as
foo:speed_t
and you want to display foo as type Miles_T, simply enter the command
foo:dashboard.miles_t
and the debugger displays the object foo as type Miles_T.
Note: The "typecast" operation, :, binds most tightly. If you have an expression to the immediate left of the :, you must use parentheses to typecast the entire expression. For example, if you type
> sym + foo:newtype
foo is recast to newtype. To recast sym+foo, you must surround the expression with parentheses.
> (sym + foo):newtypeTypecasting is available in Native debuggers.
For example, in X-window applications, you may want to look at the fields of a widget. The type Widget is defined as:
typedef struct _WidgetRec { CorePart core; } WidgetRec, *Widget;
Even though all the widget variables declared are used as though they are type Widget, in fact they are more complex structures.
For example, when a label widget is created, it returns a pointer to a Labelrec:
typedef struct _LabelRec { CorePart core; SimplePart simple; LabelPart label; } LabelRec, *LabelWidget;
If a variable Label_W has been declared as
label_w: Widget
p *label_w
displays only the fields in Corepart of the widget. To display ALL the information about this label Widget, the variable can be typecast:
p *(label_w: LabelWidget)
p (*label_w): LabelRec
As with Ada typecasting, an address may be used instead of a variable name.
There are some special restrictions with C/C++ typecasting:
- The only types that can be used are the C/C++ basic types (e.g. int, double, char, etc.) or names defined by typedefs. To use a structure, declare a typedef as shown above for Widget.
- By default, the debugger only searches for typedef's defined in the current file or in header (.h) files included in the current file. If you wish the debugger to search all the symbolic information in the entire executable, use the debugger set command:
set c_types global
Modifying Contents of Memory Locations
To modify the data in a memory location, execute the Debug > Modify Memory command. Apex displays the Modify Memory dialog box.
Another way to modify the data in a memory location is to execute the Debug > Modify Data command from an editor window. When modifying the value of a memory location via Debug > Modify Data, the Memory dialog box is not updated automatically. To update the Memory window, click the View > Refresh command in the Memory window.
You can also modify memory through the Command Pane or non-GUI debugger by entering the memory address to be modified, then the new value. For example,
>80020082 := "this is it"
Rational Software Corporation http://www.rational.com support@rational.com techpubs@rational.com Copyright © 1993-2002, Rational Software Corporation. All rights reserved. |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |