home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / sherlock.zip / sherlock.txt < prev    next >
Text File  |  1997-04-02  |  21KB  |  453 lines

  1. Sherlock - A Programmers helper.
  2.  
  3. Introduction
  4.  
  5. Trying to debug an application is usually a mystery.  Most of the time, the
  6. debugging is at the developers computer where source code is available and
  7. source level debuggers are able to quickly and easily locate problems. At
  8. this point it is easy to find problems and correct them.  At this point,
  9. there is little challenge to the mystery.
  10.  
  11. Unfortunately, after a program has gone out the door, it is inevitable that
  12. a problem occurs at the end user's site.  Although we programmers might be
  13. able to reproduce the problem if an exact description is given, sometimes
  14. we cannot due to one reason or another.  If a trace of what the program is
  15. currently doing at the customers site was available, then the developer would
  16. have a better idea of what went wrong and how to fix the problem.  Giving
  17. the user access to the original source may not be desirable, and while
  18. reporting the linear address in OS/2 2.0 may make the end user feel good,
  19. it is useless to the developer since the machine configurations may be
  20. different and therefore will have different addresses.  This is were
  21. programmers have a real mystery.  Unfortunately, usually with too many
  22. the number of pieces missing to allow the mystery to be solved.
  23.  
  24. This introduces a need into the developers market.  This need is to allow
  25. any end user with minimal instructions to provide the type of debugging
  26. information that the developer really needs.  Sherlock is a program to aid
  27. fellow programmers in this remote diagnosis of program problems.  This is
  28. done through an interfaceless debugger.  This debugger will load the desired
  29. executable for debugging, start the program and then continue until an
  30. exception occurs.  When an exception occurs, the entire program state
  31. is dumped.  Each thread will have the current register thread dumped and
  32. then attempt to trace the stack back to the root node of the thread.
  33.  
  34. Installation
  35.  
  36. Sherlock may be installed into any directory that is in you PATH statement.
  37. It is suggested that you create a new directory to store the files and add
  38. this directory to your PATH statement in CONFIG.SYS.  If you have debugging
  39. DLLs, they MUST be installed in the same directory as Sherlock.  Sherlock
  40. will search for the debugging DLLs only in the directory where Sherlock is
  41. stored.  For example:
  42.  
  43. C:      Make Drive C current.
  44. MKDIR C:\SHERLOCK       Create the Sherlock  directory.
  45. CHDIR C:\SHERLOCK       Change to the Sherlock  directory.
  46. COPY A:*.EXE *.EXE      Copy the executable.
  47. COPY A:*.DLL *.DLL      Copy the debugging  DLLs.
  48. E CONFIG.SYS    Add C:\SHERLOCK to the PATH 
  49.                         statement.
  50.  
  51. Usage
  52.  
  53. Usage of SHERLOCK is simple, even for most end users.  The target program is
  54. started as it would normally be started with the addition of listing
  55. Sherlock ahead of the target application.  For example, if you wish to test
  56. the program xyz.exe with the parameters a b.
  57.        Normal:
  58.        xyz a b
  59.  
  60.        With Sherlock:
  61.        Sherlock xyz a b
  62.  
  63. Sherlock supports only one command line argument -L which will specify the
  64. name of a new output file.  The name of the output file is specified
  65. immediately after the L.  For example:
  66.  
  67.         Sherlock -LCatch.log Catch.exe
  68.  
  69. What will I get out of Sherlock?
  70.  
  71. What Sherlock will generate is a log file of what the operator of a debugger
  72. would see.  Sherlock will try to load debugging information for each module
  73. which is loaded.  Sherlock's ability to decode debugging information is based
  74. upon loading a DLL to support each different debugging information format.
  75. Sherlock is capable of supporting many different debugging formats at the
  76. same time. Each module which is loaded by the target program will be sent to
  77. the support DLLs to ask whether it supports the debugging format of the
  78. module.  If it says yes, no other support DLLs will be ask whether they
  79. support the debugging format of the module.
  80.  
  81. Currently, only the following formats are supported:
  82.  
  83.         SYM files created by mapsym.
  84.         Microsoft C V6.0 debugging format
  85.         IBM C-Set/2 Version 1.0 and 2.x
  86.  
  87. VisualAge is not supported currently.
  88.  
  89. Sample Output
  90.  
  91. Sherlock will produce a log file with all of the output from Sherlock.  The
  92. log file is named SHERLOCK.LOG or whatever you specify on Sherlock's command
  93. line.  This file is placed into the current directory.  Each execution of
  94. Sherlock will overwrite any existing log file.  The output will be as
  95. described below.
  96.  
  97. Sherlock will log all exceptions that occur within the application to the log
  98. file.  Note that there are many types of exceptions.  Some are normal or
  99. diagnostic such as guard page exceptions.  Other exceptions are fatal, but
  100. can be recovered from.  Access Violations while usually fatal can be
  101. recovered from.  For example, C-Set/2 (Copyright IBM) can insert an exception
  102. handler which if not overridden will print a register state dump.  If
  103. overridden, it is possible to recover from the exception and continue
  104. program execution.
  105.  
  106. Sherlock will log ALL exceptions to the log file until the program has
  107. terminated. Each exception will be dumped as a set of blocks of the format:
  108. Exception block
  109. Register block
  110. Traceback from current function to first function.
  111. Code dump
  112.  
  113. For the descriptions of the different blocks below, the following program
  114. will be used:
  115.  
  116. /*
  117. ** Main entrance routine.
  118. */
  119. #define INCL_DOSDATETIME
  120. #include <os2.h>
  121.  
  122. int main(int argc, char **argv);
  123. int main(int argc, char **argv)
  124. {
  125.     DosOpen((PSZ)    0,         /* pszFileName */
  126.             (PHFILE) 0,         /* pHf         */
  127.             (PULONG) 0,         /* pulAction   */
  128.                      0,         /* cbFile          */
  129.                      0,         /* ulAttribute */
  130.                      0,         /* fsOpenFlags */
  131.                      0,         /* fsOpenMode  */
  132.             (PEAOP2) 0);        /* peaop2      */
  133.     return 0;
  134. }
  135.  
  136. This program will cause a GP fault somewhere within OS/2 since a number of
  137. NULL pointers is being passed in.  Note that the program will compile
  138. correctly without a complaint from the compiler.
  139.  
  140. Exception Block
  141. The "Exception type" block will list the type of problem that was hit and
  142. where. (e.g. - Except#: c0000005 Access Violation).  Some of the information
  143. in this block is gibberish if you have not worked with OS/2's exception
  144. mechanism.  Some of the information is gibberish even if you do work with
  145. OS/2's exception mechanism.  (Please refer to the BSEXCPT.H file from the
  146. OS/2 Toolkit for details as to what the Except # parameters mean.)  The text
  147. printed to the right of the 'Except #" is the description as defined by IBM.
  148. Note that you and others might define other exception numbers.  These might
  149. be used as signals to your application to signal some event.
  150.  
  151. For this exception number, an access violation occurred.  The exception
  152. address is the location where the error was detected.  This is usually the
  153. INSTRUCTION POINTER when the exception occurred.  For this exception type,
  154. there are two exception parameters.  The first for access violation is the
  155. address that caused the problem.  Zero would be a NULL pointer.  How that
  156. pointer was obtained cannot be found from this block.  It could have been
  157. a direct memory reference, or an indirect reference through one of the index
  158. registers in the CPU.  The module name while useful, is also misleading It
  159. is NOT the name of the module that actually had the fault, but the name of
  160. the program that was executing.  Note that in this case, there is no
  161. debugging information available where the exception actually occurred, so
  162. there is no function information available.
  163.  
  164. Exception type 1 at 1a050179
  165.   Except #: c0000005  Access violation
  166.   Flags:    00000000
  167.   Next Rec: 00000000
  168.   Except Addr: 1a050179
  169.   Num Parms: 2
  170.   Except 0: 00000000
  171.   Except 1: ffffffff
  172.   Module:   D:\SHERLOCK\CATCH\CATCH.EXE
  173.   Size:     18778
  174.   Timestamp:Mon Dec  6 21:27:20 1993
  175.  
  176.   Lo Function: UNKNOWN
  177.   Hi Function: UNKNOWN
  178.  
  179. Exception in thread: 1          ID of the thread where the exception occurred.
  180.  
  181. Register block
  182. Each thread of execution within the executing program will have its register
  183. set dumped.  The entire register set is dumped.  If you have the assembler
  184. source code for the section of code where the exception occurs, then you can
  185. relate the registers to what the program was trying to accomplish.
  186. Additionally, the segment register information is dumped.  These registers
  187. may be useful if you examine the limits.  For example, if a reference using
  188. the 'FS' register is made at offset of 0x32, then an exception will occur.
  189. By checking FSLim, then you would see that you have indexed too far into
  190. that segment.
  191.  
  192. For the test case given above, you can use the information from the Exception
  193. Block to possibly gather additional information.  For example, we know from
  194. above that the address 0 was causing a problem.  IF it was from a register
  195. index, then it would have to be from either the EBX, ESI or EDI registers.
  196. Without more information, you are still stuck.  You do not know which
  197. register is causing the problem, or if a register is causing a problem.
  198.  
  199. Pid: 000000f0   Tid:    00000001
  200. Cmd:        0   Value:  00716668
  201. Addr: 0002294c  Buffer: 00037fd4
  202. Len:  00000024  Index:  00000000
  203. MTE:  0000030c
  204. EAX: 00172a80  EBX: 00000000  ECX: 00060010  EDX:  00060007
  205. ESP: 0002291c  EBP: 00022ae4  ESI: 00000000  EDI:  00000000
  206. EIP: 0000c0a0  EFLAGS: 00002206
  207.  
  208. Carry Parity Aux Zero Sign Trap IntE Dir Flow IOPL Nested  Resume
  209.  
  210.   NC    PE      0  NE    0    0    1   DN  NO    2    0       0
  211. CSLim 1c000000  CSBase: 00000000  CSAcc: df  CSAttr: d0   CS:005b
  212. DSLim 1c000000  DSBase: 00000000  DSAcc: f3  DSAttr: d0   DS:0053
  213. ESLim 1c000000  ESBase: 00000000  ESAcc: f3  ESAttr: d0   ES:0053
  214. FSLim 00000031  FSBase: 00050030  FSAcc: f3  FSAttr: 00   FS:150b
  215. GSLim 00000000  GSBase: 00000000  GSAcc: 00  GSAttr: 00   GS:0000
  216. SSLim 1c000000  SSBase: 00000000  SSAcc: f3  SSAttr: d0   SS:0053
  217.  
  218. Traceback from current function to first function invoked
  219.  
  220. The traceback is the block that answers the question of how you got to where
  221. the problem was detected.  Sherlock takes the EBP, EIP of the current thread
  222. given by the register dump and tracks through the stack to where the program
  223. starts.  At each EBP location found, Sherlock will dump any location
  224. information that is available.  Sherlock assumes that EBP is used to save the
  225. Stack Frame base on every function call.  If this is not the case, then
  226. Sherlock may skip over functions in its trace.  Sherlock also assumes that
  227. EBP is pushed onto the stack immediately after the function has been called.
  228. If this is not the case, then Sherlock may become confused and give erratic
  229. results.  Sherlock will follow the EBP back to the beginning of the program.
  230.  
  231. Each block contains the EBP/EIP.  These are the Stack Frame pointer and the
  232. EIP of the routine.  The EIP is then translated by Sherlock into the Base
  233. Offset/ Relative Offset/ Object length/Object/ Module.  These can be used to
  234. translate into source locations if a map file of the module is known.  The
  235. Object is the Object or Segment in the linker's map file.  The Relative
  236. Offset is the offset within the Object.  By checking function addresses or
  237. line number information within the map file you can translate the Relative
  238. Offset into a function.
  239.  
  240. For unsupported debugging modules, a Hi and Lo function marker are also
  241. given.  These may be helpful or may not since these are based upon the
  242. exported functions of a module.  If a module only contains only a few
  243. functions exported, then the function markers will most likely be useless.
  244.  
  245. EBP:    00022ae4EIP:    1a02c0a0
  246.   Base: 1a020000Rel:    0000c0a0Len:    00010000
  247.   Object: 00000002
  248.   Module:   C:\OS2\DLL\DOSCALL1.DLL
  249.   Lo Function: DOSREAD
  250.   Hi Function: DOSCOPY
  251.  
  252. EBP:    00022b14EIP:    1a02a958
  253.   Base: 1a020000Rel:    0000a958Len:    00010000
  254.   Object: 00000002
  255.   Module:   C:\OS2\DLL\DOSCALL1.DLL
  256.   Lo Function: DOSREAD
  257.   Hi Function: DOSCOPY
  258.  
  259. The Function/Source/Line are the location where the program was trying to
  260. execute for the first block.  In later blocks, this is the location of where
  261. the prior function (earlier in the listing) was called from.  This entry
  262. shows the result of using one of the debugging support DLLs.  The function,
  263. source module and line number are therefore accessible and dumped.
  264.  
  265. EBP:    00022b3cEIP:    00010023
  266.   Base: 00010000Rel:    00000023Len:    00010000
  267.   Object: 00000001
  268.   Module:   C:\WORK\DEBUG\CATCH.EXE
  269.   Function: main
  270.   Source:   CATCH.C
  271.   Line:     9
  272.  
  273. This entry shows the same module as the above entry, but the area where the
  274. stack traced back to does not have debugging information, so the bounding
  275. functions are supplied as markers.  If the source code can be found, the
  276. source code will be listed after these blocks.  If there is no debugging
  277. information available, then a disassembly of the code will occur.  This
  278. disassembly will be where ever the EIP for the block is located.
  279.  
  280. EBP:    00022b58EIP:    00010596
  281.   Base: 00010000Rel:    00000596Len:    00010000
  282.   Object: 00000001
  283.   Module:   C:\WORK\DEBUG\CATCH.EXE
  284.   Lo Function: _RunExitList
  285.   Hi Function: UNKNOWN
  286.  
  287. EIP: 1a02c834, DLL: C:\OS2\DLL\DOSCALL1.DLL Func:  
  288. DOS32R3EXCEPTIONDISPATCHER
  289. 1a02c834  PUSH  EBP
  290. 1a02c835  MOV   EBP,ESP
  291. 1a02c837  SUB   ESP,0x00000100
  292. 1a02c83d  PUSH  EDI
  293. 1a02c83e  PUSH  ESI
  294. 1a02c83f  MOV   EAX,DWORD PTR [EBP 0xc]
  295. 1a02c842  CMP   DWORD PTR [EAX],0xc0010002
  296. 1a02c848  JE    $ 0x08
  297. 1a02c84a  CMP   DWORD PTR [EAX],0xc0010003
  298. 1a02c850  JNE   $ 0x0c
  299. 1a02c852  MOV   DWORD PTR [EBP 0],0x00000000
  300.  
  301.  
  302. Current Limitations
  303.  
  304. The current release has the current limitations:
  305.  
  306. 1)      Only the program started will be monitored.  Any subprocesses started
  307.         by the program will not be monitored.
  308.  
  309. 2)      The distributed version does not have the debugging DLLs.
  310.  
  311. 3)      The first release has an annoying flashing effect.  I am still
  312.         working to find why this is happening.  Hopefully the next version
  313.         will have this fixed.
  314.  
  315. 4)      Crossing 16-32 bit function calls is not currently supported for the
  316.         stack trace.
  317.  
  318. 5)      Tracing of 16 bit stacks is difficult and may be incorrect for code
  319.         with near calls in the trace.
  320.  
  321. Copyright and Legal Information
  322.  
  323. Sherlock is Copyrighted (c) 1992 - 1995 by Harfmann Software.
  324.  
  325.  
  326. Adding new Interfaces
  327.  
  328. Sherlock is designed to look in the startup directory where Sherlock has been
  329. installed to find support DLLs.  Any file  with a DLL extension is loaded and
  330. examined for two  functions to be exported by name called:
  331.  
  332.     isKnownModule
  333.     linkPriority
  334.  
  335. Link Priority
  336. Link priority is used to determine the order used to try and resolve symbolic
  337. information.  When Sherlock first starts, it tries to load every DLL in the
  338. Sherlock directory assuming that it is a support DLL for Sherlock.  It will
  339. then invoke the linkPriority function to build an internal linked list of
  340. support DLLs.
  341.  
  342. /*
  343. ** Answer the linkage priority.
  344. ** Return 1 for insert in front of list
  345. **      (first crack  at linking)
  346. ** Return 0 for add to end of list.
  347. **      (last crack  at linking)
  348. */
  349. int _System linkPriority(void)
  350. {
  351.     return 1;
  352. }
  353.  
  354. When Sherlock tries to load a module, it iterates through each support DLL in
  355. the linked list to try and determine whether the debugging format of the
  356. module is known to that support DLL.  For example, the SYM file support is
  357. given 0 priority and HLL (C Set++) is given 1 priority to allow the HLL
  358. format to take precedence over the SYM format.
  359.  
  360. Supporting a Debugging Format
  361.  
  362. When a module is loaded, Sherlock iterates through all of the support DLLs to
  363. determine if any of them can find the debugging information for the module.
  364. If the module does understand the format, it returns true, otherwise it
  365. returns false.  This is done with the isKnownModule function shown below:
  366.  
  367. /*
  368. ** Answer whether the module named is a HLL module.
  369. ** If so, set the function pointers and return true.
  370. */
  371. int _System isKnownModule(DebugModule *module,
  372.                            int (* _System DispatchCommand)
  373.                                         (int  command),
  374.                                          DebugBuffer *buffer)
  375. Where:
  376. module - Pointer to a structure that will be used to reference the module for
  377. as long as the module is loaded.  All elements except AuxData and the
  378. function pointers should be considered static and should not be changed.
  379. AuxData and the function pointers should be filled in if the module
  380. understands the debugging format of the module.
  381. DispatchCommand - Function pointer to be used by the support DLL if it needs
  382. to use the DosDebug API.
  383. buffer - Pointer to the buffer used by DosDebug().
  384.  
  385. typedef struct _DebugModule {
  386.     void   *nextModule; // Internal - DO NOT MODIFY
  387.     char   *name;               // Name of file as returned by  DosQueryModuleName
  388.     void   *AuxData;            // Spare pointer for support  DLL usage
  389.     void   *ViewData;           // Internal - DO NOT MODIFY
  390.     time_t  fTimestamp; // Time stamp of the module
  391.     ULONG   fileSize;           // File size of the module
  392.     ULONG   MTE;        // Module handle
  393.     ULONG   typeFlags;  // Module flags return by  DosQueryAppType
  394.  
  395.     /*
  396.     ** Module cleanup.  Free any support structures.
  397.     */
  398.     void    (* _System FreeModule)(
  399.                        struct _DebugModule *module);    /* Module  handle */
  400.  
  401.     /*
  402.     ** Source functions.
  403.     */
  404.     int     (* _System FindSource)(             /* 1 found / 0  - not found      */
  405.                         struct _DebugModule *module,    /* Module  handle */
  406.                         ULONG eipOffset,/* EIP for function to  find  */
  407.                         char *funcName, /* Buffer for function  name  */
  408.                         char *sourceName,       /* Buffer for source  code       */
  409.                         ULONG *lineNum);/* Pointer to line  number       */
  410.  
  411.     ULONG   (* _System FindSourceLine)( /* Return offset of  file/line*/
  412.                         struct _DebugModule *module,    /* Module  handle */
  413.                         int line,               /* Line to find          */
  414.                         char *fileName);/* File name             */
  415.  
  416.     ULONG   (* _System FindFuncAddr) (  /* Return offset of  function */
  417.                         struct _DebugModule *module,    /* Module  handle */
  418.                         char *name);            /* Function name.                 */
  419.  
  420.     /*
  421.     ** Variable functions.
  422.     */
  423.     int     (* _System GetName)(                /* State return  value from above       */
  424.                         struct _DebugModule *module,    /* Module  handle */
  425.                         State *state,           /* State information to  retrieve.
  426.                                                 ** Contains name of variable.  */
  427.                         State *state2);         /* If !NULL: state2  is element of
  428.                                                 ** structure given in state.  */
  429.  
  430.  
  431.     int     (* _System GetArray)(               /* State return  value from above */
  432.                         struct _DebugModule *module,    /* Module  handle */
  433.                         State *state,           /* Name of array         */
  434.                         State *state2);         /* Index of element  to retrieve */
  435.  
  436.     int     (* _System GetNumMembers)(  /* Return # of  elements 0 if not a structure */
  437.                         struct _DebugModule *module,    /* Module  handle */
  438.                         State *state);          /* Variable to query and  program state */
  439.  
  440.     /*
  441.     ** Get the name of the index'th structure element
  442.     */
  443.     int     (* _System GetMemberIndex)( /* State return  value from above */
  444.                         struct _DebugModule *module,    /* Module  handle */
  445.                         State *state,           /* Program state          */
  446.                         int MemberIndex,/* Index of element to  retrieve  */
  447.                         char *name);            /* Buffer to return name  into   */
  448. } DebugModule;
  449.  
  450. The FreeModule and FindSource function pointers must be filled in to support
  451. the current functionality of Sherlock.  The other function pointers may be
  452. used in future versions.
  453.