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 { 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; { idleRate _ (idleCount-lastIdleCount)/sec; maxIdleRate _ MAX[maxIdleRate, idleRate]; cpu _ 100*(1.0 - idleRate/maxIdleRate); }; { temp: INT _ SafeStorage.NWordsAllocated[]; words: INT _ temp-oldAlloc; oldAlloc _ temp; alloc _ REAL[words]*fact; }; { 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 { 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 { 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"]; }. pWatcher.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 Come up with an approximation of the maximum idle rate. This must change if Watch.IdleProcess changes! Adaptively calculate the CPU rate Allocator Disk Ethernet (10 MB) Ethernet One (3 MB) Κι˜codešœ ™ K™AK™,K™/K˜šΟk ˜ Kšœœ˜Kšœ œ+˜:Kšœ œ˜!Kšœ œ˜(Kšœ œ˜,Kšœ œ ˜Kšœœ˜Kšœœ˜KšœœD˜]Kšœœ˜Kšœ œ˜,Kšœœ ˜Kšœœ˜+Kšœœ˜ Kšœœ œ˜Kšœ œ˜$Kšœœ ˜Kšœ œ ˜2Kšœ œ˜*Kšœ œ˜!——headšœ œ˜Kšœœ ˜šœΏ˜ΖK˜—Kšœœ˜$K˜Kšœœœ ˜šœ œœ˜%Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜ Kšœ˜Kšœ˜K˜—Kšœœ˜Kšœœ˜K˜Kšœœ˜!šΟn œœ˜ Kšœœœ˜K˜.K˜Kšœœ˜ Kšœœ˜ Kšœ œ(˜6Kšœ œ Οc7˜SKšœ œ˜Kšœ œ!˜.Kšœ œD˜PKšœU˜UKšœ[˜[Kšœ,˜,Kšœ/˜/Kšœœœ˜0Kšœœœ˜9Kšœ"˜"K˜šœ˜ Kšœg™gKš œœœœœ˜K˜"šœ ˜Kšœ˜Kšœœœ˜*Kšœ˜—K˜#Kšœ:˜:K˜K˜—šœœ˜Kšœœ ˜KšœDœ˜OKšœœ˜Kšœ9˜9K˜"Kšœ,˜,Kšœ1˜1K˜ Kšœ œœ˜K˜˜Kšœœ™!Kšœ)˜)Kšœœ˜)Kšœ'˜'K˜—šœ˜Kšœ ™ Kšœœ!˜*Kšœœ˜Kšœ˜Kšœœ ˜Kšœ˜—šœ˜Kšœ™KšœœD˜MKšœœ˜Kšœ˜Kšœœœ4˜EKšœ˜—šœœœ˜Kšœ œ™Kšœ6˜6Kšœœ%˜.Kšœœ%˜.K˜Kšœ œ˜/Kšœ œ˜/Kšœ˜—šœœœ˜Kšœœ™Kšœ9˜9Kšœœ(˜1Kšœœ(˜1K˜Kšœœ˜2Kšœœ˜2Kšœ˜—Kšœœœ˜ Kšœ)˜)Kšœ-˜-Kšœ+˜+šœœ˜Kšœ;˜;—šœœ˜!Kšœ5˜5—šœœ˜!Kšœ5˜5—šœœ˜ KšœD˜D—šœœ˜$Kšœ;˜;—šœœ˜$Kšœ;˜;—Kšœ˜—Kšœ˜K˜—Kšœœ˜šΠbnœ˜ Kš œ œœœ œ˜@Kšœ˜K˜—šžœ˜#Kšœ œ˜šœ7˜7šœ˜Kšœ˜Kšœœ˜ Kšœ ˜ Kšœœ˜ Kšœ œ˜Kšœ ˜ —Kšœœ˜—Kšœ˜KšœU˜UKšœ[˜[Kšœœœ˜Kšœ&œœ˜BKšœΠœ*˜ώKšœνœ*˜›Kšœοœ*˜šœœœŸ˜6K˜;Kšœ œ˜Kšœœœ˜AKšœ9œ ˜Hšœœ˜Kšœξœ,˜ž—šœ˜Kšœœ*˜―Kšœœ0˜΅——šœœœŸ˜8K˜;Kšœ œ˜Kšœœœ˜DKšœ9œ ˜Hšœœ˜Kšœρœ,˜‘—šœ˜Kšœ„œ*˜²Kšœ„œ0˜Έ——Kšœ@˜@Kšœ>œ˜DK˜Kšœœ˜/Kšœ$˜$Kšœ˜K˜—KšœV˜VK˜Kšœ˜——…—#h,Α