home *** CD-ROM | disk | FTP | other *** search
- program CheckStackDemo;
- {-For PC-DOS 3.x versions of Turbo only}
- {$K+} {stack checking *must* be on}
-
- (*
- CHKSTK.PAS is designed to help take the guesswork out of determining the
- heap/stack requirements for a given program.
-
- The routines in CHKSTK.INC (which you should extract from CHKSTK.PAS)
- provide a relatively simple means of determining the amount of heap and
- stack space actually used by a program. The critical routine,
- CheckStack, is patched directly into the stack checking code in Turbo's
- runtime library, allowing it to monitor the values for HeapPtr (top of
- heap) and the SP register (stack pointer). InstallCheckStack performs
- the patch, and DumpHeapAndStackData may be called to display the data
- gathered by CheckStack. The third value displayed amounts to a
- recommendation for mIn heap (set via the Options menu in the compiler).
- The constant SafetyMargin can be used to incorporate a "fudge factor"
- into the recommendation. I've initialized it to $100 (4K bytes), but
- programs that contain recursive procedures should probably have a bigger
- safety margin. Those that use fixed amounts of dynamic memory and
- contain few local variables can probably get by with a lower margin.
-
- Incorporating CheckStack into a Program
- ---------------------------------------
- (1) Use the table "Values for PatchOfs" to set the proper value for
- PatchOfs (a compile-time constant) for the compiler you're using.
- (2) If the {$K-} compiler directive appears at the top of your program,
- remove it. CheckStack does nothing if stack checking is off. ($K+ is
- the default.)
- (3) Assuming that CHKSTK.INC has been created, add the line
-
- {$I CHKSTK.INC}
-
- just after the "program" statement.
- (4) At the very beginning of the main block of the program, add
-
- OrigHeap := Seg(HeapPtr^);
- HeapMax := OrigHeap;
- Inline($89/$26/>OrigSP); {MOV [>OrigSp],SP}
- LowSp := OrigSp;
- InstallCheckStack;
-
- to initialize the necessary variables and install CheckStack.
- (5) At the end of the program, call DumpHeapAndStackData.
-
- That's it. As shown in the demo program, you can easily enable/disable
- CheckStack by using a compile-time constant such as "Debug".
-
- Caveats
- -------
- Please note that the stack checking code is not invoked by a call to New
- or GetMem. If you are allocating and de-allocating dynamic memory within
- a procedure or function, and no other procedures/functions are called
- before the memory is de-allocated, HeapMax will probably be inaccurate.
- To insure accurate results, add the statement "MoreAllocated" after each
- call to New or GetMem. The procedure itself does nothing, but calling it
- exercises the stack checking routine.
-
- Also, please realize that the data gathered is valid only for a given
- run of the program. Programs that allocate varying amounts of dynamic
- memory should be tested several times, using different sets of data, if
- you want semi-reliable results.
-
- Using CheckStack with Turbo Extender
- ------------------------------------
- To use CheckStack in a program written with Turbo Extender, I recommend
- the following:
-
- (A) Incorporate the code and global declarations from CHKSTK.INC at the
- end of your .GLO file. Or, if code space is precious, put only
- CheckStack and the type, constant, and variable declarations there,
- and add the other three routines to the main module. CheckStack
- must *not* be exported, in any case.
- (B) At the beginning of the main block of the main module--*before* any
- of the calls to LoadModule--insert the five statements mentioned
- above (5).
-
- This insures that a copy of CheckStack will appear at the same offset in
- each module, and that the necessary patch will already be in place in
- each copy of the runtime library.
-
- Extra for Experts
- -----------------
- Clever types will have noticed that a modified version of CheckStack,
- patched in at another location, could replace Turbo's stack checking
- code altogether. If you want to do that, disassemble the runtime library
- starting at PatchOfs - 6. What you'll see (with p.v. 3.02A) is this:
-
- CS:0F82 8BC4 MOV AX,SP ;<-- you'd patch here
- CS:0F84 2BC1 SUB AX,CX
- CS:0F86 7214 JB 0F9C ;error condition
- CS:0F88 3D0002 CMP AX,0200 ;<-- I patch here
-
- On entry, CX contains the stack requirements (for parameters and local
- variables) for the procedure about to be called. CheckStack gets patched
- in just after the RTL has calculated what SP will be (AX) after SP has
- been adjusted. (This checks to see if SP would go lower than SS:$0200 --
- Turbo maintains a 512-byte stack margin. The next check, not shown, is
- to see if a heap/stack collision is about to occur.) The code shown
- above is the same for all PC-DOS 3.x versions of Turbo; only the
- location differs. Beyond this point, you're on your own.
-
-
- (c) Copyright 1987 by Brian Foley
- These routines are hereby released into the public domain for private
- use. They may be freely used and distributed so long as no fee is
- charged for their distribution.
-
- Please refer all comments, questions, etc. to:
-
- Brian Foley
- TurboPower Software
- CompuServe [76317,3247]
- *)
-
- {------------ begin CHKSTK.INC ------------}
- (*
- Values for PatchOfs
- ==========================
- Ver. Plain 8087 BCD
- ----- ----- ----- -----
- 3.00B $0FB8 $0FBC $0FC0
- 3.01A $0FD7 $0FDB $0FDF
- 3.02A $0F88 $0F8C $0F90
- ==========================
- *)
-
- type
- PatchRec = record
- Call : Byte;
- Ofst : Integer;
- end;
- const
- Patched : Boolean = False; {True if Patch already installed}
- PatchOfs = $0FD7; {default to 3.01A, plain vanilla}
- Unpatch : PatchRec = (Call : $3D; Ofst : $0200); {'CMP AX,$0200'}
- TurboStackMargin = 512; {Turbo maintains a stack margin of 512 bytes}
- SafetyMargin = $100; {number of paragraphs to use as a safety
- margin to prevent heap/stack collisions.
- $100 paragraphs = 4K bytes.}
- var
- Patch : PatchRec absolute CSeg : PatchOfs;
- OrigHeap, {original value: Seg(HeapPtr^)}
- OrigSp : Integer; {original value: SP register}
-
- {These two *must* be global variables in the data segment:}
- HeapMax, {maximum value: Seg(HeapPtr^)}
- LowSp : Integer; {lowest value: SP register}
-
- procedure CheckStack;
- {-Keep track of low point for SP and high point for HeapPtr. This
- procedure should not be called directly!}
- begin
- inline(
- $3B/$06/> LowSp/ {CMP AX,[>LowSp] ;AX < LowSp?}
- $73/$03/ {JNB NotLowest ;no? Not a new low}
- $A3/> LowSp/ {MOV [>LowSp],AX ;yes? LowSp = AX}
- {NotLowest:}
- $50/ {PUSH AX ;save AX}
- $A1/> HeapPtr+2/ {MOV AX,[>HeapPtr+2] ;AX = Seg(HeapPtr^)}
- $3B/$06/> HeapMax/ {CMP AX,[>HeapMax] ;AX > HeapMax?}
- $76/$03/ {JNA Done ;no? Done}
- $A3/> HeapMax/ {MOV [>HeapMax],AX ;HeapMax = AX}
- {Done:}
- $58/ {POP AX ;restore AX}
- $3D/$00/$02); {CMP AX,$0200 ;the overwritten code}
- end;
-
- procedure InstallCheckStack;
- {-Patch CheckStack into the runtime library's stack checking routine}
- begin
- {Exit if Patch has already been made}
- if Patched then
- Exit;
-
- {Check for correct version of Turbo}
- if (Patch.Call <> Unpatch.Call) or (Patch.Ofst <> Unpatch.Ofst) then
- begin
- WriteLn('Not a supported version of Turbo Pascal!');
- Halt(1);
- end;
-
- {Replace "CMP AX,$0200" with "CALL NEAR CheckStack"}
- Patch.Call := $E8;
- Patch.Ofst := Ofs(CheckStack)-(PatchOfs+3);
- Patched := True;
- end;
-
- procedure MoreAllocated;
- {-Call this after allocating dynamic memory if you need to be
- sure that HeapMax is accurate. If allocated memory is never
- disposed of, this is unnecessary.}
- begin
- {Calling MoreAllocated simply exercises the stack checking code}
- end;
-
- procedure DumpHeapAndStackData;
- {-Dump the data gathered by CheckStack}
- var
- HU, SU : Integer;
-
- procedure WriteLnHex(N : Integer);
- const
- HexDig : array[0..15] of Char = '0123456789ABCDEF';
- begin
- WriteLn(HexDig[Hi(N) shr 4], HexDig[Hi(N) and $F],
- HexDig[Lo(N) shr 4], HexDig[Lo(N) and $F]);
- end;
-
- begin
- SU := OrigSp-LowSp+TurboStackMargin;
- HU := HeapMax-OrigHeap;
- if HU <> 0 then
- HU := Succ(HU);
- Write('Maximum heap usage (paras): $');
- WriteLnHex(HU);
- Write('Maximum stack usage (bytes): $');
- WriteLnHex(SU);
- Write('Heap/stack required (paras): $');
- WriteLnHex(HU+Succ(SU shr 4)+SafetyMargin);
- end;
- {------------ end of CHKSTK.INC ------------}
-
- {--------------- Demo Program --------------}
- const
- Debug = True; {set to False to disable CheckStack}
- type
- BigArray = array[1..MaxInt] of Byte;
- var
- P1, P2 : ^BigArray;
-
- procedure Dummy;
- {-Dummy procedure with lots of locals--eats up stack space in a hurry}
- var
- S1, S2, S3 : string[255];
- BA : BigArray;
- begin
- {Do nothing}
- end;
-
- begin
- if Debug then
- begin
- {the following should be added at the very start of the program}
- OrigHeap := Seg(HeapPtr^);
- HeapMax := OrigHeap;
- inline($89/$26/> OrigSp); {MOV [>OrigSp],SP}
- LowSp := OrigSp;
- InstallCheckStack;
- end;
-
- {your program goes here}
- New(P1); {allocate some memory on the heap}
- New(P2);
- MoreAllocated; {let's make sure CheckStack knows about it}
- Dummy; {call a procedure}
-
- if Debug then
- DumpHeapAndStackData; {call at end of program to see results}
- end.