<> <> <> <> 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"]; }.