Sometime during your code development, you will need to use a debugger. The following debugging tools are available to facilitate your development of high-quality software:
The CodeWarrior debugger allows you to debug NLMs remotely.
The internal command line debugger that performs symbolic debugging, built into the NetWare operating system (see NetWare Internal Debugger).
The WATCOM linker (WLINK) can be used to include debugging information in an executable file (see Linking Debug Information with WLINK).
Additionally, the linkers can be used to generate a map file, which is a memory map of your program.
You can specify the DEBUG directive in a linker directive file to generate debugging information in the executable file. The following options can be specified with the DEBUG directive to generate the following types of debugging information:
Generates all types of debugging information (global symbol, line numbering, local symbol, and typing).
Generates global symbol debugging information that can be processed only by the NetWare Internal Debugger
Generates NetWare global symbol information for exported symbols only.
Generates WVIDEO global symbol information for exported symbols only.
The NetWare internal debugger is an assembly language debugger that is built into the NetWare operating system. This debugger is a command-line debugger that does not display source code. To use the internal debugger, you should have some knowledge of 80386 assembly language and stack-based parameter passing.
The internal debugger was designed specifically to debug NLMs. It includes a set of supplementary commands that are customized for NLMs, such as .A (display abend or break reason) and .P (display all process names and addresses). These are not part of a typical debugger. The internal debugger allows resident debugging, in which the debugger and the test application run on the same server. In addition, the internal debugger provides a way to debug multiple NLMs concurrently.
NOTE:The NDK includes debug versions of the libraries. Debug records are linked in with each of these NLMs, allowing better visibility to developers using the internal debugger.
You can access the NetWare Internal Debugger in any of the following ways:
At the server console, simultaneously press the following keys: Left-shift+Right-shift+Alt+Esc.
NOTE:If the SECURE CONSOLE command is in effect, you cannot access the NetWare Internal Debugger from the keyboard.
From a C language program, call Breakpoint.
From an assembly language program, issue an INT 3 instruction.
You can then set execution breakpoints, single-step through program execution, examine the contents of memory, and so on.
Some points to be aware of when using the internal debugger are:
NetWare runs in the 386 protected mode, using a flat memory model. In a flat memory model, the values in the segment registers do not change once they are initialized by the NetWare operating system. Since they do not change, the internal debugger does not display them.
The internal debugger supports program global symbolic information; it does not support local symbolic information. Any symbols that you want to reference from the internal debugger must be system-wide globals. To access symbolic information, the program must be linked with the DEBUG option.
The internal debugger is case-sensitive to symbols.
All numbers are entered and displayed in hexadecimal format.
Bytes, words, double-words, and pointers are pushed onto the stack as 4-byte parameters.
For more specific information on the debugger, see the following:
You can recall commands from the NetWare Internal Debugger’s command line buffer by pressing the Up-arrow key. After recalling a command from the command-line buffer, you can edit it. The Right- and Left-arrow keys move the cursor. Insert toggles overwrite. Some of the commands can be repeated by pressing the Enter ; these cases are noted in the command descriptions.
NOTE:If you decide to cancel a command, the Esc key acts like the Enter key. You must use the Delete or Backspace key to erase the command line.
There are four types of help commands in the NetWare Internal Debugger:
HE-Help on expressions
HB-Help on breakpoints
H-General Help
.H-Help with the supplementary commands
In the command summaries, a pair of square brackets in the Command indicates an optional parameter. The following categories of commands are available:
The following table lists supplementary commands.
The following lists breakpoint commands.
Displays all current breakpoints.
Clears the specified breakpoint.
Clears all breakpoints.
Sets an execution breakpoint at address.
Example : Breakpoint if MyFunction is called and the first parameter on the stack is equal to 0:
b = MyFunction [desp+4] == 0
Sets a read or write breakpoint at address.
Example : To check if the code (in the range 14500 to 15500) ever reads or writes to memory location 160FE:
br = 160FE EIP >= 14500 && eip <= 15500
Sets a write breakpoint at address.
Example : To check if the code (in the range 14500 to 15500) ever writes to memory location 160FE:
bw = 160FE EIP >= 14500 && eip <= 15500
The following lists the general debugger commands.
Interactively changes memory.
Changes memory, starting at address, to numbers.
Example : Change byte values starting at 10DFAB to FF, FE, 22.
c 10DFAB = FF,FE,22
This command is currently not supported.
Dumps length bytes of memory starting at the address. If length is not specified, 256 (decimal) bytes are dumped.
Example : Dump 16 (decimal) bytes at address 00088F20.
d 88F20 10
This command can be repeated by pressing Enter. You can visually scan for a string in the ASCII portion of the dump display by dumping a memory location and then repeatedly pressing Enter to display contiguous blocks of memory.
Traverses a linked list. If length is not specified, 256 (decimal) bytes are dumped.
Example : Suppose the first node in a linked list starts at 50 and the offset of the address of the next node is at offset 4.
To traverse the linked list, displaying 16 (decimal) bytes each time, enter the following command.
dl+4 50 10
To display each successive node in the list, press Enter.
The default link offset is 0, which indicates the end of the list. Thus, dl 50 10 uses a link offset of 0.
This command can be repeated by pressing Enter. You can dump the first node in a linked list and then dump each successive node by pressing Enter. A NULL link marks the end of the list.
Changes the specified flag. value can be 0 or 1.
Example : To change the specified flag to the new value (0 or 1), where flag is CF, AF, ZF, SF, IF, TF, PF, DF, or OF:
f CF = 0
Specifies a "Go" instruction, starting from the current EIP.
Specifies a "Go" instruction, starting at the current EIP and ending at the break address or addresses.
Example : Suppose a code breakpoint has just occurred at the start of a C function. To resume execution until the function returns to its caller, use the following command:
g [desp]
Displays general help.
Displays breakpoint help.
Displays expressions help.
Inputs a byte, word, or double-word from the specified port. The default is a byte.
Example : To input the value at port 2F0:
i 2F0
Memory search is currently not supported.
Lists all symbol names, also displaying the NLMs defined them.
Defines a new symbol name at an address.
Example : To give the value 2D46A5 the name x:
n x 2D46A5
Now x can be referenced with other commands, such as:
b=x, b=x+5, u x.
By default, the value is 10. Symbols can be defined with the n command. The y option when the server is started is used to override the default.
Removes a user-defined symbol name.
Removes all user-defined symbol names.
Outputs byte, word, or double-word to the specified port.
Example : To output 10h to port 320h:
o 320=10
Single-steps through the program code; proceeds past calls. (See the s command for stepping into calls.)
This command can be repeated by pressing Enter. A common usage for this command is to run until you hit a breakpoint, and then single-step by entering the p command and then pressing Enter repeatedly to continue single-stepping. By holding down Enter, you can quickly single-step through the program code.
Quits to DOS.
Displays the registers and flags.
Changes the register to the specified value. The registers are EAX, EBX, ECX, EDX, ESI, EDI, EBP, EIP, and EFL.
Single-steps through the program code; steps into a call. (See the p command for stepping past calls.)
This command can be repeated by pressing Enter. You can hit a breakpoint and single-step by entering the s command and then pressing Enter repeatedly to continue single-stepping. By holding down Enter, you can quickly single-step through the program code.
Same as s.
Disassembles count instructions. If you type u by itself and press Enter, the starting address is assumed to be the contents of EIP, and 16 (decimal) bytes will be disassembled.
Example : Disassemble 16 (decimal) bytes prior to the current instruction.
u eip-10
NOTE:A command such as this might not cause the disassembly to fall on an instruction boundary.
This command can be repeated by pressing Enter. You can disassemble starting at any memory location by initially entering the u command and then pressing Enter to continue the contiguous disassembly.
Displays the server’s screen(s) for viewing. Each time a key is pressed, the next screen is displayed. See the .s command.
Exchanges processor stack frames.
Evaluates the expression.
Example: To display the value at the address computed by adding EBP to EBX shifted right 16 (decimal) times.
z [ d EBP + (EBX >> 10) ]
Displays nearest symbols to address. If address is not given, EIP is used.
Example : To determine the NLM and function owning the current instruction, type the following:
?
With the NetWare Internal Debugger, you can set execute, write, or read/write breakpoints.
There are four breakpoint registers, allowing a maximum of four simultaneous breakpoints. Breakpoints can be permanent or temporary:
To set permanent breakpoints, use the b, br, and bw commands. For permanent breakpoints, you can attach a condition that specifies whether to take the breakpoint. If the condition is true, a breakpoint is taken. If it is false, execution continues without stopping.
To set temporary breakpoints, use the g command. For example, a "go" to a specific address is a temporary breakpoint. The p command can also set a temporary breakpoint if the current instruction cannot be single-stepped.
If you use all four breakpoints and issue a g [desp] command, the following is displayed:
Go out of breakpoints
If you use all four breakpoints and attempt to proceed past a function call using the p command, the following is displayed:
Breakpoint not available for proceed
The assembly repeat instructions (such as REPE), the LOOP instruction, and the CALL instruction also require p to set a temporary breakpoint.
The NetWare Internal Debugger determines the order of execution of an expression in accordance with the following:
Precedence of grouping operators
Precedence of unary, binary, and ternary operators
Common algebraic ordering
The following sections explain the various types of operators:
The grouping operators ( ), [ ], and { } indicate to the debugger the desired grouping of operations. These operators have the highest precedence (0).
(expression)
The terms inside the parentheses are evaluated first. In the case of parenthetical expressions that are nested, evaluation begins with the innermost parenthetical expression.
[size expression]
expression is evaluated first and then used as a memory address. The size specifier can be B,W, or D. The expression evaluates to byte, word, or double-word at the specified address.
For example: Suppose the data at memory location 178D10 is the following byte sequence in Intel storage format 38 F9 99 88. Then, using the z command, which evaluates expressions:
{size expression}
expression is evaluated first and then used as a port address. The size specifier can be B,W, or D. The expression evaluates to byte, word, or double-word from the port.
The unary operators have precedence 1.
The binary operators in the following table are ordered from lowest to highest precedence.
If expression1 is true, the result is the value of expression2; otherwise, the result is the value of expression3.
expression1 ? expression2 , expression3
In the following example, a break is taken on
myFunction( char *myData )
if the carry flag (FLCF) is true and EAX contains 9C, or if the carry flag is false and the first byte of myData is 0:
b = myFunction ( FLCF ? eax == 9c, [b [desp+4]] == 0 )
The 80386 registers, which can be used in expressions, are referenced by the names listed in the following table.
Register |
Name |
---|---|
EAX |
Accumulator register |
EBX |
Base register |
ECX |
Count register |
EDX |
Data register |
ESI and EDI |
Index registers |
ESP and EBP |
Base and stack pointer registers |
EIP |
Instruction pointer register |
The flags register is a 32-bit register that contains a number of status bits. This register is sometimes referred to as the status register. The following table lists the flags register bits.