-- File: IdleTime.mesa
DIRECTORY
FrameDefs: FROM "FrameDefs" USING [MakeCodeResident],
FrameOps: FROM "FrameOps" USING [MyGlobalFrame],
InlineDefs: FROM "InlineDefs" USING [DIVMOD, LongDiv, LongMult],
LogDefs: FROM "LogDefs" USING [DisplayNumber, Percentage],
Mopcodes: FROM "Mopcodes" USING [zMISC],
ProcessDefs: FROM "ProcessDefs" USING [Detach, DisableInterrupts, EnableInterrupts, Priority],
ProcessOps USING [ISetPriority],
SegmentDefs: FROM "SegmentDefs" USING [GetMemoryConfig];
IdleTime: PROGRAM
IMPORTS FrameDefs, FrameOps, InlineDefs, LogDefs, ProcessDefs, ProcessOps, SegmentDefs =
BEGIN
-- Types and Related Constants --
-- Note: The following two constants must be chosen such that:
--
windowSize*grainsPerSecond <= LAST[CARDINAL]
-- This avoids the use of software arithmetic in UpdateStatistics.
ticksPerGrain: CARDINAL = 4;
windowSize: CARDINAL = 5;
busyThreshold: CanonicalClock = [pieces[low10: 1, zero: 0, high16: 0]];
LongTicks: TYPE = LONG CARDINAL;
AltoClock: TYPE = MACHINE DEPENDENT RECORD[
SELECT OVERLAID ClockFormat FROM
AltoI => [clock: CanonicalClock],
AltoII => [junk1: [0..17B], low10: [0..1777B], junk2: [0..3B], high16: CARDINAL],
ENDCASE];
CanonicalClock: TYPE = MACHINE DEPENDENT RECORD[
SELECT OVERLAID * FROM
pieces => [low10: [0..1777B], zero: [0..77B], high16: CARDINAL],
ticks => [ticks: LongTicks],
ENDCASE];
ClockFormat: TYPE = {AltoI, AltoII};
shortTicksPerSecond: CARDINAL = 26250;-- 5.88*10↑6/224, per Alto Hardware Manual
ShortTicks: TYPE = [0..shortTicksPerSecond];
grainsPerSecond: CARDINAL = shortTicksPerSecond/ticksPerGrain;
Grains: TYPE = [0..grainsPerSecond];
-- the following constant is defined as a variable for smaller code
clockOneSecond: LongTicks ← 6321200B;
-- compiler can’t do SHIFT[shortTicksPerSecond, 6]
IdleWindow: TYPE = [0..windowSize);
-- Global Variables --
idlePercent: LogDefs.Percentage;
clockFormat: ClockFormat;
idleTimes: ARRAY IdleWindow OF ShortTicks;
rover: IdleWindow;
-- Main Procedure --
IdleProcess: PROCEDURE =
-- maintains percentage of idle time during the last ’windowSize’ seconds.
BEGIN
rawNow, rawThen: AltoClock;
clockNow, clockThen, clockLastSecond, deltaBusy: LongTicks;
busyTime: ShortTicks ← 0;
IncrementBusyTime: PROCEDURE[v: LongTicks] = INLINE
-- converts ’v’ to ShortTicks and adds it to ’busyTime’.
BEGIN
TP: TYPE = MACHINE DEPENDENT RECORD[high6: [0..77B], low10: [0..1777B]];
TickPieces: TYPE = RECORD[
SELECT OVERLAID * FROM
pieces => [pieces: TP],
value => [val: ShortTicks],
ENDCASE];
interval: CanonicalClock;
ticks: TickPieces;

interval.ticks ← v;
ticks.pieces ← [high6: interval.high16, low10: interval.low10];
busyTime ← busyTime + ticks.val;
END;
-- IncrementBusyTime
ProcessOps.ISetPriority[FIRST[ProcessDefs.Priority]];
-- Beware: "OurProcess" module is non-resident! --
rawThen ← ReadRawClock[];
clockLastSecond ← NormalizeClock[rawThen];
DO
ProcessDefs.DisableInterrupts[];
rawNow ← ReadRawClock[];
clockNow ← NormalizeClock[rawNow];
clockThen ← NormalizeClock[rawThen];
IF clockNow - clockLastSecond >= clockOneSecond THEN
BEGIN
clockNextSecond: LongTicks = clockLastSecond + clockOneSecond;
IncrementBusyTime[clockNextSecond - clockThen];
RecordBusyTime[busyTime];
busyTime ← 0;
clockThen ← clockNextSecond;
UNTIL clockNow - clockThen < clockOneSecond DO
RecordBusyTime[shortTicksPerSecond];
clockThen ← clockThen + clockOneSecond;
ENDLOOP;
clockLastSecond ← clockThen;
UpdateStatistics[];
END;
IF (deltaBusy ← clockNow - clockThen) > busyThreshold.ticks THEN
IncrementBusyTime[deltaBusy];
rawThen ← ReadRawClock[];
ProcessDefs.EnableInterrupts[];
ENDLOOP;
END;--IdleProcess
-- Internal Procedures --
Initialize: PROCEDURE =
-- once-only initialization.
BEGIN
i: IdleWindow;
clockFormat ←
SELECT SegmentDefs.GetMemoryConfig[].AltoType FROM
AltoII, AltoIIXM => AltoII,
ENDCASE => AltoI;
rover ← LAST[IdleWindow];
FOR i IN IdleWindow DO idleTimes[i] ← 0; ENDLOOP;
idlePercent ← LAST[LogDefs.Percentage];
LogDefs.DisplayNumber["Idle time"L, [percent[@idlePercent]]];
END;--Initialize
NormalizeClock: PROCEDURE[rawClock: AltoClock] RETURNS [LongTicks] =
-- eliminates machine dependencies in the clock format.
BEGIN
clock: CanonicalClock;
WITH raw: rawClock SELECT clockFormat FROM
AltoI => clock ← raw.clock;
AltoII => clock ← [pieces[low10: raw.low10, zero: , high16: raw.high16]];
ENDCASE;
clock.zero ← 0;
RETURN[clock.ticks]
END;--NormalizeClock
ReadRawClock: PROCEDURE RETURNS [AltoClock] = MACHINE CODE
-- delivers the raw high resolution clock.
BEGIN
Mopcodes.zMISC, 11B;
END;--ReadRawClock
RecordBusyTime: PROCEDURE[busy: ShortTicks] =
-- records the idle time value for the current second.
BEGIN
idleTimes[rover] ← shortTicksPerSecond - busy;
IF rover = 0 THEN rover ← LAST[IdleWindow] ELSE rover ← rover - 1;
END;--RecordBusyTime
UpdateStatistics: PROCEDURE =
-- updates the running average of idle time.
BEGIN
OPEN InlineDefs;
i: IdleWindow;
grains: Grains;
residue: [0..ticksPerGrain);
totalGrains: CARDINAL ← 0;
totalResidue: CARDINAL ← 0;
FOR i IN IdleWindow DO
[grains, residue] ← DIVMOD[idleTimes[i], ticksPerGrain];
totalGrains ← totalGrains + grains;
totalResidue ← totalResidue + residue;
ENDLOOP;
[grains, residue] ← DIVMOD[totalResidue, ticksPerGrain];
totalGrains ← totalGrains + grains + (IF residue < ticksPerGrain/2 THEN 0 ELSE 1);
idlePercent ← LongDiv[LongMult[totalGrains, 100], grainsPerSecond*windowSize];
END;--UpdateStatistics
-- Main Body --
FrameDefs.MakeCodeResident[FrameOps.MyGlobalFrame[]];
Initialize[];
ProcessDefs.Detach[FORK IdleProcess];
END.
Created by Levin on February 15, 1980 6:27 PM.
Changed by Levin on February 19, 1980 3:48 PM, rework algorithm for more accurate measurement under light loads.
Changed by Levin on March 24, 1980 10:00 AM, correct uninitialized variable in UpdateStatistics.
Changed by Levin on May 20, 1980 6:02 PM, change clock format for Dorado to be AltoI.
Changed by Birrell on June 11, 1980 12:15 PM, MemoryOps -> SegmentDefs for Mesa 6.0.
Changed by Birrell on September 10, 1980 7:00 PM, Removed use of RestartDefs.
Changed by Birrell on July 22, 1981 4:00 PM, Inline SetPriority.