SpyBreaksImpl.mesa
Last Edited by: John Maxwell on: November 28, 1983 10:52 am
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;
************************************************************
start and stop breaks
************************************************************
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;
************************************************************
user breaks
************************************************************
traceList: LIST OF Break ← NIL;
userBreakList: LIST OF Break ← NIL;
allocationBreakList: LIST OF Break;
SetAllocationBreak: PUBLIC PROC RETURNS[msg: Rope.ROPENIL] = BEGIN
proc: AMModel.Section;
IF allocationBreakList # NIL THEN RETURN;
safe storage allocation
proc ← SectionFromName["AllocatorImpl.NewSystemObject"];
IF proc = NIL THEN {ClearAllocationBreak[];
RETURN["Could not set break on AllocatorImpl.NewSystemObject."]};
allocationBreakList ← CONS[SetBreak[proc, SpyOps.AllocationBreak], allocationBreakList];
permanent safe storage allocation
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];
unsafe storage allocation
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
data: FastBreakData, frame: PrincOps.FrameHandle, sv: PrincOps.SVPointer]
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;
************************************************************
utility procedures
************************************************************
Break: TYPE = REF BreakRec;
BreakRec: TYPE = RECORD[
exit: BOOLEANFALSE,
index: FastBreak.FastBreakId ← NIL,
condProc: FastBreak.FastBreakProc ← NIL,
data: LONG POINTERNIL,
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: REFNIL, exitIfEntry: BOOLFALSE]
RETURNS[break: Break] = BEGIN
exit: BOOLEANFALSE;
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: BOOLEANTRUE;
list: LIST OF Break;
IF typescript = NIL THEN RETURN;
print user breaks
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]]};
print start and stop breaks
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]];
stop breaks
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;
section ← LocationFromGFandPC[LOOPHOLE[gf], [pc]];
IF section = NIL THEN RETURN["not implemented"];
RETURN[SetUserBreak[section, 0, NIL, 0].msg];
END;
LocationFromGFandPC: PROCEDURE[gf: PrincOps.GlobalFrameHandle, pc: PrincOps.BytePC]
RETURNS[AMModel.Section] = INLINE
{RETURN[BBObjectLocation.LocationFromGFandPC[AMBridge.TVForGFHReferent[gf], pc]]};
END..