home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / pascal / library / dos / chkstack / chkstk.pas next >
Encoding:
Pascal/Delphi Source File  |  1987-03-08  |  10.3 KB  |  262 lines

  1. program CheckStackDemo;
  2.   {-For PC-DOS 3.x versions of Turbo only}
  3.   {$K+}                       {stack checking *must* be on}
  4.  
  5. (*
  6.    CHKSTK.PAS is designed to help take the guesswork out of determining the
  7.    heap/stack requirements for a given program.
  8.  
  9.    The routines in CHKSTK.INC (which you should extract from CHKSTK.PAS)
  10.    provide a relatively simple means of determining the amount of heap and
  11.    stack space actually used by a program. The critical routine,
  12.    CheckStack, is patched directly into the stack checking code in Turbo's
  13.    runtime library, allowing it to monitor the values for HeapPtr (top of
  14.    heap) and the SP register (stack pointer). InstallCheckStack performs
  15.    the patch, and DumpHeapAndStackData may be called to display the data
  16.    gathered by CheckStack. The third value displayed amounts to a
  17.    recommendation for mIn heap (set via the Options menu in the compiler).
  18.    The constant SafetyMargin can be used to incorporate a "fudge factor"
  19.    into the recommendation. I've initialized it to $100 (4K bytes), but
  20.    programs that contain recursive procedures should probably have a bigger
  21.    safety margin. Those that use fixed amounts of dynamic memory and
  22.    contain few local variables can probably get by with a lower margin.
  23.  
  24.    Incorporating CheckStack into a Program
  25.    ---------------------------------------
  26.    (1) Use the table "Values for PatchOfs" to set the proper value for
  27.        PatchOfs (a compile-time constant) for the compiler you're using.
  28.    (2) If the {$K-} compiler directive appears at the top of your program,
  29.        remove it. CheckStack does nothing if stack checking is off. ($K+ is
  30.        the default.)
  31.    (3) Assuming that CHKSTK.INC has been created, add the line
  32.  
  33.            {$I CHKSTK.INC}
  34.  
  35.        just after the "program" statement.
  36.    (4) At the very beginning of the main block of the program, add
  37.  
  38.            OrigHeap := Seg(HeapPtr^);
  39.            HeapMax := OrigHeap;
  40.            Inline($89/$26/>OrigSP);  {MOV [>OrigSp],SP}
  41.            LowSp := OrigSp;
  42.            InstallCheckStack;
  43.  
  44.        to initialize the necessary variables and install CheckStack.
  45.    (5) At the end of the program, call DumpHeapAndStackData.
  46.  
  47.    That's it. As shown in the demo program, you can easily enable/disable
  48.    CheckStack by using a compile-time constant such as "Debug".
  49.  
  50.    Caveats
  51.    -------
  52.    Please note that the stack checking code is not invoked by a call to New
  53.    or GetMem. If you are allocating and de-allocating dynamic memory within
  54.    a procedure or function, and no other procedures/functions are called
  55.    before the memory is de-allocated, HeapMax will probably be inaccurate.
  56.    To insure accurate results, add the statement "MoreAllocated" after each
  57.    call to New or GetMem. The procedure itself does nothing, but calling it
  58.    exercises the stack checking routine.
  59.  
  60.    Also, please realize that the data gathered is valid only for a given
  61.    run of the program. Programs that allocate varying amounts of dynamic
  62.    memory should be tested several times, using different sets of data, if
  63.    you want semi-reliable results.
  64.  
  65.    Using CheckStack with Turbo Extender
  66.    ------------------------------------
  67.    To use CheckStack in a program written with Turbo Extender, I recommend
  68.    the following:
  69.  
  70.    (A) Incorporate the code and global declarations from CHKSTK.INC at the
  71.        end of your .GLO file. Or, if code space is precious, put only
  72.        CheckStack and the type, constant, and variable declarations there,
  73.        and add the other three routines to the main module. CheckStack
  74.        must *not* be exported, in any case.
  75.    (B) At the beginning of the main block of the main module--*before* any
  76.        of the calls to LoadModule--insert the five statements mentioned
  77.        above (5).
  78.  
  79.    This insures that a copy of CheckStack will appear at the same offset in
  80.    each module, and that the necessary patch will already be in place in
  81.    each copy of the runtime library.
  82.  
  83.    Extra for Experts
  84.    -----------------
  85.    Clever types will have noticed that a modified version of CheckStack,
  86.    patched in at another location, could replace Turbo's stack checking
  87.    code altogether. If you want to do that, disassemble the runtime library
  88.    starting at PatchOfs - 6. What you'll see (with p.v. 3.02A) is this:
  89.  
  90.        CS:0F82 8BC4         MOV    AX,SP    ;<-- you'd patch here
  91.        CS:0F84 2BC1         SUB    AX,CX
  92.        CS:0F86 7214         JB     0F9C     ;error condition
  93.        CS:0F88 3D0002       CMP    AX,0200  ;<-- I patch here
  94.  
  95.    On entry, CX contains the stack requirements (for parameters and local
  96.    variables) for the procedure about to be called. CheckStack gets patched
  97.    in just after the RTL has calculated what SP will be (AX) after SP has
  98.    been adjusted. (This checks to see if SP would go lower than SS:$0200 --
  99.    Turbo maintains a 512-byte stack margin. The next check, not shown, is
  100.    to see if a heap/stack collision is about to occur.) The code shown
  101.    above is the same for all PC-DOS 3.x versions of Turbo; only the
  102.    location differs. Beyond this point, you're on your own.
  103.  
  104.  
  105.    (c) Copyright 1987 by Brian Foley
  106.    These routines are hereby released into the public domain for private
  107.    use. They may be freely used and distributed so long as no fee is
  108.    charged for their distribution.
  109.  
  110.    Please refer all comments, questions, etc. to:
  111.  
  112.         Brian Foley
  113.         TurboPower Software
  114.         CompuServe [76317,3247]
  115. *)
  116.  
  117.   {------------ begin CHKSTK.INC ------------}
  118.   (*
  119.         Values for PatchOfs
  120.      ==========================
  121.      Ver.   Plain  8087    BCD
  122.      -----  -----  -----  -----
  123.      3.00B  $0FB8  $0FBC  $0FC0
  124.      3.01A  $0FD7  $0FDB  $0FDF
  125.      3.02A  $0F88  $0F8C  $0F90
  126.      ==========================
  127.   *)
  128.  
  129. type
  130.   PatchRec = record
  131.                Call : Byte;
  132.                Ofst : Integer;
  133.              end;
  134. const
  135.   Patched : Boolean = False;  {True if Patch already installed}
  136.   PatchOfs = $0FD7;           {default to 3.01A, plain vanilla}
  137.   Unpatch : PatchRec = (Call : $3D; Ofst : $0200); {'CMP AX,$0200'}
  138.   TurboStackMargin = 512;     {Turbo maintains a stack margin of 512 bytes}
  139.   SafetyMargin = $100; {number of paragraphs to use as a safety
  140.                                margin to prevent heap/stack collisions.
  141.                                $100 paragraphs = 4K bytes.}
  142. var
  143.   Patch : PatchRec absolute CSeg : PatchOfs;
  144.   OrigHeap,                   {original value: Seg(HeapPtr^)}
  145.   OrigSp : Integer;           {original value: SP register}
  146.  
  147.   {These two *must* be global variables in the data segment:}
  148.   HeapMax,                    {maximum value: Seg(HeapPtr^)}
  149.   LowSp : Integer;            {lowest value: SP register}
  150.  
  151.   procedure CheckStack;
  152.     {-Keep track of low point for SP and high point for HeapPtr. This
  153.     procedure should not be called directly!}
  154.   begin
  155.     inline(
  156.       $3B/$06/> LowSp/        {CMP AX,[>LowSp]     ;AX < LowSp?}
  157.       $73/$03/                {JNB NotLowest       ;no? Not a new low}
  158.       $A3/> LowSp/            {MOV [>LowSp],AX     ;yes? LowSp = AX}
  159.                               {NotLowest:}
  160.       $50/                    {PUSH AX             ;save AX}
  161.       $A1/> HeapPtr+2/        {MOV AX,[>HeapPtr+2] ;AX = Seg(HeapPtr^)}
  162.       $3B/$06/> HeapMax/      {CMP AX,[>HeapMax]   ;AX > HeapMax?}
  163.       $76/$03/                {JNA Done            ;no? Done}
  164.       $A3/> HeapMax/          {MOV [>HeapMax],AX   ;HeapMax = AX}
  165.                               {Done:}
  166.       $58/                    {POP AX              ;restore AX}
  167.       $3D/$00/$02);           {CMP AX,$0200        ;the overwritten code}
  168.   end;
  169.  
  170.   procedure InstallCheckStack;
  171.     {-Patch CheckStack into the runtime library's stack checking routine}
  172.   begin
  173.     {Exit if Patch has already been made}
  174.     if Patched then
  175.       Exit;
  176.  
  177.     {Check for correct version of Turbo}
  178.     if (Patch.Call <> Unpatch.Call) or (Patch.Ofst <> Unpatch.Ofst) then
  179.       begin
  180.         WriteLn('Not a supported version of Turbo Pascal!');
  181.         Halt(1);
  182.       end;
  183.  
  184.     {Replace "CMP AX,$0200" with "CALL NEAR CheckStack"}
  185.     Patch.Call := $E8;
  186.     Patch.Ofst := Ofs(CheckStack)-(PatchOfs+3);
  187.     Patched := True;
  188.   end;
  189.  
  190.   procedure MoreAllocated;
  191.     {-Call this after allocating dynamic memory if you need to be
  192.     sure that HeapMax is accurate. If allocated memory is never
  193.     disposed of, this is unnecessary.}
  194.   begin
  195.     {Calling MoreAllocated simply exercises the stack checking code}
  196.   end;
  197.  
  198.   procedure DumpHeapAndStackData;
  199.     {-Dump the data gathered by CheckStack}
  200.   var
  201.     HU, SU : Integer;
  202.  
  203.     procedure WriteLnHex(N : Integer);
  204.     const
  205.       HexDig : array[0..15] of Char = '0123456789ABCDEF';
  206.     begin
  207.       WriteLn(HexDig[Hi(N) shr 4], HexDig[Hi(N) and $F],
  208.               HexDig[Lo(N) shr 4], HexDig[Lo(N) and $F]);
  209.     end;
  210.  
  211.   begin
  212.     SU := OrigSp-LowSp+TurboStackMargin;
  213.     HU := HeapMax-OrigHeap;
  214.     if HU <> 0 then
  215.       HU := Succ(HU);
  216.     Write('Maximum heap usage  (paras): $');
  217.     WriteLnHex(HU);
  218.     Write('Maximum stack usage (bytes): $');
  219.     WriteLnHex(SU);
  220.     Write('Heap/stack required (paras): $');
  221.     WriteLnHex(HU+Succ(SU shr 4)+SafetyMargin);
  222.   end;
  223.   {------------ end of CHKSTK.INC ------------}
  224.  
  225.   {--------------- Demo Program --------------}
  226. const
  227.   Debug = True;               {set to False to disable CheckStack}
  228. type
  229.   BigArray = array[1..MaxInt] of Byte;
  230. var
  231.   P1, P2 : ^BigArray;
  232.  
  233.   procedure Dummy;
  234.     {-Dummy procedure with lots of locals--eats up stack space in a hurry}
  235.   var
  236.     S1, S2, S3 : string[255];
  237.     BA : BigArray;
  238.   begin
  239.     {Do nothing}
  240.   end;
  241.  
  242. begin
  243.   if Debug then
  244.     begin
  245.       {the following should be added at the very start of the program}
  246.       OrigHeap := Seg(HeapPtr^);
  247.       HeapMax := OrigHeap;
  248.       inline($89/$26/> OrigSp); {MOV [>OrigSp],SP}
  249.       LowSp := OrigSp;
  250.       InstallCheckStack;
  251.     end;
  252.  
  253.   {your program goes here}
  254.   New(P1);                    {allocate some memory on the heap}
  255.   New(P2);
  256.   MoreAllocated;              {let's make sure CheckStack knows about it}
  257.   Dummy;                      {call a procedure}
  258.  
  259.   if Debug then
  260.     DumpHeapAndStackData;     {call at end of program to see results}
  261. end.
  262.