DIRECTORY AMEvents USING [CallDebugger], Basics USING [BYTE], FastBreak USING [FastBreakData, FastBreakId, FastBreakProc], Loader USING [MakeProcedureResident, MakeGlobalFrameResident], PrincOps USING [BytePC, Frame, FrameHandle, GlobalFrameHandle, InstWord, NullLink, StateVector, zBRK], PrincOpsUtils USING [GetReturnFrame, ReadXTS, WriteXTS], VM USING [Allocate, Free, Interval, AddressForPageNumber, Pin, Unpin, wordsPerPage], WorldVM USING [ClearBreak, LocalWorld, Read, SetBreak]; FastBreakImpl: MONITOR IMPORTS AMEvents, Loader, PrincOpsUtils, VM, WorldVM EXPORTS FastBreak = BEGIN OPEN FastBreak; FastBreakEntryPtr: TYPE = LONG POINTER TO FastBreakEntry; FastBreakEntry: TYPE = RECORD [ count: INT _ 0, next: FastBreakEntryPtr _ NIL, code: LONG POINTER _ NIL, pc: PrincOps.BytePC _ [0], inst: Basics.BYTE _ 0, data: LONG POINTER _ NIL, proc: FastBreakProc _ NIL ]; fastBreakPages: CARDINAL = 4; maxFastBreaks: CARDINAL = (fastBreakPages*VM.wordsPerPage) / SIZE[FastBreakEntry]; FastBreakArrayPtr: TYPE = LONG POINTER TO FastBreakArray; FastBreakArray: TYPE = ARRAY [0..maxFastBreaks) OF FastBreakEntry; arrayPtr: FastBreakArrayPtr _ NIL; space: VM.Interval; -- valid iff arrayPtr # NIL oldBreakTrap: PROC _ NIL; fastBreaksLeft: NAT _ maxFastBreaks; inUseChain: FastBreakEntryPtr _ NIL; freeChain: FastBreakEntryPtr _ NIL; firstTime: BOOL _ TRUE; SpecifyDefaultBreakHandler: PUBLIC PROC [old: PROC] = { oldBreakTrap _ old; }; FastBreakHandler: PUBLIC PROC = { state: RECORD [ padding: ARRAY [0..2) OF UNSPECIFIED, v: PrincOps.StateVector]; fp: PrincOps.FrameHandle; state.v _ STATE; fp _ PrincOpsUtils.GetReturnFrame[]; IF inUseChain # NIL THEN { pc: PrincOps.BytePC _ fp.pc; gf: PrincOps.GlobalFrameHandle _ fp.accesslink; code: LONG POINTER _ gf.code.longbase; ep: FastBreakEntryPtr _ inUseChain; inst: Basics.BYTE _ PrincOps.zBRK; useOld: BOOL _ FALSE; WHILE ep # NIL DO entry: FastBreakEntry _ ep^; -- copy in one burst to reduce races IF entry.code = code AND entry.pc = pc THEN { inst _ entry.inst; ep.count _ entry.count + 1; IF entry.proc # NIL THEN IF entry.proc[entry.data, fp, @state.v] THEN useOld _ TRUE; }; ep _ entry.next; ENDLOOP; IF inst # PrincOps.zBRK THEN { IF PrincOpsUtils.ReadXTS[] = on THEN PrincOpsUtils.WriteXTS[skip1]; IF useOld THEN { AMEvents.CallDebugger["FastBreak proc requested a break."]; }; state.v.source _ PrincOps.NullLink; state.v.dest _ LOOPHOLE[fp]; state.v.instbyte _ inst; RETURN WITH state.v; }; }; IF PrincOpsUtils.ReadXTS[] = on THEN PrincOpsUtils.WriteXTS[skip1]; state.v.source _ LOOPHOLE[fp]; state.v.dest _ LOOPHOLE[oldBreakTrap]; RETURN WITH state.v; }; FastBreaksLeft: PUBLIC PROC RETURNS [NAT] = TRUSTED { RETURN [fastBreaksLeft]; }; SetFastBreak: PUBLIC ENTRY PROC [code: LONG POINTER, pc: PrincOps.BytePC, proc: FastBreakProc _ NIL, data: FastBreakData _ NIL] RETURNS [id: FastBreakId] = TRUSTED { ENABLE UNWIND => NULL; ep: FastBreakEntryPtr _ freeChain; inst: Basics.BYTE _ PrincOps.zBRK; needToSet: BOOL _ TRUE; IF arrayPtr = NIL THEN { fb: FastBreakEntryPtr; IF firstTime THEN { Loader.MakeGlobalFrameResident[SetFastBreak]; Loader.MakeProcedureResident[SetFastBreak]; Loader.MakeProcedureResident[ClearFastBreak]; Loader.MakeProcedureResident[ClearAllFastBreaks]; Loader.MakeProcedureResident[ClearInternal]; Loader.MakeProcedureResident[CountBreaksAtLocation]; Loader.MakeProcedureResident[ReadCodeByte]; firstTime _ FALSE; }; space _ VM.Allocate[fastBreakPages]; VM.Pin[space]; arrayPtr _ LOOPHOLE[VM.AddressForPageNumber[space.page]]; fastBreaksLeft _ maxFastBreaks; arrayPtr^ _ ALL[FastBreakEntry[]]; ep _ freeChain _ fb _ @arrayPtr[0]; FOR i: CARDINAL IN [0..maxFastBreaks-1) DO next: FastBreakEntryPtr _ fb + SIZE[FastBreakEntry]; fb.next _ next; fb _ next; ENDLOOP; }; IF ep = NIL THEN RETURN [NIL]; inst _ ReadCodeByte[code, pc]; IF inst = PrincOps.zBRK THEN { FOR eep: FastBreakEntryPtr _ inUseChain, eep.next WHILE eep # NIL DO IF eep.code = code AND eep.pc = pc THEN { inst _ eep.inst; needToSet _ FALSE; EXIT}; ENDLOOP; IF inst = PrincOps.zBRK THEN RETURN [NIL]; }; id _ @ep.count; freeChain _ ep.next; ep^ _ [count: 0, next: inUseChain, code: code, pc: pc, inst: inst, data: data, proc: proc]; inUseChain _ ep; IF needToSet THEN ep.inst _ WorldVM.SetBreak[WorldVM.LocalWorld[], LOOPHOLE[code], pc]; }; ClearFastBreak: PUBLIC ENTRY PROC [id: FastBreakId, proc: FastBreakProc _ NIL, data: FastBreakData _ NIL] RETURNS [found: BOOL _ FALSE] = TRUSTED { ENABLE UNWIND => NULL; ep: FastBreakEntryPtr _ inUseChain; lag: FastBreakEntryPtr _ NIL; WHILE ep # NIL DO IF LOOPHOLE[id, FastBreakEntryPtr] = ep AND ep.proc = proc AND ep.data = data THEN { ClearInternal[ep, lag]; RETURN [TRUE]; }; lag _ ep; ep _ ep.next; ENDLOOP; }; ClearAllFastBreaks: PUBLIC ENTRY PROC [releaseResources: BOOL _ TRUE] RETURNS [cleared: NAT _ 0] = TRUSTED { ENABLE UNWIND => NULL; WHILE inUseChain # NIL DO ClearInternal[inUseChain]; cleared _ cleared + 1; ENDLOOP; IF releaseResources AND arrayPtr # NIL THEN { arrayPtr _ NIL; VM.Unpin[space]; VM.Free[space]; fastBreaksLeft _ maxFastBreaks; }; }; ClearInternal: INTERNAL PROC [ep: FastBreakEntryPtr, lag: FastBreakEntryPtr _ NIL] = { SELECT CountBreaksAtLocation[ep.code, ep.pc] FROM 0 => RETURN; 1 => { WorldVM.ClearBreak[WorldVM.LocalWorld[], LOOPHOLE[ep.code], ep.pc, ep.inst]; }; ENDCASE; IF lag = NIL THEN inUseChain _ ep.next ELSE lag.next _ ep.next; ep.next _ freeChain; freeChain _ ep; fastBreaksLeft _ fastBreaksLeft + 1; }; CountBreaksAtLocation: INTERNAL PROC [code: LONG POINTER, pc: PrincOps.BytePC] RETURNS [count: CARDINAL _ 0] = { ep: FastBreakEntryPtr _ inUseChain; WHILE ep # NIL DO IF ep.code = code AND ep.pc = pc THEN count _ count + 1; ep _ ep.next; ENDLOOP; }; ReadCodeByte: PROC [code: LONG POINTER, pc: PrincOps.BytePC] RETURNS [byte: Basics.BYTE] = { addr: LONG CARDINAL _ LOOPHOLE[code]; word: PrincOps.InstWord _ LOOPHOLE[WorldVM.Read[WorldVM.LocalWorld[], addr+pc/2]]; IF pc MOD 2 = 1 THEN byte _ word.oddbyte ELSE byte _ word.evenbyte; }; END. \FastBreakImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Russ Atkinson, February 11, 1985 11:39:31 pm PST Birrell, August 2, 1983 11:02 am SpecifyDefaultBreakHandler[old] specifies the old breakpoint handler to be used when a non-fast break is encountered. It should be called before FastBreakHandler is installed. Executed by (non-worry) BRK instruction. Examines our wonder little resident data structure to determine whether or not to take a FAST break. If not, then we pass control to the old break handler (whatever it is). There can be races between this procedure and the setting/cearing procedures, but it is not easy to remove these races due to atomicity considerations. call the user's routine We have handled a fast break. Now we must clean up our act to allow continuation. Notice that we do not allow upper-level breakpoints AND fast breaks, although we allow multiple fast breaks. We have handled a fast break, but we have been requested to make this into a pseudo-break event. The easiest way is just to call the debugger. At this point we have NOT handled a fast break, so we have to go to the old break handler in the hopes that it knows what to do. FastBreaksLeft[] returns the number of fast break slots remaining in the table. SetFastBreak[code, pc, proc, data] adds a fast break at the specified location. The pointer returned is used to distinguish which break to clear when clearing the break. If NIL is returned, then the breakpoint could not be set (due to the table being full). Make a new array, and make sure that it is resident. this is the latest time that we can make ourselves resident and still be safe about it There is already a breakpoint here, so let's check our inUseChain for other instances. There is already a break at this location, so punt. Remove the entry from the free chain Fill in the entry Put the entry on the inUseChain ClearFastBreak[id, proc, data] clears the specified fast break, provided that the parameters agree with an active fast break. TRUE is returned iff such a break was found. ClearAllFastBreaks[] clears all fast breaks. It also releases system resources used by fast breaks if releaseResources = TRUE. It returns the number of fast breaks removed. Clear out one break Time to restore the old instruction Now, remove the entry from the inUseChain Put the block back on the free chain Κ ˜codešœ™Kšœ Οmœ1™Kšœ žœX˜fKšœžœ%˜8KšžœžœL˜TKšœžœ*˜7—K˜šœž˜Kšžœ"žœ ˜4Kšžœ ˜Kšœžœžœ ˜—K˜Kš œžœžœžœžœ˜9šœžœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœžœžœ˜K˜Kšœ žœ˜Kšœžœžœžœ˜Kšœž˜K˜—K˜Kšœžœ˜šœžœ˜Kšœžœžœ˜8—K˜Kš œžœžœžœžœ˜9Kšœžœžœžœ˜BK˜Kšœžœ˜"Kšœžœ Οc˜/Kšœžœžœ˜Kšœžœ˜$Kšœ žœ˜$Kšœžœ˜#Kšœ žœžœ˜K˜šΟnœž œžœ˜7Kšœ°™°Kšœ˜Kšœ˜K˜—š œž œ˜!Kšœπ™πšœžœ˜Kšœ žœžœž œ˜%K˜—K˜Kšœ žœ˜K˜$šžœžœžœ˜K˜K˜/Kšœžœžœ˜&Kšœ#˜#Kšœ žœ˜"Kšœžœžœ˜šžœžœž˜KšœŸ$˜Ašžœžœžœ˜-Kšœ˜Kšœ˜šžœžœž˜Kšœ™Kšžœ&žœ žœ˜;—K˜—Kšœ˜Kšžœ˜—šžœžœ˜Kšœΐ™ΐKšžœžœ˜Cšžœžœ˜Kšœ™K˜;K˜—Kšœ#˜#Kšœžœ˜Kšœ˜Kšžœžœ ˜K˜—Kšœ˜—Kšœ€™€Kšžœžœ˜CKšœžœ˜Kšœžœ˜&Kšžœžœ ˜K˜—K˜š  œžœžœžœžœžœ˜5KšœP™PKšžœ˜Kšœ˜K˜—š  œžœž œžœžœ-žœžœžœžœ˜₯Kšœ„™„Kšžœžœžœ˜Kšœ"˜"Kšœ žœ˜"Kšœ žœžœ˜šžœ žœžœ˜Kšœ4™4Kšœ˜šžœ žœ˜KšœV™VKšœ-˜-Kšœ+˜+Kšœ-˜-Kšœ1˜1Kšœ,˜,Kšœ4˜4Kšœ+˜+Kšœ žœ˜Kšœ˜—Kšœ$˜$K˜Kšœ žœ&˜9Kšœ˜Kšœ žœ˜"Kšœ#˜#šžœžœžœž˜*Kšœžœ˜4Kšœ˜K˜ Kšžœ˜—Kšœ˜—Kš žœžœžœžœžœ˜Kšœ˜šžœ˜šžœ˜KšœV™Všžœ/žœžœž˜Dšžœžœ žœ˜)K˜Kšœ žœ˜Kšžœ˜—Kšžœ˜—šžœž˜Kšœ3™3Kšžœžœ˜ —K˜——K˜Kšœ$™$Kšœ˜Kšœ™Kšœ[˜[Kšœ™Kšœ˜šžœ ž˜Kšœ1žœ ˜E—K˜K˜—š œžœžœžœ)žœžœžœ žœžœžœ˜“Kšœ«™«Kšžœžœžœ˜Kšœ#˜#Kšœžœ˜šžœžœž˜š žœžœžœžœžœ˜TKšœ˜Kšžœžœ˜Kšœ˜—K˜ Kšœ ˜ Kšžœ˜—K˜K˜—š œžœžœžœžœžœžœ žœžœ˜lKšœ―™―Kšžœžœžœ˜šžœžœž˜Kšœ™Kšœ˜Kšœ˜Kšžœ˜—šžœžœ žœžœ˜-Kšœ žœ˜Kšœ˜K˜Kšœ˜K˜—Kšœ˜K˜—š  œžœžœ2žœ˜Všžœ'ž˜1Kšœžœ˜ šœ˜Kšœ#™#Kšœ)žœ˜LK˜—Kšžœ˜—Kšœ)™)Kšžœžœžœžœ˜?Kšœ$™$Kšœ˜Kšœ˜K˜$K˜K˜—š œžœžœžœžœžœ žœ ˜pKšœ#˜#šžœžœž˜Kšžœžœ žœ˜8Kšœ ˜ Kšžœ˜—K˜K˜—š   œžœžœžœžœžœ˜\Kšœžœžœžœ˜%šœ˜Kšžœ0˜8—Kšžœžœžœžœ˜CKšœ˜K˜—Kšžœ˜K˜—…—n)θ