DIRECTORY AMBridge USING [ContextPC, GetWorld, GetWorldIncarnation, GFHFromTV, IsRemote, RemoteGFHFromTV], AMEvents USING [BreakID, Event, EventRec, SetBreak, ClearBreak], AMModel USING [Context, ContextChildren, ContextSection, ParentSection, Section, SectionClass, SectionSource, Source, SourceObj, SourceSection], AMModelBridge USING [LoadedSection, LoadedSectionForProc, LoadedSectionForProgPC, ProcFromLoadedSection], AMModelLocation USING [CodeLocation, EntryLocations, ExitLocations], AMTypes USING [Error, GlobalParent, TVType, UnderClass], BBAction USING [Action, NewAction, WaitForDataTaken], BBDisableBreaks USING [Enabled], BBObjectLocation USING [BreakpointHandler, LocationErrorCode, siNull], BBZones USING [GetQuantizedZone], PrincOps USING [BYTE, BytePC, FrameCodeBase, InstWord], RTBasic USING [TV], WorldVM USING [CurrentIncarnation, Incarnation, Long, LongRead, Read, World]; BBObjectLocationImpl: CEDAR MONITOR IMPORTS AMBridge, AMEvents, AMModel, AMModelBridge, AMModelLocation, AMTypes, BBAction, BBDisableBreaks, BBZones, WorldVM EXPORTS BBObjectLocation = BEGIN OPEN BBObjectLocation, RTBasic; BreakID: TYPE = AMEvents.BreakID; BYTE: TYPE = PrincOps.BYTE; BytePC: TYPE = PrincOps.BytePC; CodeLocation: TYPE = AMModelLocation.CodeLocation; Event: TYPE = AMEvents.Event; EventRec: TYPE = AMEvents.EventRec; FrameCodeBase: TYPE = PrincOps.FrameCodeBase; EmptyCodeBase: FrameCodeBase = LOOPHOLE[LONG[0], FrameCodeBase]; LoadedSection: TYPE = AMModelBridge.LoadedSection; EmptyLoadedSection: LoadedSection = [NIL, NIL, [0]]; Section: TYPE = AMModel.Section; Source: TYPE = AMModel.Source; World: TYPE = WorldVM.World; Location: TYPE = REF LocationRep; LocationRep: PUBLIC TYPE = RECORD [gf: TV _ NIL, pc: BytePC _ [0], section: AMModel.Section _ NIL]; BreakpointId: TYPE = REF BreakpointIdRep; BreakpointIdRep: PUBLIC TYPE = BreakBlockRep; BreakBlock: TYPE = REF BreakBlockRep; BreakBlockRep: TYPE = RECORD [ next: BreakBlock _ NIL, location: Location _ NIL, head: CodeHead _ NIL, handler: BreakpointHandler _ NIL, -- client's handler data: REF _ NIL -- client's data ]; CodeHeadArray: TYPE = ARRAY [0..CodeHeadMod) OF CodeHead; CodeHeadMod: CARDINAL = 32; CodeHead: TYPE = REF CodeHeadRep; CodeHeadRep: TYPE = RECORD [ next: CodeHead _ NIL, list: BreakBlock _ NIL, cid: AMEvents.BreakID _ NIL, codeLoc: CodeLocation _ [EmptyCodeBase, [0]], incarnation: WorldVM.Incarnation _ 0, world: World _ NIL]; LocationError: PUBLIC ERROR [code: LocationErrorCode] = CODE; EntryLocation: PUBLIC PROC [self: Location] RETURNS [newLoc: Location] = TRUSTED { tv: TV _ self.gf; section: Section _ self.section; pc: BytePC _ self.pc; loadSect: LoadedSection _ [self.section, self.gf, pc]; list: LIST OF CodeLocation _ NIL; SELECT AMModel.SectionClass[section] FROM statement => {section _ AMModel.ParentSection[section]}; ENDCASE => RETURN [self]; list _ AMModelLocation.EntryLocations[section].list; IF list # NIL THEN pc _ list.first.pc; RETURN [LocationFromGFandPC[tv, pc]]; }; ExitLocation: PUBLIC PROC [self: Location] RETURNS [newLoc: Location] = TRUSTED { tv: TV _ self.gf; section: Section _ self.section; pc: BytePC _ self.pc; loadSect: LoadedSection _ [self.section, self.gf, pc]; list: LIST OF CodeLocation _ NIL; SELECT AMModel.SectionClass[section] FROM statement => {section _ AMModel.ParentSection[section]}; ENDCASE => RETURN [self]; list _ AMModelLocation.ExitLocations[section].list; IF list # NIL THEN pc _ list.first.pc; RETURN [LocationFromGFandPC[tv, pc]]; }; SetBreakpoint: PUBLIC UNSAFE PROC [self: Location, handler: BreakpointHandler, data: REF _ NIL] RETURNS [rId: BreakpointId] = TRUSTED { IF self = NIL THEN RETURN [NIL]; rId _ NewBreakBlock[self]; rId.data _ data; rId.handler _ handler; }; ClearBreakpoint: PUBLIC UNSAFE PROC [self: Location, id: BreakpointId] RETURNS [rId: BreakpointId] = TRUSTED { head: CodeHead _ NIL; IF id = NIL OR self = NIL THEN RETURN [NIL]; head _ FindCodeHead[self, none]; IF head = NIL OR id.head # head THEN RETURN [NIL]; rId _ FlushBreakBlock[head, id]; }; IsLocationAtLocation: PUBLIC PROC [self: Location, loc: Location] RETURNS [yes: BOOL] = TRUSTED { IF self = loc THEN RETURN [TRUE]; IF self = NIL OR loc = NIL THEN RETURN [FALSE]; IF self.pc # loc.pc THEN RETURN [FALSE]; RETURN [SameGlobalFrames[self.gf, loc.gf]]; }; IsFrameAtLocation: PUBLIC PROC [self: Location, frame: TV -- Local frame --] RETURNS [yes: BOOL] = TRUSTED { IF self = NIL THEN RETURN [frame = NIL]; IF frame = NIL THEN RETURN [FALSE]; IF AMBridge.ContextPC[frame] # self.pc THEN RETURN [FALSE]; RETURN [SameGlobalFrames[AMTypes.GlobalParent[frame], self.gf]]; }; LocationFromGFandPC: PUBLIC PROC [gf: TV, pc: BytePC] RETURNS [new: Location] = TRUSTED { section: Section _ NIL; IF gf = NIL THEN RETURN [NIL]; IF pc = 0 THEN { section _ AMModel.ContextSection[gf]; } ELSE { section _ AMModelBridge.LoadedSectionForProgPC [gf, pc ! AMTypes.Error => CONTINUE].section; }; new _ LocationFromSection[section]; new.gf _ gf; new.pc _ pc; }; GFandPCFromLocation: PUBLIC PROC [loc: Location] RETURNS [gf: TV, pc: BytePC] = TRUSTED { RETURN [loc.gf, loc.pc]; }; TVFromLocation: PUBLIC PROC [loc: Location] RETURNS [tv: TV _ NIL] = TRUSTED { IF loc # NIL THEN { section: Section _ loc.section; gf: TV _ loc.gf; pc: BytePC _ loc.pc; IF pc = 0 THEN RETURN [gf]; SELECT AMModel.SectionClass[section] FROM statement => section _ AMModel.ParentSection[section]; proc => {}; ENDCASE => RETURN [loc.gf]; tv _ AMModelBridge.ProcFromLoadedSection[[section, gf, pc]]; }; }; ByteAtGFandPC: PUBLIC PROC [gf: TV, pc: BytePC] RETURNS [BYTE] = TRUSTED { addr: LONG CARDINAL _ LOOPHOLE[GetCodeBase[gf]]; world: World _ AMBridge.GetWorld[gf]; pair: PrincOps.InstWord _ LOOPHOLE[WorldVM.Read[world, addr], PrincOps.InstWord]; addr _ addr + pc / 2; RETURN [IF pc MOD 2 = 0 THEN pair.evenbyte ELSE pair.oddbyte]; }; CatchBreakpoint: PUBLIC UNSAFE PROC [event: Event] RETURNS [handled: BOOL _ TRUE] = TRUSTED { ev: REF EventRec[break] _ NARROW[event]; head: CodeHead _ NIL; cdata: REF = ev.clientData; IF cdata = NIL THEN RETURN [TRUE]; -- just a little racy, here WITH cdata SELECT FROM myHead: CodeHead => head _ myHead; ENDCASE => { action: BBAction.Action _ BBAction.NewAction[event, other]; BBAction.WaitForDataTaken[action, "Whose break is this?"]; RETURN; }; IF head.list # NIL THEN { cloc: CodeLocation _ head.codeLoc; addr: LONG CARDINAL _ LOOPHOLE[cloc.codeBase]; IF BBDisableBreaks.Enabled[] THEN FOR bb: BreakBlock _ head.list, bb.next UNTIL bb = NIL DO IF head.list = NIL THEN EXIT; -- somebody cleared everything!!! IF bb.head = NIL OR bb.handler = NIL THEN LOOP; -- break deleted during enumeration [] _ bb.handler[event, bb, bb.data]; ENDLOOP; [] _ WorldVM.Read [head.world, LOOPHOLE[cloc.codeBase.longbase, LONG CARDINAL] + cloc.pc/2]; }; }; TVToLocation: PUBLIC PROC [tv: TV] RETURNS [l: Location] = TRUSTED { gf: TV _ NIL; pc: BytePC _ [0]; IF tv = NIL THEN RETURN [NIL]; SELECT AMTypes.UnderClass[AMTypes.TVType[tv]] FROM localFrame => { gf _ AMTypes.GlobalParent[tv]; pc _ AMBridge.ContextPC[tv]}; globalFrame => gf _ tv; procedure => { loadSect: LoadedSection _ AMModelBridge.LoadedSectionForProc[tv]; list: LIST OF CodeLocation _ AMModelLocation.EntryLocations[loadSect.section].list; IF list = NIL THEN RETURN [NIL]; pc _ list.first.pc; gf _ AMTypes.GlobalParent[tv]; }; ENDCASE => RETURN [NIL]; RETURN [LocationFromGFandPC[gf, pc]]; }; SourceToLocation: PUBLIC PROC [tv: TV, sourceIndex: CARDINAL] RETURNS [l: Location] = TRUSTED { section: Section _ AMModel.ContextSection[tv]; source: Source _ AMModel.SectionSource[section]; pc: BytePC _ [0]; list: LIST OF CodeLocation _ NIL; model: AMModel.Context _ NIL; IF source = NIL THEN RETURN [NIL]; source _ z.NEW[AMModel.SourceObj _ [source.fileName, statement, source.versionStamp, field[sourceIndex, sourceIndex]]]; section _ AMModel.SourceSection[source, tv].section; list _ AMModelLocation.EntryLocations[section].list; IF list # NIL THEN pc _ list.first.pc; RETURN [LocationFromGFandPC[tv, pc]]; }; LocationToSource: PUBLIC PROC [loc: Location] RETURNS [gf: TV _ NIL, sourceIndex: CARDINAL _ 0] = TRUSTED { section: Section _ IF loc = NIL THEN NIL ELSE loc.section; source: Source _ NIL; index: INT _ 0; IF section = NIL THEN RETURN [NIL, siNull]; source _ AMModel.SectionSource[section]; gf _ loc.gf; WITH s: source SELECT FROM field => index _ s.firstCharIndex; ENDCASE; IF index < 0 THEN sourceIndex _ siNull ELSE sourceIndex _ index; }; NewBreakBlock: PROC [loc: Location] RETURNS [bb: BreakBlock _ NIL] = TRUSTED { head: CodeHead _ FindCodeHead[loc, add]; IF head = NIL THEN RETURN [NIL]; bb _ z.NEW[BreakBlockRep _ [next: head.list, head: head]]; head.list _ bb; bb.location _ loc; }; FindAction: TYPE = {none, add}; FindCodeHead: ENTRY PROC [loc: Location, action: FindAction _ none] RETURNS [head: CodeHead] = TRUSTED { ENABLE UNWIND => NULL; mod: CARDINAL = loc.pc MOD CodeHeadMod; cloc: CodeLocation = GetCodeLocation[loc]; world: World = AMBridge.GetWorld[loc.gf]; incarnation: WorldVM.Incarnation = WorldVM.CurrentIncarnation[world]; head _ codeHeadTab[mod]; WHILE head # NIL DO IF world = head.world AND incarnation = head.incarnation AND cloc = head.codeLoc THEN EXIT; head _ head.next; ENDLOOP; IF action = none THEN RETURN [head]; IF head = NIL THEN { head _ z.NEW[CodeHeadRep _ [ next: codeHeadTab[mod], codeLoc: cloc, incarnation: incarnation, world: world]]; codeHeadTab[mod] _ head; }; IF head.cid = NIL THEN { head.cid _ AMEvents.SetBreak[ world, LOOPHOLE[cloc.codeBase], loc.pc, head ! ABORTED => GO TO abort; ANY => GO TO oops]; }; RETURN [head]; EXITS abort => RETURN WITH ERROR ABORTED; oops => RETURN WITH ERROR LocationError[duplicateId]; }; FirstChild: PROC [parent: AMModel.Context] RETURNS [child: AMModel.Context] = TRUSTED { visit: PROC [context: AMModel.Context] RETURNS[stop: BOOL] = TRUSTED { RETURN [TRUE] }; RETURN [AMModel.ContextChildren[parent, visit]]; }; LocationFromSection: PROC [section: Section] RETURNS [Location] = TRUSTED { RETURN [z.NEW[LocationRep _ [section: section]]]; }; SameGlobalFrames: PROC [gf1,gf2: TV] RETURNS [BOOL] = TRUSTED { IF gf1 = gf2 THEN RETURN [TRUE]; IF gf1 = NIL OR gf2 = NIL THEN RETURN [FALSE]; IF NOT InSameWorld[gf1,gf2] THEN RETURN [FALSE]; RETURN [GetCodeBase[gf1].longbase = GetCodeBase[gf2].longbase]; }; GetCodeBase: PROC [gf: TV] RETURNS [base: FrameCodeBase] = TRUSTED { world: World _ AMBridge.GetWorld[gf]; short: CARDINAL; addr: LONG CARDINAL; IF gf = NIL THEN RETURN [EmptyCodeBase]; IF AMBridge.IsRemote[gf] THEN short _ LOOPHOLE[AMBridge.RemoteGFHFromTV[gf].gfh, CARDINAL] ELSE short _ LOOPHOLE[AMBridge.GFHFromTV[gf], CARDINAL]; addr _ WorldVM.Long[world, short + 1]; base _ LOOPHOLE[WorldVM.LongRead[world, addr], FrameCodeBase]; base.out _ FALSE; }; GetCodeLocation: PROC [loc: Location] RETURNS [CodeLocation] = TRUSTED { RETURN [[codeBase: GetCodeBase[loc.gf], pc: loc.pc]]; }; InSameWorld: PROC [tv1,tv2: TV] RETURNS [BOOL] = TRUSTED { RETURN [ AMBridge.GetWorld[tv1] = AMBridge.GetWorld[tv2] AND AMBridge.GetWorldIncarnation[tv1] = AMBridge.GetWorldIncarnation[tv2]]; }; FlushBreakBlock: ENTRY PROC [head: CodeHead, id: BreakpointId] RETURNS [rId: BreakpointId _ NIL] = TRUSTED { ENABLE UNWIND => NULL; lag: BreakBlock _ NIL; FOR bb: BreakBlock _ head.list, bb.next UNTIL bb = NIL DO IF bb = id THEN { IF lag = NIL THEN head.list _ bb.next ELSE lag.next _ bb.next; bb.head _ NIL; EXIT}; lag _ bb; ENDLOOP; IF head.cid # NIL AND head.list = NIL THEN { cid: AMEvents.BreakID _ head.cid; head.cid _ NIL; AMEvents.ClearBreak[cid]; }; }; z: ZONE _ BBZones.GetQuantizedZone[]; codeHeadTab: REF CodeHeadArray _ z.NEW[CodeHeadArray _ ALL[NIL]]; END. æBBObjectLocationImpl.mesa Russ Atkinson, June 30, 1983 5:33 pm The break block is used as our callback data to AMEvents.SetBreak. We can get back to our code head, and to our level of handler proc & data. By convention, the break block is no longer active when the location = NIL The code head is present to allow multiple breakpoints at a single location. Note that the physical location (CodeLocation) is stored here, while the logical location is in the break block to allow multiple logical locations for a given physical location. LocationErrorCode: TYPE = {duplicateId, notALink, cantUseGlobalFrame, badGFandPC}; SourceIndex: TYPE = CARDINAL; siNull: SourceIndex=LAST[SourceIndex]; returns the entry location for self returns the exit location for self returns the location corresponding to the given gf and pc LocationError[badGFandPC] occurs if illegal arguments are given just the global frame, not a procedure gosh, we sure hope that this is a procedure returns the GF and PC for the given location returns the TV (globalFrame or procedure) for the given location returns exact byte currently at given gf and pc Non-operations for use by BBNubImpl in handling breakpoints; WE MUST BE ABLE TO DO SOMETHING REASONABLE WITH BREAKPOINTS NOT IN OUR LIST, SINCE AMEvents ALLOWS SETTING OF BREAKPOINTS IN FUNNY WAYS. This is a breakpoint created by someone other than us, so make a new event that conveys this non-information! At this point the head is definitiely a non-NIL CodeHead. The following line is a hack that should go away when breakpoints really work! It is intended to reduce the number of times that proceeding a breakpoint causes a page fault on the instruction fetch, which then causes a spurious break to take place. UTILITY ROUTINES make a new break block for the given location make sure that the breakpoint is set, of course scan for an existing head for this location make a new head for this location & put it on the list we need to set the breakpoint time to really clear the break Êi˜šœ™Jšœ$™$—J˜šÏk ˜ šœ ˜JšœQ˜Q—Jšœ œ2˜@šœ˜ J˜‚—šœ˜J˜U—Jšœœ/˜DJšœœ+˜8Jšœ œ'˜5Jšœœ ˜ Jšœœ0˜FJšœœ˜!Jšœ œœ#˜7Jšœœœ˜šœ˜ Jšœ?˜?J˜——šœœ˜#šœF˜MJšœ+˜+—Jšœ˜Jšœœœ˜'J˜Jšœ œ˜!Jšœœ œ˜Jšœœ˜Jšœœ ˜2Jšœœ˜Jšœ œ˜#šœœ˜-Jšœœœ˜@—šœœ˜2Jšœ%œœ˜4—Jšœ œ˜ Jšœœ˜Jšœœ˜J˜Jšœ œœ ˜!šœ œœ˜!šœœœ/œ˜AJ˜——Jšœœœ˜)Jšœœœ˜-J˜JšœÚ™ÚJ˜Jšœ œœ˜%šœœœ˜Jšœœ˜Jšœœ˜Jšœœ˜JšœœÏc˜5Jšœœœž˜ J˜J˜—Jšœ€™€J˜Jšœœœœ ˜9Jšœ œ˜Jšœ œœ ˜!šœ œœ˜Jšœœ˜Jšœœ˜Jšœœ˜J˜-Jšœ%˜%šœœ˜J˜——Jšœœœœ˜=JšœR™RJ˜Jšœ™Jšœ&™&J˜—š Ïn œœœœœ˜RJšœ#™#Jšœœ ˜J˜ J˜J˜6Jšœœœœ˜!šœ˜)J˜8Jšœœ˜—J˜4Jšœœœ˜&Jšœ˜%J˜J˜—š Ÿ œœœœœ˜QJšœ"™"Jšœœ ˜J˜ J˜J˜6Jšœœœœ˜!šœ˜)J˜8Jšœœ˜—J˜3Jšœœœ˜&Jšœ˜%J˜J˜—šŸ œœœ˜!Jšœ3œœ˜=Jšœœ˜'Jš œœœœœ˜ J˜J˜J˜J˜J˜—šŸœœœ˜#Jšœ#œœ˜JJšœœ˜Jšœœœœœœœ˜,J˜ Jš œœœœœœ˜3J˜ J˜J˜—šŸœœ˜!Jšœ œœœ˜?Jšœ œœœ˜!Jšœœœœœœœ˜/Jšœœœœ˜(Jšœ%˜+J˜J˜—šŸœœ˜Jš œœžœœœœ˜MJš œœœœ œ˜(Jš œ œœœœ˜#Jšœ%œœœ˜;Jšœ:˜@J˜J˜—šŸœœ˜ Jšœœœœ˜8Jšœ9™9JšœB™BJšœœ˜Jš œœœœœ˜šœ˜ šœ˜Jšœ&™&J˜%J˜—šœ˜Jšœ+™+˜.Jšœœ ˜-—J˜——J˜#J˜ J˜ J˜J˜—šŸœœ˜ Jšœœœœ˜8Jšœ,™,Jšœ˜J˜J˜—šŸœœœœœœœ˜NJšœ@™@šœœœ˜Jšœ˜Jšœœ ˜Jšœ˜Jšœœœ˜šœ˜)J˜6J˜ Jšœœ ˜—J˜J˜J˜—Jšœ™J˜šŸœœœ˜#Jš œœ œœœ˜9Jšœ·™·Jšœœœ˜(Jšœœ˜Jšœœ˜Jš œ œœœœž˜>šœœ˜Jšœ"˜"šœ˜ Jšœm™mJšœ;˜;J˜:Jšœ˜Jšœ˜——Jšœ9™9šœ œœ˜J˜"Jšœœœœ˜.šœ˜!šœ%œœ˜9šœ ˜Jšœœž!˜,—šœ œœ˜$Jšœœž#˜/—J˜$Jšœ˜——Jšœù™ù˜Jšœ œœœ˜J—J˜—J˜J˜—š Ÿ œœœœœœ˜DJšœœœ˜ J˜Jš œœœœœ˜šœ(˜2˜J˜J˜—J˜˜J˜AJšœœœF˜SJš œœœœœ˜ J˜J˜J˜—Jšœœœ˜—Jšœ˜%J˜J˜—šŸœœ˜Jš œœœœœ˜AJ˜.J˜0J˜Jšœœœœ˜!Jšœœ˜Jš œ œœœœ˜"˜šœœ˜J˜T——J˜4J˜4Jšœœœ˜&Jšœ˜%J˜J˜—šŸœœ˜Jš œœœœœœ˜MJš œœœœœœ ˜:Jšœœ˜Jšœœ˜Jš œ œœœœ ˜+J˜(J˜ šœ œ˜J˜"Jšœ˜—Jšœ œœ˜@J˜J˜—Jšœ™J˜šŸ œ˜Jšœœœœ˜:Jšœ-™-Jšœ/™/J˜(Jš œœœœœ˜ Jšœœ0˜:J˜J˜J˜J˜—Jšœ œ˜J˜šŸ œœ˜Jšœ+œœ˜OJšœœœ˜Jšœœ œ ˜'J˜*J˜)JšœE˜EJšœ+™+J˜šœœ˜šœœœ˜PJšœœ˜ —J˜Jšœ˜—Jšœœœ˜$šœœœ˜Jšœ6™6šœ œ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜—J˜J˜—šœ œœ˜Jšœ™šœ˜Jšœœ˜,Jš œœœœœœœ˜-—J˜—Jšœ˜š˜Jš œ œœœœ˜#Jšœœœœ˜5—J˜J˜—šŸ œ˜Jšœœœ˜Fš œœœœœ˜FJšœœ˜ J˜—Jšœ*˜0J˜J˜—šŸœ˜Jšœœœ˜1Jšœœ$˜1J˜J˜—š Ÿœœ œœœœ˜?Jšœ œœœ˜ Jšœœœœœœœ˜.Jš œœœœœ˜0Jšœ9˜?J˜J˜—š Ÿ œœœœœ˜DJ˜%Jšœœ˜Jšœœœ˜Jšœœœœ˜(šœ˜Jšœ œ#œ˜AJšœ œœ˜8—J˜&Jšœœ/˜>Jšœ œ˜J˜J˜—šŸœœœœ˜HJšœ/˜5J˜J˜—š Ÿ œœ œœœœ˜:šœ˜Jšœ/˜/JšœH˜K—J˜J˜—šŸœœ˜Jšœ#œœœ˜PJšœœœ˜Jšœœ˜šœ%œœ˜9šœ œ˜Jšœœœœ˜>Jšœ œ˜Jšœ˜—J˜ Jšœ˜—š œ œœ œœ˜,Jšœ™J˜!Jšœ œ˜J˜J˜—J˜J˜—Jšœœ˜%Jš œ œœœœ˜AJ˜šœ˜J˜J˜J˜——…—-–Eå