DIRECTORY AMModel USING [Context, ContextSection, MostRecentNamedContext, ParentSection, RootContext, Section, SectionChildren, SectionClass, SectionName, SectionSource, Source, SourceObj, SourceSection], AMModelLocation USING [CodeLocation, EntryLocations, ExitLocations], BcdDefs USING [NullVersion], FastBreak USING [ClearFastBreak, FastBreakId, FastBreakProc, SetFastBreak], IO USING [card, CR, ROS, int, Put, rope, char, PutF, STREAM, RopeFromROS], PrincOps USING [BytePC, FrameCodeBase], Rope USING [Cat, Equal, Find, Length, ROPE, Substr], SpyClient USING [], -- for export SpyLog USING [active, WriteTrace], SpyOps USING [active, AllocationBreak, StackType, StartCounting, StopCounting, starts, stops, UserBreak, watching], WorldVM USING [LocalWorld]; SpyBreaksImpl: MONITOR IMPORTS AMModel, AMModelLocation, FastBreak, IO, Rope, SpyLog, SpyOps, WorldVM EXPORTS SpyClient, SpyOps = BEGIN OPEN IO; ROPE: TYPE = Rope.ROPE; breakList: LIST OF Break _ NIL; totalStarts, totalStops: INTEGER _ 0; StartSpy: PUBLIC PROCEDURE = BEGIN SpyOps.starts _ SpyOps.stops _ 0; IF breakList = NIL THEN [] _ SpyOps.StartCounting[NIL, NIL, NIL]; END; StopSpy: PUBLIC PROCEDURE = BEGIN totalStarts _ SpyOps.starts; totalStops _ SpyOps.stops; WHILE SpyOps.active > 0 DO [] _ SpyOps.StopCounting[NIL, NIL, NIL]; ENDLOOP; ClearAllocationBreak[]; END; ClearBreaks: PUBLIC PROCEDURE = BEGIN FOR breakList _ breakList, breakList.rest DO IF breakList = NIL THEN EXIT; [] _ FastBreak.ClearFastBreak[breakList.first.index]; ENDLOOP; END; SetStartBreak: PUBLIC PROCEDURE[section: AMModel.Section, procedure: ROPE, sourceIndex: INT] RETURNS[ok: BOOLEAN, msg: ROPE] = BEGIN breakId: Break; output: STREAM; IF section = NIL AND procedure = NIL THEN RETURN[FALSE, "no parameter given."]; IF section = NIL THEN section _ SectionFromName[procedure, sourceIndex]; IF section = NIL THEN RETURN[FALSE, Rope.Cat["Could not set break on ", procedure, "."]]; breakId _ SetBreak[section, SpyOps.StartCounting]; IF breakId = NIL THEN RETURN[FALSE, "Could not set break."]; breakList _ CONS[breakId, breakList]; output _ ROS[]; output.Put[rope["Set Start Break "]]; PrintBreak[output, breakId]; output.Put[char['.]]; msg _ RopeFromROS[output]; RETURN[TRUE, msg]; END; SetStopBreak: PUBLIC PROCEDURE[section: AMModel.Section, procedure: ROPE, sourceIndex: INT] RETURNS[ok: BOOLEAN, msg: ROPE] = BEGIN breakId: Break; output: STREAM; IF section = NIL AND procedure = NIL THEN RETURN[FALSE, "no parameter given."]; IF section = NIL THEN section _ SectionFromName[procedure, sourceIndex]; IF section = NIL THEN RETURN[FALSE, Rope.Cat["Could not set break on ", procedure, "."]]; breakId _ SetBreak[section: section, condProc: SpyOps.StopCounting, exitIfEntry: TRUE]; IF breakId = NIL THEN RETURN[FALSE, "Could not set break."]; breakList _ CONS[breakId, breakList]; output _ ROS[]; output.Put[rope["Set Stop Break "]]; PrintBreak[output, breakId]; output.Put[char['.]]; msg _ RopeFromROS[output]; RETURN[TRUE, msg]; END; traceList: LIST OF Break _ NIL; userBreakList: LIST OF Break _ NIL; allocationBreakList: LIST OF Break; SetAllocationBreak: PUBLIC PROC RETURNS[msg: Rope.ROPE _ NIL] = BEGIN proc: AMModel.Section; IF allocationBreakList # NIL THEN RETURN; proc _ SectionFromName["AllocatorImpl.NewSystemObject"]; IF proc = NIL THEN {ClearAllocationBreak[]; RETURN["Could not set break on AllocatorImpl.NewSystemObject."]}; allocationBreakList _ CONS[SetBreak[proc, SpyOps.AllocationBreak], allocationBreakList]; proc _ SectionFromName["AllocatorImpl.NewPermanentObject"]; IF proc = NIL THEN {ClearAllocationBreak[]; RETURN["Could not set break on AllocatorImpl.NewPermanentObject."]}; allocationBreakList _ CONS[SetBreak[section: proc, condProc: SpyOps.AllocationBreak, data: $Permanent], allocationBreakList]; proc _ SectionFromName["AllocatorImpl.NewHeapObject"]; IF proc = NIL THEN {ClearAllocationBreak[]; RETURN["Could not set break on AllocatorImpl.NewHeapObject."]}; allocationBreakList _ CONS[SetBreak[section: proc, condProc: SpyOps.AllocationBreak, data: $Unsafe], allocationBreakList]; END; ClearAllocationBreak: PUBLIC PROC = BEGIN FOR allocationBreakList _ allocationBreakList, allocationBreakList.rest WHILE allocationBreakList # NIL DO IF ~FastBreak.ClearFastBreak[allocationBreakList.first.index, allocationBreakList.first.condProc, allocationBreakList.first.data] THEN ERROR; ENDLOOP; END; SetUserBreak: PUBLIC PROCEDURE[section: AMModel.Section, type: SpyOps.StackType, procedure: ROPE, sourceIndex: INT] RETURNS[ok: BOOLEAN, msg: ROPE] = BEGIN breakId: Break; output: STREAM; IF section = NIL AND procedure = NIL THEN RETURN[FALSE, "no parameter given."]; IF section = NIL THEN section _ SectionFromName[procedure, sourceIndex]; IF section = NIL THEN RETURN[FALSE, Rope.Cat["Could not set break on ", procedure, "."]]; breakId _ SetBreak[section: section, condProc: SpyOps.UserBreak, type: type]; IF breakId = NIL THEN RETURN[FALSE, "Could not set break."]; userBreakList _ CONS[breakId, userBreakList]; output _ ROS[]; output.Put[rope["Set User Break "]]; PrintBreak[output, breakId]; output.Put[char['.]]; msg _ RopeFromROS[output]; RETURN[TRUE, msg]; END; SetTrace: PUBLIC PROC[section: AMModel.Section, procedure: Rope.ROPE, sourceIndex: INT] RETURNS[ok: BOOLEAN, msg: ROPE] = BEGIN breakId: Break; output: STREAM; IF section = NIL AND procedure = NIL THEN RETURN[FALSE, "no parameter given."]; IF section = NIL THEN section _ SectionFromName[procedure, sourceIndex]; IF section = NIL THEN RETURN[FALSE, Rope.Cat["Could not set break on ", procedure, "."]]; breakId _ SetBreak[section, TraceExecution]; IF breakId = NIL THEN RETURN[FALSE, "Could not set break."]; traceList _ CONS[breakId, traceList]; output _ ROS[]; output.Put[rope["Set Trace "]]; PrintBreak[output, breakId]; output.Put[rope[" [pc: "], card[breakId.pc], rope["]"]]; output.Put[char['.]]; msg _ RopeFromROS[output]; RETURN[TRUE, msg]; END; TraceExecution: FastBreak.FastBreakProc = BEGIN IF SpyOps.watching # userDefined THEN RETURN; IF SpyOps.active = 0 THEN RETURN; IF ~SpyLog.active THEN RETURN; SpyLog.WriteTrace[frame.accesslink.gfi, frame.pc]; END; ClearUserBreaks: PUBLIC PROCEDURE = BEGIN FOR userBreakList _ userBreakList, userBreakList.rest DO IF userBreakList = NIL THEN EXIT; [] _ FastBreak.ClearFastBreak[userBreakList.first.index]; ENDLOOP; FOR traceList _ traceList, traceList.rest DO IF traceList = NIL THEN EXIT; [] _ FastBreak.ClearFastBreak[traceList.first.index]; ENDLOOP; END; Break: TYPE = REF BreakRec; BreakRec: TYPE = RECORD[ exit: BOOLEAN _ FALSE, index: FastBreak.FastBreakId _ NIL, condProc: FastBreak.FastBreakProc _ NIL, data: LONG POINTER _ NIL, type: SpyOps.StackType _ 0, pc: PrincOps.BytePC _ [0], section: AMModel.Section _ NIL]; SetBreak: PROC[section: AMModel.Section, condProc: FastBreak.FastBreakProc, type: SpyOps.StackType _ 0, data: REF _ NIL, exitIfEntry: BOOL _ FALSE] RETURNS[break: Break] = BEGIN exit: BOOLEAN _ FALSE; locList: LIST OF AMModelLocation.CodeLocation; SELECT AMModel.SectionClass[section] FROM proc => IF exitIfEntry THEN {locList _ AMModelLocation.ExitLocations[section].list; exit _ TRUE} ELSE locList _ AMModelLocation.EntryLocations[section].list; statement => { locList _ AMModelLocation.EntryLocations[section].list; IF locList = NIL THEN RETURN[NIL]; IF exitIfEntry THEN { -- see if this is an entry statement. If it is, use the exit statement. parent: AMModel.Section; list: LIST OF AMModelLocation.CodeLocation; parent _ AMModel.ParentSection[section]; list _ AMModelLocation.EntryLocations[parent].list; IF list # NIL AND list.first = locList.first THEN {locList _ AMModelLocation.ExitLocations[parent].list; exit _ TRUE}}}; ENDCASE => RETURN[NIL]; IF locList = NIL THEN RETURN[NIL]; break _ NEW[BreakRec _ []]; break.index _ FastBreak.SetFastBreak[LOOPHOLE[locList.first.codeBase], locList.first.pc, condProc, LOOPHOLE[data]]; IF break.index = NIL THEN RETURN[NIL]; break.condProc _ condProc; break.data _ LOOPHOLE[data]; break.type _ type; IF type # 0 AND data = NIL THEN break.data _ @break.type; -- for user defined types break.pc _ locList.first.pc; break.section _ section; break.exit _ exit; END; SectionFromName: PROC[name: Rope.ROPE, sourceIndex: INT _ 0] RETURNS[section: AMModel.Section] = BEGIN dotIndex: INT; module: AMModel.Section; moduleContext: AMModel.Context; moduleName, procName: Rope.ROPE; FindProcedure: PROC[proc: AMModel.Section] RETURNS[stop: BOOL] = { name: Rope.ROPE _ AMModel.SectionName[proc]; RETURN[Rope.Equal[name, procName]]}; IF sourceIndex # 0 THEN { source: AMModel.Source _ NEW[AMModel.SourceObj _ [name, statement, BcdDefs.NullVersion, field[sourceIndex, sourceIndex]]]; section _ AMModel.SourceSection[source, AMModel.RootContext[WorldVM.LocalWorld[]]].section; RETURN}; dotIndex _ Rope.Find[name, "."]; IF dotIndex < 0 THEN RETURN[NIL]; moduleName _ name.Substr[0, dotIndex]; procName _ name.Substr[dotIndex+1, name.Length[]-dotIndex-1]; moduleContext _ AMModel.MostRecentNamedContext[moduleName, AMModel.RootContext[WorldVM.LocalWorld[]]]; IF moduleContext = NIL THEN RETURN[NIL]; module _ AMModel.ContextSection[moduleContext]; IF module = NIL THEN RETURN[NIL]; section _ AMModel.SectionChildren[module, FindProcedure]; END; PrintBreaks: PUBLIC PROCEDURE[typescript: IO.STREAM] = BEGIN first: BOOLEAN _ TRUE; list: LIST OF Break; IF typescript = NIL THEN RETURN; IF SpyOps.watching = userDefined OR breakList # NIL THEN typescript.Put[char[CR]]; IF SpyOps.watching = userDefined AND userBreakList # NIL THEN { typescript.Put[rope["User Breaks: "]]; FOR list _ userBreakList, list.rest DO IF list = NIL THEN EXIT; IF ~first THEN typescript.Put[rope[", "]]; PrintBreak[typescript, list.first]; first _ FALSE; ENDLOOP; typescript.Put[char['.], char[CR]]}; IF SpyOps.watching = userDefined AND traceList # NIL THEN { typescript.Put[rope["Trace Breaks: "]]; FOR list _ traceList, list.rest DO IF list = NIL THEN EXIT; IF ~first THEN typescript.Put[rope[",\n\t "]]; PrintBreak[typescript, list.first]; typescript.Put[rope[" [pc: "], card[list.first.pc], rope["]"]]; first _ FALSE; ENDLOOP; typescript.Put[char['.], char[CR]]}; IF breakList # NIL THEN { first _ TRUE; typescript.Put[rope["Start Breaks: "]]; FOR list _ breakList, list.rest DO IF list = NIL THEN EXIT; IF list.first.condProc # SpyOps.StartCounting THEN LOOP; IF ~first THEN typescript.Put[rope[", "]]; PrintBreak[typescript, list.first]; first _ FALSE; ENDLOOP; IF first THEN typescript.Put[char[CR]] ELSE typescript.Put[char['.], char[CR]]; first _ TRUE; typescript.Put[rope["Stop Breaks: "]]; FOR list _ breakList, list.rest DO IF list = NIL THEN EXIT; IF list.first.condProc # SpyOps.StopCounting THEN LOOP; IF ~first THEN typescript.Put[rope[", "]]; PrintBreak[typescript, list.first]; first _ FALSE; ENDLOOP; IF first THEN typescript.Put[char[CR]] ELSE typescript.Put[char['.], char[CR]]; typescript.Put[int[totalStarts], rope[" start breaks encountered.\n"]]; typescript.Put[int[totalStops], rope[" stop breaks encountered.\n"]]}; END; PrintBreak: PROCEDURE[stream: IO.STREAM, break: Break] = BEGIN SELECT AMModel.SectionClass[break.section] FROM proc => {IF break.exit THEN stream.Put[rope["exit of "], rope[AMModel.SectionName[break.section]]] ELSE stream.Put[rope["entry of "], rope[AMModel.SectionName[break.section]]]}; statement => { parent: AMModel.Section _ AMModel.ParentSection[break.section]; entry, procEntry: LIST OF AMModelLocation.CodeLocation; IF break.exit THEN {stream.Put[rope["exit of "], rope[AMModel.SectionName[parent]]]; RETURN}; procEntry _ AMModelLocation.EntryLocations[parent].list; entry _ AMModelLocation.EntryLocations[break.section].list; FOR procEntry _ procEntry, procEntry.rest WHILE procEntry # NIL DO FOR list: LIST OF AMModelLocation.CodeLocation _ entry, list.rest WHILE list # NIL DO IF list.first = procEntry.first THEN { stream.Put[rope["entry of "], rope[AMModel.SectionName[parent]]]; RETURN}; ENDLOOP; ENDLOOP; PrintSource[stream, break.section, AMModel.SectionName[parent]]}; ENDCASE => PrintSource[stream, break.section, AMModel.SectionName[break.section]]; END; PrintSource: PROCEDURE[stream: IO.STREAM, section: AMModel.Section, proc: Rope.ROPE] = BEGIN source: AMModel.Source _ AMModel.SectionSource[section]; WITH s: source^ SELECT FROM entire => stream.Put[rope["somewhere inside of "], rope[proc]]; field => stream.PutF["%g [source: %g]", rope[proc], int[s.firstCharIndex]]; ENDCASE => ERROR; END; ManualSetBreak: PROCEDURE[gf, pc: CARDINAL] RETURNS[ROPE] = BEGIN section: AMModel.Section; IF section = NIL THEN RETURN["not implemented"]; RETURN[SetUserBreak[section, 0, NIL, 0].msg]; END; END.. ÐSpyBreaksImpl.mesa Last Edited by: John Maxwell on: November 28, 1983 10:52 am ************************************************************ start and stop breaks ************************************************************ ************************************************************ user breaks ************************************************************ safe storage allocation permanent safe storage allocation unsafe storage allocation data: FastBreakData, frame: PrincOps.FrameHandle, sv: PrincOps.SVPointer] ************************************************************ utility procedures ************************************************************ print user breaks print start and stop breaks stop breaks section _ LocationFromGFandPC[LOOPHOLE[gf], [pc]]; LocationFromGFandPC: PROCEDURE[gf: PrincOps.GlobalFrameHandle, pc: PrincOps.BytePC] RETURNS[AMModel.Section] = INLINE {RETURN[BBObjectLocation.LocationFromGFandPC[AMBridge.TVForGFHReferent[gf], pc]]}; ÊݘJšœ™Jšœ;™;J˜šÏk ˜ Jšœœµ˜ÂJšœœ/˜DJšœœ˜Jšœ œ<˜KJš œœœœœ˜JJšœ œ˜'Jšœœœ ˜4Jšœ œÏc ˜!Jšœœ˜"Jšœœg˜sJšœœ˜—J˜šœœ˜šœ˜Jšœ%œ˜F—Jšœ˜!—Jšœœ˜J˜Jšœœœ˜J˜Jšœ<™˜XJ™!Jšœ;˜;šœœœ˜,Jšœ>˜D—Jšœœc˜}J™Jšœ6˜6šœœœ˜,Jšœ9˜?—Jšœœ`˜zJšœ˜J˜—šŸœœœ˜)šœE˜HJšœœ˜"šœ<˜>JšœDœœ˜O—Jšœ˜—Jšœ˜J˜—š Ÿ œœ œ>œœ˜sJšœœœ˜!Jš˜J˜Jšœœ˜Jšœ œœ œœœœ˜OJšœ œœ3˜HJš œ œœœœ9˜[J˜MJš œ œœœœ˜œ˜F———Jšœœœ˜—Jš œ œœœœ˜"Jšœœ˜Jšœ%œ6œ˜sJš œœœœœ˜&Jšœ˜Jšœ œ˜J˜Jš œ œœœž˜SJ˜Jšœ˜Jšœ˜Jšœ˜—J˜šŸœœ œœ˜=Jšœ˜)Jšœ œ˜Jšœ˜Jšœ˜Jšœœ˜ šŸ œœœœ˜BJšœ œ˜,Jšœ˜$—šœœ˜Jšœœ^˜zJšœ[˜[Jšœ˜—J˜ Jšœœœœ˜!J˜&J˜=Jšœf˜fJš œœœœœ˜(Jšœ/˜/Jš œ œœœœ˜!Jšœ9˜9Jšœ˜J˜—š Ÿ œœ œ œœ˜6Jš˜Jšœœœ˜Jšœœœ˜Jšœœœœ˜ Jšœ™Jš œœ œœœ˜Ršœœœœ˜?J˜&šœ!œ˜'šœœœœ˜Jšœœ˜*Jšœ#˜#Jšœœ˜Jšœ˜——Jšœœ˜$—šœœ œœ˜;J˜'šœœ˜#šœœœœ˜Jšœœ ˜.Jšœ#˜#J˜?Jšœœ˜Jšœ˜——Jšœœ˜$—Jšœ™šœ œœ˜Jšœœ˜ J˜'šœœ˜#Jšœœœœ˜Jšœ,œœ˜8Jšœœ˜*Jšœ#˜#Jšœœ˜Jšœ˜—šœ˜ Jšœœ˜Jšœœ˜(—Jšœ ™ Jšœœ˜ J˜&šœ˜"šœœœœ˜Jšœ+œœ˜7Jšœœ˜*Jšœ#˜#Jšœœ˜Jšœ˜——šœ˜ Jšœœ˜Jšœœ˜(—J˜GJ˜F—Jšœ˜J˜—šŸ œ œ œœ˜8Jš˜šœ%œ˜0šœ œ ˜JšœG˜KJšœJ˜N—šœ˜Jšœ?˜?Jšœœœ˜7Jšœ œCœ˜]Jšœ8˜8Jšœ;˜;šœ'œ œ˜Bš œœœ1œœ˜Ušœœ˜&J˜AJšœ˜—Jšœ˜—Jšœ˜—J˜A—JšœK˜R—Jšœ˜—J˜š Ÿ œ œ œœ'œ˜\J˜8šœ œ˜J˜?JšœK˜KJšœœ˜—Jšœ˜—J˜š Ÿœ œ œœœ˜;Jš˜J˜Jšœœ ™2Jšœ œœœ˜0Jšœ'˜-Jšœ˜J˜—šŸœ œ6™TJšœœ™"JšœœK™R—J˜Jšœ˜J˜—…—1bE