PowerAda Debugging Machine-Level Code

From OC Systems Wiki!
Jump to: navigation, search

You can debug at the machine level within the context of an Ada program. Use this capability for debugging assembly or other non-Ada code that does not contain stab string information. When debugging non-Ada code at the machine level, you should have a listing of the corresponding machine code.

Machine-level commands read and write fixed amounts of data in memory and registers, set and remove machine-level breakpoints, execute a specified number of machine instructions, and display other useful machine-level information. All machine-level commands begin with a period.

After executing a machine-level breakpoint or stepping through machine instructions, the debugger does not know the current source position; most source-level operations will not work until a source-level breakpoint has been executed. When finished debugging a foreign language routine, a source-level breakpoint encountered in the Ada program causes the debugger to return to source-level debugging.

Machine Locations

Many debugger commands take a machine location as a parameter. The debugger allows you to specify machine addresses either by a hexadecimal number, a name from the target program's link map, or the contents of one of the target program's registers.

The syntax of machine locations is:

[@] {Address | LinkName | @Register} [{-|+} Offset]

The Address is a string of digits, by default hexadecimal, where the leading digit is always numeric. A leading zero is often used to identify the number as an address, for example: 0FF1240. By default, the debugger reads, writes, moves, or disassembles four bytes of data at the location specified, unless you specify Offset bytes with the Address. In some commands like the .READ, .DISASSEMBLE, and .MOVE, the AddressRange of the format Address .. Address can be used to specify the location, for example 0123A .. 01240.

A LinkName is any string or sequence of letters, numbers, and special characters ("$", ".", and "_") corresponding to a link name generated for the program being debugged. Use quotation marks when specifying a string containing other special characters. The .SYMBOL command displays addresses for link names and link names for addresses and specifications using registers.

A Register is the name of any of the registers:

Register Purpose
c0..c7 Condition-status registers
ctr Count register
f0..f31 Floating-point registers
fpscr Floating-point status register
iar Instruction-address register or program counter
lr Link register
mq Multiplier-quotient register
r0..r31 General-purpose registers
xer Fixed-point exception register

You can access the contents of any machine address and use it as an address by preceding the address with "@".

Here are some examples of machine addresses:

@@iar         -- The contents of the contents of the iar
.lseek +30    -- 30 bytes past the beginning of .lseek
0EFFF73A      -- Absolute address
2#01111010#   -- Equivalent to 7A.


.break Location [Qualifiers][Action]

Sets a breakpoint within any code area in the application, including the elaboration code. If a source line is associated with the Location at which a machine-level breakpoint is set, the source line is reported along with the machine address when the breakpoint is reached. Otherwise, only the machine address is displayed. The remaining syntax is the same as for the BREAK command. = See "Setting and Displaying Breakpoints: BREAK". Here are some examples:

.break 40100 -- Set a breakpoint at a machine address
.break 0A34 then .read 0AEFB -- Action after breakpoint
.break 010 COUNT 5 -- Breakpoint the 5th time only
.break @iar + 8
   -- Breakpoint 8 bytes past current location
.break .lseek -- Break at a system routine



Shows, for each level in the call chain, the values of the frame pointer (FP) and instruction address register (IAR), and also a decoding of the instruction address register into a link name plus an offset.


0= 47,sec/show1_pkg (in_only_proc)
     FP=0DFFFBBC  IAR=0000260C => show1_pkg__2 + 4
1= 93,sec/show1 (show1)
     FP=0DFFFBFC  IAR=00002566 => show1__2 + 284


.disassemble {AddressRange | Address [Integer]}
        [source | nosource]
.disassemble {+ | - [Count]} [source | nosource]

Displays the specified code in symbolic form. The AddressRange or Address specifies the location of the instructions to display. The debugger interprets memory contents at the specified address as a machine instruction, displaying the operation code and operands for each instruction in turn.

Count is an integer which specifies the number of bytes to disassemble and display. The default is 4.

The SOURCE or NOSOURCE qualifier causes the debugger to insert or omit, the display of the Ada source lines intermixed with the disassembly display. The default is NOSOURCE.

The .DISASSEMBLE + and .DISASSEMBLE - variants of this command disassemble either forwards (+) or backwards (-) from the last point of disassembly. A previous .DISASSEMBLE command must have been entered before entering this form of the command.

The optional Count is also remembered and will be used for subsequent disassembles if no count is specified. The SOURCE or NOSOURCE option is remembered also.


.disassemble 40100
.disassemble @iar + 8
   -- Display instruction 8 bytes past current location
.disassemble .lseek 200
   -- Disassemble 200 bytes in .lseek routine
.disassemble + source
   -- Display from point of last disassembly;
   -- include Ada source
.disassemble d4
   -- Display register d4 as an instruction


.move Location Location [Number]

where a Location may be an AddressRange, Address, or Register. Moves the contents of a memory or register location to another memory or register location. The AddressRange and Address are machine addresses. You can move instructions in the following ways:

Address to Address
AddressRange to Address
Address to AddressRange
Address to Register
Register to Address
Register to Register

The Number qualifier specifies the number of bytes to move. The default is four bytes when moving from memory to memory, or the size of the register when moving from a register.

If you write over the program or data that the debugger is depending upon, the results are undefined.

You can use the object:LAYOUT display of the ? command to get information on the machine representation of Ada objects.

.move A0 D6        -- Register-to-register move
.move 0A0 0D6 8    -- Address-to-address move of 8 bytes
     --  (addresses start with a decimal digit)
.move D6 0345f      -- Register-to-address move
.move 0123a..01240 07654 -- AddressRange-to-Address


.read {Register |AddressRange | Address[Count]} 
      [Format] | [ as TypeSpec | RegisterClass]

Reads machine memory or registers. The AddressRange and Address are machine addresses. For more information on machine addresses and registers, see "Machine Locations". The Count qualifier specifies the number in (decimal) of bytes to read. The default is four bytes.

The Format qualifier is the format of the data to be read. The following formats are supported: ADDRESS, HEXADECIMAL (default), ASCII, BINARY, and OCTAL. ADDRESS displays each four bytes of the data as an address and shows the link name that corresponds most closely to that address. ASCII displays the data in hexadecimal along with the ASCII character which corresponds to the value of each byte. BINARY, OCTAL, and HEXADECIMAL displays the data as digits in the specified base. The optional BYTE, WORD, or LONG may be used to provide spacing in the display. The Format you specify becomes the new default format. Subsequent data displays use that format until you specify a different format.

The TypeSpec is an Ada type. The data at the specified address is to be read as if it were this type.

The RegisterClass allows the viewing of multiple registers at once. The following register classes are defined:

R = all general purpose registers
C = all condition registers
F = all floating-point registers

Here are some examples of .READ:

.read 0FFF1240
   -- Read default (4) number of bytes at address
.read  0E789520 16 -- Read 16 bytes at address
.read  iar         -- Read the program counter
.read  @rl 40 address -- Display addresses on the stack
.read  0EFFF75E as device
   -- Read the data as if it is a device type


.registers [RegisterClass]

Displays machine registers. The RegisterClass qualifier is an identifier for a group of registers. Without the qualifier, .REGISTERS displays all the machine registers. With RegisterClass, the debugger displays all registers that begin with that letter. The following register classes are defined:

R = all general purpose registers
C = all condition registers
F = all floating-point registers

For example:

.registers -- Display all the registers
.registers r -- Display R0 through R31


.skip [Count] [source | nosource] 
        [disassemble | nodisassemble]

Steps through the number of machine instructions specified by Count. The default is one. Subprogram calls will be stepped over without entering the subprogram.

SOURCE or NOSOURCE selects whether or not to display an Ada source line when the step operation reaches an address that corresponds to a source location. DISASSEMBLE or NODISASSEMBLE selects whether to display each instruction in symbolic form as single-stepping proceeds. The default is NOSOURCE and NODISASSEMBLE.

The SOURCE or NOSOURCE and DISASSEMBLE or NODISASSEMBLE options remain set for subsequent .SKIP operations.

A task switch may occur during a .SKIP command. Control returns to the user when the target reaches the instruction following a subprogram call. At this point, the target may be in a task different from the current task before the .SKIP.

After machine instructions have been stepped, the debugger may not know what the current source position is. Most source level operations do not work until a CONTINUE, STEP, or SKIP is performed.

.skip 3 disassemble
        -- execute 3 machine instructions;show disassembly


.step [Count]

Steps through machine instructions. Count provides the number of instructions to step. By default, the debugger steps one instruction at a time. Breakpoints encountered while stepping through instructions will interrupt program execution.

After machine instructions have been stepped, the debugger may not know what the current source position is; most source level operations will not work until you perform a CONTINUE, STEP, or SKIP.

For example:

.step 3   -- Execute 3 machine instructions


.symbol Address | LinkName

Displays the address corresponding to a link name or the link name corresponding to an address. The debugger looks in the target program's link map to find the symbol name that is closest to the machine address specified by Address. It displays this name along with the offset from that symbol to the specified address. For more information on machine addresses, see "Machine Locations".

Here are some examples of .SYMBOL:

.symbol .lseek -- Display the address of .lseek
.symbol @iar
   -- Display link name closest to current location


.trap [Signal [then Statement] | all]

Halts the program when a signal is received.

You specify the signal and an optional debugger command to execute when the program receives the signal. While the program is stopped, you can examine variables, set breakpoints, and so on, then resume execution using the CONTINUE command. You can specify Signal as either a mnemonic name, or the number it represents. The mnemonic names for AIX signals are the same as for the AIX signal routine; you can see a list by entering HELP .TRAP.

If you do not specify any signal, the .TRAP command displays the list of signals the debugger is trapping, along with any associated actions.

If ALL is specified, the debugger sets up a trap for all signals. The debugger does not allow an action to be associated with this command. You can use the .UNTRAP command to selectively remove traps on some of the signals.

The debugger may send the signals SIGILL, SIGTTOU, and SIGTTIN to the target program to perform certain operations. Attempts to trap these signals will not succeed. For the signals SIGHUP, SIGABRT, SIGEMT, SIGKILL, SIGBUS, SIGSYS, SIGPIPE, SIGSEGV, SIGTERM, SIGURG, SIGSTOP, SIGXCPU, SIGPRE, and SIGXFSZ, the debugger always halts and returns control to you.


.trap sigio
.trap 23     -- Alternate specification of SIGIO
.trap sigchld then put_line "Child activity!"
.trap ALL


.unbreak [Address] all | Address (BreakpointName)

Removes machine-level breakpoints at a specified machine address. For more information on machine addresses, see "Machine Locations". Descriptions of the other qualifiers appear in "Removing Breakpoints: UNBREAK". If more than one breakpoint is set at a location, the command can include the same conditions used to set the breakpoint or else the number of the breakpoint. Using .UNBREAK ALL removes all machine-level breakpoints defined for an address. Using UNBREAK Address ALL removes all machine-level and source-level breakpoints set in the program.

Here are some examples of .UNBREAK.:

.unbreak 040220
   -- Clear a machine breakpoint
.unbreak 0AEF all
   -- Clear all breakpoints at address 0AEF
.unbreak 0AE3 (3)
   -- Remove the third breakpoint at 0AE3
.unbreak 0AE3 count 4
   -- Remove a breakpoint set by .break 0AE3 count 4


.untrap Signal | all

Removes signal trapping where Signal is either an identifier or an integer identifying an AIX signal. Signal specifies which signal the debugger should stop trapping. ALL indicates that the debugger should no longer trap any signals.

After executing this command, the debugger will no longer halt execution of the target program when the program receives the specified signal. For example:

.untrap sigio 
.untrap 23     -- Alternate specification of SIGIO
.untrap all

See also ".TRAP".


.write Register Value | Address Value [Count]

Writes a value into a memory address or register. The Register and Address qualifiers specify the location to change. For information on machine addresses and registers, see "Machine Locations". The Value is the value to be written to the specified location. It is a hexadecimal number, but must begin with a number, not a letter. The Count is a decimal number that specifies the number of bytes to change. The default is four bytes.

In general, changing memory or registers with .WRITE can lead to unpredictable results. Extreme care is required with these commands. You should be very familiar with the Ada run-time model and with debugging at the machine level.

Here are some examples of .WRITE:

.write iar 10000 -- change the program counter
.write 22000 2ff0 -- Write 2ff0 into 4 bytes at 22000
.write 22000 2ff0 2 -- Write 2ff0 into 2 bytes at 22000