

                     **** 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.21

















                                                                     NederWare
                                                               Burgerstraat 17
                                                               5311 CW Gameren
                                                               The Netherlands
                                                       compuserve: 100120,3121
                                             email: 100120.3121@compuserve.com
                                                         fidonet: 2:281/527.23





                                                                              1



C           O          N           T         E         N          T                         S





Introduction Program with less pain         1     A more complex example ............      27
Why this tool?........................      1     Changes between Open Architecture
Features .............................      1     names and TDInfo names..............     28
What's in this manual .................     2     How to make use of TDInfo ............   29
Typefaces used in this manual ..........    2     Initializing the TDInfo unit...........  29
How to contact NederWare............        3     Getting information from a debug
Acknowledgements...................         3     symbol file.........................     29
                                             Getting line numbers..............            29
                                             Getting procedure names ..........            30
Part 1   User Manual
                                                  Chapter 5 Assertions                     31
Chapter 1  Working with log files           7    
How to create or open a log file.........   7    
How can you write information in the              Part 2   Reference Manual
log file..............................      8    
                                                  Chapter 6  Pascal Debug kit
Controlling the maxium size of your
                                                    Reference                              35
logfiles ..............................     9    
                                                  Sample procedure....................     35
Chapter 2 Checking your memory             11     AddBackSlash function................    35
What you must do before MemCheck                  Archive constant......................   36
can work............................       11     Assert procedure.....................    36
    Updating the standard library ........ 11     Beep procedure.......................    36
    Recompiling the system unit.........   12     BeepOff procedure....................    36
What MemCheck does................         13     BeepOn procedure ....................    37
How to enable memory checking.......       13     BrowserRecordSize variable............   37
How MemCheck works...............          14     ClassRecordSize variable..............   37
MemCheck and DPMI.................         14     CMPB function .......................    37
Bugs in the Run-Time Library you                  CMPW function ......................     38
encounter when using MemCheck ......       15     ConvMemLimit variable...............     38
                                                  CorrelationRecordSize variable ......... 38
Chapter 3 Post Mortem Debugger             17     CPos function ........................   38
What the Post Mortem debugger does ...     17     CR constant..........................    38
Using the Post Mortem Debugger.......      18     CreateBAK procedure .................    39
How much memory does PMD need ....         20     DateTime type.......................     39
The Post-Mortem-Debugger under                    DebugInfo variable...................    39
MS-Windows........................         21     Delay variable........................   39
    Extra features under Windows.......    21     dfXXXX constants....................     39
    GPF's under Windows..............      21     DirStr type...........................   40
                                                  Discard procedure ....................   40
Chapter 4 TDInfo                           23    
                                                  DisposeSLink procedure...............    40
How Borland's Debug information is
                                                  DoneMemCheck procedure ............      40
stored ...............................     23    
                                                  DonePMD procedure..................      41
How TDInfo models Borland's Debug
                                                  DoneStrings procedure ................   41
Information ..........................     25    
                                                  DosCopy procedure...................     41
    What is semantic data modeling? .....  25    
                                                  DosDel procedure....................     42
    A first example .....................  27    


                                       i







DosDelay procedure..................       42     InitMemCheck procedure..............     52
DosMove procedure..................        42     InitObjMemory procedure .............    53
DosTouch procedure ..................      43     InitPMD procedure...................     53
DosWipe procedure ...................      43     IsDirectory function...................  53
DStream variable .....................     43     IsFileOpen function ...................  53
DumpStack variable..................       44     IsValidPtr function....................  54
DumpStackProcedureType type........        44     LeadingZero function.................    54
Empty function.......................      44     LeftJustify function...................  54
ExtractFilePath function ...............   44     LF constant..........................    54
ExtractStr function ....................   44     LineNumberRecordSize variable........    55
ExtStr type...........................     45     LoadStrings procedure................    55
FancyStr function.....................     45     LogError procedure ...................   55
FatalErrorText variable................    45     LogFileOpened variable ...............   55
FCreate function......................     45     LogFileSizeLimit variable..............  56
FDefaultExtension function ............    45     LowCase function....................     56
ferr variable..........................    46     LowStr function ......................   56
FExpand function.....................      46     MaxConventionalMemoryBlock constant      56
FF constant..........................      46     MaxMemPtrs constant.................     56
FForceDir function....................     46     MaxSegments constant ................    57
FForceExtension function ..............    46     MaxWord constant....................     57
FileExist function .....................   47     MemberRecordSize variable............    57
FileRec type..........................     47     MemCheckReport procedure...........      57
fmXXXX constants ....................      47     memfXXXX constants.................      57
FOpen function.......................      47     mfStandard constant ..................   58
FormatStr procedure ..................     48     Min function .........................   58
FormFeed constant....................      48     mm XXXX constants ..................     58
fsXXXX constants .....................     48     ModuleClassRecordSize variable.......    59
FTCopy function.....................       48     ModuleRecordSize variable ............   59
GetAddrStr function..................      49     NameStr type........................     59
GetDateStr function...................     49     NewSLink function...................     59
GetEnv function ......................     49     NextChPos function...................    60
GetFileName function.................      49     OverloadRecordSize variable...........   60
GetLogicalAddr function..............      49     ParentRecordSize variable.............   60
GetObjMemory function...............       49     PathStr type..........................   60
GetPStr function......................     50     PDebugInfo type.....................     60
GetStr function .......................    50     PrintError variable....................  60
GetTextFileName function .............     50     PrintErrorType type...................   61
GetTickCount function................      50     PSegmentCache type..................     61
GetTimeStr function...................     51     PSLink type..........................    61
GetUniqueFileName function..........       51     PtrRec type..........................    61
HandleRunTimeError variable.........       51     Registers type ........................  61
      HandleRunTimeErrorProcedureType type 51     RemoveBackSlash function............     62
HexB function........................      51     RepChar function.....................    62
HexStr function......................      51     ReplaceStr procedure..................   62
InitBBError function..................     52     ReportFileName constant ..............   62
InitIntHandler procedure ..............    52     RightJustify function .................. 62


                                       ii







rsGet function........................     63     TextRec type.........................    75
rsGet1 function.......................     63     tid voidXXXX constants ...............   75
rsGet2 function.......................     63     TLineNumber object..................     76
ScanB function.......................      63     Fields.............................      76
ScanW function......................       64     Methods...........................       76
ScopeClassRecordSize variable .........    64     TMember object ......................    77
ScopeRecordSize variable..............     64     Fields.............................      77
scXXXX constants.....................      64     Methods...........................       77
SearchRec type .......................     65     TModule object.......................    77
SegmentRecordSize variable...........      65     Fields.............................      77
SetHandleCount procedure............       65     Methods...........................       78
sl XXXX constants....................      65     TModuleClass type...................     79
SmallDebugHeaderSize constant.......       66     TObject object ........................  79
SmallEndianI procedure ...............     66     Methods...........................       79
SmallEndianL procedure..............       66     TObjMemory object ...................    79
SmallEndianW procedure..............       66     Fields.............................      79
SourceFileRecordSize variable..........    66     Methods...........................       80
Spc function.........................      67     TOverload type.......................    81
Spoiled function ......................    67     TParent type .........................   81
StrB function .........................    67     TResourceCollection object.............  81
StrI function.........................     67     Methods...........................       81
Strings variable.......................    67     TResourceFile object..................   82
StripSpc function.....................     67     Fields.............................      82
StrL function .........................    68     Methods...........................       82
StrR function.........................     68     TResourceItem type...................    82
StrResBufSize variable.................    68     TrimRight function....................   82
StrS function .........................    68     TScope object........................    83
StrW function ........................     68     Fields.............................      83
stXXXX constants .....................     68     Methods...........................       83
SymbolRecordSize variable............      69     TScopeClass type .....................   84
TBrowser object......................      69     TSegment object ......................   84
    Fields.............................    69     Fields.............................      84
    Methods...........................     70     Methods...........................       85
TClass object .........................    70     TSLink type..........................    85
    Fields.............................    70     TSmartBufStream object ...............   86
    Methods...........................     70     Fields.............................      86
TCorrelation object....................    70     Methods...........................       86
    Fields.............................    71     TSourceFile object....................   86
    Methods...........................     71     Fields.............................      86
TDebugHeader type..................        71     Methods...........................       87
TDebugInfo object ....................     73     TSymbol object .......................   87
    Fields.............................    73     Fields.............................      87
    Methods...........................     74     Methods...........................       87
TDInfoPresent function................     74     TType object.........................    88
TDriveStr type.......................      74     Fields.............................      88
TextPrintError procedure..............     75     Methods...........................       89


                                       iii







TypeRecordSize variable...............     90    
UpStr function.......................      90    
ValB function.........................     90    
valcode variable ......................    90    
ValHex function ......................     91    
ValI function .........................    91    
ValL function.........................     91    
ValR function........................      91    
ValW function........................      92    
Warning procedure...................       92    
XChDir procedure ....................      92    
XMkDir function.....................       92    
ZeroRightJustify function ..............   92    
 200K constant.......................      93         

 Appendix A Errors and omissions
             in Open Architecture
             Handbook                      95    






























                                       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







   Memory allocation and deallocation tracking, with warnings on
    errors like overwriting memory and deallocating not allocated
    memory.
   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


Introduction                                                                  2







  represent terms that you can use as they are, or that you can think
  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
  the Debugger/Tools library of the BPASCAL forum (now called
  Delphi forum). The library number is 15.

  Internet

  NederWare can be reached on the Internet through
  100120.3121@compuserve.com. For the latest Debug Kit version,
  check-out ftp://garbo.uwasa.fi/pc/turbopa7.

  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


Introduction                                                                  3







  commercial software, read this book! It inspired me to put together
  all the pieces I had accumulated over the years in this package.

  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.




































Introduction                                                                  4




















P                        A                         R                          T


                                                                              1



                                                                    User Manual


























                                                                              5























































                                                                              6








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 three 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 writes information about which program was started up to
     the log file (i.e.  ParamStr(0)).
  3. 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.

  Note that the  BBError  unit should be the first unit in your program
  uses  clauses (or at least it should be listed before any other unit
  which uses logging).


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.

  At the beginning of every line the current date and time is written.

  A second method is to call BBError's  LogError  procedure which
  essentially does the same. It accepts a string as parameter.

  Example:

     program  WriteToLogError;

     uses
       BBError;

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




Chapter 1, Working with log files                                             8







Controlling the maxium size of your logfiles


  Log files grow infinitely if you don't control their growth. Luckily,
  the  BBError  unit does this too. Every time  InitBBError  is called
  (probably every time your program starts up), the size of the log
  file is checked. If this is larger than  LogFileSizeLimit  than the .log
  file is renamed to a file with extension .LO0 and a new .log file is
  created.

  If you want your logfiles to be larger or smaller, set
  LogFileSizeLimit  before calling  InitBBError.





































Chapter 1, Working with log files                                             9























































Chapter 1, Working with log files                                            10








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 it is
    explained 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:
         standard library    
  1. First remove the `old' SYSTEM.TPU from TURBO.TPL (or
     TPP.TPL or TPW.TPL, please substitute as needed).
  2. Next, add the `new' SYSTEM.TPU to TURBO.TPL (or TPP.TPL
     or TPW.TPL).

  Borland Pascal includes a utility to remove or add units from a
  Turbo Pascal Library (.TPL) file. It's called tpumover.
  For example you will type the following to replace the Dos
  real-mode SYSTEM.TPU:

     tpumover c:\bp\bin\turbo -system
     tpumover c:\bp\bin\turbo +c:\pmd\system.tpu


Chapter 2, Checking your memory                                              11







  Substitute the paths {- The Borland library path (c:/bp/bin) and
  the Post Mortem Kit path (c:/pmd) {- by the paths where you
  have put the Borland library and Post Mortem Kit.

  You will type the following to replace the Dos Protected Mode
  SYSTEM.TPP:

     tpumover c:\bp\bin\tpp -system
     tpumover c:\bp\bin\tpp +c:\pmd\system.tpp

  You will type the following to replace the Windows SYSTEM.TPW:

     tpumover c:\bp\bin\tpw -system
     tpumover c:\bp\bin\tpw +c:\pmd\system.tpw

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

  If you don't want to overwrite Borland's library files you can copy
  them to another directory (for example c:/pmd), do the replace on
  the copied libraries and startup the Borland IDE with the
  -tmynewlibrarypath switch. If you have put the libraries in
  c:/pmd, startup your IDE as follows:

       bp -tc:/pmd

  You should call the command-line compiler bpc with:

       bpc -tc:/pmd
  
          Recompiling the    
  Recompiling the system unit is more diicult. You need to have
              system unit    
  the run-time library compiled before you can recompile the new
  SYSTEM.PAS unit.

  SHAREWARE users only:  If you have the run-time library source,
  and if you want to 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.

  The  registered  users already have a complete SYSTEM.PAS file.


Chapter 2, Checking your memory                                              12







  If you have a complete SYSTEM.PAS file you can start to recompile
  the SYSTEM.PAS unit. Make sure the first entry in your unit and
  object path is set to the directory where the Post Mortem Debug
  Kit resides (c:\pmd for example).

  To compile SYSTEM.PAS with the command-line compiler you
  type:

  bpc -uc:\pmd;c:\bp\units -oc:\pmd;c:\bp\rtl\sys;c:\bp\rtl\lib system.pas


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).


How to enable memory checking


  You enable  MemCheck  by placing it in the USES clause of your
  main program and calling InitMemCheck with parameter


Chapter 2, Checking your memory                                              13







  mfStandard. Please bear in mind that using  MemCheck  will cost
  you an extra 24 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. If you
  need more pointers you can enable the  BigCollection  conditional
  directive in MEMCHECK.PAS. You need to have BIGCOL.ZIP for
  this.
  If possible, MemCheck            allocates 4 or 8 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 4 or 8 bytes more.
   If it is possible to allocate 8 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 4 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 24 (or 20) 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.


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


Chapter 2, Checking your memory                                              14







  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!


                          Bugs in the Run-Time Library you encounter when using
MemCheck


  Using MemCheck you occasionally will encounter bugs in the
  run-time library. Here's the list I know off:

   In the Memory unit (found in rtl/tv), a programmer used some
    trick to return aligned memory. When allocating too much
    memory, he gave a small part of it back...MemCheck will fail on
    this of course, because you need to give back the entire thing.
    And besides, this is an awful lousy way of programming.
   The ODialogs unit contains a bug in TEdit.Transfer. In certain
    cases this routine frees too less memory.


























Chapter 2, Checking your memory                                              15























































Chapter 2, Checking your memory                                              16








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:


  1996-05-03 21:54:02 ** M:\SRC\PMD\TEST\TESTPMDX.EXE started **
  1996-05-03 21:54:02 MemAvail on start: 15832904
  1996-05-03 21:54:02 Post Mortem Debugger installed.
  1996-05-03 21:54:02 MemAvail on exit: 15832788
  1996-05-03 21:54:02 Program terminated with ExitCode 215.
  1996-05-03 21:54:02 Error 215 at 0001:004A
  1996-05-03 21:54:02             TESTPMD.PAS (38) procedure InSide((100,0,0));
  1996-05-03 21:54:02                         w : Word = 0;
  1996-05-03 21:54:02                         d : Real = 3.1415000000E+00;
  1996-05-03 21:54:02 *** Full stack dump ***
  1996-05-03 21:54:02TESTPMD.PAS (43) procedure GenerateError((Input,'TESTPMD.PAS'),test2);
  1996-05-03 21:54:02                         s : string[255] = 'Hello world';
  1996-05-03 21:54:02   TESTPMD.PAS (77)
  1996-05-03 21:54:02 *** End of stack dump ***



Chapter 3, Post Mortem Debugger                                              17








  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
  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
       Objects,
     {$IFDEF Windows}
       WinCrt,
     {$ENDIF}
       BBError,                                       { implements the logging
  function                                 }
                                                      { should be listed before
  any unit                                }
                                                      { which uses logging
  }



Chapter 3, Post Mortem Debugger                                              18







       ObjMemory,                                     { Handles memory
  allocations larger than 64K }
       PMD;                                           { this unit implements
  the Post Mortem dumps }


     type
       types = (test1, test2, test3);


     procedure  GenerateError(var f : text; e: types);
     var
       s : string;

       procedure  InSide(o : TObjMemory);
       var
         w : word;
         d : real;
       begin
         d := 3.1415;
         w := 0;
         w := w - 1;
       end;

     begin
       s := 'Hello world';
       InSide(GetObjMemory(100, 0, memfAll)^);
     end;


     var
       f : text;
     begin
     { display information about this program }
       writeln('Post Mortem Debugger tester.');
       writeln('If this program is compiled with debug info
  on,');
       writeln('see TESTPMD.LOG after this program
  halts.');

     { initialize the kit in the next three lines. You
  will always need these }
     { lines in your program too
  }

     { initialize log file unit }
       InitBBError('TESTPMD.LOG', TRUE);

     { initialize memory handling unit }
       InitObjMemory;

     { initialize PMD with symbolic stack dump and data


Chapter 3, Post Mortem Debugger                                              19







  segment dump }
       InitPMD(dfStandard+dfLocals);

     { you can also initialize PMD with symbolic stack dump
  and data segment dump }
       InitPMD(dfStandard+dfLocals+dfDataSeg);

     { open a text file, nice to show in symbolic stack
  dump and dseg dump }
       Assign(f, 'TESTPMD.PAS');
       Reset(f);

     { generate the error }
       GenerateError(f, test2);

     { we don't need to clean up of course, because we will
  never reach this... }
     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.


How much memory does PMD need


  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.
  Note that when running from the IDE, EMS or XMS is  not
  available. You have to leav the IDE and run the program from the


Chapter 3, Post Mortem Debugger                                              20







  command-line. Or, if you have a multi-tasking OS, run it from
  another command-line box.


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 to
  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.
















Chapter 3, Post Mortem Debugger                                              21























































Chapter 3, Post Mortem Debugger                                              22








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 book contains quite a few errors, the reader is referred
  to appendix A where OA.TXT is lised. This file contains bugs and
  omissions to chapter 4 of Borland's ''Open Architecture Handbook
  for Pascal''.

  In this chapter, we first explain how Borland's Debug information
  is stored. Next we explain how we modelled Borland's Debug
  information so you can understand relationships between various
  components better and next we explain the use of TDInfo, the unit
  which can make use of Borland's Debug information.


How Borland's Debug information is stored


  . Borland's Debug information is just appended to an .EXE file.
  The basic layout of a .EXE file with debug information appended
  is:

   EXE header
   EXE image
   Debug header
   symbols
   modules
   source files
   line numbers
   scopes
   segments
   ...
   names


Chapter 4, TDInfo                                                            23







  The debug header contains various information about the debug
  tables as which version it is or how many symbols or line numbers
  there are. A shortened version of the header (for the full header
  see the reference manual) follows:

     type
       TDebugHeader =  record
         MagicNumber : word;                                { To be sure who we
  are ($52FB) }
         MinorVersion : byte;                               { in case we change
  things }
         MajorVersion : byte;
         NamesPoolSize : longint;                           { names pool size
  in bytes }
         NamesCount : word;                                 { number of names
  in pool }
         TypesCount : word;                                 { number f type
  entries }
         MembersCount : word;                               { structure members
  table }
         SymbolsCount : word;                               { number of symbols
  }
         GlobalsCount : word;                               { number of global
  symbols }
         ModulesCount : word;                               { number of modules
  (units) }
         LocalsCount : word;                                { optional; can be
  filler }
         ScopesCount : word;                                { number of scopes
  in table }
         LineNumbersCount : word;                           { number of line
  numbers }
         SourceFilesCount : word;                           { number of include
  files }
         SegmentsCount : word;                              { number of segment
  records }
         CorrelationsCount : word;                          { number of
  segment/file correlations }
       end;

  After the debug header just arrays of records follow. So after the
  header you find an array of Symbol records. How many Symbol
  records there are, is determined by the SymbolsCount field in the
  header. Let's take a look at the source files section. The source files
  section is again an array of SourceFile records. Definition:

     type
       SourceFilesArray =  array[1..Header.SourceFilesCount]
  of SourceFileRecord;



Chapter 4, TDInfo                                                            24







  A SourceFileRecord looks like:

     type
       SourceFileRecord = record
         SourceFileName : word;
         TimeStamp : longint;
       end;

  As you see, the name of a source file is not stored in the SourceFile
  record. SourceFileName is an index into the names section of the
  debug information. If SourceFileName = 10 it means that the name
  of the source file is stored as the 10th name in the names section.

  In the same manner is all other information stored. Line numbers
  are stored consecutively as an
  array[1..Header.LineNumbersCount] of LineNumberRecord, etc.

  The names section is the last section of the debug information and
  it consists if names seperated by zero's. There is no leading length
  byte as with Pascal type strings.

  A last remark: in cases when information is related, for example
  each Symbol has a Type, indexes are used. A symbol can be a
  procedure, a variable, a constant, etc. and its type tells if it is a
  procedure, a variable, a constant, etc. As you saw in the
  SourceFileRecord case, a name is not stored with the SourceFile
  but only an index to it. So doesn't a Symbol contains its Type, but
  only an index into the array of type records.


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.
  
         What is semantic     data modeling?    
  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.



Chapter 4, TDInfo                                                            25















                                                    Browser
                                                             
               Figure 4.1    
     Turbo Debug Semantic    
                    Model    
                                          LineNumber Correlation Symbol
                                                                     
                                                               oe      


                                                                           Type
                                                             Scope
                                                                                     



SourceFileSegment




Module




                                             Name
                                                     














Chapter 4, TDInfo                                                            26


                 ***************************************
                   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.


