Watcher.mesa
Copyright (C) 1985, 1986 Xerox Corporation. All rights reserved.
Hal Murray, December 3, 1986 10:45:23 am PST
Russ Atkinson (RRA) May 23, 1986 3:02:54 pm PDT
DIRECTORY
Basics USING [bitsPerWord],
BasicTime USING [GetClockPulses, Pulses, PulsesToSeconds],
CedarProcess USING [SetPriority],
Commander USING [CommandProc, Register],
CommDriver USING [GetNetworkChain, Network],
Containers USING [Create],
Convert USING [RopeFromCard],
Disk USING [GetStatistics],
EthernetDriverStats USING [EtherStats, EtherStatsRep, GetEthernetOneStats, GetEthernetStats],
File USING [wordsPerPage],
Histograph USING [AddSample, NewHistograph],
PrincOpsUtils USING [ReadWDC],
Process USING [Detach, MsecToTicks, Pause],
Pup USING [],
Rope USING [Cat, Find, ROPE],
SafeStorage USING [NWordsAllocated],
ViewerClasses USING [Viewer],
ViewerEvents USING [EventProc, RegisterEventProc],
ViewerOps USING [OpenIcon, SetOpenHeight],
WatchStats USING [GetWatchStats];
Watcher:
CEDAR
MONITOR
LOCKS data USING data: Data
IMPORTS BasicTime, CedarProcess, Commander, CommDriver, Containers, Convert, Disk, EthernetDriverStats, Histograph, PrincOpsUtils, Process, Rope, SafeStorage, ViewerEvents, ViewerOps, WatchStats = {
Viewer: TYPE = ViewerClasses.Viewer;
Data: TYPE = REF DataRecord;
DataRecord:
TYPE =
MONITORED RECORD [
container: Viewer ← NIL,
cpuGraph: Viewer ← NIL,
allocGraph: Viewer ← NIL,
diskGraph: Viewer ← NIL,
etherGraph: Viewer ← NIL,
etherRecvGraph: Viewer ← NIL,
etherSendGraph: Viewer ← NIL,
etherOneGraph: Viewer ← NIL,
etherOneRecvGraph: Viewer ← NIL,
etherOneSendGraph: Viewer ← NIL
];
averageFactor: REAL ← 0.9;
firstSampleX: NAT ← 72;
milliSecondsPerPixel: INT ← 1000;
Collector:
PROC [data: Data] = {
ENABLE ABORTED => CONTINUE;
viewer: ViewerClasses.Viewer ← data.container;
start, stop: BasicTime.Pulses;
sec: REAL;
i: NAT ← 0;
idleCount: INT ← WatchStats.GetWatchStats[].idleCount;
maxIdleRate: REAL ← 10000; -- Cedar will not really run on slower machines anyway!
idleRate: REAL ← 0;
oldAlloc: INT ← SafeStorage.NWordsAllocated[];
oldDisk: INT ← Disk.GetStatistics[].readPages + Disk.GetStatistics[].writePages;
etherStats: EthernetDriverStats.EtherStats ← EthernetDriverStats.GetEthernetStats[0];
etherOneStats: EthernetDriverStats.EtherStats ← EthernetDriverStats.GetEthernetOneStats[0];
oldEther: EthernetDriverStats.EtherStatsRep;
oldEtherOne: EthernetDriverStats.EtherStatsRep;
IF etherStats # NIL THEN oldEther ← etherStats^;
IF etherOneStats # NIL THEN oldEtherOne ← etherOneStats^;
CedarProcess.SetPriority[excited];
TRUSTED {
Come up with an approximation of the maximum idle rate. This must change if Watch.IdleProcess changes!
refInt: REF INT ← NEW[INT ← 0];
stop ← BasicTime.GetClockPulses[];
THROUGH [0..10000)
DO
refInt^ ← refInt^ + 1;
IF PrincOpsUtils.ReadWDC[] # 0 THEN ERROR;
ENDLOOP;
start ← BasicTime.GetClockPulses[];
maxIdleRate ← 10000/BasicTime.PulsesToSeconds[start-stop];
};
WHILE
NOT viewer.destroyed
DO
lastIdleCount: INT ← idleCount;
cpu, alloc, disk, etherRecv, etherSend, etherOneRecv, etherOneSend: REAL ← 0.0;
fact: REAL ← 1.0;
Process.Pause[Process.MsecToTicks[milliSecondsPerPixel]];
stop ← BasicTime.GetClockPulses[];
sec ← BasicTime.PulsesToSeconds[stop-start];
idleCount ← WatchStats.GetWatchStats[].idleCount;
start ← stop;
IF sec = 0 THEN LOOP;
fact ← 1e-3/sec;
{
Adaptively calculate the CPU rate
idleRate ← (idleCount-lastIdleCount)/sec;
maxIdleRate ← MAX[maxIdleRate, idleRate];
cpu ← 100*(1.0 - idleRate/maxIdleRate);
};
{
Allocator
temp: INT ← SafeStorage.NWordsAllocated[];
words: INT ← temp-oldAlloc;
oldAlloc ← temp;
alloc ← REAL[words]*fact;
};
{
Disk
temp: INT ← Disk.GetStatistics[].readPages + Disk.GetStatistics[].writePages;
pages: INT ← temp-oldDisk;
oldDisk ← temp;
disk ← REAL[(LONG[Basics.bitsPerWord]*File.wordsPerPage)*pages]*fact;
};
IF etherStats #
NIL
THEN {
Ethernet (10 MB)
temp: EthernetDriverStats.EtherStatsRep ← etherStats^;
recv: INT ← temp.wordsRecv-oldEther.wordsRecv;
send: INT ← temp.wordsSent-oldEther.wordsSent;
oldEther ← temp;
etherRecv ← REAL[Basics.bitsPerWord*recv]*fact;
etherSend ← REAL[Basics.bitsPerWord*send]*fact;
};
IF etherOneStats #
NIL
THEN {
Ethernet One (3 MB)
temp: EthernetDriverStats.EtherStatsRep ← etherOneStats^;
recv: INT ← temp.wordsRecv-oldEtherOne.wordsRecv;
send: INT ← temp.wordsSent-oldEtherOne.wordsSent;
oldEtherOne ← temp;
etherOneRecv ← REAL[Basics.bitsPerWord*recv]*fact;
etherOneSend ← REAL[Basics.bitsPerWord*send]*fact;
};
IF viewer.destroyed THEN RETURN;
Histograph.AddSample[data.cpuGraph, cpu];
Histograph.AddSample[data.allocGraph, alloc];
Histograph.AddSample[data.diskGraph, disk];
IF data.etherGraph #
NIL
THEN
Histograph.AddSample[data.etherGraph, etherRecv+etherSend];
IF data.etherRecvGraph #
NIL
THEN
Histograph.AddSample[data.etherRecvGraph, etherRecv];
IF data.etherSendGraph #
NIL
THEN
Histograph.AddSample[data.etherSendGraph, etherSend];
IF data.etherOneGraph #
NIL
THEN
Histograph.AddSample[data.etherOneGraph, etherOneRecv+etherOneSend];
IF data.etherOneRecvGraph #
NIL
THEN
Histograph.AddSample[data.etherOneRecvGraph, etherOneRecv];
IF data.etherOneSendGraph #
NIL
THEN
Histograph.AddSample[data.etherOneSendGraph, etherOneSend];
ENDLOOP;
};
global: Data ← NIL;
Poof: ViewerEvents.EventProc = {
IF global # NIL AND global.container = viewer THEN global ← NIL;
};
MakeTool: Commander.CommandProc = {
data: Data ← NEW [DataRecord];
container: Viewer ← data.container ← Containers.Create[
info: [
name: "Watcher",
iconic: TRUE,
column: left,
menu: NIL,
scrollable: FALSE,
data: data ],
paint: FALSE];
lastViewer: Viewer;
etherStats: EthernetDriverStats.EtherStats ← EthernetDriverStats.GetEthernetStats[0];
etherOneStats: EthernetDriverStats.EtherStats ← EthernetDriverStats.GetEthernetOneStats[0];
separateEther: BOOL ← FALSE;
IF Rope.Find[cmd.commandLine, "-"] # -1 THEN separateEther ← TRUE;
lastViewer ← data.cpuGraph ← Histograph.NewHistograph[parent: container, maxSample: 100, averageFactor: averageFactor, title: "Cpu", subTitle: "% busy", firstSampleX: firstSampleX, wx: 0, wy: 0, childXbound: TRUE, tickY: 25, dataHeight: 50, vertiLog: 1];
lastViewer ← data.allocGraph ← Histograph.NewHistograph[parent: container, maxSample: 64, averageFactor: averageFactor, title: "Alloc", subTitle: "Kw/sec", firstSampleX: firstSampleX, wx: 0, wy: lastViewer.wy+lastViewer.wh, childXbound: TRUE, tickY: 16, dataHeight: 40, vertiLog: 8];
lastViewer ← data.diskGraph ← Histograph.NewHistograph[parent: container, maxSample: 3200, averageFactor: averageFactor, title: "Disk", subTitle: "Kbit/sec", firstSampleX: firstSampleX, wx: 0, wy: lastViewer.wy+lastViewer.wh, childXbound: TRUE, tickY: 16, dataHeight: 40, vertiLog: 8];
IF etherStats #
NIL
THEN {
-- We have a 10 MB Ethernet
network: CommDriver.Network ← CommDriver.GetNetworkChain[];
name: Rope.ROPE;
UNTIL network.type = ethernet DO network ← network.next; ENDLOOP;
name ← Rope.Cat[Convert.RopeFromCard[network.pup.net, 8, FALSE], "##" ];
IF ~separateEther
THEN {
lastViewer ← data.etherGraph ← Histograph.NewHistograph[parent: container, maxSample: 3200, averageFactor: averageFactor, title: name, subTitle: "Kbit/sec", firstSampleX: firstSampleX, wx: 0, wy: lastViewer.wy+lastViewer.wh, childXbound: TRUE, tickY: 16, dataHeight: 40, vertiLog: 8]; }
ELSE {
lastViewer ← data.etherRecvGraph ← Histograph.NewHistograph[parent: container, maxSample: 3200, averageFactor: averageFactor, title: Rope.Cat[name, "R"], subTitle: "Kbit/sec", firstSampleX: firstSampleX, wx: 0, wy: lastViewer.wy+lastViewer.wh, childXbound: TRUE, tickY: 16, dataHeight: 40, vertiLog: 8];
lastViewer ← data.etherSendGraph ← Histograph.NewHistograph[parent: container, maxSample: 3200, averageFactor: averageFactor, title: Rope.Cat[name, "S"], subTitle: "Kbit/sec", firstSampleX: firstSampleX, wx: 0, wy: lastViewer.wy+lastViewer.wh, childXbound: TRUE, tickY: 16, dataHeight: 40, vertiLog: 8]; }; };
IF etherOneStats #
NIL
THEN {
-- We have a 3 MB Ethernet
network: CommDriver.Network ← CommDriver.GetNetworkChain[];
name: Rope.ROPE;
UNTIL network.type = ethernetOne DO network ← network.next; ENDLOOP;
name ← Rope.Cat[Convert.RopeFromCard[network.pup.net, 8, FALSE], "##" ];
IF ~separateEther
THEN {
lastViewer ← data.etherOneGraph ← Histograph.NewHistograph[parent: container, maxSample: 3200, averageFactor: averageFactor, title: name, subTitle: "Kbit/sec", firstSampleX: firstSampleX, wx: 0, wy: lastViewer.wy+lastViewer.wh, childXbound: TRUE, tickY: 16, dataHeight: 40, vertiLog: 8]; }
ELSE {
lastViewer ← data.etherOneRecvGraph ← Histograph.NewHistograph[parent: container, maxSample: 3200, averageFactor: averageFactor, title: Rope.Cat[name, "R"], subTitle: "Kbit/sec", firstSampleX: firstSampleX, wx: 0, wy: lastViewer.wy+lastViewer.wh, childXbound: TRUE, tickY: 16, dataHeight: 40, vertiLog: 8];
lastViewer ← data.etherOneSendGraph ← Histograph.NewHistograph[parent: container, maxSample: 3200, averageFactor: averageFactor, title: Rope.Cat[name, "S"], subTitle: "Kbit/sec", firstSampleX: firstSampleX, wx: 0, wy: lastViewer.wy+lastViewer.wh, childXbound: TRUE, tickY: 16, dataHeight: 40, vertiLog: 8]; }; };
ViewerOps.SetOpenHeight[container, lastViewer.wy+lastViewer.wh];
[] ← ViewerEvents.RegisterEventProc[Poof, destroy, container, TRUE];
global ← data;
TRUSTED {Process.Detach[FORK Collector[data]]};
ViewerOps.OpenIcon[icon: container];
};
Commander.Register["Watcher", MakeTool, "Make Tool for watching performance numbers"];
}.