The Ladebug debugger lets you debug your programs at the machine- code level as well as at the source-code level. Using debugger commands, you can examine and edit values in memory, print the values of all machine registers, and step through program execution one machine instruction at a time.
Only those users familiar with machine-language programming and executable-file-code structure will find low-level debugging useful.
You can examine the value contained at an address in memory as follows:
<examine address>
command prints the
value contained at the address in one of a number of formats
(decimal, octal, hexadecimal, and so on).
print
command, with the appropriate
pointer arithmetic, prints the value contained at the address in
decimal.
The <examine address>
command has two main syntaxes.
The following syntax prints a range of addresses by specifying the
beginning and end of the range:
start_address, end_address / mode
If a symbol precedes the slash (/) in an address expression, you may need to enclose the expression in parentheses. For example:
(ladebug) ($pc), ($pc+12) / i
The following command syntax prints a range of addresses by specifying the beginning address and the total number of memory locations display:
start_address / count mode
You can enter memory addresses in decimal or in hexadecimal by
preceding the number with 0x.
The mode
variable determines how the values are displayed. Table 18-1 lists the valid modes.
Mode | Description |
---|---|
d | Print a short word in decimal |
u |
Print a short word in unsigned decimal |
D | Print a long word in decimal |
U |
Print a long word in unsigned decimal |
o | Print a short word in octal |
O | Print a long word in octal |
x
| Print a short word in hexadecimal |
X | Print a long word in hexadecimal |
b
| Print a byte in hexadecimal |
c | Print a byte as a character |
s |
Print a string of characters (a C-style string that ends in null) |
f |
Print a single-precision real number |
g | Print a double-precision real number |
i |
Disassemble machine instructions |
Example 18-1 shows how to disassemble a range of memory.
(ladebug) 0x120001180, 0x120001185 / i [main:4, 0x120001180] addq zero, 0x1, t0 [main:4, 0x120001184] stl t0, 24(sp) (ladebug) 0x120001180 / 2 i [main:4, 0x120001180] addq zero, 0x1, t0 [main:4, 0x120001184] stl t0, 24(sp) (ladebug)
In this example, the same range of addresses was accessed using
start_address
command in both the end_address
syntax and the / count
syntax.
You can use C and C++ pointer-type conversions to display the
contents of a single address in decimal. Using the print
command, the syntax is as follows:
print *(int *)(address)
Using the same pointer arithmetic, you can use the assign
command to alter the contents of a single address. Use the
following syntax:
assign *(int *)(address) = value
Example 18-2 shows how to use pointer arithmetic to examine and change the contents of a single address.
(ladebug) print *(int*)(0x10000000) 4198916 (ladebug) assign *(int*)(0x10000000) = 4194744 (ladebug) print *(int*)(0x10000000) 4194744 (ladebug)
The printregs
command prints the values of all
machine-level registers. The registers displayed by the debugger
are machine dependent. The values are in decimal or hexadecimal,
depending on the value of the $hexints
variable (the
default is 0, decimal). The register aliases are shown; for example,
$r1 [$t0]
.
Example 18-3 shows Digital UNIX Alpha machine-level registers.
(ladebug) printregs $r0 [$v0] = 10 $r1 [$t0] = 1 $r2 [$t1] = 4831844048 $r3 [$t2] = 5368719424 $r4 [$t3] = 0 $r5 [$t4] = 0 $r6 [$t5] = 4396972783304 $r7 [$t6] = 2 $r8 [$t7] = 10 $r9 [$s0] = 337129856 $r10 [$s1] = 337127744 $r11 [$s2] = 4396973344608 $r12 [$s3] = 0 $r13 [$s4] = 5368847640 $r14 [$s5] = 5368753616 $r15 [$s6] = 20 $r16 [$a0] = 1 $r17 [$a1] = 4831835496 $r18 [$a2] = 4831835512 $r19 [$a3] = 4831835848 $r20 [$a4] = 4396981193976 $r21 [$a5] = 5 $r22 [$t8] = 9 $r23 [$t9] = 9 $r24 [$t10] = 4831842472 $r25 [$t11] = 1648 $r26 [$ra] = 4831842828 $r27 [$t12] = 4831842912 $r28 [$at] = 4396981208928 $r29 [$gp] = 5368742064 $r30 [$sp] = 4831835408 $r31 [$zero]= 4831842928 $f0 = 0.1 $f1 = 0 $f2 = 0 $f3 = 0 $f4 = 0 $f5 = 0 $f6 = 0 $f7 = 0 $f8 = 0 $f9 = 0 $f10 = 0 $f11 = 0 $f12 = 0 $f13 = 0 $f14 = 2.035550460865936e-320 $f15 = 4120 $f16 = 0 $f17 = 0 $f18 = 0 $f19 = 0 $f20 = 0 $f21 = 0 $f22 = 0 $f23 = 0 $f24 = 0 $f25 = 0 $f26 = 0 $f27 = 0 $f28 = 0 $f29 = 0 $f30 = 0 $f31 = 0 $pc = 0x120001270 (ladebug)
The stepi
and nexti
commands let
you step through program execution incrementally, like the
step
and next
commands described in
Chapter 9. The stepi
and
nexti
commands execute one machine instruction at
a time, as opposed to one line of source code. Example 18-4 shows stepping at the machine-instruction level.
(ladebug) stop in main [#1: stop in main ] (ladebug) run [1] stopped at [main:4 0x120001180] 4 for (i=1 ; i<3 ; i++) { (ladebug) stepi stopped at [main:4 0x120001184] stl t0, 24(sp) (ladebug) <Return> stopped at [main:5 0x120001188] ldl a0, 24(sp) (ladebug) <Return> stopped at [main:5 0x12000118c] ldq t12, -32664(gp) (ladebug) <Return> stopped at [main:5 0x120001190] bsr ra, (ladebug) <Return> stopped at [factorial:12 0x120001210] ldah gp, 8192(t12) (ladebug)
At the machine-instruction level, you can step into, rather than over, a function's prolog. While within a function prolog, you may find that the stack trace, variable scope, and parameter list are not correct. Stepping out of the prolog and into the actual function updates the stack trace and variable information kept by the debugger.
Single-stepping through function prologs that initialize large
local variables is slow. As a workaround, use the next
command.