SpyBreaksImpl.mesa
Copyright Ó 1985, 1986, 1987 by Xerox Corporation. All rights reserved.
John Maxwell on: November 28, 1983 10:52 am
Russ Atkinson (RRA) February 16, 1987 11:36:50 am PST
Mike Spreitzer September 23, 1986 9:01:40 pm PDT
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, FastBreakData, FastBreakId, FastBreakProc, SetFastBreak],
IO USING [Put, PutF, PutRope, RopeFromROS, ROS, STREAM],
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, starts, StopCounting, stops, UserBreak, watching],
WorldVM USING [LocalWorld];
SpyBreaksImpl: MONITOR
IMPORTS AMModel, AMModelLocation, FastBreak, IO, Rope, SpyLog, SpyOps, WorldVM
EXPORTS SpyClient, SpyOps = {
ROPE: TYPE = Rope.ROPE;
Section: TYPE = AMModel.Section;
STREAM: TYPE = IO.STREAM;
Start and Stop breaks
breakList: LIST OF Break ← NIL;
totalStarts, totalStops: INTEGER ← 0;
StartSpy: PUBLIC PROC = {
SpyOps.starts ← SpyOps.stops ← 0;
IF breakList = NIL THEN [] ← SpyOps.StartCounting[NIL, NIL, NIL];
};
StopSpy: PUBLIC PROC = {
totalStarts ← SpyOps.starts; totalStops ← SpyOps.stops;
WHILE SpyOps.active > 0 DO [] ← SpyOps.StopCounting[NIL, NIL, NIL]; ENDLOOP;
ClearAllocationBreak[];
};
ClearBreaks: PUBLIC PROC = {
FOR breakList ← breakList, breakList.rest DO
IF breakList = NIL THEN EXIT;
[] ← ClearSingleBreak[breakList.first];
ENDLOOP;
};
SetSingleBreak: PROC [code: LONG POINTER, pc: PrincOps.BytePC, proc: FastBreak.FastBreakProc, data: FastBreak.FastBreakData]
RETURNS [break: Break] = {
break ← NEW[BreakRec ← [pc: pc, condProc: proc, data: data, breakData: data]];
break.index ← FastBreak.SetFastBreak[code, pc, proc, data];
IF break.index = NIL THEN RETURN [NIL];
};
ClearSingleBreak: PROC [break: Break] RETURNS [ok: BOOL] = {
ok ← FastBreak.ClearFastBreak[break.index, break.condProc, break.breakData];
};
SetStartBreak: PUBLIC PROC [section: Section, procedure: ROPE, sourceIndex: INT]
RETURNS
[ok: BOOL, msg: ROPE] = {
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 ← IO.ROS[];
output.Put[[rope["Set Start Break "]]];
PrintBreak[output, breakId];
output.Put[[character['.]]];
msg ← IO.RopeFromROS[output];
RETURN [TRUE, msg];
};
SetStopBreak: PUBLIC PROC [section: Section, procedure: ROPE, sourceIndex: INT]
RETURNS
[ok: BOOL, msg: ROPE] = {
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 ← IO.ROS[];
output.Put[[rope["Set Stop Break "]]];
PrintBreak[output, breakId];
output.Put[[character['.]]];
msg ← IO.RopeFromROS[output];
RETURN [TRUE, msg];
};
User Breaks
traceList: LIST OF Break ← NIL;
userBreakList: LIST OF Break ← NIL;
allocationBreakList: LIST OF Break;
SetAllocationBreak: PUBLIC PROC RETURNS [msg: ROPENIL] = {
proc: 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[section: proc, condProc: SpyOps.AllocationBreak, exitIfEntry: TRUE], 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, exitIfEntry: TRUE], 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, exitIfEntry: TRUE], allocationBreakList];
};
ClearAllocationBreak: PUBLIC PROC = {
FOR allocationBreakList ← allocationBreakList, allocationBreakList.rest
WHILE allocationBreakList # NIL DO
IF ~ClearSingleBreak[allocationBreakList.first] THEN ERROR;
ENDLOOP;
};
SetUserBreak: PUBLIC PROC
[section: Section, type: SpyOps.StackType, procedure: ROPE, sourceIndex: INT]
RETURNS
[ok: BOOL, msg: ROPE] = {
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 ← IO.ROS[];
output.Put[[rope["Set User Break "]]];
PrintBreak[output, breakId];
output.Put[[character['.]]];
msg ← IO.RopeFromROS[output];
RETURN [TRUE, msg];
};
SetTrace: PUBLIC PROC [section: Section, procedure: ROPE, sourceIndex: INT]
RETURNS
[ok: BOOL, msg: ROPE] = {
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 ← IO.ROS[];
output.Put[[rope["Set Trace "]]];
PrintBreak[output, breakId];
output.Put[[rope[" [pc: "]], [cardinal[breakId.pc]], [rope["]"]]];
output.Put[[character['.]]];
msg ← IO.RopeFromROS[output];
RETURN [TRUE, msg];
};
TraceExecution: FastBreak.FastBreakProc = {
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, frame.pc];
};
ClearUserBreaks: PUBLIC PROC = {
FOR userBreakList ← userBreakList, userBreakList.rest WHILE userBreakList # NIL DO
[] ← ClearSingleBreak[userBreakList.first];
ENDLOOP;
FOR traceList ← traceList, traceList.rest WHILE traceList # NIL DO
IF traceList = NIL THEN EXIT;
[] ← ClearSingleBreak[traceList.first];
ENDLOOP;
};
Utility procedures
Break: TYPE = REF BreakRec;
BreakRec: TYPE = RECORD[
exit: BOOLFALSE,
index: FastBreak.FastBreakId ← NIL,
condProc: FastBreak.FastBreakProc ← NIL,
breakData: FastBreak.FastBreakData ← NIL,
data: LONG POINTERNIL,
type: SpyOps.StackType ← ready,
pc: PrincOps.BytePC ← [0],
section: Section ← NIL];
SetBreak: PROC [section: Section, condProc: FastBreak.FastBreakProc, type: SpyOps.StackType ← ready, data: REFNIL, exitIfEntry: BOOLFALSE]
RETURNS [break: Break] = {
exit: BOOLFALSE;
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: 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];
IF (break ← SetSingleBreak[LOOPHOLE[locList.first.codeBase], locList.first.pc, condProc, LOOPHOLE[data]]) = NIL THEN RETURN;
break.type ← type;
IF type # ready AND data = NIL THEN break.data ← @break.type; -- for user defined types
break.section ← section;
break.exit ← exit;
};
SectionFromName: PROC [name: ROPE, sourceIndex: INT ← 0]
RETURNS [section: Section] = {
dotIndex: INT;
module: Section;
moduleContext: AMModel.Context;
moduleName, procName: ROPE;
FindProcedure: PROC [proc: Section] RETURNS [stop: BOOL] = {
name: 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];
};
PrintBreaks: PUBLIC PROC [typescript: STREAM] ={
first: BOOLTRUE;
list: LIST OF Break;
IF typescript = NIL THEN RETURN;
print user breaks
IF SpyOps.watching = userDefined OR breakList # NIL THEN typescript.Put[[character['\n]]];
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[[character['.]], [character['\n]]]};
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: "]], [cardinal[list.first.pc]], [rope["]"]]];
first ← FALSE;
ENDLOOP;
typescript.Put[[character['.]], [character['\n]]]};
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[[character['\n]]]
ELSE typescript.Put[[character['.]], [character['\n]]];
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[[character['\n]]]
ELSE typescript.Put[[character['.]], [character['\n]]];
typescript.Put[[integer[totalStarts]], [rope[" start breaks encountered.\n"]]];
typescript.Put[[integer[totalStops]], [rope[" stop breaks encountered.\n"]]];
};
};
PrintBreak: PROC [stream: STREAM, break: Break] ={
SELECT AMModel.SectionClass[break.section] FROM
proc => {
IF break.exit
THEN IO.PutRope[stream, "exit of "]
ELSE IO.PutRope[stream, "entry of "];
IO.PutRope[stream, AMModel.SectionName[break.section]];
};
statement => {
parent: Section ← AMModel.ParentSection[break.section];
parentName: ROPE ← AMModel.SectionName[parent];
entry, procEntry: LIST OF AMModelLocation.CodeLocation;
IF break.exit THEN {
IO.PutRope[stream, "exit of "];
IO.PutRope[stream, parentName];
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 {
IO.PutRope[stream, "entry of "];
IO.PutRope[stream, parentName];
RETURN;
};
ENDLOOP;
ENDLOOP;
PrintSource[stream, break.section, AMModel.SectionName[parent]];
};
ENDCASE => PrintSource[stream, break.section, AMModel.SectionName[break.section]];
};
PrintSource: PROC [stream: STREAM, section: Section, proc: ROPE] = {
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]], [integer[s.firstCharIndex]]];
ENDCASE => ERROR;
};
}..