LupineExerciserManagerImpl.mesa.
Copyright © 1985 by Xerox Corporation. All rights reserved.
Last edited by BZM on 18-Mar-82 13:25:29.
Last edited by Andrew Birrell on July 7, 1982 6:35 pm
Last edited by Bob Hagmann on February 11, 1985 8:41:06 am PST
DIRECTORY
BasicTime USING [GetClockPulses, GMT, Now, Pulses],
Convert USING [Error, IntFromRope],
IO USING [card, GetLineRope, int, PutChar, PutF, PutFR, PutRope, rope, STREAM, time],
Loader USING [BCDBuildTime],
LupineExerciser USING [
Counter, Exercise, ExerciseIndex, ExerciseList,
StandardPasses, StandardTrialsPerPass, StandardTestsPerTrial,
String ],
LupineExerciserPrivate USING [
ExerciseHandle, ExerciseObject, FinishPrecisionTimings,
InitPrecisionTimings, InitTestParameters, SpyOperation ],
SpyClient USING [DisplayData, StandardSpy, StartSpy, StopSpy, ZeroData],
Process USING[ CheckForAbort],
Random USING [ChooseInt, Create],
Rope USING [InlineLength, InlineFetch, ROPE, Substr],
ViewerIO USING [CreateViewerStreams];
LupineExerciserManagerImpl: PROGRAM
IMPORTS
BasicTime, Convert, IO, LupineExerciserPrivate, Loader, Process,
RandomInt, Rope, ViewerIO
EXPORTS LupineExerciser, LupineExerciserPrivate
= BEGIN OPEN LupineExerciser;
Exerciser instance data.
Handle: PUBLIC TYPE = LupineExerciserPrivate.ExerciseHandle;
Runtime inconsistency found; a parameter check failed.
BadExercise: PUBLIC SIGNAL = CODE;
PerformExercises MUST be forked as a separate process!!!
PerformExercises: PUBLIC PROCEDURE [
nameOfExercisedInterface: String,
exercises: ExerciseList ] =
BEGIN ENABLE ABORTED => CONTINUE;
logFileName: Rope.ROPE = UniqueName [
root: nameOfExercisedInterface, extension: "log" ];
inLogStream, outLogStream: IO.STREAM;
LogStreamPutChar: PROC [char: CHARACTER] = {IO.PutChar[outLogStream, char]};
exerciseObject: LupineExerciserPrivate.ExerciseObject;
[inLogStream, outLogStream] ← ViewerIO.CreateViewerStreams["Lupine Exerciser Manager"];
exerciseObject ← [inLogStream: inLogStream, outLogStream: outLogStream];
DoExercises[
self: @exerciseObject,
exerciseName: nameOfExercisedInterface,
logFileName: logFileName,
exercises: exercises
! ABORTED => CONTINUE ];
END;
DoExercises: PROCEDURE [
self: Handle,
exerciseName, logFileName: String,
exercises: ExerciseList ] =
BEGIN
DO OPEN tp: self.tp;
ENABLE {
QuitExercises => EXIT;
AbortExercises => CONTINUE };
pass: Counter;
numExercises: ExerciseIndex = LENGTH[exercises];
EstablishTestParameters[self, exerciseName, logFileName];
IF tp.spying THEN CallSpy[self,
IF tp.spyOnProcs THEN startAndWatchProcs ELSE startAndWatchModules ];
FOR pass IN [1..tp.passes] DO
self.outLogStream.PutF["\n\nStarting pass %g of %g...\n", IO.int[pass], IO.int[tp.passes]];
FOR exerciseMarch: ExerciseIndex IN [0..numExercises) DO
exercise: ExerciseIndex;
IF tp.testRandomly
THEN BEGIN OPEN Random;
exercise ← ChooseInt[min: 0, max: numExercises-1];
tp.trialsPerPass ← ChooseInt[min: 1, max: MAX[1,tp.maxTrialsPerPass] ];
tp.testsPerTrial ← ChooseInt[min: 0, max: tp.maxTestsPerTrial];
END
ELSE BEGIN
exercise ← exerciseMarch;
tp.trialsPerPass ← tp.maxTrialsPerPass;
tp.testsPerTrial ← tp.maxTestsPerTrial;
END;
LupineExerciserPrivate.InitPrecisionTimings[self];
exercises[exercise].routine[
exerciser: self,
name: exercises[exercise].name,
trials: tp.trialsPerPass,
testsPerTrial: tp.testsPerTrial,
checkResults: tp.checkResults
! UNWIND => LupineExerciserPrivate.FinishPrecisionTimings[self] ];
LupineExerciserPrivate.FinishPrecisionTimings[self];
ENDLOOP;
ENDLOOP;
IF tp.spying THEN CallSpy[self,
(SELECT tp.showSpyData FROM
afterEachTest => stop,
afterAllTests => stopAndDisplayStats,
ENDCASE => ERROR) ];
ENDLOOP;
END;
User interface routines.
QuitExercises: PRIVATE ERROR = CODE;
EstablishTestParameters: PROCEDURE [
self: Handle,
exerciseName, logFileName: String ] =
BEGIN
now: BasicTime.GMT ← BasicTime.Now[];
built: BasicTime.GMT ← Loader.BCDBuildTime[];
self.tp ← LupineExerciserPrivate.InitTestParameters;
self.outLogStream.PutF["\nLupine RPC Exerciser of %g on %g.\nStarting %g exercise; the log is %g.\n\n", IO.time[built], IO.time[now], IO.rope[exerciseName], IO.rope[logFileName]];
DO OPEN tp: self.tp;
BEGIN ENABLE Convert.Error => GOTO TryAgain;
GetCount: PROC RETURNS [Counter] =
INLINE {RETURN[Convert.IntFromRope[reply.Substr[start: 1]]]};
reply: Rope.ROPENIL;
self.outLogStream.PutRope[">>"];
CheckAbort[self];
reply ← self.inLogStream.GetLineRope[];
CheckAbort[self];
IF reply.InlineLength[] > 0 THEN SELECT reply.InlineFetch[0] FROM
'd, 'D => {tp.checkResults ← FALSE};
'e, 'E => {tp.useDoradoClock ← tp.countOnlyEmulatorCycles ← TRUE};
'f, 'F => {tp ← LupineExerciserPrivate.InitTestParameters};
'g, 'G => {EXIT};
'h, 'H => {tp.useDoradoClock ← TRUE};
'm, 'M => {tp.spying ← TRUE; tp.spyOnProcs ← FALSE};
'p, 'P => {tp.passes ← GetCount[]};
'o, 'O => {tp.maxTrialsPerPass ← GetCount[]};
't, 'T => {tp.maxTestsPerTrial ← GetCount[]};
'r, 'R => {tp.testRandomly ← TRUE};
's, 'S => {tp.spying ← TRUE; tp.showSpyData ← afterAllTests};
'x, 'x => {tp.spying ← TRUE; tp.showSpyData ← afterEachTest};
'q, 'Q => {ERROR QuitExercises};
'- => {NULL};
'? => {TypeHelp[self]};
ENDCASE => GOTO TryAgain;
EXITS
TryAgain => self.outLogStream.PutRope["???\n"]
END;
ENDLOOP;
END;
TypeHelp: PROCEDURE [self: Handle] =
BEGIN
self.outLogStream.PutRope["
The exerciser options are:
D Don't check results of remote calls for correctness.
E Exclude nonemulator cycles in timings (Dorados only).
F Flush and start over.
G Go (type this last).
H High precision timings (Dorados only).
M Module-level (coarse) spying.
Pn Number of passes of entire exercise.
On Number of trials of each pass.
Tn Number of tests (eg, calls) per trial.
R Choose trials, tests, and exercises randomly.
S Spy after all passes are over.
X Spy at the exit of each trial.
Q Quit this program immediately.
? Type this explanation.
^DEL To abort at any time.
The default options are P1 Q1 R2000 G.\n\n" ];
END;
AbortExercises: PRIVATE ERROR = CODE;
Raised by CheckAbort and caught in DoExercises.
CheckAbort: PUBLIC PROC [self: Handle] = BEGIN
Process.CheckForAbort[! ABORTED => {
self.outLogStream.PutRope["\nTest aborted...\n\n"];
ERROR AbortExercises;
};
];
END;
UniqueName: PROCEDURE [root, extension: String]
RETURNS [rootSuffix: String] =
INLINE BEGIN
pulses: BasicTime.Pulses ← BasicTime.GetClockPulses[];
rootSuffix ← IO.PutFR["%g.%g.%g", IO.rope[root], IO.card[pulses], IO.rope[extension]];
END;
Procedural access to the spy.
CallSpy: PUBLIC PROCEDURE [
self: Handle,
operation: LupineExerciserPrivate.SpyOperation ] =
--TEMP, until spy converts to 3.2-- {};
BEGIN OPEN SpyClient;
SELECT operation FROM
startAndWatchProcs, startAndWatchModules => {StandardSpy; ZeroData};
startSpying => StartSpy;
stopSpying => StopSpy;
displayStats, stopAndDisplayStats =>
{StopSpy; DisplayData[herald: "Called by LupineExerciser.", stream: NIL]};
stop => StopSpy;
ENDCASE => ERROR;
END;
Module Initialization.
[] ← Random.Create[seed: -1];
END. -- LupineExerciserManagerImpl.
Pilot/CoPilot spying interface:
CallSpy: PROCEDURE[operation: LupineExerciserPrivate.SpyOperation] =
BEGIN
SELECT operation FROM
start =>
Runtime.CallDebugger["Start Spy..."L];
startAndWatchDetails =>
Runtime.CallDebugger["Start Spy and finger specific procs..."L];
startSpying =>
SpyClient.StartCounting;
stopSpying =>
SpyClient.StopCounting;
displayStats =>
Runtime.CallDebugger["Examine Spy statistics..."L];
stop =>
Runtime.CallDebugger["Stop Spy..."L];
stopAndDisplayStats =>
Runtime.CallDebugger["Stop Spy and examine statistics..."L];
ENDCASE => ERROR;
END;
Cause random interruptions in RPC processes to test concurrency behavior.
InterruptControl: TYPE = RECORD [
state: {started, stopping, finished} ← finished,
maxInterval, maxBusy: Process.Milliseconds ];
StartInterruptions: PROCEDURE [self: Handle] =
BEGIN
Interrupt: PROC [busyPulses: BasicTime.Pulses, idleTicks: Process.Ticks] =
BEGIN
self.interrupt.state ← started;
Process.SetPriority[Process.priorityForeground];
WHILE self.interrupt.state = started DO
stopSpinning: BasicTime.Pulses =
GetClockPulses[] + Random.ChooseInt[min: 0, max: busyPulses];
UNTIL BasicTime.GetClockPulses[] >= stopSpinning DO ENDLOOP;
Process.Pause[Random.ChooseInt[min: idleTicks/2, max: idleTicks]];
ENDLOOP;
self.interrupt.state ← finished;
END;
Process.Detach[FORK Interrupt [
busyPulses: System.MicrosecondsToPulses[self.interruption.maxBusy],
idleTicks: Process.MsecToTicks[
self.interruption.maxInterval-self.interruption.maxBusy] ];
END;
StopInterruptions: PROCEDURE [self: Handle] =
BEGIN
self.interrupt.state ← stopping;
UNTIL self.interrupt.state = finished DO
Process.Pause[Process.MsecToTicks[100]];
ENDLOOP;
END;