home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
sherlock.zip
/
sherlock.txt
< prev
next >
Wrap
Text File
|
1997-04-02
|
21KB
|
453 lines
Sherlock - A Programmers helper.
Introduction
Trying to debug an application is usually a mystery. Most of the time, the
debugging is at the developers computer where source code is available and
source level debuggers are able to quickly and easily locate problems. At
this point it is easy to find problems and correct them. At this point,
there is little challenge to the mystery.
Unfortunately, after a program has gone out the door, it is inevitable that
a problem occurs at the end user's site. Although we programmers might be
able to reproduce the problem if an exact description is given, sometimes
we cannot due to one reason or another. If a trace of what the program is
currently doing at the customers site was available, then the developer would
have a better idea of what went wrong and how to fix the problem. Giving
the user access to the original source may not be desirable, and while
reporting the linear address in OS/2 2.0 may make the end user feel good,
it is useless to the developer since the machine configurations may be
different and therefore will have different addresses. This is were
programmers have a real mystery. Unfortunately, usually with too many
the number of pieces missing to allow the mystery to be solved.
This introduces a need into the developers market. This need is to allow
any end user with minimal instructions to provide the type of debugging
information that the developer really needs. Sherlock is a program to aid
fellow programmers in this remote diagnosis of program problems. This is
done through an interfaceless debugger. This debugger will load the desired
executable for debugging, start the program and then continue until an
exception occurs. When an exception occurs, the entire program state
is dumped. Each thread will have the current register thread dumped and
then attempt to trace the stack back to the root node of the thread.
Installation
Sherlock may be installed into any directory that is in you PATH statement.
It is suggested that you create a new directory to store the files and add
this directory to your PATH statement in CONFIG.SYS. If you have debugging
DLLs, they MUST be installed in the same directory as Sherlock. Sherlock
will search for the debugging DLLs only in the directory where Sherlock is
stored. For example:
C: Make Drive C current.
MKDIR C:\SHERLOCK Create the Sherlock directory.
CHDIR C:\SHERLOCK Change to the Sherlock directory.
COPY A:*.EXE *.EXE Copy the executable.
COPY A:*.DLL *.DLL Copy the debugging DLLs.
E CONFIG.SYS Add C:\SHERLOCK to the PATH
statement.
Usage
Usage of SHERLOCK is simple, even for most end users. The target program is
started as it would normally be started with the addition of listing
Sherlock ahead of the target application. For example, if you wish to test
the program xyz.exe with the parameters a b.
Normal:
xyz a b
With Sherlock:
Sherlock xyz a b
Sherlock supports only one command line argument -L which will specify the
name of a new output file. The name of the output file is specified
immediately after the L. For example:
Sherlock -LCatch.log Catch.exe
What will I get out of Sherlock?
What Sherlock will generate is a log file of what the operator of a debugger
would see. Sherlock will try to load debugging information for each module
which is loaded. Sherlock's ability to decode debugging information is based
upon loading a DLL to support each different debugging information format.
Sherlock is capable of supporting many different debugging formats at the
same time. Each module which is loaded by the target program will be sent to
the support DLLs to ask whether it supports the debugging format of the
module. If it says yes, no other support DLLs will be ask whether they
support the debugging format of the module.
Currently, only the following formats are supported:
SYM files created by mapsym.
Microsoft C V6.0 debugging format
IBM C-Set/2 Version 1.0 and 2.x
VisualAge is not supported currently.
Sample Output
Sherlock will produce a log file with all of the output from Sherlock. The
log file is named SHERLOCK.LOG or whatever you specify on Sherlock's command
line. This file is placed into the current directory. Each execution of
Sherlock will overwrite any existing log file. The output will be as
described below.
Sherlock will log all exceptions that occur within the application to the log
file. Note that there are many types of exceptions. Some are normal or
diagnostic such as guard page exceptions. Other exceptions are fatal, but
can be recovered from. Access Violations while usually fatal can be
recovered from. For example, C-Set/2 (Copyright IBM) can insert an exception
handler which if not overridden will print a register state dump. If
overridden, it is possible to recover from the exception and continue
program execution.
Sherlock will log ALL exceptions to the log file until the program has
terminated. Each exception will be dumped as a set of blocks of the format:
Exception block
Register block
Traceback from current function to first function.
Code dump
For the descriptions of the different blocks below, the following program
will be used:
/*
** Main entrance routine.
*/
#define INCL_DOSDATETIME
#include <os2.h>
int main(int argc, char **argv);
int main(int argc, char **argv)
{
DosOpen((PSZ) 0, /* pszFileName */
(PHFILE) 0, /* pHf */
(PULONG) 0, /* pulAction */
0, /* cbFile */
0, /* ulAttribute */
0, /* fsOpenFlags */
0, /* fsOpenMode */
(PEAOP2) 0); /* peaop2 */
return 0;
}
This program will cause a GP fault somewhere within OS/2 since a number of
NULL pointers is being passed in. Note that the program will compile
correctly without a complaint from the compiler.
Exception Block
The "Exception type" block will list the type of problem that was hit and
where. (e.g. - Except#: c0000005 Access Violation). Some of the information
in this block is gibberish if you have not worked with OS/2's exception
mechanism. Some of the information is gibberish even if you do work with
OS/2's exception mechanism. (Please refer to the BSEXCPT.H file from the
OS/2 Toolkit for details as to what the Except # parameters mean.) The text
printed to the right of the 'Except #" is the description as defined by IBM.
Note that you and others might define other exception numbers. These might
be used as signals to your application to signal some event.
For this exception number, an access violation occurred. The exception
address is the location where the error was detected. This is usually the
INSTRUCTION POINTER when the exception occurred. For this exception type,
there are two exception parameters. The first for access violation is the
address that caused the problem. Zero would be a NULL pointer. How that
pointer was obtained cannot be found from this block. It could have been
a direct memory reference, or an indirect reference through one of the index
registers in the CPU. The module name while useful, is also misleading It
is NOT the name of the module that actually had the fault, but the name of
the program that was executing. Note that in this case, there is no
debugging information available where the exception actually occurred, so
there is no function information available.
Exception type 1 at 1a050179
Except #: c0000005 Access violation
Flags: 00000000
Next Rec: 00000000
Except Addr: 1a050179
Num Parms: 2
Except 0: 00000000
Except 1: ffffffff
Module: D:\SHERLOCK\CATCH\CATCH.EXE
Size: 18778
Timestamp:Mon Dec 6 21:27:20 1993
Lo Function: UNKNOWN
Hi Function: UNKNOWN
Exception in thread: 1 ID of the thread where the exception occurred.
Register block
Each thread of execution within the executing program will have its register
set dumped. The entire register set is dumped. If you have the assembler
source code for the section of code where the exception occurs, then you can
relate the registers to what the program was trying to accomplish.
Additionally, the segment register information is dumped. These registers
may be useful if you examine the limits. For example, if a reference using
the 'FS' register is made at offset of 0x32, then an exception will occur.
By checking FSLim, then you would see that you have indexed too far into
that segment.
For the test case given above, you can use the information from the Exception
Block to possibly gather additional information. For example, we know from
above that the address 0 was causing a problem. IF it was from a register
index, then it would have to be from either the EBX, ESI or EDI registers.
Without more information, you are still stuck. You do not know which
register is causing the problem, or if a register is causing a problem.
Pid: 000000f0 Tid: 00000001
Cmd: 0 Value: 00716668
Addr: 0002294c Buffer: 00037fd4
Len: 00000024 Index: 00000000
MTE: 0000030c
EAX: 00172a80 EBX: 00000000 ECX: 00060010 EDX: 00060007
ESP: 0002291c EBP: 00022ae4 ESI: 00000000 EDI: 00000000
EIP: 0000c0a0 EFLAGS: 00002206
Carry Parity Aux Zero Sign Trap IntE Dir Flow IOPL Nested Resume
NC PE 0 NE 0 0 1 DN NO 2 0 0
CSLim 1c000000 CSBase: 00000000 CSAcc: df CSAttr: d0 CS:005b
DSLim 1c000000 DSBase: 00000000 DSAcc: f3 DSAttr: d0 DS:0053
ESLim 1c000000 ESBase: 00000000 ESAcc: f3 ESAttr: d0 ES:0053
FSLim 00000031 FSBase: 00050030 FSAcc: f3 FSAttr: 00 FS:150b
GSLim 00000000 GSBase: 00000000 GSAcc: 00 GSAttr: 00 GS:0000
SSLim 1c000000 SSBase: 00000000 SSAcc: f3 SSAttr: d0 SS:0053
Traceback from current function to first function invoked
The traceback is the block that answers the question of how you got to where
the problem was detected. Sherlock takes the EBP, EIP of the current thread
given by the register dump and tracks through the stack to where the program
starts. At each EBP location found, Sherlock will dump any location
information that is available. Sherlock assumes that EBP is used to save the
Stack Frame base on every function call. If this is not the case, then
Sherlock may skip over functions in its trace. Sherlock also assumes that
EBP is pushed onto the stack immediately after the function has been called.
If this is not the case, then Sherlock may become confused and give erratic
results. Sherlock will follow the EBP back to the beginning of the program.
Each block contains the EBP/EIP. These are the Stack Frame pointer and the
EIP of the routine. The EIP is then translated by Sherlock into the Base
Offset/ Relative Offset/ Object length/Object/ Module. These can be used to
translate into source locations if a map file of the module is known. The
Object is the Object or Segment in the linker's map file. The Relative
Offset is the offset within the Object. By checking function addresses or
line number information within the map file you can translate the Relative
Offset into a function.
For unsupported debugging modules, a Hi and Lo function marker are also
given. These may be helpful or may not since these are based upon the
exported functions of a module. If a module only contains only a few
functions exported, then the function markers will most likely be useless.
EBP: 00022ae4EIP: 1a02c0a0
Base: 1a020000Rel: 0000c0a0Len: 00010000
Object: 00000002
Module: C:\OS2\DLL\DOSCALL1.DLL
Lo Function: DOSREAD
Hi Function: DOSCOPY
EBP: 00022b14EIP: 1a02a958
Base: 1a020000Rel: 0000a958Len: 00010000
Object: 00000002
Module: C:\OS2\DLL\DOSCALL1.DLL
Lo Function: DOSREAD
Hi Function: DOSCOPY
The Function/Source/Line are the location where the program was trying to
execute for the first block. In later blocks, this is the location of where
the prior function (earlier in the listing) was called from. This entry
shows the result of using one of the debugging support DLLs. The function,
source module and line number are therefore accessible and dumped.
EBP: 00022b3cEIP: 00010023
Base: 00010000Rel: 00000023Len: 00010000
Object: 00000001
Module: C:\WORK\DEBUG\CATCH.EXE
Function: main
Source: CATCH.C
Line: 9
This entry shows the same module as the above entry, but the area where the
stack traced back to does not have debugging information, so the bounding
functions are supplied as markers. If the source code can be found, the
source code will be listed after these blocks. If there is no debugging
information available, then a disassembly of the code will occur. This
disassembly will be where ever the EIP for the block is located.
EBP: 00022b58EIP: 00010596
Base: 00010000Rel: 00000596Len: 00010000
Object: 00000001
Module: C:\WORK\DEBUG\CATCH.EXE
Lo Function: _RunExitList
Hi Function: UNKNOWN
EIP: 1a02c834, DLL: C:\OS2\DLL\DOSCALL1.DLL Func:
DOS32R3EXCEPTIONDISPATCHER
1a02c834 PUSH EBP
1a02c835 MOV EBP,ESP
1a02c837 SUB ESP,0x00000100
1a02c83d PUSH EDI
1a02c83e PUSH ESI
1a02c83f MOV EAX,DWORD PTR [EBP 0xc]
1a02c842 CMP DWORD PTR [EAX],0xc0010002
1a02c848 JE $ 0x08
1a02c84a CMP DWORD PTR [EAX],0xc0010003
1a02c850 JNE $ 0x0c
1a02c852 MOV DWORD PTR [EBP 0],0x00000000
Current Limitations
The current release has the current limitations:
1) Only the program started will be monitored. Any subprocesses started
by the program will not be monitored.
2) The distributed version does not have the debugging DLLs.
3) The first release has an annoying flashing effect. I am still
working to find why this is happening. Hopefully the next version
will have this fixed.
4) Crossing 16-32 bit function calls is not currently supported for the
stack trace.
5) Tracing of 16 bit stacks is difficult and may be incorrect for code
with near calls in the trace.
Copyright and Legal Information
Sherlock is Copyrighted (c) 1992 - 1995 by Harfmann Software.
Adding new Interfaces
Sherlock is designed to look in the startup directory where Sherlock has been
installed to find support DLLs. Any file with a DLL extension is loaded and
examined for two functions to be exported by name called:
isKnownModule
linkPriority
Link Priority
Link priority is used to determine the order used to try and resolve symbolic
information. When Sherlock first starts, it tries to load every DLL in the
Sherlock directory assuming that it is a support DLL for Sherlock. It will
then invoke the linkPriority function to build an internal linked list of
support DLLs.
/*
** Answer the linkage priority.
** Return 1 for insert in front of list
** (first crack at linking)
** Return 0 for add to end of list.
** (last crack at linking)
*/
int _System linkPriority(void)
{
return 1;
}
When Sherlock tries to load a module, it iterates through each support DLL in
the linked list to try and determine whether the debugging format of the
module is known to that support DLL. For example, the SYM file support is
given 0 priority and HLL (C Set++) is given 1 priority to allow the HLL
format to take precedence over the SYM format.
Supporting a Debugging Format
When a module is loaded, Sherlock iterates through all of the support DLLs to
determine if any of them can find the debugging information for the module.
If the module does understand the format, it returns true, otherwise it
returns false. This is done with the isKnownModule function shown below:
/*
** Answer whether the module named is a HLL module.
** If so, set the function pointers and return true.
*/
int _System isKnownModule(DebugModule *module,
int (* _System DispatchCommand)
(int command),
DebugBuffer *buffer)
Where:
module - Pointer to a structure that will be used to reference the module for
as long as the module is loaded. All elements except AuxData and the
function pointers should be considered static and should not be changed.
AuxData and the function pointers should be filled in if the module
understands the debugging format of the module.
DispatchCommand - Function pointer to be used by the support DLL if it needs
to use the DosDebug API.
buffer - Pointer to the buffer used by DosDebug().
typedef struct _DebugModule {
void *nextModule; // Internal - DO NOT MODIFY
char *name; // Name of file as returned by DosQueryModuleName
void *AuxData; // Spare pointer for support DLL usage
void *ViewData; // Internal - DO NOT MODIFY
time_t fTimestamp; // Time stamp of the module
ULONG fileSize; // File size of the module
ULONG MTE; // Module handle
ULONG typeFlags; // Module flags return by DosQueryAppType
/*
** Module cleanup. Free any support structures.
*/
void (* _System FreeModule)(
struct _DebugModule *module); /* Module handle */
/*
** Source functions.
*/
int (* _System FindSource)( /* 1 found / 0 - not found */
struct _DebugModule *module, /* Module handle */
ULONG eipOffset,/* EIP for function to find */
char *funcName, /* Buffer for function name */
char *sourceName, /* Buffer for source code */
ULONG *lineNum);/* Pointer to line number */
ULONG (* _System FindSourceLine)( /* Return offset of file/line*/
struct _DebugModule *module, /* Module handle */
int line, /* Line to find */
char *fileName);/* File name */
ULONG (* _System FindFuncAddr) ( /* Return offset of function */
struct _DebugModule *module, /* Module handle */
char *name); /* Function name. */
/*
** Variable functions.
*/
int (* _System GetName)( /* State return value from above */
struct _DebugModule *module, /* Module handle */
State *state, /* State information to retrieve.
** Contains name of variable. */
State *state2); /* If !NULL: state2 is element of
** structure given in state. */
int (* _System GetArray)( /* State return value from above */
struct _DebugModule *module, /* Module handle */
State *state, /* Name of array */
State *state2); /* Index of element to retrieve */
int (* _System GetNumMembers)( /* Return # of elements 0 if not a structure */
struct _DebugModule *module, /* Module handle */
State *state); /* Variable to query and program state */
/*
** Get the name of the index'th structure element
*/
int (* _System GetMemberIndex)( /* State return value from above */
struct _DebugModule *module, /* Module handle */
State *state, /* Program state */
int MemberIndex,/* Index of element to retrieve */
char *name); /* Buffer to return name into */
} DebugModule;
The FreeModule and FindSource function pointers must be filled in to support
the current functionality of Sherlock. The other function pointers may be
used in future versions.