SpyBreaksImpl.mesa
Copyright © 1985, 1986 by Xerox Corporation. All rights reserved.
John Maxwell on: November 28, 1983 10:52 am
Russ Atkinson (RRA) March 31, 1986 2:23:59 pm PST
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 [ROS, Put, 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 = {
ROPE: TYPE = Rope.ROPE;
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: AMModel.Section, procedure: ROPE, sourceIndex: INT] RETURNS[ok: BOOLEAN, 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: AMModel.Section, procedure: ROPE, sourceIndex: INT] RETURNS[ok: BOOLEAN, 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: Rope.ROPENIL] = {
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];
};
ClearAllocationBreak: PUBLIC PROC = {
FOR allocationBreakList ← allocationBreakList, allocationBreakList.rest
WHILE allocationBreakList # NIL DO
IF ~ClearSingleBreak[allocationBreakList.first] THEN ERROR;
ENDLOOP;
};
SetUserBreak: PUBLIC PROC[section: AMModel.Section, type: SpyOps.StackType, procedure: ROPE, sourceIndex: INT] RETURNS[ok: BOOLEAN, 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: AMModel.Section, procedure: Rope.ROPE, sourceIndex: INT] RETURNS[ok: BOOLEAN, 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: BOOLEANFALSE,
index: FastBreak.FastBreakId ← NIL,
condProc: FastBreak.FastBreakProc ← NIL,
breakData: FastBreak.FastBreakData ← 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] = {
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];
IF (break ← SetSingleBreak[LOOPHOLE[locList.first.codeBase], locList.first.pc, condProc, LOOPHOLE[data]]) = NIL THEN RETURN;
break.type ← type;
IF type # 0 AND data = NIL THEN break.data ← @break.type; -- for user defined types
break.section ← section;
break.exit ← exit;
};
SectionFromName: PROC[name: Rope.ROPE, sourceIndex: INT ← 0] RETURNS[section: AMModel.Section] = {
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];
};
PrintBreaks: PUBLIC PROC[typescript: STREAM] ={
first: BOOLEANTRUE;
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 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]];
};
PrintSource: PROC[stream: STREAM, section: AMModel.Section, proc: Rope.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;
};
}..