home *** CD-ROM | disk | FTP | other *** search
Text File | 1991-09-12 | 22.2 KB | 476 lines | [TEXT/MPS ] |
- ; Leaks.a
- ;
- ; Copyright © 1990-91 by Apple Computer, Inc., all rights reserved.
- ;
- ; by Bo3b Johnson 10/9/90
- ; MS 37-DS
- ;
- ; This file is the assembly routines I need in order to interface to the toolbox in
- ; a sensible fashion. The OS is funky, and there is no good way to do these things
- ; from Pascal or C. The use of INLINE code is OK, but not always enough.
- ;
- ; In order to fashion a good patch to NewPtr/NewHandle, I need to use PC
- ; relative addressing. The basic problem is that there is no good way to get back
- ; to the dcmd globals, while the patch is executing. Certainly you can't rely upon
- ; A5, A6, or A7; and no other registers are available for use. I could stick something
- ; into AppParms, CurApName, or a number of other atrocities, but I don't own
- ; any of those spots, and could quite easily end up competing with some other code
- ; for the spot. In the past, this code patched the Chain trap, since it is not in use,
- ; but that has competition problems too. If I use PC-relative addressing to get to
- ; some global information, then this becomes self-modifying code, since I will
- ; change the code in this block, using a piece of code space as variable storage.
- ; This seems the most risk-free version for now, notwithstanding certain strident
- ; if misguided warnings from various groups. It's not enough to tell me not to
- ; do something, you have to give me an alternative. If you have a better alternative,
- ; by all means let me know.
- ;
- ; I patch the four traps of NewPtr/DisposPtr, NewHandle/DisposHandle; and all
- ; these pieces are almost identical. The only difference really is the variable being
- ; used. The basic idea is to have one routine to save off the old address, gotten from
- ; an NGetTrapAddress; and to save that into the code here, using PC-relative addressing.
- ; The trap is patched by the Pascal code, and whenever it gets called, it will call here
- ; to have us call through the old version of the trap. That way it gets control before
- ; the trap executes, but I still drive the old code.
- ;
- ; Realize that the code in this file is getting run mostly during the normal operation
- ; of the Mac. The stack is whatever the current app is using, as well as that A5 world,
- ; and stack crawl. I don't where I'll be so, I can't rely on those things. This
- ; is very different from the code in the dcmd, where it knows the stack it is using is
- ; in Macsbug, and very small. Pieces of this code will get run a tiny bit from the dcmd,
- ; since it will call here in order to get access to my global variables, the treeTop, emptyQ,
- ; and activeState.
- ;
- ; One of the goals in this file is to avoid needing an asm version of the record structure
- ; I use. I really just want to pass around pointers, so that this file can be a little
- ; more independent of the basic structures. One of the things that's get used though is
- ; the size of the Stack Crawl array that I pass around. There is a constant here, but
- ; if it changes in the Pascal code, it should change here.
-
- ; By the way, this file looks best if viewed in Palatino 12.
-
-
- include 'SysErr.a'
- include 'SysEqu.a'
- include 'Traps.a'
-
- proc
- import AddNewBlock, KillOldBlock
-
- ; ——————————————————————————————————————————————————————————————————————————————————————————————————
- ;
- ; Storage for the old patch addresses, used to call through once the patch code executes.
- ; These are essentially globals, used by the asm code. They are specifically not exported,
- ; so that the Pascal code cannot access them directly. There are a number of interface
- ; routines I set up so that Pascal can get and set them, but has to go through this file.
- ; You know, sort of object like.
- ;
- pOldNewPtr dc.l 0
- pOldNewHandle dc.l 0
- pOldDisposPtr dc.l 0
- pOldDisposHandle dc.l 0
-
- pEmptyQ dc.l 0 ; list of empty elements available.
- pTreeTop dc.l 0 ; list of active elements.
- pActive dc.w 0 ; whether to watch blocks or not.
-
-
- saveEm reg a0-a3/d0-d3 ; a few to keep, since I'm a patch.
- saveEmSize EQU (8*4) ; 8 registers times 4 bytes each.
-
-
- kCrawlArraySize EQU 8 ; number of stack crawl elements to do.
-
-
- ; ——————————————————————————————————————————————————————————————————————————————————————————————————
- ; When I'm am setting up the world, I call NGetTrapAddress to get the old version
- ; of the traps. I need to save that dude off so I can get back there when needed.
- ; This routine is a handy interface to the high-level world, isolating this asm junk
- ; from the code. All these routines are the same, just a different variable being affected.
- ; This hunk uses the PC-Relative addressing mode in order to get the address of the
- ; variable being set. This allows the code to function without any explicit global
- ; space, since the code acts like globals here.
- ; The interface is:
- ; PROCEDURE SetOldNewPtr (address: LongInt);
- ; PROCEDURE SetOldNewHandle (address: LongInt);
- ; PROCEDURE SetOldDisposPtr (address: LongInt);
- ; PROCEDURE SetOldDisposHandle (address: LongInt);
-
- export SetOldNewPtr, SetOldNewHandle, SetOldDisposPtr, SetOldDisposHandle
-
- SetOldNewPtr
- LEA pOldNewPtr,A1 ; the variable to be setting
- BRA.S Common ; do common stuffing code.
- SetOldNewHandle
- LEA pOldNewHandle,A1 ; the variable to set
- BRA.S Common ; do common stuffing code.
- SetOldDisposPtr
- LEA pOldDisposPtr,A1 ; the variable to do
- BRA.S Common ; do common stuffing code.
- SetOldDisposHandle
- LEA pOldDisposHandle,A1 ; the variable
- Common
- MOVE.L (SP)+,A0 ; get the return address.
- MOVE.L (SP)+,(A1) ; save it, pulling parameter too.
- JMP (A0) ; it's saved, return to high-level.
-
-
- ; ——————————————————————————————————————————————————————————————————————————————————————————————————
- ; These routines are used by the dcmd in order to get to the global variables, to find the
- ; treeTop, emptyQ, and activeState. They aren't used by the b-tree management stuff,
- ; since I always pass these variables to those routines. In addition, the patch code above
- ; doesn't use this code, since it has direct access to the variables, and does so, using the
- ; pc-relative approach.
- ;
- ;
- ; GetTreeInfo:
- ; When desired, the pascal callers may need to get the tree info in order to be able to drive
- ; the trees for info display reasons. This is an interface to let them see us, but I get to
- ; pass back the info, rather than have them groping around directly for the data.
- ; FUNCTION GetTreeTop: TrackEntryPtr; EXTERNAL;
- ; FUNCTION GetEmptyQ: TrackEntryPtr; EXTERNAL;
- ; FUNCTION TrackActive: Boolean; EXTERNAL;
-
- export GetTreeTop, GetEmptyQ, TrackActive
-
- ; GetTreeTop:
- ; This routine just returns the tree top as a parameter. The assembly stuff just grabs
- ; these guys directly when it needs to, but the Pascal code comes through this interface
- ; to get the references.
- ; FUNCTION GetTreeTop: TreeEntryPtr; EXTERNAL;
-
- GetTreeTop
- MOVE.L pTreeTop,4(SP) ; stuff the return result (pc-relative for source.)
- RTS ; and return to caller.
-
- ; GetEmptyQ:
- ; FUNCTION GetEmptyQ: TreeEntryPtr; EXTERNAL;
-
- GetEmptyQ
- MOVE.L pEmptyQ,4(SP) ; stuff the return result (pc-relative for source.)
- RTS ; and return to caller.
-
- ; TrackActive:
- ; Just return the active state as a boolean. This is whether I am actively watching
- ; and recording block addresses or not. I always want to be able to turn it off sometimes,
- ; since I want to save a given state for viewing.
- ; FUNCTION TrackActive: Boolean; EXTERNAL;
-
- TrackActive
- MOVE.W pActive,4(SP) ; stuff function result (pc-relative source).
- RTS ; and return to caller.
-
-
- ; ——————————————————————————————————————————————————————————————————————————————————————————————————
- ; As part of the interface to the dcmd, I have setting routines too, to set the variables
- ; to a known state. This way I don't have to put that sort of tree-based init code in
- ; assembly. I need to turn it on and off using the dcmd, so it will call here to do so.
- ;
- ; PROCEDURE SetTreeTop (address: TrackEntryPtr); EXTERNAL;
- ; PROCEDURE SetEmptyQ (address: TrackEntryPtr); EXTERNAL;
- ; PROCEDURE SetActive (state: Boolean); EXTERNAL;
-
-
- export SetTreeTop, SetEmptyQ, SetActive
-
- ; PROCEDURE SetTreeTop (address: TrackEntryPtr); EXTERNAL;
-
- SetTreeTop
- MOVE.L (SP)+,A0 ; the return address, for safe keeping.
- LEA pTreeTop,A1 ; the variable I need to set, pc-relative.
- MOVE.L (SP)+,(A1) ; save off the new variable, clearing parameter.
- JMP (A0) ; and return to caller.
-
- ; PROCEDURE SetEmptyQ (address: TrackEntryPtr); EXTERNAL;
-
- SetEmptyQ
- MOVE.L (SP)+,A0 ; the return address, for safe keeping.
- LEA pEmptyQ,A1 ; the variable I need to set, pc-relative.
- MOVE.L (SP)+,(A1) ; save off the new variable, clearing parameter.
- JMP (A0) ; and return to caller.
-
- ; PROCEDURE SetActive (state: Boolean); EXTERNAL;
-
- SetActive
- MOVE.L (SP)+,A0 ; the return address, for safe keeping.
- LEA pActive,A1 ; the variable I need to set, pc-relative.
- MOVE.W (SP)+,(A1) ; save off the new state, clearing parameter.
- JMP (A0) ; and return to caller.
-
-
- ; ——————————————————————————————————————————————————————————————————————————————————————————————————
- ; This is the patch code for the various pieces I patch. This is in assembly
- ; since I need to preserve registers and do some other low level jacking around,
- ; like trying to do a stack crawl whenever I allocate blocks. These routines are the
- ; actual code that gets called when one of the four traps is executed.
-
- ; WatchNewPtr:
- ; The routine to patch the NewPtr trap, and I get first dibs at getting the info I want
- ; out of the registers. I need to save off the size to start with, since D0 gets pounded
- ; to the return result once I actually call the old NewPtr code. Once I have that
- ; safely stowed on the stack, I call the old code, using the PC-relative addressing in
- ; order to get the old address that I'm supposed to call. This was set up when the
- ; dcmd called me to save off the value. When that old NewPtr junk returns, I get
- ; the actual address allocated, if enough memory, then do the funky stack crawl,
- ; and finally pass all these tidbits off to the AddNewBlock routine.
-
- export WatchNewPtr
-
-
- WatchNewPtr
- MOVE.L D0,-(SP) ; save size off.
-
- ; First, allocate the pointer.
-
- PEA @1 ; coming back here, as return address.
- MOVE.L pOldNewPtr,-(SP) ; the old address I am going to,
- RTS ; JMP there, when it RTSes I come back
- @1 ; --here.
-
- ; A0 is set up as the handle to track, add it to the b-tree list. If it is NIL, meaning the
- ; NewPtr failed, then don't add it to the list, it cannot be disposed. I do the save of
- ; registers here, after the allocation of memory, so that I can save the registers as they
- ; are coming out of the call, not going in.
-
- MOVEM.L saveEm,-(SP) ; save registers in trap patches.
-
- MOVE.W pActive,D0 ; is it turned on to watch?
- BEQ.S @exit ; if not, skip saving.
- CMP.L #0,A0 ; is it a NIL return?
- BEQ.S @exit ; if so, bag it.
-
- ; Now I am supposed to track the block that was just allocated, so I need to call the AddNewBlock
- ; routine in order to tie it in. In order to set up the parameters for the call, I have to do a stack
- ; crawl, to get the previous return addresses. The problem of course is that the stack crawl has
- ; no known length, and is often less than kCrawlArraySize. So... I have to look at each one as
- ; it is created, and make sure that it is valid; and continue no farther when I find a bad one. If
- ; I go on, I risk dying from a bus error, since the addresses won't chain nicely in all cases. I will call
- ; the routine, where the stackToAdd is an array, built on the stack:
- ; PROCEDURE AddNewBlock (addressToAdd, sizeToAdd: LongInt; stackToAdd: StackArray;
- ; VAR treeTop, emptyQ: TrackEntryPtr);
-
- MOVE.L saveEmSize(SP),D1 ; retrieve size parameter.
-
- MOVEQ #kCrawlArraySize-1,D0 ; for 8 times in DBRA loop.
- @Clr
- CLR.L -(SP) ; make a stack crawl array on the stack,
- DBRA D0,@Clr ; by looping 8 times. (all zeroed.)
-
- MOVE.L SP,A1 ; the address of the stack crawl array.
-
- ; Now with that array built and zeroed I want to add the parameters to the stack. If I have to
- ; bail out of the stack crawl before it hits all kCrawlArraySize elements, then the rest have
- ; already been zeroed, which marks them as non-valid. I'm going to set up for the call
- ; here, so the registers are freed up for doing the funky stack crawl checks.
-
- MOVE.L A0,-(SP) ; addressToAdd: block being added to tracker.
- MOVE.L D1,-(SP) ; sizeToAdd: size of block being tracked.
- MOVE.L A1,-(SP) ; stackToAdd: the address of the array of crawls.
- PEA pTreeTop ; treeTop: bTree top. (as var parameters)
- PEA pEmptyQ ; emptyQ: top of empties list.
-
- ; All the parameters are set up, do the stack crawl checks. This will modify the stackToAdd
- ; array, for each crawl that is valid. The stackToAdd is still in A1.
-
- MOVE.L A6,A0 ; get stack frame head.
- MOVEQ #kCrawlArraySize-1,D0 ; doing a loop for 8 stack crawls.
-
- @Frame
- CMP.L A7,A0 ; make sure it is greater than a7
- BLO.S @noFrame ; skip any more if not valid.
- CMP.L CurStackBase,A0 ; make sure it is less than base of stack,
- BHI.S @noFrame ; If not, skip checking rest of crawl.
-
- ; It's a valid frame, go ahead and save off the return address in the array. A1 is the address of
- ; the next array element to set. It is autoincremented to the next entry each time I can stow a
- ; valid pc return address.
-
- MOVE.L 4(A0),(A1)+ ; the pc return address for this frame, into Array.
-
- MOVE.L (A0),A0 ; next stack frame back. (as a big chain)
- DBRA D0,@Frame ; loop for all 8.
-
- ; If there was a bogus frame in there somewhere, I bailed out to here. All the rest of the stackArray
- ; entries are nil though, so it doesn't matter, I'll just call the AddNewBlock anyway. I got here if
- ; all the entries were valid, too, and just fell out of the DBRA loop.
-
- @noFrame
- BSR AddNewBlock ; track a new block on heap.
- ADD.L #kCrawlArraySize*4,SP ; kill the crawl array from stack.
- @exit
- MOVEM.L (SP)+,saveEm ; save registers in trap patches.
- ADDQ #4,SP ; kill size that was saved. (from D0)
- RTS ; Return to caller of NewPtr.
-
-
- ; ——————————————————————————————————————————————————————————————————————————————————————————————————
- ; WatchDisposPtr:
- ; This routine is my patch to DisposPtr, and it watches the address being passed in
- ; A0 to see if it is one of the blocks I am tracking in the table. If it is, I release that
- ; record from my b-Tree code, since it is obviously not a leak when it is released.
- ; If the block is not found, then I just skip it too, since there are presumably a
- ; ton of blocks that I didn't see being allocated, since I'm not watching from the
- ; start of the system. This is a head patch, since I do my thing, then jump to
- ; the old routine. When that routine finishes, it will call RTS and go back to
- ; wherever I was called from.
-
- export WatchDisposPtr
-
- WatchDisposPtr
- MOVEM.L saveEm,-(SP) ; save registers in trap patches.
-
- MOVE.W pActive,D0 ; is it turned on to watch?
- BEQ.S CallOldDisposePtr ; if not, skip saving.
-
- MOVE.L A0,-(SP) ; block being added to tracker.
- PEA pTreeTop ; bTree top. (as var parameters)
- PEA pEmptyQ ; top of empties list.
- BSR KillOldBlock ; get it out of my dang table
-
- CallOldDisposePtr
- MOVEM.L (SP)+,saveEm ; save registers in trap patches.
-
- MOVE.L pOldDisposPtr,-(SP) ; get address of old routine.
- RTS ; and jump there.
- ; when it RTSes, I'll go back to the caller.
-
-
- ; ——————————————————————————————————————————————————————————————————————————————————————————————————
-
- ; WatchNewHandle:
- ; The routine to patch the NewHandle trap, and I get first dibs at getting the info I want
- ; out of the registers. I need to save off the size to start with, since D0 gets pounded
- ; to the return result once I actually call the old NewHandle code. Once I have that
- ; safely stowed on the stack, I call the old code, using the PC-relative addressing in
- ; order to get the old address that I'm supposed to call. This was set up when the
- ; dcmd called me to save off the value. When that old NewHandle junk returns, I get
- ; the actual address allocated, if enough memory, then do the funky stack crawl,
- ; and finally pass all these tidbits off to the AddNewBlock routine. The AddNewBlock
- ; doesn't differentiate between pointers being watched or handles being watched, it is
- ; just an address.
-
- export WatchNewHandle
-
-
- WatchNewHandle
- MOVE.L D0,-(SP) ; save size off.
-
- ; First, allocate the handle.
-
- PEA @1 ; coming back here, as return address.
- MOVE.L pOldNewHandle,-(SP) ; the old address I am going to,
- RTS ; JMP there, when it RTSes I come back
- @1 ; --here.
-
- ; A0 is set up as the handle to track, add it to the b-tree list. If it is NIL, meaning the
- ; NewHandle failed, then don't add it to the list, it cannot be disposed. I do the save of
- ; registers here, after the allocation of memory, so that I can save the registers as they
- ; are coming out of the call, not going in.
-
- MOVEM.L saveEm,-(SP) ; save registers in trap patches.
-
- MOVE.W pActive,D0 ; is it turned on to watch?
- BEQ.S @exit ; if not, skip saving.
- CMP.L #0,A0 ; is it a NIL return?
- BEQ.S @exit ; if so, bag it.
-
- ; Now I am supposed to track the block that was just allocated, so I need to call the AddNewBlock
- ; routine in order to tie it in. In order to set up the parameters for the call, I have to do a stack
- ; crawl, to get the previous return addresses. The problem of course is that the stack crawl has
- ; no known length, and is often less than kCrawlArraySize. So... I have to look at each one as
- ; it is created, and make sure that it is valid; and continue no farther when I find a bad one. If
- ; I go on, I risk dying from a bus error, since the addresses won't chain nicely in all cases. I will call
- ; the routine, where the stackToAdd is an array, built on the stack:
- ; PROCEDURE AddNewBlock (addressToAdd, sizeToAdd: LongInt; stackToAdd: StackArray;
- ; VAR treeTop, emptyQ: TrackEntryPtr);
-
- MOVE.L saveEmSize(SP),D1 ; retrieve size parameter.
-
- MOVEQ #kCrawlArraySize-1,D0 ; for 8 times in DBRA loop.
- @Clr
- CLR.L -(SP) ; make a stack crawl array on the stack,
- DBRA D0,@Clr ; by looping 8 times. (all zeroed.)
-
- MOVE.L SP,A1 ; the address of the stack crawl array.
-
- ; Now with that array built and zeroed I want to add the parameters to the stack. If I have to
- ; bail out of the stack crawl before it hits all kCrawlArraySize elements, then the rest have
- ; already been zeroed, which marks them as non-valid. I'm going to set up for the call
- ; here, so the registers are freed up for doing the funky stack crawl checks.
-
- MOVE.L A0,-(SP) ; addressToAdd: block being added to tracker.
- MOVE.L D1,-(SP) ; sizeToAdd: size of block being tracked.
- MOVE.L A1,-(SP) ; stackToAdd: the address of the array of crawls.
- PEA pTreeTop ; treeTop: bTree top. (as var parameters)
- PEA pEmptyQ ; emptyQ: top of empties list.
-
- ; All the parameters are set up, do the stack crawl checks. This will modify the stackToAdd
- ; array, for each crawl that is valid. The stackToAdd is still in A1.
-
- MOVE.L A6,A0 ; get stack frame head.
- MOVEQ #kCrawlArraySize-1,D0 ; doing a loop for 8 stack crawls.
-
- @Frame
- CMP.L A7,A0 ; make sure it is greater than a7
- BLO.S @noFrame ; skip any more if not valid.
- CMP.L CurStackBase,A0 ; make sure it is less than base of stack,
- BHI.S @noFrame ; If not, skip checking rest of crawl.
-
- ; It's a valid frame, go ahead and save off the return address in the array. A1 is the address of
- ; the next array element to set. It is autoincremented to the next entry each time I can stow a
- ; valid pc return address.
-
- MOVE.L 4(A0),(A1)+ ; the pc return address for this frame, into Array.
-
- MOVE.L (A0),A0 ; next stack frame back. (as a big chain)
- DBRA D0,@Frame ; loop for all 8.
-
- ; If there was a bogus frame in there somewhere, I bailed out to here. All the rest of the stackArray
- ; entries are nil though, so it doesn't matter, I'll just call the AddNewBlock anyway. I got here if
- ; all the entries were valid, too, and just fell out of the DBRA loop.
-
- @noFrame
- BSR AddNewBlock ; track a new block on heap.
- ADD.L #kCrawlArraySize*4,SP ; kill the crawl array from stack.
- @exit
- MOVEM.L (SP)+,saveEm ; save registers in trap patches.
- ADDQ #4,SP ; kill size that was saved. (from D0)
- RTS ; Return to caller of NewPtr.
-
-
- ; ——————————————————————————————————————————————————————————————————————————————————————————————————
- ; WatchDisposHandle:
- ; This routine is my patch to DisposHandle, and it watches the address being passed in
- ; A0 to see if it is one of the blocks I am tracking in the table. If it is, I release that
- ; record from my b-Tree code, since it is obviously not a leak when it is released.
- ; If the block is not found, then I just skip it too, since there are presumably a
- ; ton of blocks that I didn't see being allocated, since I'm not watching from the
- ; start of the system. This is a head patch, since I do my thing, then jump to
- ; the old routine. When that routine finishes, it will call RTS and go back to
- ; wherever I was called from.
-
- export WatchDisposHandle
-
- WatchDisposHandle
- MOVEM.L saveEm,-(SP) ; save registers in trap patches.
-
- MOVE.W pActive,D0 ; is it turned on to watch?
- BEQ.S CallOldDisposeHandle ; if not, skip saving.
-
- MOVE.L A0,-(SP) ; block being added to tracker.
- PEA pTreeTop ; bTree top. (as var parameters)
- PEA pEmptyQ ; top of empties list.
- BSR KillOldBlock ; get it out of my dang table
-
- CallOldDisposeHandle
- MOVEM.L (SP)+,saveEm ; save registers in trap patches.
-
- MOVE.L pOldDisposHandle,-(SP) ; get address of old routine.
- RTS ; and jump there.
- ; when it RTSes, I'll go back to the caller.
-
- endproc
-
- ; ——————————————————————————————————————————————————————————————————————————————————————————————————
-
- end
-