Novell is now a part of Micro Focus

Debugging NLMS with the Dexter Debugger Extender

Articles and Tips: article

MORGAN B. ADAIR
Senior Research Engineer
Novell Systems Research

01 Jan 1995


The Dexter Debugger Extender adds commands to NetWare's internal debugger to make it easier for you to test and debug NLMs (NetWare Loadable Modules). This DevNote describes the features Dexter provides and illustrates how to use them.

Introduction

An article in the August 1991 issue of NetWare Application Notes (sister publication to the DevNotes, now called Novell Developer Notes), "Using the NetWare 3.x Internal Debugger," introduced how to use NetWare's internal debugger to develop and test NLMs. Dexter, a product of Alexander LAN, Inc. of Nashua, New Hampshire, extends the functionality of NetWare's internal debugger. Dexter runs on all versions of NetWare greater than 3.11. Dexter adds debugging commands to supplement those in the NetWare internal debugger.

Dexter provides access to the operating system and processor structures described below.

Call Stack for Running Process

When you are testing an NLM under development, you can usually keep track of where the NLM is in its execution and how it got there by printing messages to the screen, or by setting breakpoints. This information is harder to get after your NLM has crashed. Dexter allows you to look at the call stack by function name to analyze the sequence of function calls that resulted in the NLM crash.

Call Stack for Non-active Processes

Dexter provides symbolic access to the call stack for non-active processes (threads), just as with active processes. This is valuable, since a server crash could result from an action taken by a process that is no longer active. When developing multi-threaded NLMs it is always useful to determine the execution point of all threads comprising the NLM.

Symbol List

In order to provide symbolic access to the call stack, Dexter must first have a list of all symbols that are exported by NetWare and all NLMs. Dexter allows you to view this list, so that you can find which functions are exported by which NLMs.

Global Descriptor Table

Intel processors running in protected mode have a Global Descriptor Table (GDT) that keeps track of where memory segments are in physical memory. To resolve a memory reference, the processor uses an index into the GDT (called a selector) to locate the segment in physical memory, then use the offset relative to the beginning of the segment. Under most circumstances, you don't need to refer to the GDT when debugging an NLM. Dexter makes it available to the NET-Check memory protection utility (which you can also get from Alexander LAN), and, on the off chance that you might be interested in it, lets you look at it, too.

Interrupt Descriptor Table

An Intel processor running in protected mode has a table of 256 selector:offset addresses for each of up to 256 interrupt handlers that can be installed. When debugging an NLM, you can set a breakpoint at the handler for an exception that you know your NLM generates under specific conditions. For example, if your NLM generates a divide error, you can set a breakpoint at the divide error exception handler, then attempt to replicate the conditions that lead to the error.

When a divide error occurs, your server will break into the debugger, and Dexter will allow you to view the function call sequence that led to the exception.

Control Registers

The control registers control and provide status of protected mode and memory paging. As with the GDT, access to the control registers is primarily for memory protection software like NET-Check, and is of limited usefulness to NLM programmers.

System Memory Map

The system memory map (available only in NetWare 4) gives the addresses of operating system and process regions, such as NetWare's global code and data regions, the running process code, and the OS branch table.

Installing and Using Dexter

Dexter is a single NLM that you simply copy to SYS:SYSTEM and load from the server's AUTOEXEC.NCF. For NetWare 3.11, there is a second NLM that must be loaded before Dexter. Like the NetWare internal debugger, Dexter is inactive until

  • you press ALT-SHIFT-SHIFT-ESC,

  • an NLM executes interrupt 3,

  • an NLM calls the CLIB EnterDebugger function (which executes interrupt 3), or

  • the server abends.

You can therefore leave Dexter installed on test, development, or production servers without worrying about Dexter's impact on server performance. In fact, it is a good ideas to always load Dexter on production servers, because you can use Dexter to diagnose the reason for server abends.

TESTWALK.NLM

To help you get familiar with Dexter's capabilities, Alexander LAN provides a small example NLM called TESTWALK with Dexter. As soon as it is loaded, TESTWALK executes an INT 3 to enter the debugger. You can then single-step through the NLM and view the function call stack as TESTWALK calls its own local functions and CLIB functions. Source code for TESTWALK is given below.

/*---------------------------------------------------------------------------,

 1993,94  Alexander LAN, Inc.

     All rights reserved.





NOTES

  This is a VERY small NLM that is useful for trying out DEXTER's stack

  walking features.  The actual NLM is also included on the DEXTER program

  disk.



  The best way to use it is the following:



  0. Look over this source code carefully and understand what it does.

  1. Load DEXTER                                             

  2. Load TESTWALK (which will bring you into the debugger)  

  3. use the '?' command to see where you are in the TESTWALK code

  4. use the '/' to walk the stack frame chain.

  5. see how you're now in TESTWALK's main function?

  6. see how that function was called from CLIB's _SetupArgv?

  7. continue to trace ('T' command) all the way through the call

     to CLIB's malloc in MyFcnOne (below).  Try the '/' key again.

.--------------------------------------------------------------------------)*/
#include <stdio.h<<
#include <stdlib.h<<
#include <conio.h<<
#include <malloc.h<<
#include <string.h<<
LONG MyFcnOne(LONG test);   // prototype for MyFcnOne

void _int3(void);           // debug breakpoints...

#pragma aux _int3 = 0xCC;   // INT 3
/*--------------------------------------------------------------------------),main



                    here's where we start.  note

    that we'll do an INT3 right away which will

      put us into the debugger before anything

    else happens.  That way we can    go through

    the code step by step.                  

             note also that after using the 'g'

    key from the debugger to get back to    the

    console, you have to hit any key to continue

    with TESTWALK..--------------------------------------------------------------------------)*/
void main(void)

{

_int3();                            // do an INT3: gets us into debugger

   MyFcnOne(0xface);               // ...so we have something to look at

   while(kbhit(--                  // clear the keyboard buffer

       getch();

   printf("Hit any key to quit this thing.\n\r");

   while(!kbhit(--

       ThreadSwitch();             // spin while waiting for a key

   exit(0);                        // back to the console

}

LONG MyFcnOne(LONG test)

{

   char *MyBuffer;

   MyBuffer = (char *)malloc(0xabe);   // something to do

   memset(MyBuffer, 0, 0xabe);         // something else to do

   free(MyBuffer);                     // better undo it

   return(1);                          // return to main

}

When you load TESTWALK, it immediately breaks in to the debugger:

Break at F1019017 because of INT 3 breakpoint

EAX = 00617E80 EBX = 00617F44 ECX = 00000000 EDX = 00617F68

ESI = 00000000 EDI = 00617F21 EBX = 006C3D7C ESP = 006C3D7C

EIP = F1019017 FLAGS = 00000202 (IF)

F1019017 68CEFA0000     PUSH FACE

Following the instructions included in the NLM code, if you executed Dexter's ? command, it gives you the current location in TESTWALK's code, with the symbol name for the function being executed.

# ?

Dexter reports address in TESTWALK.NLM at code start +00000017h

Previous:  -00000017  F1019000 TESTWALK.NLM|main

Current:    00000000  F1019017

Next:      +0000004D  F1019064 TESTWALK.NLM|MyFcnOne

ESP+04  F1019084   TESTWALK.NLM|DATA      LocalCode

The / command gives you the chain of function calls that got you to this point, in this case _SetupArgv, __get_stderr, then main.

# /

frm addr   ret addr   (+ofs) location

006C3D7C   F1019017   +0017 TESTWALK.NLM|main

006C3D98   F101C9BF   +0062 TESTWALK.NLM|__get_stderr

006C3F86   F1163AE7   +0773     CLIB.NLM|_SetupArgv

End of stack frame chain.

Press any key for Dexter to continue, <ESC< to quit<
ESP&__Value;&Symbolic Area;&_;OS_Memory_Region&_;

ESP+04  F1019084   TESTWALK.NLM|DATA      LocalCode 

ESP+08  00000ABE   LOCAL DATA

ESP+0C  F1010008   UNICODE.NLM |DATA      LocalCode 

ESP+10  00010312   UNKNOWN     |UNKNOWN   physical memory

ESP+14  006BDD7C   UNKNOWN     |UNKNOWN   physical memory

ESP+18  0070FF21   UNKNOWN     |UNKNOWN   physical memory

ESP+1C  00000000   LOCAL DATA

ESP+20  0070FF44   UNKNOWN     |UNKNOWN   physical memory

ESP+24  F1019021   TESTWALK.NLM|DATA      LocalCode 

ESP+28  0000FACE   LOCAL DATA

ESP+2C  006BDD98   UNKNOWN     |UNKNOWN   physical memory

ESP+30  0070FF21   UNKNOWN     |UNKNOWN   physical memory

The ability to know the sequence of function calls that got you to a given point in a program can be invaluable in complex, multi-threaded programs.

Commands Added to NetWare 3.1x

When used on NetWare 3.11 or 3.12, Dexter adds a few commands that are in the NetWare 4 debugger, but not in NetWare 3. These commands are described below.

.G The .G command gives you the Global Descriptor Table.

Dexter reports the Global Descriptor Table (length:28)

OFS  Base      Limit  Lv  Flags

08:  00000000  007FF  0   CODE  RD/AC

10:  00000000  007FF  0   DATA  WR/AC

18:  000121B0  0FFFF  0   CODE  RD/AC

20:  000121B0  0FFFF  0   DATA  WR/AC

.I The .I command displays the Interrupt Descriptor Table.

Dexter reports the Interrupt Descriptor Table (length:3BF)

INT  DPL P  Gate  SEL:OFFSET     Purpose

00:   0  P  INTR  0008:0001464C  Divide Error

01:   0  P  INTR  0008:00014685  Debug Exception

02:   0  P  INTR  0008:000146CA  NMI Interrupt

03:   0  P  INTR  0008:0001473B  Breakpoint (INT 3)

04:   0  P  INTR  0008:00014774  INTO Detected Overflow

05:   0  P  INTR  0008:000147AD  BOUND Range Exceeded

06:   0  P  INTR  0008:000147E6  Invalid Opcode

07:   0  P  INTR  0008:0001481F  Coprocessor Not Available

08:   0  P  INTR  0008:00014878  DoubleFault

09:   0  P  INTR  0008:000148E4  Coprocessor Segment Overrun

0A:   0  P  INTR  0008:00014952  Invalid TSS

0B:   0  P  INTR  0008:000149BE  Segment Not Present

0C:   0  P  INTR  0008:00014A2A  Stack Fault

0D:   0  P  INTR  0008:00014A96  General Protection Exception

0E:   0  P  INTR  0008:00014B02  Page Fault

0F:   0  P  INTR  0008:00014EEB  reserved

10:   0  P  INTR  0008:00014B6E  Coprocessor Error

11:   0  P  INTR  0008:00014BA7  Alignment Check

12:   0  P  INTR  0008:00014BDE

RC The RC command displays the status of the processor control registers.

Dexter reports Control Registers

CR0 = 7FFFFFE5 CR1 = reserved CR2 = 00000000 CR3 = 00000000

Command Added to NetWare 4

Dexter adds the MAP command to NetWare 4 to give you the system memory map.

# MAP

Dexter's OS Memory Map

Region Name           

                  Start Address

GlobalData / LocalDataEnd                 0xF0000000

OSBranchTable                             0xF03C0000

GlobalDataEnd / OSExternalTable           0xF0400000

PDataSegmentLimit                         0xF0800000

LocalCode                                 0xF1000000

GlobalCode                                0xF5000000

GlobalCodeEnd / OSCode                    0xF8000000

OSCodeEnd                                 0xF8400000

OSData                                    0xF9000000

OSDataEnd                                 0xFA000000

OS_CACHE_CONTROL_AREA                     0xFB000000

RunningProcessMAP                         0xFC000000

RunningProcessPCB                         0xFC3FF000

RunningProcess                            0xFC3FF07C

AlternateProcessPCB                       0xFC7FF000

Additional Commands in All Versions of Dexter

You already saw how to use Dexter's / and ? commands with TESTWALK. Dexter provides two other commands, > and SYM, when running with any supported version of NetWare. The > command displays the call stack for non-active processes. To use it, first execute the internal debugger's .P command to get the address of the process you are interested in. Then execute the > command, passing the address as a parameter.

.p

Running process:

FB001000 testwalk__P     0 Process

Processes on the run queue (in run order):

Low priority processes (in run order):

Processes not on the run queue:

FB009000 Console Command Process (Wait for an interrupt wakeup)

FB008000 TimeSynchMain Process (Wait for an interrupt wakeup)

FB007000 Server 02 Process (Not In Use)

FB006000 Server 01 Process (Not In Use)

FB005000 Remirror Process (Wait for an interrupt wakeup)

FB004000 Media Manager Process (Wait for an interrupt wakeup)

FB003000 Sync Clock Event Process (Wait for an interrupt wakeup)

FB002000 Server 00 Process (Not In Use)

FB000000 MakeThread Process (Not In Use)

> FB00000000>
Stack Pointer:00038BBC, Stack Limit:00035C00, Using Frame:00038B7C

frm addr   ret addr   (+ofs) location

00038B7C   Start of process/thread trace. Previous values are unknown.

00038BAC   F80ADCD0   +FFFF   SERVER.NLM|GetMaximumNumberOfNameSpaces

00038BEC   F802280F   +5187   SERVER.NLM|StuffValue

 ????     F80AE32D   +FFFF   SERVER.NLM|GetMaximumNumberOfNameSpaces

The SYM command lets you search for symbols exported by NetWare or any loaded NLM. Executing the command with no parameters gives you a list of all defined symbols, along with the name of the exporting module. If you pass the name of an NLM to the SYM command, it will give you a list of all symbols exported by that NLM. And finally, you can pass a regular expression to the SYM command, to search for symbols that match that pattern. For example, you could execute

SYM clib|str*

to get a list of all the string functions exported by CLIB.NLM. The symbol list for TESTWALK is shown below.

sym testwalk

0x005346D4   TESTWALK.NLM|_end

0x005346D4   TESTWALK.NLM|_edata

0x005344E3   TESTWALK.NLM|_cstart

0x00534509   TESTWALK.NLM|main

0x00534490   TESTWALK.NLM|_Prelude

0x005344F1   TESTWALK.NLM|_Stop

0x00534503   TESTWALK.NLM|__Null_Argv

0x00534503   TESTWALK.NLM|__Init_Argv

0x00534504   TESTWALK.NLM|__VersionEnforcement

0x005346C8   TESTWALK.NLM|_argc

0x005346D0   TESTWALK.NLM|_fltused

0x005345F6   TESTWALK.NLM|MyFcnTwo

0x005345B3   TESTWALK.NLM|MyFcnOne

0x005346C4   TESTWALK.NLM|KeepGoing

0x00534623   TESTWALK.NLM|MyThreadFcn

Getting Dexter

Dexter is available for $249 (list price) from:

Alexander LAN, Inc. 100 Perimeter Rd., 2nd Flr. Nashua, NH 03063-1301

Phone (603) 880-8800 Fax: (603) 880-8881

* Originally published in Novell AppNotes


Disclaimer

The origin of this information may be internal or external to Novell. While Novell makes all reasonable efforts to verify this information, Novell does not make explicit or implied claims to its validity.

© Copyright Micro Focus or one of its affiliates