When SCODB is awaiting input, it displays the following prompt:
debugn:m>
SCODB has a history mechanism similar to that of ksh(1). Pressing <Esc> puts the current input line into edit mode, where vi-style editing can be used.
To configure scodb:
Be sure that the driver you are debugging is also linked into the kernel.
The newdebug command can also be issued in the kdb.rc file, which will cause scodb to automatically come up first when you enter the debugger.
Any of the described SCODB commands can now be executed, including the newterm command which allows the input/output device to be switched. The following command switches the input/output device to COM1:
newterm 0 iasyLikewise, the following command switches input/output control back to the console:
newterm 0 kd
Note the following about issuing commands:
The simplest expressions that can be given to SCODB are hexadecimal numbers:
debug0:1> D012
D012
Decimal input may be given by preceding the number with a dollar
sign:
debug0:2> $128
80
Octal input may be given by the number with a leading zero:
debug0:3> 040
20
debug0:4>If not in the kstruct file, data symbols are assumed to be of type int, and text symbols are assumed to be of type int( ) (function returning an integer). This type can be modified using the declare command (see ``Displaying symbol and expression types''), or by the symbol being present in the kstruct file.&u
E0000000 u data debug0:5>&read
D00A46D0 read text
The value of a symbol with a value type (in other words, not a structure or union) can be obtained quite simply:
debug0:6> sysdump_selective
1
type expression /* Show type of expression */ declare | dcl C declaration /* Give a system variable a type */ undeclare | undcl [*|variable...] /* Undeclare system variables */It can be useful to determine the type of an expression in case there is some question. For instance, if a symbol is not defined in the kstruct file, it will hold a default type:
debug0:1> type u
"u" is int
But, if it is defined in the kstruct file,
it is shown with the proper type:
debug0:2> type u
"u" is struct user
If the type of a symbol is not defined
in the kstruct file
and it would be useful to use the symbol properly,
use the declare command
to override the original type assigned to the symbol:
debug0:3>It is not necessary to specify the number of elements when declaring an array.type cdevsw
"cdevsw" is int debug0:4>declare struct cdevsw cdevsw[3C]
debug0:5>type cdevsw
"cdevsw" is struct cdevsw [3C]
Use undeclare to return the type of a symbol to its original state, if it is not in the kstruct file:
debug0:6>This is usually done to free space in the declaration table.undeclare cdevsw
debug0:7>type cdevsw
"cdevsw" is int
undeclare can also be used to prevent SCODB from printing the return value after a function call. For example:
debug0:8>patch_nop(&main)
1 debug0:9>declare void patch_nop()
debug0:10>patch_nop(&main)
debug0:11>
debug0:7>Unlike in C, pointer arithmetic is no different than integer arithmetic.1+2
3 debug0:8>(1+2)*3
9
The C pseudo-function sizeof is available and supports all integral data types.
If, given its type, a symbol is a C type lvalue, the symbol may appear on the left side of an assignment expression, and the increment/decrement operators can be used on it. For example:
debug0:9>Types are not checked on assignment.debugkey = 0
0 debug0:10>i++
0 debug0:11>++i
2
SCODB treats expressions of void type the same as it treats those of int type, except that if given an expression of void type, no value is printed.
var [name initial value] /* Create debugger variables */ unvar [*|variable...] /* Remove debugger variables */When debugging, certain values come up time and time again, and re-entering the value each time it is needed can be tedious and gives great opportunity for error.
Use the var command to create a variable with an initial value and type:
debug0:1>Variables are also useful for counting iterations in breakpoint commands:var ino (struct inode *)D01401A0
debug0:2>d $ino
...
debug0:3>The number of variables available is a configurable parameter. To reuse variables, unset them using the unvar command:var x 0
debug0:4>$x
0 debug0:5>++$x
1 debug0:5>++$x
2
debug0:9>unvar may be given the ``'' argument, which clears all variables currently set.unvar x
debug0:10>$x
Error: Unknown variable
debug0:14>$x
0 debug0:15>++$x
1
The address of a breakpoint can be obtained by using a hash symbol (#). Though breakpoint addresses are considered lvalues, be extremely careful when modifying their values.
Registers can be accessed by using a percent (%) in front of the register name, and are considered lvalues (though extreme care should be used in modifying them):
debug0:12>The registers are:%eax
FFFFFFFF debug0:13>%eax = 0
0
General registers | |
---|---|
eax | function return value |
ebx | general use |
ecx | general use, counter |
edx | general use |
ebp | stack frame base pointer |
esp | kernel stack pointer |
uesp | user process stack pointer |
esi | general use, source index |
edi | general use, destination index |
Segment registers | |
cs | code |
ds | data |
es | extra data |
fs | extra data |
gs | extra data |
ss | stack |
Memory management registers | |
gdtr | Global Descriptor Table Register |
ldtr | Local Descriptor Table Register |
idtr | Interrupt Descriptor Table Register |
tr | Task register |
Control registers | |
cr0 | system control flags |
cr1 | unused |
cr2 | page fault linear address |
cr3 | page directory base |
Other registers and pseudo-registers | |
eip | location processor is executing code |
efl | flags |
trap | system trap number |
proc | processor running on |
The stack segment register, memory-management and control registers,
as well as the pseudo-registers trap
and proc
,
are not considered lvalues.
r [-t][address] /* Display registers */
Specifying the -t option toggles between two different forms of register display.
If an expression is of type char, unsigned char, short, or unsigned short, the appropriate amount of information is printed.
To modify output, a calculator input line is preceded by an open-curly-bracket ({), which is followed by a list of specifiers and ended with a close-curly-bracket (}). Strings may also be given to output before or after the value is given; normal character escapes as accepted by echo(1) are allowed. The specifiers are:
? | list output modifiers |
> | no value output |
b | output value as a byte |
s | output value as a short |
2 | output value in binary |
o | output value in octal |
d | output value in decimal |
: | output string |
Value
and to
have the string ok
output after the value,
with the value in decimal:
debug0:24> {d:Value:ok}value
Value 10 ok
Note that expressions of type void have
an output length of zero (no value is given).
Breakpoints are normally set in code, so that when a given instruction is reached, execution is suspended and the debugger is called. The breakpoint occurs before the instruction is executed. These breakpoints are set by replacing the first byte of the instruction at the breakpoint address with an INT 3 instruction (0xCC), remembering the replaced byte. When a breakpoint occurs, the byte is restored so that the instruction can be executed.
Breakpoints can also be set such that the debugger will be invoked on a data reference. In this case, the breakpoint happens after the data reference. Data reference breakpoints are implemented using the processor's four debug registers, and may not be used when an ICE (In-Circuit Emulator) is in use on the system.
Commands may be given to the debugger to be executed when a breakpoint is encountered. If the debugger is in control after the breakpoint (in other words, the breakpoint commands did not call quit), the debugger displays the breakpoint value and name.
Use the breakpoint commands listed here to set, list, and clear breakpoints (see ``Setting breakpoints'', and``Listing breakpoints'', ``Clearing breakpoints''):
bc addr|*|DRn /* Breakpoint clear */ bl [name] /* Breakpoint list */ bp [[rwi][b|s|l]|x]] address [condition value] /* Breakpoint set */ bp mod address /* Modify breakpoint commands */ bp name address /* Breakpoint set with name */ bp [en|dis] address /* (En)able or (Dis)able previously set breakpoint */
bp addressis used to set a breakpoint at address, unless address is in ROM, in which case the data breakpoint form must be used:
bp x addressSee ``Accessing debugger variables, breakpoint addresses, and registers'' for more information on breakpoint addresses.
When setting data reference breakpoints, call bp with arguments that specify read (r) and/or write (w) references of a byte (b), short (s), or long (l). For example, to trap on references to write a long at F00F072C:
debug0:7> bp wl F00F072C
This specifies that all accesses to location F00F072C
will cause an entry into the debugger.
If desired, data breakpoints can be set up to break only
when the location is written with a certain value:
debug0:7> bp wl &lbolt == 1200
This causes an entry into the debugger
when lbolt is written with the value 1200.
In this example, the optional
condition is (==) and value is 1200.
condition can be one of the following:
== | equal |
!= | not equal |
>= | greater than or equal |
<= | less than or equal |
& | logical-and |
The logical-and condition evaluates true when the location AND-ed with value is non-zero. value can be any SCODB expression; its value is only calculated once.
If name is used to set a breakpoint
(for example: bp name read+1
),
the debugger prompts for a descriptive name
to associate with the breakpoint.
This descriptive name can be used as an alternative to reference the
breakpoint.
This is done by prefixing the descriptive name with a hash sign (#)
(for example, bc #testpoint
).
The name can be up to 31 characters long.
The default action is not to prompt for this name,
but to use in its place the expression
that the user typed to specify the breakpoint address.
When the bp name
form is used on a
breakpoint that already exists,
it changes only the name of the breakpoint.
Specify the mod argument to make bp prompt for commands to execute when the breakpoint occurs. Commands are entered in an editing mode very similar to vi. In vi mode, enter <Esc>Q to stop entering commands. Note that vi mode comes up in insert mode, and empty lines are automatically deleted. These commands are normal debugger commands, and are executed sequentially until there are no more commands or a quit command occurs:
debug0:8>The above example, assuming thatbp mod read
Enter commands to execute at breakpoint: [1]quitif ++$x != 4
[2]r
[3] <Esc>: :q
$x
is initialized as 0,
causes the first three breakpoints at read
to go by
unnoticed,
while the fourth will print the register contents
and drop into the debugger.
These commands can be later modified
by using the mod argument to
bp in exactly the same way.
debug0:9>When using bl to list breakpoints, they are listed in the order they were input, which is not necessarily alphabetic.bl
D00A6B6F read+3 read debug0:10>bl read
D00A6B6F read+3 read quitif ++$x != 4 r
debug0:11>A simple method of clearing a code breakpoint immediately after it occurs is:bc DR0
debug0:12>bc &u.u_uid
debug0:13> bc %eip
This clears a breakpoint at the current eip
.
Other breakpoints can be cleared by giving either the
name of the breakpoint or its address. For example:
debug0:14> bc s5readi
u.u_uid
debug0:1>bp s5readi
debug0:2>bp wl &u.u_uid
debug0:3>bp wl &lbolt >= 1000
debug0:4>bl
F00620EF s5readi+3: s5readi E00010EA u+10EA: DR0: write long u.u_uid F0117660 lbolt: DR1: write long lbolt >= 00001000 debug0:5>bc s5readi
debug0:6>bc DR0
debug0:7>bc DR1
debug0:8>bl
No breakpoints.
d address /* Dump memory as longs */ db address /* Dump memory as bytes */ ds address /* Dump memory as shorts */ dl address /* Dump memory as longs */ dn address /* Dump memory as symbols */If address is given that is known by SCODB to be a structure or union, SCODB automatically dumps it member by member. This can be overridden by typecasting address to (char *). For example:
debug0:1> d (char *)&u
The ASCII value of each byte
is displayed on the right of the screen,
with a dot (.) denoting
a non-printable character or white space, as in
hd(1).
Note that the bytes are displayed
in the ASCII dump from low to high memory,
while the memory dump displays memory
in possibly larger memory groups which are byte-reversed.
After each line of memory is displayed, the user may move around the dump using the following vi(1) movement keys: <j>, <k>, <l>, <h>, <Tab> , and <Space> . The <Return> key acts as a quit character and exits from the dump.
The following are some examples of dumping memory:
debug0:2>d &init_tbl
init_tbl D0089368 D009C544 D007E09C D0085CBC h...D.......... init_tbl+10 D0083F3C D008265C D00863A8 D00826C8 <?.....c...&.. ... debug0:3>ds &dmapage
dmapage 0087 0083 0081 0082 0000 008B 0089 008A ................ ... debug0:4>db &maptab
maptab+60 00 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F .ABCDEFGHIJKLMNO maptab+70 50 51 52 53 54 55 56 57 58 59 5A 00 00 00 00 00 PQRSTUVWXYZ..... ... debug0:5>dn &u+d00
u+D00 selwait 0000011A u+E34 sysent+2B8 ...
c address /* Change memory as longs */ cb address /* Change memory as bytes */ cs address /* Change memory as shorts */
debug0:1> c (char *)&u
Commands can be given to change location and value of memory:
Key | Action |
---|---|
<Space> | move right one digit in field |
j | move to next line |
k | move to previous line |
h | move to previous field |
l, <Tab> | move to next field |
<Bksp> | move left one digit in field, or to last field |
hexadecimal digit | change current digit |
<Esc> | enter calculator-input mode |
u | undo last digit-change |
U | undo all changes to current field |
<Return> or quit character (other than <Esc>) | exit change mode |
Pressing the <Esc> key allows the user to give input to the SCODB calculator so that complex address expressions can be evaluated and entered as value for that field:
debug0:2> c &init_tbl
init_tbl D0089368 D009C544 D007E09C D0085CBC
h...D..........
init_tbl+10 D0083F3C D008265C D00863A8 D00826C8
<?.....c...&..
Value:
To exit the change mode,
enter a newline or quit character (such as the delete key) other than <Esc>.
A <Tab> advances the cursor to the next cell, while a <Bksp> entered at the beginning (left) of a cell causes the cursor to go to the beginning of the previous cell.
A space advances to the next digit, up to the end of the cell (the right side), with no modification to the contents. Incorrect entry is signaled by a bell.
Use the j and k commands to go to the next and previous fields, respectively.
On a numeric field (1, 2, or 4-byte, including pointer types) , hexadecimal input can be given to modify the value of the field. An <Esc> can be given to enter calculator mode.
On an array, bit field, structure or union field, a prompt will be given:
debug0:3> c &u
0000 0E84 char u_stack[E84] Change [ynq]?
If y (for ``yes'') is entered,
the memory region to change is given.
Modification of memory past the range given
(in this case, 0xE84 bytes) is not permitted,
and a beep to signals the error.
Modification of bit fields is not currently guarded: use the calculator features of SCODB for changing bit field values.
stack /* Print stack backtrace */ stack address /* Start backtrace at address */ stack [stack_addr | -p pid | -p proc_addr] /* Print backtrace for process */
Stack backtraces, which are indispensable in debugging, are obtained by examining the stack. Refer to the kdb(1M) manual page for details of the stack command output.
Following is an example of unassembly of a function:
debug0:1> u open
open pushl %edi
open+1 movl 8(%esp),%edi
open+5 mov1 C(%esp),%edx
open+9 pushl %edx
open+A mov1 8(%edi),%ecx
open+D pushl %ecx
open+E movl 4(%edi),%eax
.
.
.
The following vi-style keys can be used for movement when in the disassembly mode:
Key | Action |
---|---|
<Space>, j | forward one line |
<Ctrl>d | forward half a screen |
s [-r] /* Single-step */Single-stepping is used when precise control over instruction execution is required. As each instruction is executed, control is passed back to the debugger, which disassembles the next instruction to be executed. The user must give input at this point before execution will continue.
SCODB can dump the system registers at each instruction; Specifying the -r option toggles on and off the automatic displaying of registers during the single-stepping. This mode is useful for observing register value changes from each instruction.
debug0:1>If the next instruction is a call, the user is prompted for input:s
read+3 inc sysinfo+5C <Space> read+9 pushb 1r
systrap+207 cmpl &aud_cont+1,0q
debug0:2>The call can be ``jumped over'' by entering a j:s
read+B call rdwr [ejr]?q
debug0:3>The function called can be entered by entering an e:s
read+B call rdwr [ejr]?j
read+10 leave <Space> read+11 retq
debug0:4>Other instructions are executed by entering a <Space>. The e and j keys are aliases to <Space> and also cause the displayed instruction to be executed. When held down and used in a repeat fashion, these keys allow continuous single-stepping without interruptions for call instructions.s
read+B call rdwr [ejr]?e
rdwr pushl %ebp <Space> rdwr+1 movl %ebp,%esp <Space> rdwr+3 subl %esp,1C <Space> rdwr+6 pushl %ebxr
read+10 leaveq
The C key causes instructions to be single-stepped until the next control transfer instruction is encountered (that is, up until the next jmp/call/ret instruction).
No breakpoints are in effect while single-stepping.
[yes]
or [no]
after the instruction,
stating whether or not the jump will be taken:
debug0:4> s
rdwr+22 je rdwr+339 [no]
Using the r command to return from the current function will have unexpected results in the rare case that the function does not have an explicit return instruction (for example, longjmp( ), which uses a jump to return control).
struct stname [-> member] [member...] address /* Show structure field values */The stname (structure name) argument does not have to be given if the type of address can be determined.
The following example illustrates dumping memory from address &u (the user structure) as a struct user:
debug0:1> struct user &u
struct user size = 1390 bytes
&u = E0000000:
0000 0E84 char u_stack[E84]
0E84 00F8 union u_fps u_fps
.
.
.
10EA 0002 unsigned short u_uid 0304
10EC 0002 unsigned short u_gid 000A
.
.
.
1148 0018 int u_arg[6]
.
.
.
Bit fields of a structure are printed in the format:
offset declaration hexval = binaryvalFor example:
0010 unsigned int a_base0015:16 0000 = 0000000000000000Other fields in the structure are printed in the format:
offset size declaration value <symbol>For example:
10EA 0002 unsigned short u_uid 0304For bit fields, offset is the offset, in bits, from the beginning of the structure (in the bit field example, 10 (hexadecimal) bits offset). For other fields, offset is given in bytes (in this case, 10EA).
size is the size of the field (in the example, 2 bytes). The size of a bit field is given in its declaration, in decimal (in the first example above, 16 bits).
declaration is the declaration of the field as given in C
(in the example, unsigned short u_uid).
If the debugger does not know the name of a
structure pointer field,
the declaration field reads as
struct ??? fieldname
.
A type cast may be used to access the structure field.
Two values are given for bit fields,
hexval and binaryval.
binvaryval is printed
with the least significant bit (bit 0) to the right.
For other fields, value is given
for 1, 2, and 4 byte-sized fields
(here, 0304) in hexadecimal.
If value is near a symbol,
the symbol and offset are printed.
Fields of other sizes can be dumped using the d command
(for example, to dump the u.u_arg
array:
d u.u_arg
).
By default, struct starts printing structures at addr, and continues incrementing addresses by the size of the structure until the user issues a quit command.
->
field argument
to specify that the structure is a linked list, with
field given as the ``next'' pointer.
In this case, struct prints a structure at
address,
then another at *(address+(field offset)), and so
on.
For example, to dump the STREAMS data block freelist
as a linked list
following the db_freep
member of the structure:
debug0:2> struct datab -> db_freep &dbfreelist
If structure fields are given (other than that required by
the -> argument, if present),
only those members of the structure are printed.
(In this example, db_freep
is listed twice in the command,
once to declare it as the ``next'' pointer,
once to have it printed):
debug0:3>.struct datab -> db_freep db_freep db_base &dbfreelist
struct datab size = 10 bytes D00F072C: 0000 0000 struct datab *db_freep D00EFB18 0004 0004 unsigned char *db_base D00EEB18D00EFB18: 0000 0000 struct datab *db_freep 00000000 0004 0004 unsigned char *db_base D12D5BFC End of list reached.
alias [word [new command...]] /* Set or list aliases */ unalias aliases... /* Unset alias */Aliases allow the user to customize the SCODB environment by simplifying the use of commonly-used complex commands. When SCODB is given a command by the user, it checks the command against the list of aliases. If the command matches word of an alias, substitution is performed. When performing alias substitution of simple aliases (those without argument substitution), the first word of the user's command is substituted by the entire alias, with the arguments to the user's command appended to the end. Aliases may reference other aliases, as long as no loop results. Loops are detected by restricting the number of alias substitutions per command.
unalias is used to unset aliases. Given an argument of ``'', unalias clears all aliases; otherwise it clears only those listed.
alias used with no arguments lists the currently set aliases. Given one argument, alias lists the alias for that word. An example of a simple alias:
debug0:1>More complex aliases can be made that will perform argument substitution by replacing part of the alias with a given argument to that alias. Argument substitution is done when a ``!'' is found inside an alias:alias dbp bp wl
debug0:2>alias
dbp bp wl debug0:3>dbp &u.u_error
... debug0:4>alias dbpe dbp &u.u_error
debug0:5>alias
dbpe dbp &u.u_error dbp bp wl debug0:6>dbpe
0-9 | argument number |
^ | first argument (same as !1) |
$ | last argument |
* | all arguments (^ through $, inclusive) |
debug0:7>All alias information is lost when the computer reboots. To configure aliases to be present automatically in SCODB, modify the alias table in the SCODB space.c file (/etc/conf/pack.d/scodb/space.c).unalias *
Clear all aliases?Y
es debug0:8>alias dbp bp wl &!1
...
As released, the alias table is empty:
struct alias dbalias[NDBALIAS];To add, for example, the following alias:
debug0:11> alias dbp bp wl &!1
Modify the table to:
struct alias dbalias[NDBALIAS] = { { "dbp", "bp wl &!1" }, };SCODB does not necessarily detect errors in such a configured table, so exercise caution when modifying the configuration. Note that the NDBALIAS constant is a tunable parameter and should not be modified inside the space.c file.
#include
's
the structures of interest.
This must be compiled with the cc -g -W0,-d1 command to
generate DWARF I format debugging information.
Ensure that you build the object with
make(1)
flags matching the
DEBUG/non-DEBUG UNIPROC/non-UNIPROC
status of the kernel in which
scodb is installed.