howiegp%cfa1@HARVARD.HARVARD.EDU.UUCP (04/17/87)
I've tested the patched monitor against an Excelan LANALYZER and believe the statistics to be reasonably accurate; one regrettable limitation is that on a system with two DELUAs or DEQNAs, statistics are only gathered on UNA/QNA-0. Howie McCausland Center for Astrophysics 60 Garden St. Cambridge MA HOWIE@CFA1.BITNET
LEICHTER-JERRY@YALE.ARPA.UUCP (04/18/87)
The following program - which provides a MONITOR-like display of Ethernet statistics, and seems to provide a couple of values that the MONITOR ETHER command the published patch gives you omits - appeared on INFO-VAX a while back, at the tail end of a document on how to use the VMS Ethernet drivers. (The document later appeared in Pageswapper; I've omitted the body of the document here as it is quite long, and partly obsolete.) Oh, BTW - don't bother looking for documentation on the special protocol the program uses to pick up the counters - unless you want to look at the fiche. -- Jerry Using the DEUNA/DEQNA Ethernet Interface Under VMS Ned Freed, Kevin Carosso and Dan Newman MATHLIB Project Harvey Mudd College Claremont, CA 91711 ... 7.0 A DEUNA monitoring program The following program monitors DEUNA performance by periodically sampling the internal hardware counters via a $QIO. Both transmission and reception information is presented in the form of "current" (e.g. bytes received/second), "average" (e.g. average bytes received/second), "minimum" and "maximum" (e.g. minimum and maximum bytes received/second) and "counter" (e.g. present value of the DEUNA's bytes received counter). The result is a MONITOR-like display of DEUNA activity. XEA0 is monitored by default. If you want to monitor a different device, just define a symbol to run the program. The device name to monitor will then be read off the command line with a LIB$GET_FOREIGN call. The program begins operations by clearing the terminal screen and drawing its display. To exit type either control-c or control-z. To repaint the screen, type contrl-w. The counters will be reset if the "seconds since last reset" counter exceeds 65530. The counters can be reset manually by typing control-r. The program uses the protocol type 61-02, which is not reserved for any specific purpose. This protocol type can be changed if it conflicts with anything. The program is written in VAX Pascal V2 and requires no special privileges. (* Monitor DEUNA performance statistics. Written by Dan Newman and Ned Freed, 21-July-84, MATHLIB Project, Harvey Mudd College *) [inherit ('SYS$LIBRARY:STARLET.PEN')] Program DEUNA; Const NMA$C_PCLI_BFN = %x451; NMA$C_PCLI_PTY = %xB0E; NMA$C_CTLIN_ZER = %x000; NMA$C_CTLIN_DBR = %x3F2; NMA$C_CTLIN_MBL = %x3F4; NMA$C_CTLIN_RFL = %x426; NMA$C_CTLIN_BRC = %x3E8; NMA$C_CTLIN_MBY = %x3EA; NMA$C_CTLIN_OVR = %x428; NMA$C_CTLIN_LBE = %x411; NMA$C_CTLIN_DBS = %x3F3; NMA$C_CTLIN_MBS = %xA8D; NMA$C_CTLIN_BSM = %x3F7; NMA$C_CTLIN_BS1 = %x3F6; NMA$C_CTLIN_BID = %x3F5; NMA$C_CTLIN_BSN = %x3E9; NMA$C_CTLIN_MSN = %xA8E; NMA$C_CTLIN_SFL = %x424; NMA$C_CTLIN_CDC = %x425; NMA$C_CTLIN_UFD = %x427; Type UnsignedWord = [Word] 0..65535; $Word = [Word] -32768..32767; CounterType = array [1..256] of unsigned; Counters = (BRC, DBR, MBY, MBL, RFL, OVR, LBE, BSN, DBS, MSN, MBS, BID, SFL, BSM, BS1, CDC, UFD); StartupType = Record PCLI_BFN : UnsignedWord; PCLI_BFN_VALUE : unsigned; PCLI_PTY : UnsignedWord; PCLI_PTY_VALUE : unsigned; end; (* StartupType *) StatType = Record Current, Previous : array [Counters] of unsigned; Rate, Accumulated, Min, Max : array [Counters] of real; end; (* StatType *) Var DEUNAChannel, TTYChannel : $Word; Count, Flag, Status : integer := 0; CounterBuffer : CounterType; StartupBuffer : StartupType; Stats : StatType; CounterDescriptor, IOStatus, StartupDescriptor : array [1..2] of integer; CurrentTime, LastTime : unsigned; Exit : boolean := false; DeviceName : varying [20] of char; DeviceNameLength : integer; First : boolean := true; Index : Counters; Lines : array [Counters] of integer; LongPointer : ^unsigned; WordPointer : ^UnsignedWord; [Unbound, External (LIB$GET_FOREIGN)] Function $GetForeign ( var GetStr : varying [l1] of char; Prompt : [Class_S] packed array [l2..u2 : integer] of char := %Immed 0; %Ref OutLen, Force : integer := %Immed 0) : integer; extern; [Unbound, External (LIB$STOP)] Function $STOP ( %Immed Error : integer) : integer; extern; [Unbound, External (LIB$SET_BUFFER)] Function $SetBuffer ( Buffer : [Class_S] packed array [l1..u1 : integer] of char := %Immed 0; %Ref OldBuffer : packed array [l2..u2 : integer] of char := %Immed 0) : integer; extern; [Unbound, External (LIB$PUT_BUFFER)] Function $PutBuffer ( %Ref OldBuffer : packed array [l1..u1 : integer] of char := %Immed 0) : integer; extern; [Unbound, External (LIB$PUT_SCREEN)] Function $PutScreen ( Text : [Class_S] packed array [l1..u1 : integer] of char; %Ref LineNo, ColNo, Flags : integer) : integer; extern; [Unbound, External (LIB$ERASE_PAGE)] Function $ErasePage ( %Ref LineNo, ColNo : integer := %Immed 0) : integer; extern; Function Min (Real1, Real2 : real) : real; Begin Min := Real1; if Real2 < Real1 then Min := Real2; End; Function Max (Real1, Real2 : real) : real; Begin Max := Real1; if Real2 > Real1 then Max := Real2; End; (* Max *) Procedure IncrementPointers (Inc : integer); Begin (* IncrementPointers *) LongPointer::unsigned := LongPointer::unsigned + Inc; WordPointer::unsigned := WordPointer::unsigned + Inc; End; (* IncrementPointers *) Procedure ReadCounterBuffer; var CurrentCounter : UnsignedWord; i : integer; Begin (* ReadCounterBuffer *) WordPointer := nil; LongPointer := nil; IncrementPointers (Iaddress (Counterbuffer)); while LongPointer::unsigned < Iaddress (Counterbuffer) + size (Counterbuffer) do begin CurrentCounter := WordPointer^; IncrementPointers (2); i := int (uand (%xFFF, CurrentCounter)); if CurrentCounter = 0 then IncrementPointers (256) else if i = NMA$C_CTLIN_ZER then CurrentTime := WordPointer^ else if i = NMA$C_CTLIN_DBR then Stats.Current[DBR] := LongPointer^ else if i = NMA$C_CTLIN_MBL then Stats.Current[MBL] := LongPointer^ else if i = NMA$C_CTLIN_RFL then Stats.Current[RFL] := WordPointer^ else if i = NMA$C_CTLIN_BRC then Stats.Current[BRC] := LongPointer^ else if i = NMA$C_CTLIN_MBY then Stats.Current[MBY] := LongPointer^ else if i = NMA$C_CTLIN_OVR then Stats.Current[OVR] := WordPointer^ else if i = NMA$C_CTLIN_LBE then Stats.Current[LBE] := WordPointer^ else if i = NMA$C_CTLIN_DBS then Stats.Current[DBS] := LongPointer^ else if i = NMA$C_CTLIN_MBS then Stats.Current[MBS] := LongPointer^ else if i = NMA$C_CTLIN_BSM then Stats.Current[BSM] := LongPointer^ else if i = NMA$C_CTLIN_BS1 then Stats.Current[BS1] := LongPointer^ else if i = NMA$C_CTLIN_BID then Stats.Current[BID] := LongPointer^ else if i = NMA$C_CTLIN_BSN then Stats.Current[BSN] := LongPointer^ else if i = NMA$C_CTLIN_MSN then Stats.Current[MSN] := LongPointer^ else if i = NMA$C_CTLIN_SFL then Stats.Current[SFL] := WordPointer^ else if i = NMA$C_CTLIN_CDC then Stats.Current[CDC] := WordPointer^ else if i = NMA$C_CTLIN_UFD then Stats.Current[UFD] := WordPointer^; if CurrentCounter > %xCFFF then IncrementPointers (4) else IncrementPointers (2); end; (* while *) End; (* ReadCounterBuffer *) Procedure PutScreen (Text : packed array [l1..u1 : integer] of char; LineNo, ColNo, Flags : integer); var Status : integer; Begin (* PutScreen *) Status := $PutScreen (Text, LineNo, ColNo, Flags); if not odd (Status) then $STOP (Status); End; (* PutScreen *) Procedure UpdateLine (Rate, Average, Min, Max : real; Current : unsigned; LineNo : integer); var String : varying [9] of char; Begin (* UpdateLine *) writev (String, Rate:7:1); PutScreen (String, LineNo, 37, 0); writev (String, Average:7:1); PutScreen (String, LineNo, 46, 0); writev (String, Min:7:1); PutScreen (String, LineNo, 55, 0); writev (String, Max:7:1); PutScreen (String, LineNo, 64, 0); writev (String, Current:9); PutScreen (String, LineNo, 72, 0); End; (* UpdateLine *) Procedure Update; var Buffer : packed array [1..1000] of char; String : varying [7] of char; Date : packed array [1..23] of char; Index : Counters; Status : integer; Begin (* Update *) (* Set up the screen buffer *) Status := $SetBuffer (Buffer); if not odd (Status) then $STOP (Status); (* Get the date *) $ASCTIM (,Date); for Status := 23 downto 13 do Date[Status] := Date[Status-1]; (* Build the Buffer *) PutScreen (Date, 2, 29, 0); for Index := BRC to UFD do UpdateLine (Stats.Rate[Index], Stats.Accumulated[Index]/Count, Stats.Min[Index], Stats.Max[Index], Stats.Current[Index], Lines[Index]); writev (String, CurrentTime:6); Status := 1; While String[Status] = ' ' do Status := succ (Status); PutScreen (substr (String, Status, 1+length(String)-Status), 24, 35, 0); (* Post the buffer to the screen via one $QIO *) Status := $PutBuffer; if not odd (Status) then $STOP (Status); (* Stop blocking the AST's *) Status := $SETAST (1); if not odd (Status) then $STOP (Status); (* See if the "Resetting the Counters" flag needs to be removed *) if Flag = 2 then begin Flag := 0; PutScreen (' ', 1, 59, 0); PutScreen (' ', 24, 35, 0); end; (* if Flag *) if Flag = 1 then Flag := succ (Flag); End; (* Update *) Procedure Paint; var Buffer : packed array [1..900] of char; Status : integer; Begin (* Paint *) (* Set up the Screen buffer *) Status := $SetBuffer (Buffer); if not odd (Status) then $STOP (Status); (* Erase the page *) $ErasePage (1,1); (* Put up the headers *) PutScreen ('DEUNA Performance Statistics', 1, 26, 1); PutScreen ('Device', 4, 1, 0); PutScreen (DeviceName, 4, 8, 0); PutScreen ('Current', 4, 37, 8); PutScreen ('Average', 4, 46, 8); PutScreen ('Minimum', 4, 55, 8); PutScreen ('Maximum', 4, 64, 8); PutScreen ('Counter', 4, 74, 8); PutScreen ('Received:', 5, 1, 0); PutScreen ('Bytes', 6, 3, 0); PutScreen ('Packets', 7, 3, 0); PutScreen ('Multicast Bytes', 8, 3, 0); PutScreen ('Multicast Packets', 9, 3, 0); PutScreen ('Packets in Error', 10, 3, 0); PutScreen ('Lost - Internal Buffer Errors', 11, 3, 0); PutScreen ('Lost - Local Buffer Errors', 12, 3, 0); PutScreen ('Transmitted:', 13, 1, 0); PutScreen ('Bytes', 14, 3, 0); PutScreen ('Packets', 15, 3, 0); PutScreen ('Multicast Bytes', 16, 3, 0); PutScreen ('Multicast Packets', 17, 3, 0); PutScreen ('Packets Deferred', 18, 3, 0); PutScreen ('Packets Aborted', 19, 3, 0); PutScreen ('Packets with Several Errors', 20, 3, 0); PutScreen ('Packets with 1 Error', 21, 3, 0); PutScreen ('Collision Check Failures', 22, 3, 0); PutScreen ('Unrecognized Frame Destinations', 23, 1, 0); PutScreen ('Seconds Since Last Counter Reset:', 24, 1, 0); (* Now put the info to the screen in one $QIO *) Status := $PutBuffer; if not odd (Status) then $STOP (Status); End; (* Paint *) Procedure ResetDEUNA; var Status : integer; Begin (* ResetDEUNA *) Flag := 1; PutScreen ('Resetting the Counters' + chr (7), 1, 59, 3); Status := $QIOW (Chan := DEUNAChannel, Iosb := IOStatus, Func := IO$_SENSEMODE + IO$M_CTRL + IO$M_CLR_COUNT + IO$M_RD_COUNT, P2 := IAddress (CounterDescriptor)); if not odd (Status) then $STOP (Status); if not odd (IOStatus[1]) then $STOP (IOStatus[1]); ReadCounterBuffer; for Index := BRC to UFD do Stats.Previous[Index] := Stats.Current[Index]; LastTime := CurrentTime; PutScreen (' ', 24, 34, 0); End; (* ResetDEUNA *) Procedure EnableAST; forward; Procedure AST (var Character : char); var Status : integer; Begin (* AST *) (* Kill any other I/O requests on this channel *) Status := $CANCEL (TTYChannel); if not odd (Status) then $STOP (Status); case IAddress (Character) of (* ^C and ^Z -- Exit gracefully *) 03, 26 : begin (* Shut the DEUNA down *) RetStatus := $CANCEL (DEUNAChannel); if not odd (RetStatus) then $STOP (RetStatus); RetStatus := $QIOW (Chan := DEUNAChannel, Iosb := IOStatus, Func := IO$_SETMODE + IO$M_CTRL + IO$M_SHUTDOWN); if not odd (RetStatus) then $STOP (RetStatus); RetStatus := $DASSGN (DEUNAChannel); if not odd (RetStatus) then $STOP (RetStatus); Exit := true; end; (* ^R -- Reset the DEUNA *) 18 : ResetDEUNA; (* ^W -- Repaint the screen *) 23 : begin Paint; Update; end; end; (* case *) (* Reset the AST *) EnableAST; End; (* AST *) Procedure EnableAST; var Mask : array [1..2] of integer; Status : integer; Begin (* EnableAST *) (* Out-of-band AST Mask -- Trap ^C, ^R, ^W and ^Z*) Mask[1] := 0; Mask[2] := (2**3) + (2**18) + (2**23) + (2**26); (* Queue the out-of-band AST *) Status := $QIOW (Chan := TTYChannel, Func := IO$_SETMODE + IO$M_OUTBAND, P1 := %Immed AST, P2 := IAddress (Mask)); if not odd (Status) then $STOP (Status); End; (* EnableAST *) Begin (* DEUNA *) (* These numbers represent which lines on the screen to place the data on *) Lines[BRC] := 6; Lines[DBR] := 7; Lines[MBY] := 8; Lines[MBL] := 9; Lines[RFL] := 10; Lines[OVR] := 11; Lines[LBE] := 12; Lines[BSN] := 14; Lines[DBS] := 15; Lines[MSN] := 16; Lines[MBS] := 17; Lines[BID] := 18; Lines[SFL] := 19; Lines[BSM] := 20; Lines[BS1] := 21; Lines[CDC] := 22; Lines[UFD] := 23; (* Setup the information to initialize the DEUNA *) with StartupBuffer do begin PCLI_BFN := NMA$C_PCLI_BFN; PCLI_BFN_VALUE := 1; PCLI_PTY := NMA$C_PCLI_PTY; PCLI_PTY_VALUE := %x0261; (* some unused protocol here *) end; (* with *) (* Build the descriptors for the $QIO calls *) CounterDescriptor[1] := size (CounterBuffer); CounterDescriptor[2] := IAddress (CounterBuffer); StartupDescriptor[1] := size (StartupBuffer); StartupDescriptor[2] := IAddress (StartupBuffer); (* Get a Channel to the terminal *) Status := $ASSIGN ('TT:', TTYChannel); if not odd (Status) then $STOP (Status); (* Get a Channel to the DEUNA *) Status := $GetForeign (DeviceName, , DeviceNameLength); if not odd (Status) then $STOP (Status); if DeviceNameLength = 0 then DeviceName := 'XEA0'; Status := $ASSIGN (DeviceName, DEUNAChannel); if not odd (Status) then $STOP (Status); (* Set up the ^C, ^R, ^W and ^Z traps *) EnableAST; (* Initialize the DEUNA *) Status := $QIOW (Chan := DEUNAChannel, Iosb := IOStatus, Func := IO$_SETMODE + IO$M_STARTUP + IO$M_CTRL, P2 := IAddress (StartupDescriptor)); if not odd (Status) then $STOP (Status); if not odd (IOStatus[1]) then $STOP (IOStatus[1]); (* Start our loop *) Repeat (* Read the DEUNA counters *) Status := $QIOW (Chan := DEUNAChannel, Iosb := IOStatus, Func := IO$_SENSEMODE + IO$M_RD_COUNT + IO$M_CTRL, P2 := IAddress (CounterDescriptor)); if not odd (Status) then $STOP (Status); if not odd (IOStatus[1]) then $STOP (IOStatus[1]); (* Dissect the returned information *) ReadCounterBuffer; (* Now generate the statisitics *) if CurrentTime > 65530 then ResetDEUNA else begin if LastTime <> CurrentTime then begin if First then begin for Index := BRC to UFD do begin Stats.Previous[Index] := Stats.Current[Index]; Stats.Accumulated[Index] := 0; Stats.Min[Index] := maxint; Stats.Max[Index] := -1; end; (* for *) end else begin Count := succ (Count); for Index := BRC to UFD do begin Stats.Rate[Index] := (Stats.Current[Index] - Stats.Previous[Index]) / (CurrentTime - LastTime); Stats.Accumulated[Index] := Stats.Accumulated[Index] + Stats.Rate[Index]; Stats.Min[Index] := Min (Stats.Min[Index], Stats.Rate[Index]); Stats.Max[Index] := Max (Stats.Max[Index], Stats.Rate[Index]); Stats.Previous[Index] := Stats.Current[Index]; end; (* for *) end; (* if First *) (* Display the information on the terminal *) if First then begin First := false; Paint; end else Update; if Exit then begin $ErasePage (1,1); $EXIT (1); end; (* if Exit *) end; (* if LastTime <> CurrentTime *) LastTime := CurrentTime; end; (* if CurrentTime > 65530 *) until 1 = 2; End. (* DEUNA *) -------