

                     **** W A R N I N G ****

    The documentation for this program is typesetted exactly like the
    Borland manuals. When this documentation is converted to plain ascii
    it doesn't look very pretty. Especially graphics are distorted,
    don't see chapter 4.

    This manual is primarily intended to give you an idea of its
    contents. Registered users receive a printed and bound manual.
    Besides they receive the manual in postscript format on disk.

                            ****  ****
















                                                       Borland Pascal Debug Kit
  

                                                              Programming Guide





                                                                   Version 1.10

















                                                                      NederWare
                                                                Burgerstraat 22
                                                                5311 CX Gameren
                                                                The Netherlands
                                                        compuserve: 100120,3121
                                                    email: berend@beard.nest.nl
                                                          fidonet: 2:281/527.23


C           O          N           T         E         N          T           S





Introduction Program with less pain         1     Chapter 5 Assertions                     23
Why this tool?........................      1    
Features  .............................     1    
                                                  Part 2   Reference Manual
What's in this manual  .................    2    
Typefaces used in this manual  ..........   2    
                                                  Chapter 6  Pascal Debug kit
How to contact NederWare   ............     3    
                                                    Reference                              27
Acknowledgements ...................        3    
                                                  Sample procedure  ....................   27
                                                  Archive constant......................   27
                                                  Assert procedure   ..................... 28
Part 1   User Manual
                                                  Beep procedure.......................    28
Chapter 1  Working with log files           7     BeepOff procedure....................    28
How to create or open a log file .........  7     BeepOn procedure  ....................   28
How can you write information in the              BrowserRecordSize variable............   29
log file  ..............................    8     BrowsersOffset variable   ...............29
                                                  ClassesOffset variable  .................29
Chapter 2 Checking your memory              9     ClassRecordSize variable   ..............29
What you must do before MemCheck                  CMPB function  .......................   29
can work   ............................     9     CMPW function  ......................    30
    Updating the standard library  ........ 9     ConvMemLimit variable...............     30
    Recompiling the system unit  ......... 10     CorrelationRecordSize variable  .........30
What MemCheck does  ................       10     CorrelationsOffset variable............. 30
How to enable memory checking  .......     11     CPos function  ........................  30
How MemCheck works  ...............        11     CR constant ..........................   30
MemCheck and DPMI.................         12     CreateBAK procedure  .................   31
                                                  DataOffset variable   ...................31
Chapter 3 Post Mortem Debugger             13    
                                                  DateTime type  .......................   31
What the Post Mortem debugger does  ...    13    
                                                  DebugHeader variable   ................  31
Using the Post Mortem Debugger.......      14    
                                                  DebugInfoStart variable  ............... 31
The Post-Mortem-Debugger under
                                                  Delay variable........................   32
MS-Windows  ........................       16    
                                                  dfXXXX constants  ....................   32
    Extra features under Windows  .......  16    
                                                  DirStr type...........................   32
    GPF's under Windows  ..............    16    
                                                  Discard procedure  ....................  32
Chapter 4 TDInfo                           17     DisposeSLink procedure...............    33
How TDInfo models Borland's Debug                 DonePMD procedure..................      33
Information  ..........................    17     DoneStrings procedure  ................  33
Changes between Open Architecture                 DosCopy procedure...................     33
names and TDInfo names..............       19     DosDel procedure   ....................  34
How to make use of TDInfo  ............    20     DosDelay procedure   ..................  34
    Initializing the TDInfo unit...........20     DosMove procedure  ..................    34
    Getting information from a debug              DosTouch procedure  ..................   35
    symbol file.........................   20     DosWipe procedure  ...................   35
    Getting line numbers..............     21     DStream variable  .....................  36
    Getting procedures  ...............    21     DumpStack variable  ..................   36
                                                  DumpStackProcedureType type   ........   36


                                                                                            i







Empty function.......................      36     LoadStrings procedure  ................  46
ExtractStr function  ....................  36     LogError procedure  ...................  46
ExtStr type...........................     36     LowCase function   ....................  46
FancyStr function  .....................   37     LowStr function  ......................  46
FatalErrorText variable  ................  37     MaxConventionalMemoryBlock constant      46
FCreate function......................     37     MaxMemPtrs constant.................     46
FDefaultExtension function  ............   37     MaxWord constant....................     47
ferr variable..........................    37     MemberRecordSize variable............    47
FExpand function.....................      38     MembersOffset variable  ...............  47
FF constant   ..........................   38     MemCheckReport procedure ...........     47
FForceDir function....................     38     memfXXXX constants  .................    47
FForceExtension function  ..............   38     Min function  .........................  48
FileExist function  .....................  38     ModuleClassesOffset variable  .......... 48
FileRec type..........................     38     ModuleClassRecordSize variable   ....... 48
fmXXXX constants  ....................     39     ModuleRecordSize variable  ............  48
FOpen function.......................      39     ModulesOffset variable................   48
FormatStr procedure  ..................    39     NamesOffset variable   ................. 49
FormFeed constant....................      39     NameStr type  ........................   49
FTCopy function  .....................     40     NewSLink function  ...................   49
GetAddrStr function  ..................    40     OverloadRecordSize variable...........   49
GetDateStr function ...................    40     ParentRecordSize variable  ............. 49
GetEnv function  ......................    40     ParentsOffset variable  .................49
GetFileName function  .................    40     PathStr type..........................   50
GetLogicalAddr function  ..............    40     PrintError variable  ....................50
GetObjMemory function...............       41     PrintErrorType type...................   50
GetStr function  .......................   41     prnXXXX constants  ...................   50
GetTextFileName function  .............    41     PSLink type..........................    51
GetTickCount function  ................    41     Registers type  ........................ 51
GetTimeStr function...................     42     RepChar function.....................    51
GetUniqueFileName function  ..........     42     ReplaceStr procedure..................   51
HandleRunTimeError variable  .........     42     ReportFileName constant  ..............  51
      HandleRunTimeErrorProcedureType type 42     RightJustify function  ..................52
HexB function........................      42     rsGet function  ........................ 52
HexStr function  ......................    42     rsGet1 function  ....................... 52
InitBBError function  ..................   43     rsGet2 function  ....................... 52
InitIntHandler procedure  ..............   43     ScanB function  .......................  53
InitObjMemory procedure  .............     43     ScanW function  ......................   53
InitPMD procedure  ...................     43     ScopeClassesOffset variable  ............53
IsDirectory function...................    44     ScopeClassRecordSize variable  ......... 53
IsFileOpen function  ...................   44     ScopeRecordSize variable  .............. 53
IsValidPtr function....................    44     ScopesOffset variable  ................. 53
LeadingZero function  .................    44     scXXXX constants.....................    53
LeftJustify function   ................... 45     SearchRec type  .......................  54
LF constant  ..........................    45     SegmentRecordSize variable   ........... 54
LineNumberRecordSize variable........      45     SegmentsOffset variable  ............... 54
LineNumbersOffset variable  ...........    45     SetHandleCount procedure  ............   54


                                       ii







SmallDebugHeaderSize constant  .......     55         Methods...........................   67
SmallEndianI procedure  ...............    55     TObjMemory object  ...................   67
SmallEndianL procedure  ..............     55         Fields  .............................67
SmallEndianW procedure..............       55         Methods...........................   67
SourceFileRecordSize variable  ..........  55     TOverload type.......................    68
SourceFilesOffset variable  .............  55     TParent type  .........................  68
Spc function  .........................    56     TResourceCollection object.............  69
Spoiled function  ......................   56         Methods...........................   69
StrB function  .........................   56     TResourceFile object   ..................69
StrI function  .........................   56         Fields  .............................69
Strings variable.......................    56         Methods...........................   69
StripSpc function  .....................   56     TResourceItem type...................    70
StrL function  .........................   57     TrimRight function....................   70
StrR function.........................     57     TScope object  ........................  70
StrResBufSize variable.................    57         Fields  .............................70
StrS function  .........................   57         Methods...........................   71
StrW function  ........................    57     TScopeClass type  .....................  71
stXXXX constants  .....................    57     TSegment object  ......................  71
SymbolRecordSize variable   ............   58         Fields  .............................71
SymbolsOffset variable  ................   58         Methods...........................   72
TBrowser object   ......................   58     TSLink type..........................    73
    Fields  .............................  58     TSmartBufStream object  ...............  73
    Methods...........................     59         Fields  .............................73
TClass object  .........................   59         Methods...........................   73
    Fields  .............................  59     TSourceFile object  .................... 73
    Methods...........................     59         Fields  .............................74
TCorrelation object....................    59         Methods...........................   74
    Fields  .............................  60     TSymbol object  .......................  74
    Methods...........................     60         Fields  .............................74
TDebugHeader type   ..................     60         Methods...........................   74
TDInfoPresent function................     62     TType object   ......................... 75
TDriveStr type   .......................   62         Fields  .............................76
TextPrintError procedure  ..............   62         Methods...........................   76
TextRec type  .........................    63     TypeRecordSize variable...............   77
tid voidXXXX constants  ...............    63     TypesOffset variable   ..................77
TLineNumber object  ..................     63     UpStr function   ....................... 77
    Fields  .............................  63     ValB function.........................   77
    Methods...........................     63     valcode variable  ...................... 77
TMember object  ......................     63     ValHex function  ......................  77
    Fields  .............................  65     ValI function  ......................... 78
    Methods...........................     65     ValL function.........................   78
TModule object.......................      65     ValR function  ........................  78
    Fields  .............................  65     ValW function........................    78
    Methods...........................     66     Warning procedure   ...................  78
TModuleClass type  ...................     66     ZeroRightJustify function  ..............79
TObject object  ........................   67    


                                       iii







 Appendix A Errors and omissions
             in Open Architecture
             Handbook                      81    













































                                       iv








I     N       T     R      O       D      U      C       T     I      O       N



                                                         Program with less pain


Why this tool?

  Once, I tracked down a bug that caused some strange and
  unpredictable behaviour. After finally having located the problem,
  it appeared that I had released a bigger block of memory than
  what I had previously allocated. Borland Pascal, naturally, didn't
  like this. Well, neither did I since, all in all, this bug took me 10
  hours to find and fix.
  Some time later, I encountered another bug that caused a
  behaviour which I remembered but all too well. At that stage I
  thought: let's put these 10 hours to some better use.

  That thought was the start of  MemCheck, a utility that tracks
  memory allocations and deallocations. If you release too much
  memory or too little,  MemCheck  aborts your application, writing
  the address of the erroneous code to a log file.
  MemCheck  has since been enhanced to write the name of the file
  and the line of code using  TDInfo, a utility that accesses the Debug
  information, if present, in an executable file.

  The above mentioned tools,  MemCheck,  TDInfo  and  PMD, together
  with  Assertions, a unit which provides you with C like assertions,
  form the Borland Pascal Debug Kit. It's my sincere hope that these
  tools will prove useful to Pascal programmers all around the
  world, helping them to develop bug-free programs easier and
  faster.


Features

  The Borland Pascal Debug Kit provides you with many things that
  make debugging easier. If you use these tools properly, you can
  even be sure that some bugs will not occur in your programs! For
  example, releasing more or less memory than previously allocated
  immediately halts your program and the offending line is written
  to a log file.
  In summary, the Borland Pascal Debug Kit gives you:


Introduction                                                                  1







   Allocation and deallocation tracking
   A report of not deallocated memory after your program
    termination
   A full stack dump (procedure names and parameters) if a
    run-time error occurs
   C-like assertions to `fortify your subsystems'


What's in this manual


  This manual explains how to use the Borland Pascal Debug Kit. It
  doesn't tell you how to write Borland Pascal programs.

  The first part of this manual contains the following chapters:

   Chapter 1, ``Using log files'', describes how to use log files to
    make debugging easier, especially when your program is
    running at a remote site.
   Chapter 2, ``Memory Checker'', introduces the memory
    allocation and deallocation tracker and explains how to use it.
   Chapter 3, ``Post Mortem Debugger'', describes how to get
    annotated stack dumps.
   Chapter 4, ``TDInfo'', explains a unit which gives access to
    Borland's debug information.
   Chapter 5, ``Assertions'', introduces you to assertions and how
    to use them in your programs.

  The second part of this manual is contains reference material for
  the source code supplied with the Borland Pascal Debug Kit.


Typefaces used in this manual


  This manual was typeset using LATE X2, converted to postscript
  using dvips, and printed on a HP Laserjet IV. All typefaces used in
  this manual are standard Postscript fonts. Their uses are as
  follows:

  The monospace typeface represents text as it appears on-screen or
  in a program. It is also used for anything you must type (e.g. BP          to
  start up the Borland Pascal IDE).

  The boldface typeface is used in text for command line options.

  Italics is used to indicate identifiers that appear in text. They can
  represent terms that you can use as they are, or that you can think


2                                               Borland Pascal Debug Kit Manual







  up new names for (your choice, usually). They are also used to
  emphasize certain words, such as new terms.

  This typeface indicates a key on your keyboard. For example,
  ``Press  Esc  to exit this menu.''

  Key combinations produced by holding down one or more keys
  simultaneously are represented as  Key1+Key2.


How to contact NederWare


  NederWare offers a variety of services to answer your questions
  about the Borland Pascal Debug Kit. Of course, this is only true for
  registered  users. If you haven't registered yet, support shareware
  (and me and my family) by registering this Debug Kit today.
  We've made it very easy for you. You can register by CompuServe,
  fax, email or telephone. Please consult REGISTER.FRM for more
  details.

  CompuServe

  Subscribers to CompuServe can reach NederWare at 100120,3121. 
  The latest version of the Borland Pascal Debug Kit is available in
  library 3 of the BPASCAL forum.

  Internet

  NederWare can be reached on the Internet through
  NederWare@beard.nest.nl or berend@beard.nest.nl. For the latest 
  Debug Kit version, check-out garbo.uwasa.fi:/pc/turbopas.

  Fidonet

  NederWare can be reached on Fidonet at 2:281/527.23. The latest
  version of the Borland Pascal Debug Kit is also available at
  2:281/527, Contrast BBS, The Netherlands. Its full international
  telephone number is +31 70-3234903. From Holland dial
  070-3234903.


Acknowledgements


  First and foremost, many thanks to Stephen Maguire, who in his
  excellent book [Maguire93] showed us all that writing bug free
  (well, almost) code is possible. If you are serious about writing
  commercial software, read this book! It inspired me to put together
  all the pieces I had accumulated over the years in this package.


Introduction                                                                  3







  Many thanks also to Andy McFarland, author of TDI, a utility that
  displays the contents of the debug information the the BP compiler
  appends to an executable. Andy generously provided me with the
  source of his utility, which gave me a jump start in developing my
  TDInfo               unit. Andy can be reached at CompuServe as 71055,2743 or
  by email as amcfarl@ndlc.occ.uky.edu.

  Last but not least thanks to Dag Hovden
  (dhovden@runner.knoware.nl) for cleaning up this manual and
  improving its english. All remaining faults are mine.







































4                                               Borland Pascal Debug Kit Manual




















P                        A                         R                          T


                                                                              1



                                                                    User Manual


























                                                                              5























































6                                               Borland Pascal Debug Kit Manual








C            H            A            P            T            E            R


                                                                              1



                                                         Working with log files


  Log files form the basis of Post Mortem Debugging. Using log files
  means no more scribbling down error codes and addresses on a
  piece of paper when your program crashes because it's already all
  there, in the log file. This feature becomes especially useful when
  you use it in tandem with  PMD, the Post Mortem Debugger,
  because  PMD  gives you a full symbolic stack dump. Try to write
  that down as it scrolls past you on the screen!
  Another benefit of using log files is that you know exactly what
  your program was up to when it crashed running at a customer
  site. No more long telephone conversations, simply ask the
  customer to modem (or mail) you the log file.


How to create or open a log file

  The unit  BBError  makes using log files easy. All you have to is to
  call  InitBBError  with the name of the logfile and a Boolean
  parameter saying to create or to open the log file if it already exists.
  The following program demonstrates this:

     program Test;

     uses
       BBError;

     begin
       writeln('Installing log file writing');
       InitBBError('TEST.LOG', TRUE);
       writeln('Done!');
     end.

  A call to the procedure  InitBBError  does two things:

  1. It creates the log file if it does not exists or if the second
     parameter is FALSE. The file is opened in append mode if it
     exists and the second parameter is TRUE.


Chapter 1, Working with log files                                             7







  2. It installs an exit procedure which writes the error address and
     exit code to the log file if an error occurs.
     This exit procedure also dumps a stack trace to the log file with
     the addresses of all the callers that lead up to the code that
     caused the error.


How can you write information in the log file


  You can write your own data to the log file using the  ferr  text file
  variable by adding  BBError  to the  USES  statement of any unit that
  wants to write to the log file.

  Example:

     program WriteToferr;

     uses
       BBError;

     begin
       InitBBError('TEST.LOG', TRUE);
       writeln(ferr, 'This goes to the log file.');
     end.

  A second method is to call BBError's  LogError  procedure which
  first writes the current date and time to the log file and next any
  text you passed to it. Calling LogError is the preferred method.

  Example:

     program WriteToLogError;

     uses
       BBError;

     begin
       InitBBError('TEST.LOG', TRUE);
       LogError('This goes to the log file.');
     end.










8                                               Borland Pascal Debug Kit Manual








C            H            A            P            T            E            R


                                                                              2



                                                           Checking your memory


  Use the  MemCheck  unit, if you want to check if your allocations
  match your deallocations or if you want to check if you didn't
  overwrite memory before or after an allocated memory block. This
  chapter tells you how to use  MemCheck  and what  MemCheck  does.


What you must do before MemCheck can work


  Before you can use  MemCheck  you need to replace the standard
  SYSTEM unit by the SYSTEM unit supplied with the Borland
  Pascal Debug Kit. There are two cases:

   If you always work with the Borland Pascal library TURBO.TPL,
    TPP.TPL or TPW.TPL, you need to replace the SYSTEM unit
    contained in the library by the supplied one. Below we will
    explain how to do this.
   If you don't load the library at startup but instead link in the
    units as separate files, simply copy the supplied SYSTEM.TPx to
    the directory where you keep these units.
  
             Updating the    
  Replacing SYSTEM.TPx is quite easy. First remove the `old'
         standard library    
  SYSTEM.TPU from TURBO.TPL (or TPP.TPL or TPW.TPL, please
  substitute as needed). You can do this with the following
  command:

       tpumover turbo -system

  Next install the new SYSTEM.TPU by:

       tpumover turbo +system.tpu

  To install the new SYSTEM.TPW in the windows library type:

       tpumover tpw +system.tpw

  Note: don't forget tot type the correct extension after sytem, e.g.
  .tpu, .tpp or .tpw!


Chapter 2, Checking your memory                                                             9






  
          Recompiling the    
  If you have the run-time library source, and if you want to
              system unit    
  recompile the SYSTEM unit with our changes incorporated, you
  need to patch the original SYSTEM.PAS, HEAP.ASM and
  WMEM.ASM with the diff (i.e. difference) files SYSTEM.DIF,
  HEAP.DIF and WMEM.DIF. Please note: these files are new
  structure diff files, created with the GNU diff utility.

  You can use the GNU patch utility (we use the one coming with
  the DJGPP GNU C compiler port) to patch your run-time library
  source. For example, execute

       patch system.dif c:/bp/rtl/sys/system.pas

  to update the SYSTEM.PAS file in the C:/BP/RTL/SYS directory.


What MemCheck does


  The  MemCheck  unit checks for the following three bugs:

   Attempts to dispose a pointer, specifying a size other than the
    value used when creating the pointer. This causes a program
    halt.
   Allocating memory and never deallocating it (i.e. memory
    leakage). By calling  MemCheckReport, you obtain a list of
    pointers still allocated together with the addresses in your
    program where these pointers were created.
   Writing to memory that lies outside an allocated block of
    memory, i.e. before or after the block. This check is equivalent to
    the Range check ({$R+}) option for normal arrays. This bug also
    causes a program halt.

  The demo program TestMem demonstrates these three bugs.
  Execute TestMem and select one of the three options presented to
  you. Afterwards, see TESTMEM.LOG for choice 1 and 2, and
  MEMCHECK.RPT for choice 3.

  If you have appended debug information to your executable and if
  you have initialized the post mortem debugger,  MemCheck  will
  also print the source file, line number and procedure name where
  the error occured. This is also true for MEMCHECK.RPT. When
  debug informaton is appended, you will find in MEMCHECK.RPT
  the source file and line number file where the allocation occured
  and on the next indented line the source file and line number of
  the caller of that routine (if any).




10                                                            Borland Pascal Debug Kit Manual







How to enable memory checking


  You enable  MemCheck  by placing it in the USES clause of your
  main program. No additional calls are needed. Please bear in
  mind that using  MemCheck  will cost you an extra 32 bytes per
  allocated memory block!

  Errors detected by MemCheck             are normally printed to the screen. A
  much better alternative is to use  MemCheck  together with  BBError,
  the log file unit. When you use  BBError  the errors will be written
  to a log file instead.


How MemCheck works


  MemCheck  keeps track of every allocated pointer and the size
  associated with it. For each allocated pointer, 16 bytes are needed
  to store this information. The information itself is stored in a
  collection which can hold a maximum of 16384 pointers. It is easy
  to enlarge this but as yet, there has been no need for that. If you
  should need more, please let me know and I'll enhance  MemCheck.

  If possible,  MemCheck  allocates 8 or 16 bytes more for every
  memory block than what your program asked for. Because the
  maximum memory block that can be allocated is 65536-8 bytes,
  MemCheck  always checks if it is possible to allocate 8 or 16 bytes
  more.

   If it is possible to allocate 16 bytes more,  MemCheck  is able to
    check for memory overwrites before and after your allocated
    memory block.  MemCheck  does this by filling the first and last 8
    bytes with the value 0CCh. When you dispose this memory
    block,  MemCheck  checks if these values are still there. If not,
    memory has been overwritten and  MemCheck  halts your
    program.
   If it is possible to allocate only 8 bytes more,  MemCheck  only
    checks for overwrites at the end of your memory block.

  To recap: every pointer needs 16 bytes to store information about
  it plus an extra 16 (or 8) bytes to check for 'out of range' errors.
  Together, this accounts for the 32 (or 24) bytes extra per allocated
  block. Under protected mode or in Windows, this is not a serious
  problem. Under real mode, however, this could well be a serious
  obstacle in using  MemCheck, especially if your application uses
  many, small memory blocks.



Chapter 2, Checking your memory                                              11







MemCheck and DPMI


  When running under protected mode, as some of you may know,
  you can check for writing outside an allocated memory block by
  setting  HeapLimit  to zero. This will enable the processor's built-in
  segment checking. The problem with this approach is that you can
  easily run out of selectors, especially if you use  MemCheck  at the
  same time. As we've seen above,  MemCheck  allocates a 16 byte
  memory block from the heap for every pointer (and uses that block
  to store information about the pointer). With  HeapLimit  set to zero,
  every allocation actually costs you  two  selectors!





































12                                                            Borland Pascal Debug Kit Manual








C            H            A            P            T            E            R


                                                                              3



                                                           Post Mortem Debugger


  The Post Mortem Debugger gives you the ability to print full
  symbolic stack dumps when an error condition occurs in your
  program. This is a feature you will not want to miss as soon as you
  have used it to develop programs.


What the Post Mortem debugger does


  Currently, the name debugger is a bit of a misnomer. The Post
  Mortem Debugger does nothing more than hooking into the exit
  procedure chain and dumping the stack, with all procedure names
  and parameter values, to the log file if an error occurs.
  If you have enabled the Post Mortem Debugger,  MemCheck  writes
  actual source file names and line numbers instead of hexadecimal
  addresses when it creates its report of not disposed memory.

  An example of the log entries created when a program exits with a
  fatal error is:

     ** Program started on 1994-02-26 at 19:07:38 **
     Post Mortem Debugger (PMD) installed.
     1994-02-26 19:07:38 Error 215 at 0001:0031
     1994-02-26 19:07:38 TESTPMD.PAS (26) procedure
  InSide(100,0,0);
     1994-02-26 19:07:38 *** Full stack dump ***
       TESTPMD.PAS (31) procedure
  TestError((Open,'TESTPMD.BAK'),test2);
       TESTPMD.PAS (44)

  This is the log file created if you run the TestPMD program.

  The advantage of a symbolic stack dump is great, perhaps far
  greater than you would expect. You find errors much faster due to
  the full stack trace created. Previously, you could only get such an
  overview when running inside the debugger. For a frequently


Chapter 3, Post Mortem Debugger                                              13







  called procedure, failing perhaps only every 100th time, it is not
  really easy (or much fun) to use breakpoints to wait for the correct
  moment to halt and view the stack.

  The Post-Mortem debugger has one flaw which shows up
  sometimes. If the stack trace consists of near calls,  PMD  may not
  always be able to find the address. As soon as a far call occurs in
  the stack frame, the stack dump is correct from that point on.
  Currently we do not know how to fix this. Luckily this case occurs
  seldom.


Using the Post Mortem Debugger


  To use the Post Mortem Debugger, you need to include three units
  in your USES clause. And you need three calls to initialize these
  three units. The order in which you call these routines is
  important. The order in which they appear in the USES clause is
  not important.

  The three units are  BBError,  ObjMemory  and  PMD. The
  initialization routines in their correct oder are  InitBBError,
  InitObjMemory  and  InitPMD.

  The program listed below, also available as TESTPMD.PAS on
  your distribution disk, demonstrates the Post Mortem Debugger.

     {$Q+}
     program TestPMD;

     uses
       BBError, BBUtil,
       Objects, ObjMemory,
       {$IFDEF Windows}
       WinCrt,
       {$ENDIF}
       PMD;

     type
       types = (test1, test2, test3);


     procedure  TestError(var  f : text; e : types);

       procedure  InSide(o : TObjMemory);
       var
         w : word;
         d : word;
       begin
         w := 0;


14                                                            Borland Pascal Debug Kit Manual







         w := w - 1;
       end;

     begin
       InSide(GetObjMemory(100, 0, memfAll)^);
     end;


     var
       f : text;
     begin
       writeln(prnCR, 'Post Mortem Debugger tester.');

     (* initialize *)
       InitBBError('TESTPMD.LOG', TRUE);
       InitObjMemory;
       InitPMD(dfStandard);

     (* and show the error *)
       Assign(f, 'TESTPMD.PAS');
       Reset(f);
       TestError(f, test2);
     end.

  As you can see, there are three initialization routines that must be
  called before  PMD  can be used:  InitBBError  to open or create a log
  file,  InitObjMemory  to initialize the memory management routines
  that  PMD  uses, and (finally)  InitPMD  to initialize the Post Mortem
  Debugger itself. The  InitPMD  procedure accepts certain flags on
  which the Post Mortem Debugger bases its behaviour. The default
  is  dfStandard, you will get only a symbolic stack dump when an
  error occurs. If you specify the  dfDataSeg  flag as well
  (InitPMD(dfStandard + dfDataSeg)             ) the datasegment is also dumped
  to the log file.

  Compile the example program with full debug information
  enabled by typing

       tpc -m -l -v testpmd

  at the Dos command line prompt. The program contains a
  delibarate error, namely an overflow bug (therefore it has to be
  compiled with {$Q+}).. Run it, then look at the created log file
  TESTPMD.LOG.

  To use  PMD  you need quite some free memory. For large
  programs this can be as much as 100K or even more. Under real
  mode, the Post Mortem Debugger uses EMS or XMS memory if
  that's available.



Chapter 3, Post Mortem Debugger                                              15







The Post-Mortem-Debugger under MS-Windows


  Under MS-Windows the Post-Mortem debugger has some extra
  capabilities including the ability to give a stack dump when a gpf
  occurs.
  
           Extra features    
  When the  PMD  unit is included in a windows program, you get
            under Windows    
  the following extra features: MS-Windows:

   Any text send to OutputDebugString is written to the log file
    also.
   Any error Windows displays, is written to the log file also.
   If you make a parameter error when calling a Windows
    function, this is logged. Almost no need for WinScope or
    BoundsChecker anymore!
  
              GPF's under    
  GPF's are not caught under MS-Windows unlike under DPMI. If
                  Windows    
  your program crashes, Windows will still display the UAE dialog
  box. If you want a symbolic stack dump in this case, you need to
  call  InstallIntHandler  in the  PMD  unit.

  InstallIntHandler  is not called by default, because I couldn't get it
  work right when using the debugger. Without the debugger,
  everything runs fine. When I use the debugger to examine a
  program, leave the debugger to go back to the IDE, edit something
  and recompile, I'm bombed back to Dos, or Windows just hangs. I
  would be indebted, if anyone has a fix for this.




















16                                                            Borland Pascal Debug Kit Manual








C            H            A            P            T            E            R


                                                                              4



                                                                         TDInfo


  The  TDInfo  unit is used by  MemCheck  and  PMD  to provide access
  to the debug info stored in your executable. Information about this
  debug info can be found in [Borland92]. You probably need this
  book if you want to use  TDInfo  yourself.
  Because this books contains quite a few errors, I refer the reader to
  appendix A where I've listed OA.TXT, a file containing bugs and
  omissions to chapter 4 of Borland's "Open Architecture Handbook
  for Pascal".


How TDInfo models Borland's Debug Information


  Since the presented information in [Borland92] is less than clear,
  I've used semantic principles (see [Bekke92]) to make the
  information contained in the debug symbol tables and their
  mutual relationships clearer. I'll explain the semantic approach
  using the Turbo Debug Semantic Model presented in figure 4.1.
  Semantic data modeling has two notions: types and relationships.
  Types are basic data elements. Types are not stand-alone, but are
  related to each other. As can be seen in figure 4.1, types are drawn
  as rectangles. Relationships between types are drawn as lines.
  A type can be related to another type in two ways:
   When a type consists of other types we talk about an aggregated
    type.
   When a type is a special kind of another type, we talk about a
    specialized type.
  Aggregation can be seen in the Browser type, the uppermost
  rectangle. Somewhat simplified, aggregation can be said to be
  equal to the Pascal record type. A record type has a name and
  consists of various fields. So does the Browser. Each Browser
                                                              1 to a SourceFile
  record in the symbol table consists of a pointer
                         1The Symbol table actually does not store pointers, but record numbers


Chapter 4, TDInfo                                                            17













               Figure 4.1    
     Turbo Debug Semantic                           Browser
                    Model                                    



                                          LineNumber Correlation Symbol
                                                                     
                                                               oe 


                                                                           Type
                                                             Scope
                                                                                     
                                                                   


                           SourceFile       Segment
                                                     



                                            Module
                                                     



                                             Name
                                                     















18                                                            Borland Pascal Debug Kit Manual







  record, a LineNumber record and a Symbol record. The latter
  record is also an aggregated type. It consists of a pointer to a
  Module record, a Name record, a Scope record and a Type record.
  There is no example of specialization in this figure, but
  specialization can be compared to inheritance in Turbo Pascal.

  The way the types (records) are drawn is also important. Read
  from top to bottom, we always find  fixed  properties, i.e. a Browser
  always  contains a SourceFile. Read from bottom to top, we have
  optional  properties. Take for example the type Module. A Module
  can have zero or more Segments. In the same way, a Scope can
  have zero or more Symbols.

  The relationships expressed in figure 4.1 show the ways you can
  access information. If you have a certain line number, you first
  have to search the correct LineNumber record. If you now want to
  know in which source file this line appears, then you must first go
  to the LineNumber's Correlation record and from there to the
  SourceFile. If you want to know the name of the SourceFile you've
  found, you can go directly from the SourceFile record to the Name
  record.

  In the semantic query language Xplain, we would express this as:

                          get  LineNumber  its  Correlation  its  SourceFile  its  Name.

  Note that you can also get the name of the source file in which the
  line number appears in by going through Correlation  its  Segment
  its  Module and, finally,  its  Name.


                                              Changes between Open Architecture names and TD-
Info names


  The  TDInfo  unit tries to model the debug symbol information as
  semanticly correct as possible. Each type in TDInfo          is prefixed with
  a `T'. Note that I have changed some names so that they are not
  consistent with [Borland92] (which is fairly liberal with naming
  conventions to say the least).

  A list of Open Architecture names and TDInfo names is presented
  below. The TDInfo names refer to objects. The names in the Open
  Architecture book refer to records. The types are listed in the order
  they appear in the Open Architecture handbook.
  Note that almost all fields in TDInfo's object are different from their
  corresponding names in the Open Architecture Handbook too.


Chapter 4, TDInfo                                                            19






                Table 4.1    
                               TDInfo name            OA name
List of corresponding TDInfo    
       names and OA names      TSymbol                TSymbolRecord
                               TModule                TModuleHeader
                               TSourceFile            SourceFile
                               TLineNumber            Line number
                               TScope                 Scope
                               TSegment               Segment info
                               TCorrelation           Typedef
                               TType                  Type Rec
                               TMember                Struct offset rec
                               TClass                 TParent Table (?)
                               TParent                TParent Table
                               TOverload              Overload
                               TScopeClass            TClassTable
                               TModuleClass           LocalClass
                               TBrowser               TDefinitionRecord


How to make use of TDInfo


  In this section some information is given how you can make use of
  the  TDInfo  unit yourself. The information is this section is not yet
  complete. You should really take a look at the sources of the PMD
  unit and the AnnotateLog program (see ANNLOG.PAS, only in
  the registered version of this program).
  
         Initializing the    
  Before you can make use of TDInfo, you need to initialize it by
              TDInfo unit    
  calling the function  TDInfoPresent. Pass  TDInfoPresent  a filename
  which contains debug information. This file may be a stand-alone
  symbol file created with TDStrip or an executable with debug
  information appended to it.  TDInfoPresent  returns TRUE when it
  has found debug information in the file. Only when it returns
  TRUE is it save to use the other objects of the  TDInfo  unit.
  
                  Getting     information from     a debug symbol    
  You can almost always directly express a `semantic query' using
  TDInfo. To get the name of a module, you use TModule.ItsName.
  To get the scope of a symbol you say TSymbol.ItsScope. These
  ItsXXXX methods always return a pointer to the object that
                     file    
  corresponds with the name of the method.

  When you call an ItsXXXX method for the first time, a new object
  of that type is created. When you call the ItsXXXX method for the
  second time, this object is returned again, so an object is not
  created twice. You don't have to dispose these pointers, they are
  disposed by the provider when you dispose the provider, i.e. if


20                                                            Borland Pascal Debug Kit Manual







  you have called TSymbol.ItsScope, TSymbol.Done disposes the
  allocated TScope object.

  Let's take a look at a basic example. Given an physical address,
             Getting line    
  how can we display its the source file and line number? The first
                  numbers    
  thing we need to do is to turn physical addresses into logical
  addresses. This is done by calling GetLogicalAddr. A physical
  address goes in, a logical address comes out. Using this logical
  address we can now proceed to find the source file and line
  number. One approach would be to look the logical address up in
  a .MAP file. Much easier is it to use the debug information
  appended to an executable.
  We get the LineNumber by calling the constructor
  TLineNumber.AtAddr. For example:

       LineNumber := New(PLineNumber, AtAddr(Addr));

  LineNumber will be nil if no line number could be found. Else it
  will contain a pointer to a TLineNumber object. To display the line
  number we can write LineNumber^.Value. To get the name of the
  source file, see also figure 4.1, we say
  LineNumber^.ItsCorrelation^.ItsSourceFile^.ItsName.

       Getting procedures    
  To get the procedure to which this line number belongs, we have
  to know that a procedure is a symbol. To find a symbol at a given
  address, we can use TSymbol.AtSegment. We need to pass it a
  PSegment and an address. Because we already have a line number
  (see the previous section), the segment is
  LineNumber^.ItsCorrelation^.ItsSegment. The address is simply
  the logical address. The AtSegment constructor does quite an
  amount of work. It searches all scope records belonging to a
  certain segment to find a scope which covers the given address. It
  does this by calling TSegment.FirstScopeThat. This repeatedly
  calls the passed function until this function returns true. If this
  function returns TRUE, TSegment.FirstScopeThat returns the
  scope record for which the function returned TRUE. Although this
  is not displayed in figure 4.1, there exists a certain specialization of
  Scope, which contains a pointer to a Symbol record.
  TSymbol.AtSegment checks if TSegment.FirstScopeThat has
  returned such a scope. If that's the case, TSymbol.AtSegment
  loads the correct symbol record from the debug symbol table and
  returns.
  With the Symbol we now have, we have the procedure or function
  to which this line number belongs. The name of the procedure or


Chapter 4, TDInfo                                                            21







  function is Symbol^.ItsName. To determine of we have a
  procedure or a function we look at the type of the symbol. If
  Symbol^.ItsType^.ReturnType = 1 we have a procedure , else we
  have a function.

  There is a lot more to say about TDInfo, but this should get you
  started. In the PMD.PAS, ANNLOG.PAS and MEMCHECK.PAS
  you find some other or more elaborated uses of TDInfo.









































22                                                            Borland Pascal Debug Kit Manual








C            H            A            P            T            E            R


                                                                              5



                                                                     Assertions


  Once you have acquainted yourself with Assertions, you will not
  want to be without them! The Assertions unit provided here,
  makes it easy for Borland Pascal programmers to use them. Add
  Assertions  to the USES clause of any unit or program which wants
  to use assertions. In your code, simply call the procedure  Assert
  with a Boolean value and a string containing the message to be
  printed if the Boolean value evaluates to FALSE. After the message
  has been printed (which means that the assertion failed), your
  program halts.

  Example program:

     program Example

     uses
       BBError,
       Assertions;

     begin
       InitBBError('TEST.LOG', TRUE);
       Assert(TRUE, 'This assertion passes.');
       Assert(FALSE, 'This assertion fails.');
     end.

  If you call InitBBError     , the message is written to the log file as well.
  If you use the  PMD  unit (properly initialized!), Assert also prints a
  symbolic stack trace.
  You will probably want to use Assertions only in your test code,
  not in your production code. All Assertions take time and space,
  and some Assertions take quite a lot of time. That's no problem, or
  less of a problem, as long as you're working with test code, but
  your production code should be as fast as possible. Also, you
  shouldn't need assertions there.
  To conditionally include Assertions in your program, embrace
  calls to Assert within a pair of conditional defines. The example
  program then looks like this:


Chapter 5, Assertions                                                        23








     {$DEFINE Debug}
     program Example

     uses
       BBError
     {$IFDEF Debug}
       , Assertions
     {$ENDIF}
       ;

     begin
       InitBBError('TEST.LOG', TRUE);
     {$IFDEF Debug}
       Assert(TRUE, 'This assertion passes.');
       Assert(FALSE, 'This assertion fails.');
     {$ENDIF}
     end.

  If you enable the Debug directive, the Assertions are included. If
  you disable the Debug directive, they are not included.





























24                                                            Borland Pascal Debug Kit Manual




















P                        A                         R                          T


                                                                              2



                                                               Reference Manual


























                                                                                           25


                 ***************************************
                   E N D   O F   T H I S   M A N U A L
                 ***************************************


    The complete reference manual is distributed with the registered
    version only. Again, the reference manual looks exactly like to
    Borland reference manuals.

    The registered version manual also contains a list of bugs in the
    Borland Pascal Open Architecture Book, compiled by Andy McFarland.


