DIRECTORY BasicTime USING [GetClockPulses, Pulses, PulsesToMicroseconds, SetTime], DebuggerSwap USING [WorryCallDebugger], Disk USING [GetStatistics], File USING [SystemVolume], FileBackdoor USING [GetVolumePages], FSBackdoor USING [RemoteEvent, RemoteOp], PrincOps USING [GFT, GFTItem, PageCount, SD, sGFTLength], PrincOpsUtils USING [ReadWDC], Process USING [Detach, MsecToTicks, Pause, SecondsToTicks, SetPriority, SetTimeout], ProcessorFace USING [SetMP], Real USING [RoundC], Rope USING [ROPE], SafeStorage USING [NWordsAllocated], VM USING [Allocate, CantAllocate, PageCount], VMStatistics USING [VirtualAllocation], WatchStats USING [WatchStatsRecord]; TTYWatch: CEDAR MONITOR IMPORTS BasicTime, DebuggerSwap, Disk, File, FileBackdoor, PrincOpsUtils, Process, ProcessorFace, Real, SafeStorage, VM, VMStatistics EXPORTS WatchStats = BEGIN RemoteEventHandle: TYPE = REF READONLY FSBackdoor.RemoteEvent; RemoteOp: TYPE = FSBackdoor.RemoteOp; ROPE: TYPE = Rope.ROPE; watchStats: WatchStats.WatchStatsRecord _ [0, 0, 0, 0, 0, 0, 0.0]; GetWatchStats: PUBLIC ENTRY PROC RETURNS [WatchStats.WatchStatsRecord] = { RETURN [watchStats]; }; quit: BOOLEAN _ FALSE; -- watched by various processes for exit notification waitCond: CONDITION; fsPause: CONDITION _ [timeout: Process.MsecToTicks[200]]; defaultInterval: INT _ 16000; defaultSample: INT _ 8; longPause: INT _ 30 * 1000; millisSinceLastBigBang: INT _ longPause; minutesSpentIdle: INT _ 0; millisSpentIdle: INT _ 0; minutesSinceTimeSet: INT _ 0; idleCpuRate: REAL _ 0.05; -- all samples must remain below this rate decayCpuRate: REAL _ 0.0; CountGFI: PROC RETURNS [free: NAT] = TRUSTED { free _ 0; FOR i: CARDINAL DECREASING IN [1..PrincOps.SD[PrincOps.sGFTLength]) DO item: PrincOps.GFTItem = PrincOps.GFT[i]; IF item.data = 0 THEN free _ free + 1; ENDLOOP; }; WaitForUpdate: ENTRY PROC = { ENABLE UNWIND => NULL; WAIT waitCond; }; SetPause: ENTRY PROC [parm: CARDINAL] = TRUSTED { ENABLE UNWIND => NULL; Process.SetTimeout[@waitCond, Process.SecondsToTicks[parm]]; BROADCAST waitCond; }; TestForIdle: PROC [deltaMillis: INT] = { millisSpentIdle _ millisSpentIdle + deltaMillis; WHILE millisSpentIdle >= 60*LONG[1000] DO minutesSpentIdle _ minutesSpentIdle + 1; millisSpentIdle _ millisSpentIdle - 60*LONG[1000]; ENDLOOP; }; IdleProcess: PROC = TRUSTED { Process.SetPriority[LOOPHOLE[0]]; -- priorityIdleProcess: Priority = 0 WHILE NOT quit DO watchStats.idleCount _ watchStats.idleCount + 1; IF PrincOpsUtils.ReadWDC[] # 0 THEN DebuggerSwap.WorryCallDebugger["Idle with interrupts disabled?"]; ENDLOOP; }; Watcher: PROC = { ENABLE ABORTED => GO TO done; words, wordsRate, oldWordsRate: INT _ 0; oldActiveDiskPulses, oldTotalDiskPulses: BasicTime.Pulses _ 0; oldDiskPercent: REAL _ 0.0; maxIdleRate: INT _ 1; oldIdleRate, idleRate, lastIdle: INT _ 0; mark: BasicTime.Pulses _ 0; millisSinceTimeSet: INT _ 0; SetPause[2]; TRUSTED { Process.Detach[FORK IdleProcess[]]; }; WHILE NOT quit DO reads,writes,readPgs,writePgs: INT; diskPercent: REAL _ 0.0; deltaMillis: LONG CARDINAL; diskIO: INT _ 0; { nextMark: BasicTime.Pulses = BasicTime.GetClockPulses[]; idleTemp: INT _ watchStats.idleCount; delta: LONG CARDINAL _ BasicTime.PulsesToMicroseconds[nextMark - mark]; deltaMillis _ (delta + 500) / 1000; IF deltaMillis <= 10 THEN { Process.Pause[1]; LOOP}; mark _ nextMark; idleRate _ (idleTemp-lastIdle) / deltaMillis; lastIdle _ idleTemp; IF idleRate > maxIdleRate THEN IF deltaMillis > 100 THEN maxIdleRate _ idleRate ELSE idleRate _ maxIdleRate; }; { newActiveDiskPulses, newTotalDiskPulses: BasicTime.Pulses; [active: newActiveDiskPulses, total: newTotalDiskPulses, reads: reads, writes: writes, readPages: readPgs, writePages: writePgs] _ Disk.GetStatistics[]; diskIO _ reads+writes; IF newTotalDiskPulses # oldTotalDiskPulses THEN { IF newActiveDiskPulses # oldActiveDiskPulses THEN { diskPercent _ (1.0*(newActiveDiskPulses-oldActiveDiskPulses)) / (newTotalDiskPulses-oldTotalDiskPulses); oldActiveDiskPulses _ newActiveDiskPulses; }; oldTotalDiskPulses _ newTotalDiskPulses; }; }; { deltaWords: INT _ SafeStorage.NWordsAllocated[] - words; words _ words + deltaWords; wordsRate _ (deltaWords * 1000 + 500) / deltaMillis; }; { frac: REAL _ deltaMillis * 1E-4; IF frac > 1.0 THEN frac _ 1.0; watchStats.cpuLoad _ 1 - idleRate/(maxIdleRate*1.0); decayCpuRate _ (1.0-frac)*decayCpuRate + frac*watchStats.cpuLoad; }; millisSinceLastBigBang _ MIN[longPause, millisSinceLastBigBang + deltaMillis]; millisSinceTimeSet _ millisSinceTimeSet + deltaMillis; IF decayCpuRate < idleCpuRate AND diskPercent = 0.0 AND wordsRate = 0 THEN { TestForIdle[deltaMillis]; IF millisSinceTimeSet/60000 > minutesSinceTimeSet THEN { BasicTime.SetTime[]; millisSinceTimeSet _ 0; }; } ELSE minutesSpentIdle _ millisSpentIdle _ 0; { pagesAllocated, pagesFreed, pagesInPartition: VM.PageCount; TRUSTED { temp: INT; [pagesAllocated, pagesFreed, pagesInPartition] _ VMStatistics.VirtualAllocation[mds]; temp _ MAX[0, pagesInPartition - pagesAllocated + pagesFreed]; watchStats.mdsFree _ temp; [pagesAllocated, pagesFreed, pagesInPartition] _ VMStatistics.VirtualAllocation[normalVM]; temp _ MAX[0, pagesInPartition - pagesAllocated + pagesFreed]; watchStats.vmFree _ temp; }; IF millisSinceLastBigBang >= longPause THEN { millisSinceLastBigBang _ 0; [] _ VM.Allocate[pagesInPartition+pagesInPartition ! VM.CantAllocate => {watchStats.vmRun _ bestInterval.count; CONTINUE}; ]; watchStats.gfiFree _ CountGFI[]; mark _ BasicTime.GetClockPulses[]; lastIdle _ watchStats.idleCount; }; }; watchStats.diskFree _ FileBackdoor.GetVolumePages[File.SystemVolume[]].free; ProcessorFace.SetMP[Real.RoundC[decayCpuRate * 100.0]]; WaitForUpdate[]; ENDLOOP; GO TO done; EXITS done => {quit _ TRUE}; }; TRUSTED { Process.Detach[FORK Watcher]; }; END. TTYWatch.mesa Copyright c 1984, 1985 by Xerox Corporation. All rights reserved. Doug Wyatt, April 25, 1985 0:29:36 am PST Russ Atkinson (RRA) November 25, 1985 7:37:26 pm PST Tim Diebert: June 26, 1986 2:38:32 pm PDT **** Useful types from other interfaces **** **** The following stats are exported via GetWatchStats **** **** Global variables for Watch **** **** Procedures for Watch **** This procedure counts the free GFIs via a linear scan of the table. We have been "idle" for during the last interval, so test for it being enough. There is only one idle process in the system at a time, and the idle process is the only one that runs at priority 0. The idle process checks for page faults while interrupts are disabled by looking at the wakeup disable counter. Now we come to the main Watcher loop Get the time delta Make this follow the sampling of GetClockPulses as quickly as possible Not enough precision, so wait for the minimum time, then loop Update the idle rate data If enough time has elapsed, then use new (higher) idle rate. If the test interval is too short, then don't trust the new idle rate (error is likely to be too high). Update the disk numbers Update the alloc data Maintain the exponentially decaying CPU rate. Every 15 minutes we should reset the time to keep it from drifting too far Sample the new VM & GFI numbers Calculate the stats for the max VM run (but only if they will be displayed). We do this infrequently since it it relatively expensive. Calculate the # of GFIs free To avoid a spike in the CPU usage (which can make it look like we are not idle when we really are), we sample the idle count and clock pulses again here. Display Free line: disk, mds, gfi, VM, VM run Set the mp to the load. Lastly, wait for the pause interval **** Initialization code **** Κ˜codešœ ™ Kšœ Οmœ7™BK™)K™4K™)—K™šΟk ˜ Kšœ žœ9˜HKšœ žœ˜'Kšœžœ˜Kšœžœ˜Kšœ žœ˜$Kšœ žœ˜)Kšœ žœžœžœ˜9Kšœžœ ˜KšœžœG˜TKšœžœ ˜Kšœžœ ˜Kšœžœžœ˜Kšœ žœ˜$Kšžœžœ%˜-Kšœ žœ˜'šœ žœ˜$K˜——šΠblœžœž˜Kšžœnžœ˜…Kšžœ ˜Kšœž˜—K˜šœ,™,Kšœžœžœžœ˜>Kšœ žœ˜%Kšžœžœžœ˜—K˜K˜šœ<™Kšœžœ˜K˜Kšœ žœ˜Kšœ!žœ˜)Kšœ˜Kšœžœ˜K˜K˜ šžœ˜ Kšœžœ˜#K˜—K˜K˜K˜Kšœ$™$šžœžœž˜Kšœžœ˜#Kšœ žœ˜Kšœ žœžœ˜Kšœžœ˜K˜˜Kšœ™Kšœ8˜8šœ žœ˜%KšœF™F—Kšœžœžœ3˜GKšœ#˜#šžœžœ˜Kšœ=™=Kšœ˜Kšžœ˜—Kšœ˜K˜Kšœ™Kšœ-˜-Kšœ˜šžœž˜Kšœ₯™₯Kšžœžœžœ˜M—K˜—˜K˜—šœ˜Kšœ™K˜:Kšœ˜˜˜Kšœ˜šžœ)žœ˜1šžœ+žœ˜3šœ=˜=Kšœ*˜*—Kšœ*˜*K˜—Kšœ(˜(K˜—K˜—K˜˜Kšœ™Kšœ žœ)˜8K˜K˜4K˜—K˜˜Kšœ-™-Kšœžœ˜ Kšžœ žœ ˜Kšœ4˜4KšœA˜AK˜K˜—Kšœžœ2˜NKšœ6˜6šžœžœžœ˜Ešžœ˜Kšœ˜šžœ0žœ˜8KšœJ™JK˜Kšœ˜K˜—K˜—Kšžœ(˜,K˜—K˜šœ˜Kšœ™Kšœ.žœ ˜;šžœ˜ Kšœžœ˜ šœ.˜.Kšœ&˜&—Kšœžœ4˜>Kšœ˜šœ.˜.Kšœ+˜+—Kšœžœ4˜>Kšœ˜K˜—šžœ%žœ˜-Kšœ‡™‡Kšœ˜šœžœ+˜2Kšœžœ9žœ˜GKšœ˜—Kšœ™Kšœ ˜ Kšœ™™™Kšœ"˜"Kšœ ˜ K˜—Kšœ˜—K˜K™Kšœ-™-KšœL˜LK˜K™Kšœ7˜7Kšœ#™#K˜K˜Kšžœ˜K˜—Kšžœžœ˜ Kšžœžœ˜K˜——K˜šœ™šžœ˜ Kšœžœ ˜Kšœ˜——K˜šžœ˜K˜K˜K˜——…—¦%Δ