$! Here is a VMS driver you can load which will keep track of the $!load average in a way you can retrieve it. The algorithm used by it is $!not necessarily the same as LAT, but closer to TOPS. To read the $!load average, $ASSIGN or open a channel to the device _LAV0: and $!read 36 bytes from it. The first 12 bytes are three "F"-type floats $!which are the 1, 5, and 15 minute load averages respectively. The $!next 12 bytes are the 1, 5, and 15 minute average blocking priority (if $!your process is below the average blocking priority you aren't going to $!get much CPU, if above it you will get a lot). The last three are the $!1, 5, and 15 minute averages of the longest I/O queue length on any disk, $!a decent indication of how backed up your I/O system is. $! $! This copy of the driver is the same as the one I posted in late $!July 1988, so if you grabbed a copy then, there is no need to bother $!unpacking it now. Note that for VMS V5 you need to add a line VMS_V5=1 $!to the top to turn on the VMS V5 conditionals. $! $! $! Kenneth Adelman $! TGV $! $! $! $!...................... Cut between dotted lines and save. ..................... $!............................................................................. $! VAX/VMS archive file created by VMS_SHARE V06.00 26-May-1988. $! $! VMS_SHARE was written by James Gray (Gray:OSBUSouth@Xerox.COM) from $! VMS_SHAR by Michael Bednarek (U3369429@ucsvc.dn.mu.oz.au). $! $! To unpack, simply save, concatinate all parts into one file and $! execute (@) that file. $! $! This archive was created by user ADELMAN $! on 31-JUL-1990 11:33:02.37. $! $! It contains the following 1 file: $! LAVDRIVER.MAR $! $!============================================================================== $ SET SYMBOL/SCOPE=( NOLOCAL, NOGLOBAL ) $ VERSION = F$GETSYI( "VERSION" ) $ IF VERSION .GES "V4.4" THEN GOTO VERSION_OK $ WRITE SYS$OUTPUT "You are running VMS ''VERSION'; ", - "VMS_SHARE V06.00 26-May-1988 requires VMS V4.4 or higher." $ EXIT 44 $VERSION_OK: $ GOTO START $ $UNPACK_FILE: $ WRITE SYS$OUTPUT "Creating ''FILE_IS'" $ DEFINE/USER_MODE SYS$OUTPUT NL: $ EDIT/TPU/COMMAND=SYS$INPUT/NODISPLAY/OUTPUT='FILE_IS'/NOSECTION - VMS_SHARE_DUMMY.DUMMY b_part := CREATE_BUFFER( "{Part}", GET_INFO( COMMAND_LINE, "file_name" ) ) ; s_file_spec := GET_INFO( COMMAND_LINE, "output_file" ); SET( OUTPUT_FILE , b_part, s_file_spec ); b_errors := CREATE_BUFFER( "{Errors}" ); i_errors := 0 ; pat_beg_1 := ANCHOR & "-+-+-+ Beginning"; pat_beg_2 := LINE_BEGIN & "+-+-+-+ Beginning"; pat_end := ANCHOR & "+-+-+-+-+ End"; POSITION ( BEGINNING_OF( b_part ) ); i_append_line := 0; LOOP EXITIF MARK( NONE ) = END_OF( b_part ); s_x := ERASE_CHARACTER( 1 ); IF s_x = "+" THEN r_skip := SEARCH( pat_beg_1, FORWARD, EXACT ); IF r_skip <> 0 THEN s_x := "" ; MOVE_HORIZONTAL( -CURRENT_OFFSET ); ERASE_LINE; ENDIF; ENDIF ; IF s_x = "-" THEN r_skip := SEARCH( pat_end, FORWARD, EXACT ); IF r_skip < > 0 THEN s_x := ""; MOVE_HORIZONTAL( -CURRENT_OFFSET ); m_skip := MARK( NONE ) ; r_skip := SEARCH( pat_beg_2, FORWARD, EXACT ); IF r_skip <> 0 THEN POSITION ( END_OF( r_skip ) ); MOVE_HORIZONTAL( -CURRENT_OFFSET ); MOVE_VERTICAL( 1 ) ; MOVE_HORIZONTAL( -1 ); ELSE POSITION( END_OF( b_part ) ); ENDIF; ERASE ( CREATE_RANGE( m_skip, MARK( NONE ), NONE ) ); ENDIF; ENDIF ; IF s_x = "V" THEN s_x := ""; IF i_append_line <> 0 THEN APPEND_LINE ; MOVE_HORIZONTAL( -CURRENT_OFFSET ); ENDIF; i_append_line := 1; MOVE_VERTICAL ( 1 ); ENDIF; IF s_x = "X" THEN s_x := ""; IF i_append_line < > 0 THEN APPEND_LINE; MOVE_HORIZONTAL( -CURRENT_OFFSET ); ENDIF; i_append_line := 0; MOVE_VERTICAL( 1 ); ENDIF; IF s_x <> "" THEN i_errors := i_errors + 1 ; s_text := CURRENT_LINE; POSITION( b_errors ); COPY_TEXT ( "The following line could not be unpacked properly:" ); SPLIT_LINE; COPY_TEXT ( s_x ); COPY_TEXT( s_text ); POSITION( b_part ); MOVE_VERTICAL( 1 ); ENDIF ; ENDLOOP; POSITION( BEGINNING_OF( b_part ) ); LOOP r_x := SEARCH( "`", FORWARD , EXACT ); EXITIF r_x = 0; POSITION( r_x ); ERASE_CHARACTER( 1 ) ; IF CURRENT_CHARACTER = "`" THEN MOVE_HORIZONTAL( 1 ); ELSE COPY_TEXT( ASCII ( INT( ERASE_CHARACTER( 3 ) ) ) ); ENDIF; ENDLOOP; IF i_errors = 0 THEN SET ( NO_WRITE, b_errors, ON ); ELSE POSITION( BEGINNING_OF( b_errors ) ) ; COPY_TEXT( FAO( "The following !UL errors were detected while unpacking !AS" , i_errors, s_file_spec ) ); SPLIT_LINE; SET( OUTPUT_FILE, b_errors , "SYS$COMMAND" ); ENDIF; EXIT; $ DELETE VMS_SHARE_DUMMY.DUMMY;* $ CHECKSUM 'FILE_IS $ WRITE SYS$OUTPUT " CHECKSUM ", - F$ELEMENT( CHECKSUM_IS .EQ. CHECKSUM$CHECKSUM, ",", "failed!,passed." ) $ RETURN $ $START: $ FILE_IS = "LAVDRIVER.MAR" $ CHECKSUM_IS = 1784354413 $ COPY SYS$INPUT VMS_SHARE_DUMMY.DUMMY X`009.TITLE`009LAVDRIVER`009VAX/VMS Load Average Pseudo Device X`009.IDENT`009/Version 4.00/ X`009.SBTTL`009DOCUMENTATION X;---------------S-R-I-International-Artificial-Intelligence-Center----------- X; X; X; MODULE NAME: X; X;`009LAVDRIVER X; X; PURPOSE: X; X;`009This is a pseudo device which supplies load average information to X;`009programs wishing to display some indication of system load. X; X; KEYWORDS: X; X;`009Drivers X; X; USAGE: X; X;`009Standard system QIO calls. X; X;`009The driver is installed via sysgen. The following is the correct X;`009`009sysgen command sequence. X; X;`009`009SYSGEN> LOAD LAVDRIVER X;`009`009SYSGEN> CONNECT LAV0 /NOADA/DRIVER=LAVDRIVER X; X;`009`009Note: LAVDRIVER.EXE should reside in [SYS$LDR] X; X; X; SPECIAL REQUIREMENTS: X; X;`009none X; X; LANGUAGE: X; X;`009VAX-11 MACRO X; X; PARAMETERS: X; X;`009none X; X; SPECIAL TECHNIQUES: X; X;`009none X; X; AUTHOR: X; X;`009David Kashtan X; X; DATE CREATED: X; X;`009Wed Jul 21 11:25:19 1982 X; X; MAINTAINED BY: X; X;`009David Kashtan X; X; UPDATES: X; X;`009Modified for VMS V5 SMP Thu Oct 8 1987 Kenneth Adelman / LBL X; X;---------------S-R-I-International-Artificial-Intelligence-Center----------- X`012 X`009.SBTTL`009DEFINITIONS X`009.LIBRARY`009/SYS$LIBRARY:LIB.MLB/ X;+ --- X; X; DEFINE CONTROL BLOCK OFFSETS & OTHER SYSTEM STUFF BY CALLING 'DEF' MACROS X; X;- --- X.IF`009DF,VMS_V5 X`009$CPUDEF`009`009`009;DEFINE PER-CPU STRUCTURE X.ENDC X`009$CRBDEF`009`009`009;DEFINE CHANNEL REQUEST BLOCK X`009$DCDEF`009`009`009;DEFINE DEVICE TYPES X`009$DDBDEF`009`009`009;DEFINE DEVICE DATA BLOCK X`009$DEVDEF`009`009`009;DEFINE DEVICE CONSTANTS X`009$DPTDEF`009`009`009;DEFINE DRIVER PROLOGUE TABLE X`009$DYNDEF`009`009`009;DEFINE DYNAMIC STRUCTURE CODES X`009$FKBDEF`009`009`009;DEFINE FORK BLOCK CODES X`009$IODEF`009`009`009;DEFINE I/O FUNCTION CODES X.IF`009NDF,VMS_V5 X`009$IPLDEF`009`009`009;DEFINE INTERRUPT PRIORITY LEVELS X.ENDC X`009$IRPDEF`009`009`009;DEFINE I/O REQUEST PACKET X`009$PCBDEF`009`009`009;DEFINE PROCESS CONTROL BLOCK X`009$PHDDEF`009`009`009;DEFINE PROCESS HEADER X`009$PRVDEF`009`009`009;DEFINE VMS PRIVS X`009$TQEDEF`009`009`009;DEFINE TIMER QUEUE ENTRY X`009$UCBDEF`009`009`009;DEFINE UNIT CONTROL BLOCK X`009$VECDEF`009`009`009;DEFINE VECTOR OFFSETS X; X`012 X`009.SBTTL`009DRIVER PROLOGUE TABLE X;+ --- X; X; DRIVER CHARACTERISTICS X; X;- --- X.IF`009NDF,VMS_V5 X`009DPTAB`009 - X`009`009`009END=LAV_END,`009 -;END OF DRIVER (SIZE) X`009`009`009ADAPTER=NULL,`009 -;UNIBUS DEVICE X`009`009`009UCBSIZE=UCB$C_Length, -;UNIT CONTROL BLOCK LENGTH X`009`009`009MAXUNITS=1,`009 -;ONLY ONE LAV0 DEVICE X`009`009`009UNLOAD=LAV_UNLOAD, -;UNLOAD ROUTINE X`009`009`009NAME=LAVDRIVER`009`009;DRIVER NAME X.IFF X`009DPTAB`009 - X`009`009`009END=LAV_END,`009 -;END OF DRIVER (SIZE) X`009`009`009ADAPTER=NULL,`009 -;UNIBUS DEVICE X`009`009`009UCBSIZE=UCB$C_Length, -;UNIT CONTROL BLOCK LENGTH X`009`009`009MAXUNITS=1,`009 -;ONLY ONE LAV0 DEVICE X`009`009`009UNLOAD=LAV_UNLOAD, -;UNLOAD ROUTINE X`009`009`009NAME=LAVDRIVER, -;DRIVER NAME X`009`009`009SMP=YES`009`009`009;Works under SMP X.ENDC X;+ --- X; X; INITIALIZATION INFORMATION X; X;- --- X`009DPT_STORE`009INIT X.IF`009NDF,VMS_V5 X`009DPT_STORE`009UCB,UCB$B_FIPL,B,8`009;FORK IPL LEVEL X.IFF X`009DPT_STORE`009UCB,UCB$B_FLCK,B,SPL$C_IOLOCK8 ;FORK SPINLOCK X.ENDC X`009DPT_STORE`009UCB,UCB$B_DIPL,B,21`009;DEVICE IPL LEVEL (BR5 = 21) X`009DPT_STORE`009UCB,UCB$L_DEVCHAR,L, -;DEVICE CHARACTERISTICS: X`009`009 <- X`009`009`009DEV$M_AVL!`009 -; DEVICE AVAILABLE X`009`009`009DEV$M_REC!`009 -; RECORD ORIENTED X`009`009`009DEV$M_IDV!`009 -; INPUT DEVICE X`009`009`009DEV$M_SHR`009 -; SHARED X`009`009 > X`009DPT_STORE`009UCB,UCB$W_DEVBUFSIZ, -;DEVICE BUFFER SIZE X`009`009`009`009W,36`009`009; (36 bytes) X;+ --- X; X; RE-INITIALIZATION INFORMATION X; X;- --- X`009DPT_STORE`009REINIT X`009DPT_STORE`009DDB,DDB$L_DDT,D,LAV$DDT ;DRIVER DISPATCH TABLE X`009DPT_STORE`009CRB,`009`009 -;CONTROLLER INITIALIZATION ROUTINE X`009`009`009 CRB$L_INTD+VEC$L_INITIAL,D,- X`009`009`009`009LAV_CONTROLLER_INIT X`009DPT_STORE`009CRB,`009`009 -;UNIT INITIALIZATION ROUTINE X`009`009`009 CRB$L_INTD+VEC$L_UNITINIT,D,- X`009`009`009`009LAV_UNIT_INIT X`009DPT_STORE`009END X; X`012 X`009.SBTTL`009DRIVER DISPATCH TABLE X;+ --- X; X; ADDRESSES OF VARIOUS DRIVER ENTRY POINTS X; X;- --- X`009DDTAB`009- X`009`009 DEVNAM=LAV,`009 -;DEVICE NAME X`009`009 START=0,`009 -;ADDRESS OF START I/O ENTRY POINT X`009`009 UNSOLIC=0,`009 -;ADDRESS OF UNSOLICITED INT. ROUTINE X`009`009 FUNCTB=LAV_FUNCTABLE, -;ADDRESS OF FUNCTION DECISION TABLE X`009`009 CANCEL=0,`009 -;ADDRESS OF CANCEL I/O ENTRY POINT X`009`009 REGDMP=0,`009 -;ADDRESS OF REGISTER DUMP ROUTINE X`009`009 DIAGBF=0,`009 -;SIZE OF DIAGNOSTIC BUFFER X`009`009 ERLGBF=0`009`009;SIZE OF ERROR LOG BUFFER X; X`012 X`009.SBTTL`009FUNCTION DECISION TABLE XLAV_FUNCTABLE: X;+ --- X; X; SPECIFY LEGAL FUNCTION CODES X; X;- --- X`009FUNCTAB ,`009 -;LEGAL FUNCTION CODE ENTRY X`009`009<`009 -; X`009`009 READVBLK, -;READ VIRTUAL BLOCK X`009`009 READLBLK, -;READ LOGICAL BLOCK X`009`009 READPBLK, -;READ PHYSICAL BLOCK X`009`009 WRITEVBLK, -;WRITE VIRTUAL BLOCK X`009`009 WRITELBLK, -;WRITE LOGICAL BLOCK X`009`009 WRITEPBLK, -;WRITE PHYSICAL BLOCK X`009`009> X;+ --- X; X; SPECIFY BUFFERED I/O FUNCTION CODES X; X;- --- X`009FUNCTAB ,`009 -;BUFFERED FUNCTION CODE ENTRY X`009`009<>`009`009;NONE X;+ --- X; X; SPECIFY 1ST FDT ROUTINE X; X;- --- X`009FUNCTAB GET_LOAD_AVE, -;GET THE CURRENT LOAD AVERAGES X`009`009<`009 -;USED BY: X`009`009 READVBLK, -;READ VIRTUAL BLOCK X`009`009 READLBLK, -;READ LOGICAL BLOCK X`009`009 READPBLK -;READ PHYSICAL BLOCK X`009`009>`009`009; X;+ --- X; X; SPECIFY 2ND FDT ROUTINE X; X;- --- X`009FUNCTAB SET_PRIORITIES, -;SET WHICH PRIORITIES ARE SCANNED X`009`009<`009`009-;USED BY: X`009`009 WRITEVBLK,`009-;WRITE VIRTUAL BLOCK X`009`009 WRITELBLK,`009-;WRITE LOGICAL BLOCK X`009`009 WRITEPBLK`009-;WRITE PHYSICAL BLOCK X`009`009>`009`009; X; X`012 X`009.SBTTL`009READ FDT ROUTINE X;+ --- X; X; GET_LOAD_AVE`009`009READ FDT ROUTINE X; X; FUNCTIONAL DESCRIPTION: X; X;`009All this routine does is verify the read arguments and return M1->M15 X; X; INPUTS: X; X;`009R3 = I/O Packet address X;`009R4 = Current Process Control Block address X;`009R5 = Unit Control Block Address X;`009R6 = Channel Control Block Address X;`009R7 = Function Code X;`009R8 = Scratch X;`009R9 = Scratch X; X;`0090(AP) --> Read Size X;`0094(AP) --> Read Buffer X; X; OUTPUTS: X; X;`009none X; X;- --- XGET_LOAD_AVE: X X;+ --- X; Get Read Size (limited to 36 bytes) X;- --- X`009MOVZWL`0094(AP),R0`009`009`009;Get Read Size X`009CMPL`009R0,#36`009`009`009`009;Bigger than 36?? X`009BLEQU`00910$`009`009`009`009;No: X`009MOVL`009#36,R0`009`009`009`009;Yes: Truncate to 36 bytes X10$: X;+ --- X; Make sure that the buffer is writeable X;- --- X`009IFNOWRT R0,@(AP),ACCVIO`009`009`009;Return SS$_ACCVIO if no write X;+ --- X; Move the data X;- --- X`009PUSHR`009#`094M`009`009;Be conservative!!! X`009MOVC3`009R0,M1,@(AP)`009`009`009;Move the Data X`009POPR`009#`094M`009`009;Restore registers X;+ --- X; Complete the I/O request X;- --- X`009ASHL`009#16,R0,R0`009`009`009;Set transfer size X`009BISW2`009#SS$_NORMAL,R0`009`009`009;Set Completion code X`009JMP`009G`094EXE$FINISHIOC`009`009`009;Complete the request X X X;+ --- X; Return an access violation X;- --- XACCVIO: X`009MOVZWL`009#SS$_ACCVIO,R0`009`009`009;Set return code X`009CLRL`009R1 X`009JMP`009G`094EXE$ABORTIO`009`009`009;Abort the I/O request X X`012 X`009.SBTTL`009WRITE FDT ROUTINE X;+ --- X; X; SET_PRIORITIES`009WRITE FDT ROUTINE X; X; FUNCTIONAL DESCRIPTION: X; X;`009All this routine does is verify the write arguments and change the X;`009priorities mask. X; X; INPUTS: X; X;`009R3 = I/O Packet address X;`009R4 = Current Process Control Block address X;`009R5 = Unit Control Block Address X;`009R6 = Channel Control Block Address X;`009R7 = Function Code X;`009R8 = Scratch X;`009R9 = Scratch X; X;`0090(AP) --> Write Size X;`0094(AP) --> Write Buffer X; X; OUTPUTS: X; X;`009none X; X;- --- XSET_PRIORITIES: X;+ --- X; Check privs (must have CMKRNL) X;- --- X X`009ASSUME`009PHD$Q_PRIVMSK EQ 0 X`009BBC`009#PRV$V_CMKRNL,@PCB$L_PHD(R4),NOPRIV X X;+ --- X; Get Write Size (limited to 4 bytes) X;- --- X`009MOVZWL`0094(AP),R0`009`009`009;Get Write Size X`009CMPL`009R0,#4`009`009`009`009;Bigger than 4?? X`009BLEQU`00910$`009`009`009`009;No: X`009MOVL`009#4,R0`009`009`009`009;Yes: Truncate to 4 bytes X10$: X;+ --- X; Make sure that the buffer is readable X;- --- X`009IFNORD`009R0,@(AP),ACCVIO`009`009`009;Return SS$_ACCVIO if no write X;+ --- X; Move the data X;- --- X`009MOVL`009@(AP),PRIORITY_MASK X;+ --- X; Complete the I/O request X;- --- X`009ASHL`009#16,R0,R0`009`009`009;Set transfer size X`009BISW2`009#SS$_NORMAL,R0`009`009`009;Set Completion code X`009JMP`009G`094EXE$FINISHIOC`009`009`009;Complete the request X X;+ --- X; Return and access violation X;- --- XNOPRIV: X`009MOVZWL`009#SS$_NOPRIV,R0`009`009`009;Set return code X`009CLRL`009R1 X`009JMP`009G`094EXE$ABORTIO`009`009`009;Abort the I/O request X X`012 X`009.SBTTL`009CONTROLLER INITIALIZATION ROUTINE X;+ --- X; X; ROUTINE CALLED WHEN THE LOADING PROCEDURE SETS UP THE CONTROLLER X; X; INPUT REGISTERS: X;`009R0 = SCRATCH X;`009R5 = ADDRESS OF UNIT CONTROL BLOCK X; X; REGISTERS MODIFIED: X;`009R0 = SCRATCH X; X; SIDE EFFECTS: X;`009LOAD AVERAGE WATCHER IS SET UP X; X;- --- XLAV_CONTROLLER_INIT: X`009TSTL`009HAVEINITED`009`009; If power failure recovery, X`009BEQL`0091$`009`009 ; don't reenter the TQE block. X`009RSB X1$:`009MOVL`009#1,HAVEINITED X X; SET LOAD AVES TO ZERO X X`009CLRF`009M1 X`009CLRF`009M5 X`009CLRF`009M15 X`009CLRF`009P1 X`009CLRF`009P5 X`009CLRF`009P15 X`009CLRF`009Q1 X`009CLRF`009Q5 X`009CLRF`009Q15 X X; SET UP THE LOAD AVERAGE WATCHING CODE (INSERT THE TIMER QUEUE ENTRY) X; X; Fork to a lower IPL X; X`009MOVQ`009R4,-(SP)`009 ; Not allowed to modify R4 or R5 X`009MOVAQ`009FKB,R5 X`009PUSHAB`00910$`009`009 ; Trick EXE$FORK into returning to us. X`009PUSHAB`00920$`009`009 ; Address to fork to X`009JMP`009G`094EXE$FORK X10$: X`009MOVQ`009(SP)+,R4`009 ; Restore R4, R5 X15$:`009RSB`009`009`009 ; Return from unit init routine X; X; Now running at IPL 6 X; X20$:`009MOVAB`009UPDATE_LOAD_AVERAGE,TQE_PC X`009MOVAL`009TQE,R5 X`009MOVB`009#1,RUNNING`009 ; CURRENTLY RUNNING X`009CLRB`009STOPPING`009 ; AND NOT STOPPING X.IF`009NDF,VMS_V5 X`009MOVQ`009@#EXE$GQ_SYSTIME,TQE_DUE X`009INSQUE`009TQE,@#EXE$GL_TQFL X.IFF X`009READ_SYSTIME R0 X`009JSB`009G`094EXE$INSTIMQ X.ENDC X30$:`009RSB`009`009`009`009`009;AND RETURN X; X`012 X`009.SBTTL`009CONTROLLER UNLOAD ROUTINE X;+ --- X; X; ROUTINE CALLED WHEN THE DRIVER IS RELOADED X; X; INPUT REGISTERS: X;`009R0 = SCRATCH X;`009R5 = ADDRESS OF UNIT CONTROL BLOCK X; X; REGISTERS MODIFIED: X;`009R0 = SCRATCH X; X; SIDE EFFECTS: X;`0091st call TQE entry is removed X;`0092nd call driver is unloaded X;- --- XLAV_UNLOAD: X`009TSTB`009RUNNING`009`009 ; If not running, unload X`009BNEQ`00910$ X`009MOVL`009#SS$_NORMAL,R0`009 ; If not running, ok to unload driver X`009RSB X10$:`009MOVB`009#1,STOPPING`009 ; SET THE STOPPING FLAG X`009CLRL`009R0 X`009RSB X; X`012 X`009.SBTTL`009UNIT INITIALIZATION ROUTINE X;+ --- X; X; ROUTINE CALLED WHEN THE LOADING PROCEDURE SETS UP THE UNIT. X; THE DEVICE ONLINE BIT IS SET IN THE UCB. X; X; INPUT REGISTERS: X;`009R0 = SCRATCH X;`009R5 = ADDRESS OF UNIT CONTROL BLOCK X; X; REGISTERS MODIFIED: X;`009R0 = SCRATCH X; X; SIDE EFFECTS: X;`009UNIT ONLINE BIT IS SET IN UCB X; X;- --- XLAV_UNIT_INIT: X`009BISW`009#UCB$M_ONLINE,UCB$W_STS(R5)`009;SET STATUS = ONLINE X`009RSB X; X`012 X`009.SBTTL`009LOCAL DATA X;+ --- X; Local Data for the Load Average Watcher X;- --- XM1:`009.FLOAT`0090.0`009`009`009`009;1 Min. Load Ave. XM5:`009.FLOAT`0090.0`009`009`009`009;5 Min. Load Ave. XM15:`009.FLOAT`0090.0`009`009`009`009;15 Min. Load Ave. X XP1:`009.FLOAT`0090.0`009`009`009`009;1 Min. Priority Ave. XP5:`009.FLOAT`0090.0`009`009`009`009;5 Min. Priority Ave. XP15:`009.FLOAT`0090.0`009`009`009`009;15 Min. Priority Ave. X XQ1:`009.FLOAT`0090.0`009`009`009`009;1 Min. Ave disk queue len XQ5:`009.FLOAT`0090.0`009`009`009`009;5 Min. Ave disk queue len XQ15:`009.FLOAT`0090.0`009`009`009`009;15 Min. Ave disk queue len X XF1:`009.FLOAT`0090.983471453`009`009`009;1 min. load ave. constants XF11:`009.FLOAT`0090.016528547 X XF2:`009.FLOAT`0090.996672213`009`009`009;5 min. load ave. constants XF12:`009.FLOAT`0090.003327787 X XF3:`009.FLOAT`0090.998889506`009`009`009;15 min. load ave. constants XF13:`009.FLOAT`0090.001110494 X X XTQE:`009.LONG`0090`009`009`009`009;TIMER QUEUE ELEMENT (FLINK) X`009.LONG`0090`009`009`009`009;(BLINK) X`009.WORD`009TQE_LEN`009`009`009`009;SIZE X`009.BYTE`009DYN$C_TQE`009`009`009;TYPE X`009.BYTE`009TQE$C_SSREPT`009`009`009;REQUEST == REPEAT XTQE_PC: .LONG`0090`009`009`009`009;PC TO CALL X`009.LONG`0090`009`009`009`009;N/A X`009.LONG`0090`009`009`009`009;N/A XTQE_DUE:.LONG`0090`009`009`009`009;DUE TIME X`009.LONG`0090 X`009.LONG`009100000*100`009`009`009;Repeat time (1 Sec.) X`009.LONG`0090 XTQE_LEN = .-TQE X XHAVEINITED: X`009.LONG`0090`009`009`009`009; Set if alread initied XRUNNING: X`009.BYTE`0090`009`009`009`009; True if TQE entry is in queue. XSTOPPING: X`009.BYTE`0090`009`009`009`009; SET TO REQUEST TQE TO ABORT XPRIORITY_MASK: X`009.LONG`0090`009`009`009`009; Check all priorities X XFKB:`009.LONG`0090`009`009`009`009; FQFL X`009.LONG`0090`009`009`009`009; FQBL X`009.WORD`0090`009`009`009`009; SIZE X`009.BYTE`009DYN$C_FRK`009`009`009; TYPE X`009.BYTE`009IPL$_TIMER`009`009`009; IPL LOW ENOUGH TO LOCK TIMER XFKB_PC: .LONG`0090`009`009`009`009; PC X`009.LONG`0090`009`009`009`009; R3 X`009.LONG`0090`009`009`009`009; R4 X`012 X`009.SBTTL`009Load Average Watching Code X XUPDATE_LOAD_AVERAGE:`009`009`009`009;We link into the X`009`009`009`009`009`009;VMS kernel via the timer q X`009`009`009`009`009`009;and is entered by JSB when X`009`009`009`009`009`009;each 1 sec. interval ends. X`009`009`009`009`009`009;R0-R4 are available X X;--- X; See if we are supposed to abort X;--- X`009TSTB`009STOPPING X`009BEQL`00910$ X.IF`009NDF,VMS_V5 X`009MOVAL`009@#EXE$AL_TQENOREPT,R5 X.IFF X`009MOVL`009G`094EXE$AR_TQENOREPT,R5 X.ENDC X`009CLRB`009RUNNING X`009RSB X10$: X X`009CLRL`009R2`009`009`009`009;Initialize the run count=0 X;+ --- X; Check the run queues for the number of processes in 'COM' state X;- --- X`009CLRL`009R1`009`009`009`009;Initialize summary word pos=0 X X.IF`009NDF,VMS_V5 X`009DSBINT`009#IPL$_SYNCH X.IFF X`009LOCK`009SCHED X.ENDC X`009BICL3`009PRIORITY_MASK,@#SCH$GL_Comqs,R4 ;Get masked queues XFind_Comq:`009`009`009`009`009;Find a run queue with entries X`009SUBL3`009R1,#32,R0`009`009`009;Calculate #bits left (32-R1) X`009FFS`009R1,R0,R4,R1`009`009`009;Look for an active queue X`009BEQL`009Ncomo`009`009`009`009;None: look for procs waiting X`009`009`009`009`009`009;`009for inswap X`009MOVAQ`009@#SCH$AQ_Comh[R1],R0`009`009;Got one: get its queue header X`009MOVL`009R0,R3`009`009`009`009;Save terminating address X X10$:`009MOVL`009(R0),R0`009`009`009`009;Get the next process in the q X`009CMPL`009R0,R3`009`009`009`009;End of queue? X`009BEQL`00920$`009`009`009`009;Yes: look for next queue X`009INCL`009R2`009`009`009`009;No: count this process X`009BRB`00910$`009`009`009`009;and try the next one X X20$:`009INCL`009R1`009`009`009`009;Queue done, look for next one X`009BRB`009Find_Comq X X;+ --- X; Check the COMO queues for the number of processes in 'COM' state (outswap) X;- --- XNcomo:`009CLRL`009R1`009`009`009`009;Initialize summary word pos=0 X`009BICL3`009PRIORITY_MASK,@#SCH$GL_Comoqs,R4 ;Get masked queues XFind_Comoq:`009`009`009`009`009;Find a run queue with entries X`009SUBL3`009R1,#32,R0`009`009`009;Calculate #bits left (32-R1) X`009FFS`009R1,R0,R4,R1`009`009`009;Look for an active queue X`009BEQL`009Nwait`009`009`009`009;None: look for procs waiting X`009`009`009`009`009`009;`009for resources X`009MOVAQ`009@#SCH$AQ_Comoh[R1],R0`009`009;Got one: get its queue header X`009MOVL`009R0,R3`009`009`009`009;Save terminating address X X10$:`009MOVL`009(R0),R0`009`009`009`009;Get the next process in the q X`009CMPL`009R0,R3`009`009`009`009;End of queue? X`009BEQL`00920$`009`009`009`009;Yes: look for next queue X`009INCL`009R2`009`009`009`009;No: count this process X`009BRB`00910$`009`009`009`009;and try the next one X X20$:`009INCL`009R1`009`009`009`009;Queue done, look for next one X`009BRB`009Find_Comoq X X;+ --- X; Count processes in collided page wait X;- --- XNwait: X`009MOVAQ`009@#SCH$GQ_Colpgwq,R0`009`009;Get the collided page wait q X X10$:`009MOVL`009(R0),R0`009`009`009`009;Get the next process in the q X`009CMPL`009R0,#SCH$GQ_Colpgwq`009`009;End of queue? X`009BEQL`009Npfw`009`009`009`009;Yes: look at page fault waits X`009INCL`009R2`009`009`009`009;No: count this process X`009BRB`00910$`009`009`009`009;and try the next one X X;+ --- X; Count processes in page fault wait X;- --- XNpfw: X`009MOVAQ`009@#SCH$GQ_PfwQ,R0`009`009;Get the page fault wait q X X10$:`009MOVL`009(R0),R0`009`009`009`009;Get the next process in the q X`009CMPL`009R0,#SCH$GQ_Pfwq`009`009`009;End of queue? X`009BEQL`009Nfpw`009`009`009`009;Yes: look at free page waits X`009INCL`009R2`009`009`009`009;No: count this process X`009BRB`00910$`009`009`009`009;and try the next one X X;+ --- X; Count processes in free page wait X;- --- XNfpw: X`009MOVAQ`009@#SCH$GQ_FpgwQ,R0`009`009;Get the free page wait q X X10$:`009MOVL`009(R0),R0`009`009`009`009;Get the next process in the q X`009CMPL`009R0,#SCH$GQ_Fpgwq`009`009;End of queue? X`009BEQL`009Count_Done`009`009`009;Yes: all done X`009INCL`009R2`009`009`009`009;No: count this process X`009BRB`00910$`009`009`009`009;and try the next one X XCount_Done: X.IF`009NDF,VMS_V5 X`009ENBINT X.IFF X`009UNLOCK`009SCHED X.ENDC X.IF`009NDF,VMS_V5 X; X; Adjust for the NULL job, which might be missing if the mask X; exclused the priority 0 queue. X; X`009BBS`009#31,PRIORITY_MASK,10$ X`009DECL`009R2`009`009`009`009; SUBTRACT THE NULL JOB X10$: X; X; Adjust for the CURrent job if the priority is high enough. X; X`009BBS`009@#SCH$GB_PRI,PRIORITY_MASK,20$ X`009INCL`009R2`009`009`009`009; ADD THE CURRENT JOB X20$: X.ENDC X.IF`009NDF,VMS_V5 X`009MOVL`009G`094SCH$GB_PRI,R3 X.IFF X; X; Count done, now scan through each CPU. Store the LOWEST priority X; found and the number of executing CPUs. X; X`009CLRL`009R3`009`009`009`009; Lowest priority X`009CLRL`009R4`009`009`009`009; Number of CPUs active X`009CLRL`009R1`009`009`009`009; Current CPU index X11$:`009SUBL3`009R1,#32,R0`009`009`009;Calculate #bits left (32-R1) X`009FFS`009R1,R0,@#SMP$GL_ACTIVE_CPUS,R1`009;Look for an active CPU X`009BEQL`00935$ X15$:`009MOVL`009g`094SMP$GL_CPU_DATA[R1],R0`009; Get the CPU database X`009BEQL`00930$`009`009`009`009; This CPU doesn't exist X`009INCL`009R4`009`009`009`009; Seen one more CPU X`009CMPL`009CPU$L_CURPCB(R0),@#SCH$AR_NULLPCB ; Running 'the null job'? X`009BEQL`00920$ X`009MOVZBL`009CPU$B_CUR_PRI(R0),-(SP) X`009BBS`009(SP)+,PRIORITY_MASK,20$`009`009; but priority is 'masked' X`009INCL`009R2`009`009`009`009; CPU is active. V20$:`009CMPB`009CPU$B_CUR_PRI(R0),R3`009`009; Store the lowest executing priori Xty X`009BLEQU`00930$ X`009MOVB`009CPU$B_CUR_PRI(R0),R3 X30$:`009INCL`009R1 X`009BRB`00911$`009`009`009`009; Check for all 32 CPUs. X35$: X.ENDC X X;+ --- X; Count done, calculate the load ave. X;- --- X`009CVTLF`009R2,R2`009`009`009`009;Float the count of processes X.IF`009DF,VMS_V5 X`009CVTLF`009R4,R4`009`009`009`009;Float the count of CPUs X`009DIVF2`009R4,R2`009`009`009`009;Load is Processes/CPUs. X.ENDC X; 1 min. load ave. X`009MULF3`009R2,F11,R0`009`009`009;La=OldLa*F+NewLa*(1-F) X`009MULF3`009M1,F1,R1 X`009ADDF3`009R0,R1,M1 X; 5 min. load ave. X`009MULF3`009R2,F12,R0 X`009MULF3`009M5,F2,R1 X`009ADDF3`009R0,R1,M5 X; 15 min. load ave. X`009MULF3`009R2,F13,R0 X`009MULF3`009M15,F3,R1 X`009ADDF3`009R0,R1,M15 X X`009CMPB`009R3,#255`009`009`009`009; NULL JOB X`009BNEQ`00940$ X`009MOVB`009#32,R3`009`009`009`009; TREAT AS PRIORITY 0 X40$:`009SUBB3`009R3,#32,R2 X`009CVTBF`009R2,R2 X; 1 min. priority ave. X`009MULF3`009R2,F11,R0`009`009`009;La=OldLa*F+NewLa*(1-F) X`009MULF3`009P1,F1,R1 X`009ADDF3`009R0,R1,P1 X; 5 min. priority ave. X`009MULF3`009R2,F12,R0 X`009MULF3`009P5,F2,R1 X`009ADDF3`009R0,R1,P5 X; 15 min. priority ave. X`009MULF3`009R2,F13,R0 X`009MULF3`009P15,F3,R1 X`009ADDF3`009R0,R1,P15 X X; X; Update the disk queue length information X; XQUEUE_LEN: X`009CLRL`009R2`009`009`009`009; R2 = Sun of queue lengths X`009PUSHR`009#`094M X`009CLRQ`009R10`009`009`009`009; Indicate start of I/O d.b. V10$:`009JSB`009G`094IOC$SCAN_IODB`009`009`009; Get next DDB and UCB into R10, R X11 X`009BLBC`009R0,40$`009`009`009`009; no more, done X`009CMPB`009#DC$_DISK,UCB$B_DEVCLASS(R10)`009; Is this controller a disk? X`009BEQL`00920$ X`009CLRL`009R10`009`009`009`009; Go for next controller X`009BRB`00910$ X20$:`009BBC`009#DEV$V_MNT,UCB$L_DEVCHAR(R10),10$ ; Skip if not mounted V`009BITL`009#,UCB$L_DEVCHAR2(R10) ; class driver or shadow X set member? X`009BNEQ`00910$`009`009`009`009;`009 if so, skip X`009CVTWL`009UCB$W_QLEN(R10),R0`009`009; Get queue length X`009BGEQ`00930$ X`009CLRL`009R0`009`009`009`009; negative queu length is a temporary condition X30$:`009CMPL`009R0,R2 X`009BLEQ`00910$ X`009MOVL`009R0,R2 X`009BRB`00910$ X40$:`009POPR`009#`094M`009`009`009; restore registers X X`009CVTLF`009R2,R2`009`009`009`009;Float the count X; 1 min. queue ave. X`009MULF3`009R2,F11,R0`009`009`009;La=OldLa*F+NewLa*(1-F) X`009MULF3`009Q1,F1,R1 X`009ADDF3`009R0,R1,Q1 X; 5 min. queue ave. X`009MULF3`009R2,F12,R0 X`009MULF3`009Q5,F2,R1 X`009ADDF3`009R0,R1,Q5 X; 15 min. queue ave. X`009MULF3`009R2,F13,R0 X`009MULF3`009Q15,F3,R1 X`009ADDF3`009R0,R1,Q15 X`009RSB`009`009`009`009`009;Return to clock dispatcher X X X`012 X`009.SBTTL`009END OF DRIVER X;+ --- X; X; END OF DRIVER X; X;- --- XLAV_END:`009`009`009`009`009;LET DPTAB KNOW THE DRIVER SIZE X; X`009.END $ GOSUB UNPACK_FILE $ EXIT /* ---------- */