.TITLE SYSLOG .IDENT /04.2/ ;+ ; SYSLOG is an accounting system for RSX11M which is aimed at providing ; the user with statistics useful for measuring the system performance and ; activity. While it is not aimed at providing charge back accounting the ; amount of information provided in the accounting log files is adequate ; for writing a simple charge back system. ; ; SYSLOG imposes absolutely no executive overhead unless it is running. ; The system is totally compatable with a real time RSX11M environment. ; All features are individually selectable. The user can opt for minimum ; configurations in which little is monitored, and very little logfile ; space is used daily, or he/she can opt for the full version in which ; everyting is monitored, and the log files become larger. SYSLOG can be ; dynamically reconfigured on line, turning on and off various features. ; Again when accounting is stopped, system overhead for accounting becomes ; zero. The origional idea for how to do this was presented to the RSX11M ; comunity by Greg Basset several years ago. The idea is to insert into ; the Exec hooks which branch into sections of PIC code located in a node ; in POOL. ; ; For example, the instruction at 'UPTIM' is replaced by A 'JSR' to a ; section of PIC code in POOL which extends the functionality of the normal ; clock isr. The PIC code executes and then 'returns' back into the exec. ; When SYSLOG exits, the origional contents of the exec(earlier replaced ; by JSR'S) are restored. This section of PIC code keeps track of how many ; tics of clock time have gone by for the null and user tasks. The total null ; time measures how loaded down the system is. If the system is not executing ; the null task during a clock tick, the current task UCB is found from ; $TKTCB, and one tic is added to U.TIC1 of the appropriate termianal UCB. ; In this fashion each clock tick is owned by someone, either the null task, ; a terminal, CO: or VT:. ; ; It must be emphasized that this task will absolutely crash the ; system unless the appropriate data structure changes have been made to ; the TT: driver tables, the CO: driver tables, and the VT: driver tables. ; The CO: driver must have been selected as a SYSGEN option. But it need ; not be used as long as CO: has a complete UCB. ; ; At this point some mention should be made as regards the decission to ; intercept each clock tic. The other choice for CPU time accounting was ; to intercept each context switch, add in the time to the UCB word belonging ; to the task being left, check to see if we had to account for a zero time ; tic in that UCB, and then insert the current time in the UCB of the next ; task to be executed. This was decided against for several reasons. First ; the experience of the RSX11M-PLUS accounting scheme indicated that for a ; 60hz to 100 hz clock, there were a large number of context switches ; with no measureable time. They then also had to account for 'zero tics'. ; this added code to the accounting routine. Also if there are a number ; of context switches between tics, the total amount of code which must ; be executed by grabbing each context switch, is considerably larger than ; simply assinging each tic to its last owner. Granting that the M-PLUS ; method is more accurate, it was desired to opt for the simpler method ; which likely would be as low or lower in overhead as the other. Also ; unless SYSLOG is started the overhead is zero, which makes it ideal for ; an RSX11M system. ; ; Following creating the PIC code node, SYSLOG will periodically send data ; to a task LOG... The rate at which data is sent from SYSLOG is variable ; and can be reset externally. because time is accumulated as tics in a two ; word array, SYSLOG can run for a maximum of about 5.9 hours counting null ; time before overflow will wipe out the data. This requires that every so ; often SYSLOG unstop itself and send out its data. The initial send time is ; set to be thirty min. ; ; SYSLOG stores the pool address in which the PIC code node is storred in ; $ACCHD. If SYSLOG is requested to run(via acc) and it sees that there is ; a value (other than zero) in $ACCHD, it assumes that some klutz has aborted ; SYSLOG. Now aborting SYSLOG won't crash a system, but one wouldn't want ; the code node to be lost. So if a non zero address exists, SYSLOG uses ; that address, inserts it into CODNOD, skips the initial loading of the ; code node, and is ready to accept a command to do anything it would ; ordinarily do. ; ; SYSLOG is designed to be assembled in two versions, with and without ; support for selective task logging. TSKLOG is a task designed to run ; in conjunction with SYSLOG to provide a performance measurement tool. ; At this time 'TSKLOG' only will account for CPU time and QIO's for a ; list of tasks typed in when it starts up. 'TSKLOG' uses SYSLOG to ; provide it with information on tics the task runs for, its CPU time and ; its total QIO count. The ambitious user could (i suppose) add memory used ; with yet even more overhead. 'TSKLOG' requires more exec intercept code than ; does 'SYSLOG' assembled for no task accounting. When assembled for task ; accounting(if assembled with T$$ACC defined), SYSLOG adds code to ; keep track of the time at which a task is in core, and to add QIO's into ; the task accounting block. In addition additional exec intercept code ; resides in 'TSKLOG' to intercept context switches and task starts and stops. ; Also at each QIO completion, the intercept code in 'SYSLOG' must scan ; a linked list of task account blocks to find the right one and add in a QIO. ; The overhead for a lot of tasks is substantial. ; ; Anyway, forwarned is forarmed. The interested user can assemble SYSLOG to ; support a performance measurement type of task accounting. ; J. G. DOWNWARD 23-MAY-79 ; ; Modified by: ; J. Downward 14-FEB-81 ; JGD1 Send off a type 0 packet (DAYFLAG) ; with the full time buffer if the day should ; change while SYSLOG is running. In this ; way SYSLOG.DAT will get a new full time ; packet at least once a day within 1 ; minute of midnight. Send off start status ; information with start packet. ; ; JGD2 In the time interrupt routine, increment a ; counter in SYSCM ($UTLIZ) each time the ; CPU is active (ie not a null tic). RMDEMO ; can use this value to provide a real-time ; display of the %system-utilization provided ; RMDEMO's cycle time is not >32000 tics. ; ; JGD3 Output startup/exit type messages to CO: ; RSX11M V4.0 compatable. ; ; JGD4 Fix bug in $IOFIN intercept address ; 19-Sep-1982 would crash systems with XDT ; ; JGD5 Save "JSR PC" before branch ; 09-Mar-1983 ; ; ; CPU USAGE LOGGING TASK ; ; ASSEMBLY PARAMETERS ; ; THE SYMBOL "$$CVEC" SHOULD BE SET EQUAL TO THE SYSTEM CLOCK ; INTERRUPT VECTOR ADDRESS. THE STANDARD ADDRESSES FOLLOW: ; ; KW-11L LINE FREQUENCY CLOCK 100 ; KW-11P PROGRAMMABLE CLOCK 104 ; ; FOR EASE OF USE, THE VALUES OF $$CVEC MAY BE SET AT TASKBUILD VIA GLOBAL DEFINITIONS. ;$$CVEC=104 ; STANDARD PROGRAMABLE CLOCK ;T$$ACC=1 ; DEFINE TO INCLUDE TASK ACCOUNTING ABILITY .IF DF T$$KAC ; IF TASK ACCOUNTING SELECTED AT SYSGEN T$$ACC=1 ; Define to include task accounting ability .ENDC ; .END T$$KAC .MCALL QIOW$C,QIOW$,DIR$,EXIT$S,GTIM$ .MCALL PKTDF$,TCBDF$,HWDDF$,UCBDF$ .MCALL RCST$,ASTX$,MRKT$,USTP$,CMKT$S,SDAT$,RQST$ .MCALL RCVD$,ENAR$S,DSAR$S PKTDF$ ; DEFINE PACKET OFFSETS TCBDF$ ; DEFINE TCB OFFSETS HWDDF$ ; DEFINE HARDWARE UCBDF$ ; DEFINE UCB OFFSETS ; DEFINE THE LOCATIONS IN THE MODIFIED UCB WHICH ARE ; USED FOR STORING CPU TIME(IN TICS) .IFDF M$$CLI!A$$CLI ; IF MULTIPLE CLI SUPPORT(DECNET) BASE=U.CLI ; U.CLI IS TOP OF UCB .IFF ; IF SINGLE CLI(MCR) SUPPORT ONLY BASE=U.LUIC ; THEN U.LUIC IS TOP OF UCB .ENDC ; .END M$$CLI .IF DF M$$MUP ; MULTI USER PROTECTION IS REQUIRED FOR NOW U.QIO1=BASE-22 ; TERMINAL UCB QIO COUNTER(LOW VAL) U.QIO2=BASE-20 ; TERMINAL UCB QIO COUNTER(HIGH VAL) U.ATCT=BASE-16 ; NUMBER OF TIMES ...AT. ACTIVATED U.INCT=BASE-14 ; NUMBER OF INSTALL REQUESTS U.RNCT=BASE-12 ; NUMBER OF TASKS RUN VIA MCR U.TIC1=BASE-10 ; LOW ORDER TIC COUNT IN UCB U.TIC2=BASE-6 ; HIGH ORDER TIC COUNT IN UCB .ENDC ; ; TASK ACCOUNTING BLOCK OFFSETS ; IF TASK ACCOUNTING ENABLED U.TKAC CONTAINS THE ADDRESS OF THE ; ACCOUNTING PACKET ; .IF DF T$$ACC ; IF TASK ACCOUNTING DEFINED B.CPU1=12 ; CPU TIME LOVAL B.CPU2=10 ; CPU TIME HIVAL B.QIO2=20 ; QIO COUNT HIVAL B.QIO1=22 ; QIO COUNT LOVAL ; .ENDC ; .END T$$ACC .IF NDF M$$MUP ; IF NOT DEFINED WE WON'T ASSEMBLE .ERROR ; THE TERMINAL DATA BASE OFFSETS ASSUME MULTI USER PROTECTION .ENDC ; END M$$MUP ;DEBUG=1 ; IF DEFINED BYPASS INSERTION OF CODE NODE AND ; CLEANUP OF NODE ON EXIT ; ; LIMITS ; DEFINE LIMITS FOR THE SIZE(IN K WORDS) OF FILES IN CORE OR OUT OF CORE ; (ON CHECKPOINT FILE) AT WHICH POINT THE SYSTEM WILL SEND OFF A MEMORY ; USE ACCOUNTING PACKET TO LOG... INVAL=75. ; LOG STATISTICS IF MORE THAN 75. K WORDS IN CORE OUTVAL=10. ; LOG STATISTICS IF MORE THAN 10.K WORDS ON CHECK POINT FILES SIZINN: .WORD INVAL ; LOG STATISTICS FENCE SIZOUT: .WORD OUTVAL ; LOG STATISTICS FENCE ; ; ACCOUNTING DATA STRUCTURES MINDAY=24.*60. ; MINUTES/DAY ; JGD1 CODNOD: .WORD 0 ; CODE NODE ADDRESS IOST: .BLKW 2 ; I/O STATUS BLOCK RECTYP: .WORD 0 ; SEND DATA RECORD TYPE STRTTM: .BLKW 13. ; START TIME AND SEND BUFFER TIM: .BLKW 8. ; TIME BUFFER OLDDAY: .WORD 0 ; HERE WE KEEP THE OLD DAY VALUE ERROR: .WORD 0 ; ALLOCATION FAILURE ERROR SWITCH .IF DF M$$ACC ; MEMORY UTILIZATION ACCOUNTING POOL: .BLKW 3 ; STORAGE FOR POOL USAGE CALCULATIONS TKSIN: .WORD 0 ; # OF TASKS IN CORE SIZIN: .WORD 0 ; MEMORY USED(nnK) BY IN CORE TASKS TKSOT: .WORD 0 ; # OF TASKS CHECKPOINTED SIZOT: .WORD 0 ; MEMORY USED(nnK) BY TASKS CHECKPOINTED PLTOT: .WORD 0 ; TOTAL FREE POOL PLLRG: .WORD 0 ; LARGEST FREE CHUNK OF POOL PLFRG: .WORD 0 ; # OF FRAGMENTS POOL BROKEN INTO .ENDC ; END CONDITIONAL - M$$ACC ; SEND DATA BUFFERS ; TYPICAL SEND DATA STRUCTURES ; START RECORD : 1,HR,MIN,SEC,YR,MO,DA,TICS,TICS/SEC,DELTA,STATUS,,$ACMSK ; STATUS ; BIT 0 STARTUP AFTER CRASH ; BIT 1-15 -UNDEFINED WILL TELL SYSTEM CONFIGURATION LATER ; NEWDAY RECORD : 0,HR,MIN,SEC,YR,MO,DA,TICS,TICS/SEC,DELTA,,,$ACMSK ; STOP RECORD :-1,HR,MIN,SEC,YR,MO,DA,TICS,TICS/SEC,DELTA,TSKNM1,TSKNM2,$ACMSK ; TASKS : 2,HR,MIN,SEC,NULLOW,NULHI,SHFCT,CKPCT,LDRCT,QIOCT,QIOCT2,RUNCT,DIRCT ; MEMORY RECORD : 3,HR,MIN,SEC,# TSKS,MEM USED,#CKP TSK,MEM USED,TOT POOL,LRG FRAG,# FRAG, ; # PKTS NOT SENT ; CHANGE DELTA : 4,HR,MIN,SEC,YR,MO,DA,TICS,TICS/SEC,DELTA,TSKNM1,TSKNM2,$ACMSK ; HELLO PACKET : 5,HR,MIN,SEC,TERMINAL ID,ACNT #,BIN UIC, # ILLEGAL LOGIN ATTEMPTS ; BYE PACKET ; 6,HR,MIN,SEC,TERMINAL ID,ACNT #,BIN UIC, CPU TIME1,CPU TIME2, QIOCNT1,QIOCNT2 ; UPD PACKET ; 7,HR,MIN,SEC,ACNT #,SYSTEM DEVICE(4 ASCII BYTES), FILES USED, BLOCKS USED, BLOCKS ALLOCATED ; CO0: PACKET ; 8,HR,MIN,SEC,0,0,0,CPU TIME1,CPU TIME2,QIOCNT1,QIOCNT2 ; TASK ACC PKT ; 9,HR,MIN,SEC,ACNT #,RUN TIME1,RUN TIME2,CPU TIME1,CPU TIME2,QIOCNT1,QIOCNT2 GETTIM: GTIM$ STRTTM ; GET TIME GTTIM: GTIM$ TIM ; .MACRO PRINT STRING MOV #STRING,R0 CALL PNTLIN .ENDM PRINT ; ; DIRECTIVE DPB'S ; RCVSTP: RCST$ ...ACC,BUF ; RECEIVE DATA OR STOP BUF: .BLKW 15. ; FOR RECIEVE DATA RECV: RCVD$ ...ACC,BUF ; RECEIVE DATA AS PART OF INITIAL STARTUP MARKTM: MRKT$ ,1,3,MRKAST ; MARK TIME FOR 1 MINUTE ASTEXT: ASTX$ ; AST EXIT UNSTOP: USTP$ SYSLOG ; UNSTOP OURSELVES UNSTP: USTP$ ...ACC ; UNSTOP ...ACC WHICH IS WAITING FOR DELTA VALUE RQSTLG: RQST$ LOG... ; REQUEST LOG... USTPLG: USTP$ LOG... ; UNSTOP LOGGING TASK SENDAT: SDAT$ LOG...,RECTYP ; SEND DATA TO LOG... AGAIN: .WORD 0 ; FLAG, IF=1, WRITE OUT STATISTICS AND GO BACK ; FOR MORE. OUTFLG: .WORD 0 ; IF SET EXIT FROM SENDIT INSTEAD OF LOOPING CPUON: .WORD 0 ; IF =0, CPU TIME LOGGING NEVER STARTED. ; IF =1, CPU TIME LOGGING GOING ON, MUST REMOVE CODE NODE DOIT: .WORD 0 ; FLAG TO SAY DO POOL USAGE REGARDLESS NOPOOL: .WORD 0 ; NUMBER OF TIMES DATA PACKET NOT SENT BECAUSE OURUCB: .WORD 0 ; UCB OF TI: AT STARTUP ; TOTAL POOL .LE. 200. WORDS .EVEN .SBTTL TEXT MESSAGES .ENABL LC QIODPB: QIOW$ IO.WVB,2,2,,,,<0,0,0> .NLIST BEX M1: .ASCIZ <15><12>/SYSLOG -- Task LOG... not installed/<15><12> M2: .ASCIZ <15><12>/SYSLOG -- Send data failure to LOG.../<15><12> M3: .ASCIZ <15><12>/SYSLOG -- Illegal sampling interval requested/<15><12> M4: .ASCIZ <15><12>/SYSLOG -- Starting CPU time accounting/<15><12> M5: .ASCIZ <15><12>/SYSLOG -- LOG... not active/<15><12> M6: .ASCIZ <15><12>/SYSLOG -- GTIM$ failed/<15><12> M7: .ASCIZ <15><12>/SYSLOG -- Accounting feature mask not set - Exiting/<15><12> M8: .ASCIZ <15><12>/SYSLOG -- Terminal not privleged - Exiting/<15><12> M10: .ASCIZ <15><12>/SYSLOG -- Memory allocation failure/<15><12> M12: .ASCIZ <15><12>/SYSLOG -- Exiting/<15><12>/>/ M13: .ASCIZ <15><12>/SYSLOG -- SYSLOG may only be started using ...ACC/ M14: .ASCIZ <15><12>/SYSLOG -- Node header active, Skipping initialization/<15><12> .EVEN .LIST BEX ; ; BIT DEFINITIONS OF THE SYSTEM ACCOUNTING MASK, $ACMSK ; ; BIT 0 1 CPU TIME ACCOUNTING IS DESIRED/ENABLED(USER AND NULL TIME)) ; BIT 1 2 SYSTEM WIDE QIO ACCOUNTING DESIRED/ENABLED ; BIT 2 4 DIRECTIVE ACCOUNTING DESIRED/ENABLED ; BIT 3 10 LOG GLOBAL CPU USAGE STATISTICS AT SPECIFIED INTERVALS ; BIT 4 20 LOG MEMORY USAGE STATISTICS AT SPECIFIED INTERVALS ; BIT 5 40 LOG MEMORY STATISTICS IF CORE FULL/FILES CHECKPOINTED ; BIT 6 100 LOG MEMORY STATISTICS IF POOL LOW OR TWO FRAGMENTED ; BIT 7 200 LOG HELLO AND BYE SIGN ON/OFF TIMES AND TERMINALS ; BIT 8 400 LOG CO0: STATISTICS(CPU LOGGING MUST BE ONGOING ALSO) ; BIT 9 1000 USER BY USER QIO ACCOUNTING ENABLED AS WELL AS SYSTEM WIDE QIO ACCOUNTING ; BIT 10 2000 SELECTIVE TASK ACCOUNTING ENABLED ; BIT 11 4000 TASK ACCOUNTING STATISTICS PRINTED ON TI: ; BIT 12 10000 ENABLE LOGGING OF PRINT SPOOLER USAGE ; BIT 13-15 UNDEFINED .SBTTL ACCOUNTING INITALIZATION CODE .ENABL LSB START: ; REF. LABEL DIR$ #RECV ; SEE IF ...ACC IS KICKING US OFF CMP #IS.SUC,$DSW ; DO WE HAVE DATA BNE NORUN ; IF NE 0, NO MOV BUF+6,$DELTA ; GET NEW $DELTA TIME TST $ACMSK ; IS ACCOUNTING ACTIVATED BNE 1$ ; IF NE, YES PRINT M7 ; ELSE PRINT WARNING MESSAGE TO TI: EXIT$S ; AND EXIT NORUN: PRINT M13 ; THIS TASK MAY NOT BE RUN EXIT$S ; SO EXIT 1$: ; MOV $TKTCB,R0 ; GET OURTASK HEADER MOV T.UCB(R0),R0 ; GET UCB ADDRESS MOV R0,OURUCB ; SAVE UCB ADDRESS FOR LATER BIT #U2.PRV,U.CW2(R0) ; IS TERMINAL PRIVLEGED BNE 2$ ; IF NE, YES PRINT M8 ; WARN USER HE'S NOT PRIVLEGED EXIT$S ; AND EXIT 2$: ; CALL ZERO ; ZERO GLOBAL VARIABLES IN SYSCM MOV $DELTA,MARKTM+M.KTMG ; SET THE MARKTIME MAGNITUDE DIR$ #RQSTLG ; REQUEST LOG... TO START LOGGING BCC 5$ ; CONTINUE IF ALL OK CMP $DSW,#IE.INS ; IS TASK NOT INSTALLED BNE 5$ ; ALREADY ACTIVE PRINT M1 ; WARN USER LOG... NOT INSTALLED JMP XIT ; AND EXIT 5$: CLR ERROR ; CLEAR ERROR INDICATOR DIR$ #GTTIM ; GET THE TIME CALL LODSND ; LOAD THE SEND BUFFER WITH BACKWARDS TIME MOV TIM+4,OLDDAY ; SAVE THE DAY WE STARTED MOV #1,RECTYP ; SET RECORD TYPE MOV $DELTA,STRTTM+20; SHOW INITIAL SCHEDULE INTERVAL MOV BUF+10,STRTTM+22; SET START STATUS FLAG DIR$ #SENDAT ; SEND DATA TO LOG... BCC 10$ ; ALL OK PRINT M2 ; WARN USER , SEND DATA ERROR TO LOG... JMP XIT ; AND EXIT 10$: ; REF LABLE DIR$ #USTPLG ; UNSTOP THE LOGGING TASK 15$: ; REF LABLE .IF DF DEBUG JMP REPEAT ; SKIP INSERTION OF CODE .ENDC INC CPUON ; SET SO WE CAN LATER KNOW IT IS ON GOING BIS #1,$ACMSK ; AND BE SURE TO SET BIT IN $ACMSK 35$: ; REF. LABEL TST $ACCHD ; IS A CODE NODE ALREADY PRESENT BEQ 37$ ; IF EQ, NO, PROCEED AS ALWAYS MOV $ACCHD,CODNOD ; IT IS, SOME KLUTZ MUST HAVE ABORTED US PRINT M14 ; WARN USER WHAT WE ARE DOING JMP REPEAT ; SKIP PUTTING IN A NEW NODE 37$: ; REF LABLE .IF NDF M$$MGE MOV #CODST,CODNOD ; ASSUME NO NODE NEEDED MOV $TKTCB,R5 ; GET OUR TCB ADDRESS BIT #T2.CHK,T.ST2(R5) ; ARE WE CHECKPOINTABLE? BNE 50$ ; IF NO, THEN NO NODE NEEDED .ENDC ; NDF M$$MGE CLR CODNOD ; CLEAR FLAG TO STORE CODE NODE MOV #CODSIZ,R1 ; GET CODE SIZE CALL $SWSTK,50$ ; SWITCH TO SYSTEM STATE CALL $ALOCB ;; ALLOCATE THE CODE BLOCK FROM DSR BCS 40$ ;; IF UNABLE THEN LEAVE FLAG 0 MOV R0,CODNOD ;; STORE NODE ADDRESS 40$: RETURN ;; EXIT FROM SYSTEM STATE 50$: MOV CODNOD,R5 ; GET NODE ADDRESS MOV R5,$ACCHD ; LETS KEEP ADRESS FOR POSTERITY MOV R5,R0 ; COPY TO R0 BNE 55$ ; IF .NE. 0 THEN WE GOT ONE INC ERROR ; INDICATE AN ERROR HAS OCCURRED JMP ALFAIL ; ALLOCATION FAILURE 55$: MOV #CODSIZ,R1 ; GET TOTAL CODE SIZE INC R1 ; ROUND UP TO WORD BOUNDARY ASR R1 ; CONVERT TO WORDS MOV #CODST,R2 ; GET CODE STARTING ADDRESS 60$: MOV (R2)+,(R0)+ ; STORE WORD DEC R1 ; ANY MORE TO TRANSFER? BNE 60$ ; YES. CONTINUE ; ; SET UP CLOCK INTERRUPT VECTOR ; BIT #1,$ACMSK ; IS CPU TIME ACCOUNTING DESIRED MOV #4737,R4 ; STORE A "JSR PC" FOR LATER ; JGD5 BEQ 63$ ; IF EQ 0, NO, DON'T INSERT TIME HOOKS ; JGD5 MOV #KADDTB,R1 ;;; GET ADDRESS OF CLOCK INTERCEPT TABLE ;**-2 MOV #KOFFTB,R2 ;;; GET CLOCK NODE OFFSET TABLE ADDRESS CALL $SWSTK,62$ ;;; SWITCH TO SYSTEM STATE BIS #340,PS ;;; RAISE PRIORITY TO 7 MOV (R1),R0 ;;; GET INTERCEPT ADDRESS MOV R4,(R0)+ ;;; INSERT A "JSR PC" MOV R5,(R0) ;;; GET NODE ADDRESS ADD (R2),(R0) ;;; ADD IN INTERCEPT NODE OFFSET CLRB PS ;;; DROP PRIORITY TO ZERO RETURN ;;; BACK TO USER STATE 62$: ; REF LABLE 63$: ; REF LABLE ; ; SET UP QIO COUNTING IF DESIRED ; BIT #2,$ACMSK ; HAS QIO COUNTING BEEN SELECTED BEQ 65$ ; IF EQ 0, NO, DON'T INSERT INTERCEPTS MOV #QADDTB,R1 ; GET ADDRESS OF QIO INTERCEPT TABLI MOV #QOFFTB,R2 ; GET QIO NODE OFFSET TABLE ADDRESS CALL $SWSTK,65$ ; SWITCH TO SYSTEM STATE MOV (R1),R0 ;; GET INTERCEPT ADDRESS MOV R4,(R0)+ ;; INSERT A "JSR PC" INTO EXEC MOV R5,(R0) ;; PLACE NODE ADDRESS IN THERE ADD (R2),(R0) ;; ADD INTERCEPT NODE OFFSET RETURN ;; ALL DONE 65$: ; REF LABLE ; ; SET UP GLOBAL STATISTICS COUNTING HOOKS INTO EXEC ; 1. FOR CHECKPOINT REQUESTS ; 2. FOR SHUFFLER RUN REQUESTS ; 3. FOR LDR RUN REQUESTS ; 4. FOR GENERAL RUN REQUESTS BIT #10,$ACMSK ; HAS GLOBAL STATISTICS COUNTING BEEN ENABLED BEQ 70$ ; IF EQ 0 , NO MOV #CADDTB,R1 ; GET ADDRESS OF CHECKPOINT REQUEST INTERCEPT TABLE MOV #COFFTB,R2 ; GET ADDRESS OF CKP NODE OFFSET TABLE CALL INSERT ; INSERT HOOKS INTO EXEC MOV #SADDTB,R1 ; GET ADDRESS OF SHF REQUEST INTERCEPT TABLE MOV #SOFFTB,R2 ; GET ADDRESS OF SHF NODE OFFSET TABLE CALL INSERT ; INSERT HOOKS INTO EXEC MOV #RADDTB,R1 ; GET ADDRESS OF RUN REQUEST INTERCEPT TABLE MOV #ROFFTB,R2 ; GET ADDRESS OF RUN REQUEST NODE OFFSET TABLE CALL INSERT ; INSERT RUN REQUEST HOOK INTO EXEC 70$: BR 80$ ;; REF LABLE ;+ ; INSERT -- INSERT HOOKS INTO EXEC WHERE A 3 WORD INSTRUCTION USED TO BE ;- INSERT: CALL $SWSTK,75$ ; SWITCH TO THE SYSTEM STATE MOV (R1),R0 ;; GET INTERCEPT ADDRESS MOV R4,(R0)+ ;; INSERT "JSR PC" THERE MOV R5,(R0) ;; PLACE NODE ADDRESS IN THERE ADD (R2),(R0)+ ;; ADD INTECEPT NODE OFFSET MOV #240,(R0) ;; INSERT A NOP(REPLACING A 3 WORD INSTRUCTION) RETURN ;; ALL DONE, BACK TO USER STATE 75$: RETURN ; BACK TO CALLER .PAGE .SBTTL PERIODICALLY SEND OUT DATA ; ; NOW WE STOP AND WAIT UNTIL ASKED TO EXIT BY ACC. ; ALSO WE WILL MARK TIME. WHEN THE MARK TIME IS SATISFIED ; WE WILL ALSO SEND OFF ALL SYSTEM STATISTICS AND REZERO ; THE COUNTER LOCTIONS IN SYSCM. SINCE THE ELLAPSED NULL TIME ; IS REPRESENTED AS AN INTEGER DOUBLE WORD, AND ASSUMING 100% ; NULL TIME, THE LONGEST APART FOR SENDING OFF DATA IS ABOUT ; 5.9 HOURS(IF USING A 100 TPS PROGRAMABLE CLOCK). IF THE TIME WENT ; BEYOND THIS, OVERFLOW MAY OCCUR WHICH CAUSES DATA TO BE LOST. ; NORMALLY, THE RESCHEDUAL MARKTIME SHOULD BE NO LONGER THAN 4 HOURS. ; NOTE ALSO THAT THE VARIOUS SYSTEM GLOBAL VARIABLES WHICH ; SHOW HOW MANY CHECKPOINTS, LDR RUNS, SHFL RUNS, ETC, ARE ; SINGLE WORD INTEGERS. ONLY ABOUT 32000 COUNTS OCCUR BEFORE ; OVERFLOW. HENCE IF THIS DATA IS DESIRED IT BEHOVES ONE TO ADJUST ; THE RESCHEDUAL INTERVAL ACCORDINGLY. ; ; ACC IS THE MCR INTERFACE TO CONTROL THE FUNCTIONS OF SYSLOG. ; ONCE ACC STARTS SYSLOG RUNNING, THE FIRST WORD IN THE SEND BUFFER ; CONTROLS HOW SYSLOG INTERPRETS THE DATA PACKET. A 1 AS THE FIRST ; WORD OF THE BUFFER, SIGNALS SYSLOG TO EXIT. IF THE FIRST WORD IS A ; 2, THEN THE DELTA VALUE FOR THE SCHEDULE INTERVAL IS CHANGED. ; NOTE THAT ALL SCHEDULE INTERVALS ARE IN MINUTES. ; 80$: MOV $TKTCB,R0 ; GET OUR TASK HEADER ADDRESS ;JGD3 MOV #.CO0,T.UCB(R0) ; Make CO: our TI: ;JGD3 PRINT M4 ; SAY THAT CPU TIME ACCOUNTING HAS STARTED DIR$ #MARKTM ; MARK TIME, EXECUTE AST TO UNSTOP US REPEAT: ; REF LABLE CLR BUF+4 ; RESET THE SEND BUFFER FLAG CLR AGAIN ; RESET THE REPEAT INDICATOR ENAR$S ; ENABLE AST RECOGNITION FOR MARKTIME 82$: DIR$ #RCVSTP ; RECIEVE DATA OR STOP CMP #IS.SET,$DSW ; SOMETHING HAPPENED, DO WE HAVE DATA BNE 85$ ; WE HAVE DATA SO PROCEED TST AGAIN ; SOMETHING UNSTOPPED US WAS IT THE MARKTIME BEQ 82$ ; SOMETHING ELSE UNSTOPPED US, NOT EITHER THE ; MARKTIME OR ACC, SO LETS JUST CONTINUE 85$: ; REF LABLE DSAR$S ; DISABLE AST RECOGNITION CMP #1,BUF+4 ; IS THIS A SIGNAL TO EXIT BEQ EXIT1 ; YES, SO COOPERATE CMP #2,BUF+4 ; PERHAPS COMMAND TO RESET MARKTIME TO NEW VALUE BEQ RESET ; YES, GO RESET TO NEW TIME BR SENDIT ; ELSE GO SEND OFF DATA PACKET EXIT1: INC OUTFLG ; SHOW WE MUST EXIT AFTER SENDING LAST DATA BR SENDIT ; CHECK TO SEE IF ANY DATA TO SEND OFF .ENABL LSB .SBTTL RESET MARKTIME TO NEW VALUE RESET: ; REF LABLE CMKT$S ; CANCEL THE OLD MARKTIME CMP BUF+6,#1 ; IS IT LESS THAN 1 MIN INTERVALS BLT 30$ ; IF LT, YES MOV BUF+6,MARKTM+M.KTMG ; INSERT NEW VALUE IN DPB MOV BUF+6,$DELTA ; SAVE VALUE FOR LATER MOV #4,RECTYP ; SHOW ITS A CHANGE INTERVAL RECORD DIR$ #GTTIM ; GET THE TIME CALL LODSND ; LOAD THE SEND BUFFER WITH ASS BACKWARDS TIME MOV $DELTA,STRTTM+20; SET THE VALUE OF THE NEW INTERVAL MOV BUF,STRTTM+22 ; SET 1ST HALF(RAD50) OF REQUESTING TASK MOV BUF+2,STRTTM+24 ; SET 2ND HALF(RAD50) OF REQUESTING TASK CALL SEND ; SEND OFF THE DATA BR 35$ ; BACK FOR MORE 30$: PRINT M3 ; WARN USER ILLEGAL INTERVAL CHOSEN 35$: DIR$ #MARKTM ; ISSUE NEW MARKTIME JMP REPEAT ; BACK FOR MORE .SBTTL SEND DATA SENDIT: ; REF LABLE CLR BUF+4 ; RESET THE SEND BUFFER SO WON'T GET CONFUSED LATER MOV #TIM,GETTIM+G.TIBA ; GET TIME BUFFER ADDRESS DIR$ #GETTIM ; GET THE TIME BCC 88$ ; SUCCESS IF CC PRINT M6 ; WARN IF GETTIME FAILED 88$: CMP TIM+4,OLDDAY ; HAS THE DAY CHANGED? ;JGD1 BEQ 89$ ; IF EQ, NO, PROCEED ;JGD1 CLR RECTYP ; MAKE IT A NEWDAY RECORD TYPE ;JGD1 MOV TIM+4,OLDDAY ; SAVE THE NEW DAY ;JGD1 MOV $DELTA,STRTTM+20; SET THE VALUE OF THE NEW INTERVAL ;JGD1 CALL LODSND ; LOAD THE SEND BUFFER ;JGD1 CALL SEND ; SEND THE PACKET TO LOG... ;JGD1 89$: BIT #10,$ACMSK ; SHOULD WE LOG CPU STATISTICS ;JGD1 BEQ 200$ ; IF EQ, NO MOV #2,RECTYP ; SET RECORD TYPE(CPU GLOBAL QTYS) MOV TIM+6,STRTTM ; SET HOUR MOV TIM+10,STRTTM+2 ; SET MIN MOV TIM+12,STRTTM+4 ; SET SEC TST CPUON ; IS CPU TIME LOGGING TURNED ON BEQ 90$ ; IF EQ 0, NO, SO SKIP MOV CODNOD,R0 ; GET ADDRESS OF CODE NODE ADD #NULTIM-CODST,R0 ; GET ADDRESS OF NULTIM COUNTER MOV 2(R0),STRTTM+6 ; GET LOW ORDER NULL TIME MOV (R0),STRTTM+10 ; GET HIGH ORDER NULL TIME(IN TICS) CLR (R0) ; RESET LOW ORDER NULL TIME CLR 2(R0) ; AND NULL IT OUT BR 92$ ; SKIP OVER 90$: ; CLR STRTTM+6 ; NO DATA LEAVE IT 0 CLR STRTTM+10 ; NO DATA LEAVE IT 0 92$: MOV $SHFRQ,STRTTM+12; GET NUMBER OF TIMES SHUFFLER REQUESTED CLR $SHFRQ ; RESET THE REQUEST COUNT MOV $CKPRQ,STRTTM+14; GET THE NUMBER OF TASK CHECKPOINT REQUESTS CLR $CKPRQ ; RESET THE COUNT MOV $LDRQ,STRTTM+16; GET THE NUMBER OF LDR REQUESTS CLR $LDRQ ; CLEAR IT OUT MOV $QIOCT,STRTTM+20; GET NUMBER OF QIO'S MOV $QIOCT+2,STRTTM+22; AND HIGH ORDER VALUE CLR $QIOCT ; RESET COUNTER CLR $QIOCT+2 ; RESET THE COUNT AND HIGH ORDER VALUES ;++++++++++++++TEMP CODE+++++++++++++++++++++++++++++++++++++ ;+++++++ CLR STRTTM+26 ;++++TEMP (MAYBE DIRECTIVE COUNT/10000 WILL GO HERE) ;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ MOV $RUNRQ,STRTTM+24; GET RUN REQUEST COUNT CLR $RUNRQ ; RESET THE COUNT ; MOV $DIRCT,STRTTM+26; DIRECTIVE COUNT ; CLR $DIRCT ; RESET THE COUNT CALL SEND ; SEND OFF THE DATA BCC 200$ ; IF ALL IS OK JMP EXIT ; REMOVE NODE AND EXIT 200$: ; REF LABLE .IF DF M$$ACC ; IF MEMORY UTILIZATION ACCOUNTING IN SYSTEM CLR DOIT ; RESET INDICATOR BIT #20,$ACMSK ; IS IT DESIRED TO ACCUMULATE MEMORY STATISTICS ; ON EVERY PASS BEQ 250$ ; IF EQ, NO, SO CHECK FOR SELECTIVE LOGGING INC DOIT ; SHOW WE MUST SEND IT REGARDLESS 250$: ; REF LABLE CALL GTTSKS ; GET #TASKS IN/OUT AND SIZE MOV TKSIN,STRTTM+6 ; GET # TASKS IN CORE MOV SIZIN,STRTTM+10 ; GET MEMORY USED BY IN CORE TASKS(IN K WORD) CMP SIZIN,SIZINN ; IS IT MORE THAN THE MAXIMUM ALLOWED USAGE? BLT 260$ ; IF LESS THAN DO NOT WORRY BIT #40,$ACMSK ; SHOULD WE OUTPUT IF A CORE SPACE PROBLEM BEQ 260$ ; IF EQ, NO INC DOIT ; YES , WE MUST DO IT 260$: MOV TKSOT,STRTTM+12 ; GET TOTAL NUMBER OF TASKS OUT OF MOV SIZOT,STRTTM+14 ; GET AMOUNT OF CORE OCCUPIED BY CHECKPOINTED TASKS CMP SIZOT,SIZOUT ; SEE IF THIS IS BIGGER THAN THE MAX ALLOWED OUT BLT 270$ ; IF LT, NO WORRY BIT #40,$ACMSK ; SHOULD WE SEND OUT STAT,IF TOO MANY FILES CKPT BEQ 270$ ; IF EQ, NO INC DOIT ; IF NE, YES WE MUST DOIT 270$: ; CALL GTPOOL ; GET POOL STATISTICS MOV PLTOT,STRTTM+16 ; GET TOTAL SIZE OF POOL CMP PLTOT,#500. ; IF TOTAL POOL LESS THAN 500 WORDS BGT 280$ ; IF GT, DON'T WORRY BIT #100,$ACMSK ; SHOULD WE SEND OFF DATA IF POOL VERY LOW BEQ 280$ ; IF EQ, NO INC DOIT ; IF NE, YES WE MUST DO IT 280$: MOV PLLRG,STRTTM+20 ; GET LARGEST POOL FRAGMENT MOV PLFRG,STRTTM+22 ; GET NUMBER OF POOL FRAGMENTS MOV NOPOOL,STRTTM+24; SEND OUT # TIMES POOL DROPPED BELOW 200. CLR STRTTM+26 ; TST DOIT ; IF EQ,ZERO DON'T SEND OUT DATA BEQ 310$ ; IF YES, EQ ZERO, SKIP SENDING DATA MOV #3,RECTYP ; CALL SEND ; SEND OFF THE DATA BCC 310$ ; IF CC, ALL IS OK PRINT M2 ; WARN OF SEND/REQUEST FAILURE ; BUT DON'T EXIT .ENDC ; END CONDITIONAL M$$ACC 310$: ; REF LABLE BIT #400,$ACMSK ; IS CO: ACCOUNITNG DESIRED BEQ 315$ ; IF EQ 0, NO CALL LOGCO ; ELSE LOG CO: DATA 315$: ; REF LABLE TST OUTFLG ; SHOULD WE EXIT BEQ 320$ ; OK, REPEAT JMP EXIT ; YES, DO SO NOW 320$: MOV $DELTA,MARKTM+M.KTMG ; SET THE MARKTIME MAGNITUDE ; JGD1 MOV TIM+6,R0 ; SET CURRENT HOUR ; JGD1 MOV #60.,R1 ; SET MIN/HOUR ; JGD1 CALL $MUL ; MULTIPLY, RESULT IN R1 ; JGD1 MOV #MINDAY,R0 ; GET # MINUTES IN A DAY ; JGD1 SUB R1,R0 ; GET HOW MANY MIN LEFT IN TODAY ; JGD1 SUB TIM+10,R0 ; ALSO SUBTRACT OFF MIN IN HOUR ; JGD1 CMP R0,$DELTA ; IS THIS VALUE LARGER THAN $DELTA ; JGD1 BGT 325$ ; IF YES, USE $DELTA FOR MARKTIME ; JGD1 INC R0 ; RETURN 1 MIN PAST MIDNIGHT ; JGD1 MOV R0,MARKTM+M.KTMG; IF NO, MARKTIM UNTIL MIDNIGHT+1 MIN ; JGD1 325$: DIR$ #MARKTM ; STARTUP NEW MARKTIME ; JGD1 JMP REPEAT ; NO, SO GO BACK FOR MORE .IF DF M$$ACC ; IF MEMORY ACCOUNTING A SYSGEN OPTION .SBTTL DISPLAY POOL INFORMATION ; ; GET THE NUMBER OF WORDS IN POOL, THE NUMBER OF FRAGMENTS, AND THE LARGEST ; BLOCK OF POOL. THIS CODE WAS LIFTED FROM RMDEMO. ; GTPOOL: ; REF LABLE MOV #POOL,R1 ; GET BUFFER ADD CLR (R1)+ ; CLEAR NUMBER OF FRAGMENTS CLR (R1)+ ; CLEAR TOTAL POOL CLR (R1) ; CLEAR MAXIMUM PIECE CALL $SWSTK,5$ ; SWITCH TO SYSTEM STATE MOV #$CRAVL,R4 ; GET ADDRESS OF POOL LISTHEAD BR 4$ ; BRANCH INTO LOOP 2$: CMP (R1),2(R4) ; IS MAX > THIS BLOCK? BHIS 3$ ; IF HIS YES MOV 2(R4),(R1) ; NO, SET NEW MAX 3$: ADD 2(R4),-(R1) ; ADD THIS SPACE TO TOTAL INC -(R1) ; INCREMENT NUMBER OF POOL FRAGMENTS CMP (R1)+,(R1)+ ; POINT R1 TO END OF RAW DATA BUFFER 4$: MOV (R4),R4 ; GET ADDR OF NEXT BLOCK BNE 2$ ; IF NE MORE TO EXAMINE RETURN ; ELSE, RETURN TO TASK STATE 5$: ; REF MOV (R1),PLLRG ; SET MAX. CONTIGUOUS CHUNK OF POOL MOV -(R1),PLTOT ; SET TOTAL FREE POOL MOV -(R1),PLFRG ; SET TOTAL NUMBER OF FRAGMENTS 10$: RETURN ; ALL DONE, GO HOME .ENDC ; .END M$$ACC .IF DF M$$ACC .SBTTL COMPUTE TASKS IN AND OUT OF MEMORY WITH MEMORY USAGE ;+ ; ; DISPLAY THE NUMBER OF ACTIVE TASKS OUT OF MEMORY AND THEIR TOTAL ; SIZE, AND THE NUMBER OF TASKS IN MEMORY WITH THEIR TOTAL SIZE. ;- .ENABLE LSB GTTSKS: ; INITIALIZE THE COUNTERS CLR SIZIN ; MEMORY FOR IN TASKS CLR TKSIN ; NUMBER OF TASKS IN CLR SIZOT ; MEMORY FOR TASKS OUT CLR TKSOT ; NUMBER OF TASKS OUT ; ; NOW SCAN TASK LIST AND LOOK FOR ACTIVE TASKS ; CALL $SWSTK,40$ ; SYSTEM STATE MOV #$TSKHD-T.TCBL,R3 ; FIRST POINTER 10$: MOV R3,R4 ; SET UP FOR NEXT TCB MOV T.TCBL(R4),R3 ; GET NEXT TCB ADDRESS CMP #$HEADR,R3 ; IS IT NULL TASK BEQ 30$ ; IF EQ YES BIT #TS.EXE,T.STAT(R3) ; IS TASK NOT ACTIVE BNE 10$ ; IF NE YES CMP R3,$LDRPT ; IS IT LOADR BEQ 10$ ; IF EQ YES, SKIP IT MOV T.PCB(R3),R4 ; GET PCB ADDRESS MOV P.SIZE(R4),-(SP) .IF NDF M$$MGE ; CLC ; CONVERT TO 32. WORD UNITS SO AS TO BE STANDARD WITH ROR (SP) ; MAPPED SYSTEMS. .REPT 5 ASR (SP) .ENDR .ENDC ; M$$MGE ASR (SP) ; MAKE IT 64.WORD UNIT INC (SP) ; ROUND IT UP A LITTLE ASR (SP) ; NOW 128. WORD UNITS BIT #TS.OUT,T.STAT(R3) ; IS IT IN MEMORY BNE 20$ ; IF NE NO , TASK IS NOT IN MEMORY INC TKSIN ; TASK IS IN MEMORY, ADD ONE MORE TASK IN MEMORY ADD (SP)+,SIZIN ; MORE MEMORY IN 128. WORD UNITS BR 10$ ; GO BACK FOR MORE 20$: INC TKSOT ; TASK IS OUT OF MEMORY(CHECKPOINT FILE), BUMP COUNTER ADD (SP)+,SIZOT ; MORE MEMORY BR 10$ ; GO BACK FOR MORE 30$: RETURN ; NO MORE LEFT TO DO -- FOUND NULL ; TASK 40$: ; GET SIZE OF ALL TASKS IN 1K UNITS ASR SIZIN ; 256 WORD UNITS ASR SIZIN ; 512 WORD UNITS INC SIZIN ; SORT OF ROUND UP ASR SIZIN ; 1024 WORD UNITS ASR SIZOT ; 256 WORD UNITS ASR SIZOT ; 512 WORD UNITS INC SIZOT ; ROUND UP(SORT OF) ASR SIZOT ; 1024 WORD UNITS RETURN ; ALL DONE .ENDC ; .END M$$ACC .ENABL LSB .SBTTL SEND ;+ ; SEND -- SEND OFF A DATA PACKET TO LOG... ; 1. SEND THE DATA ; 2. UNSTOP THE LOGGER ; 3. IF UNSTOPPING FAILS, REQUEST THE LOGTSK ; ; DO NOT SEND DATA IF POOL DROPS BELOW 200 WORDS, IF POOL ACCOUNTING ; IS INCLUDED. ;- SEND: ; REF LABLE .IF DF M$$ACC ; IF POOL/MEMORY ACCOUNTING ENABLED AT SYSGEN TST PLTOT ; CHECK TO SEE IF POOL ACCOUNTING SELECTED BEQ 5$ ; IF EQ 0, NO POOL ACCOUNTING OR DEAD SYSTEM ; ASSUME NO POOL ACCOUNTING CMP PLTOT,#200. ; HAVE WE LESS THAN 200. WORDS LEFT BGT 5$ ; CONTINUE THERE IS ENOUGH POOL INC NOPOOL ; INC COUNT OF # TIMES COULDN'T SEND DATA BR 80$ ; AND EXIT .ENDC ; ENDC M$$ACC 5$: DIR$ #SENDAT ; SEND OFF THE DATA BCC 10$ ; IF CC, ALL OK PRINT M2 ; WARN USER OF SEND DATA FAILURE BR 80$ ; 10$: DIR$ #USTPLG ; UNSTOP THE LOGGER BEQ 20$ ; IF SO IS OK DIR$ #RQSTLG ; COULD NOT UNSTOP TASK, TRY REQUESTING IT BCC 20$ ; IF CC ALL OK CMP $DSW,#IE.ACT ; PERHAPS TASK WAS ACTIVE BEQ 20$ ; IF SO IS OK BR 80$ ; ELSE ERROR REQUESTING LOG... 20$: CLR NOPOOL ; RESET COUNTER CLC ; SHOW SUCCESS BR 85$ ; EXIT 80$: SEC ; SHOW ERROR 85$: RETURN ; BACK TO MAINLINE .ENABL LSB .SBTTL LODSND - LOAD THE SEND BUFFER WITH TIME/DATE ;+ ; LODSND -- INSERT THE TIME FROM THE TIME BUFFER INTO ; THE SEND BUFFER WITH THE HR,MIN,SEC ; BEFORE THE YR,MO,DAY ;- LODSND: MOV TIM+6,STRTTM ; SET HOUR MOV TIM+10,STRTTM+2 ; SET MIN MOV TIM+12,STRTTM+4 ; SET SEC MOV TIM+14,STRTTM+14; SET TICS MOV TIM+16,STRTTM+16; SET TICS/SEC MOV TIM,STRTTM+6 ; SET YEAR MOV TIM+2,STRTTM+10 ; SET MONTH MOV TIM+4,STRTTM+12 ; SET DAY MOV $ACMSK,STRTTM+26; SET IN ACCOUNT FEATURE MASK RETURN ; .SBTTL CPU TIME ACCOUNTING EXIT RUNDOWN ; ; THIS CODE WILL ; OUTPUT SYSTEM STATISTICS, AND ; WILL EXIT SYSLOG. ; EXIT: ; REF. LABEL MOV $TKTCB,R0 ; GET OUR TASK HEADER ;JGD3 MOV OURUCB,T.UCB(R0); RESTORE ADDRESS OF INITIAL TI: ;JGD3 TST CPUON ; IS CPU TIME LOGGING ON BNE 105$ ; YES, REMOVE NODE FIRST JMP XIT ; NO, JUST EXIT WITH NO FUSS 105$: ; CALL CLEAN ; RESTORE CLOCK INTERRUPT INTERCEPT ALFAIL: 106$: TST ERROR ; IS THIS AN ERROR CONDITION? BEQ 107$ ; NO. SKIP ERROR MESSAGE PRINT M10 ; ALLOCATION FAILURE 107$: ; REF. LABEL XIT: DIR$ #GTTIM ; GET THE TIME CALL LODSND ; LOAD THE SEND BUFFER WITH BACKWARDS TIME MOV #-1,RECTYP ; SET RECORD TYPE MOV $DELTA,STRTTM+20; SET THE EXITING SCHEDUAL INTERVAL MOV BUF,STRTTM+22 ; SET THE FIRST HALF OF RAD50 TASK REQUESTING EXIT MOV BUF+2,STRTTM+24 ; SET THE SECOND HALF OF RAD50 TASK REQUESTING EXIT CALL SEND ; SEND OFF THE DATA BCC 109$ ; IF CC, ALL OK 109$: CLR $ACMSK ; RESET THE SYSTEM ACCOUNTING MASK WORD CLR $ACCHD ; CLEAR THE CODE NODE ADDRESS KEEPER MOV $TKTCB,R0 ; GET OUR TASK HEADER MOV #.CO0,T.UCB(R0) ; OUTPUT EXIT MESSAGE TO CO: PRINT M12 ; PRINT EXIT MESSAGE EXIT$S ; AND DO IT ;+ ; ZERO -- ZERO OUT THE SYSCM GLOBAL LOCATIONS USED BY ; ACCOUNTING BEFORE STARTING UP ACCOUNTING ; ; R0 IS USED ;- ZERO: ; REF LABLE .IF NDF R$$PRV ; IF RUN ALLOCATION PRIVLEGES NOT DEFINED .ERROR ; R$$PRV IS NOT DEFINED .ENDC ; THEN BOMB OUT .IF NDF S$$ACC ; IF SYSTEM WIDE ACCOUNTING NOT DEFINED .ERROR ; S$$ACC, SYSTEM WIDE ACCOUNTING NOT IN SYSTEM .ENDC ; S$$ACC NOT DEFINED CLR $SHFRQ ; CLEAR SHUFFLER REQUEST COUNTER CLR $CKPRQ ; CLEAR CHECKPOINT REQUEST COUNTER CLR $LDRQ ; CLEAR LOADER REQUEST COUNTER CLR $QIOCT ; CLEAR QIO COUNTER CLR $QIOCT+2 ; ; CLR $DIRCT ; CLEAR DIRECTIVE COUNTER ; CLR $DIRCT+2 ; CLR $RUNRQ ; CLEAR RUN REQUEST COUNTER MOV #.CO0,R0 ; GET ADDRESS OF CO:'S UCB CLR U.TIC1(R0) ; CLEAR OUT CLOCK TIC COUNT CLR U.TIC2(R0) ; CLEAR OUT CLOCK TIC COUNT CLR U.RNCT(R0) ; CLEAR OUT USER RUN COUNT .IF DF Q$$CNT ; IF TERMINAL QIO ACCOUNTING SELECTED AT SYSGEN CLR U.QIO1(R0) ; CLEAR OUT QIO COUNT CLR U.QIO2(R0) ; CLEAR OUT QIO COUNT .ENDC ; .ENDC Q$$CNT RETURN ; ALL DONE ;+ ; LOGCO - LOG CO STATISTICS ; ;- LOGCO: MOV #.CO0,R0 ; GET CO:'S UCB ADDRESS MOV #8.,RECTYP ; ITS A TYPE 8 RECORD CALL LODSND ; LOAD IN THE TIME CLR STRTTM+6 ; ZERO OUT UNUSED PART OF RECORD CLR STRTTM+10 ; " CLR STRTTM+12 ; " MOV U.TIC1(R0),STRTTM+14 ; GET THE CPU TIME ON CO0: MOV U.TIC2(R0),STRTTM+16 ; GET HIGH ORDER TIME CLR STRTTM+20 ; CLEAR UNUSED PART OF RECORD JUST IN CASE CLR STRTTM+22 ; TERMINAL QIO ACCOUNTING NOT ENABLED .IF DF Q$$CNT ; IF QIO ACCOUNTING A SYSGEN OPTION BIT #1000,$ACMSK ; IS TERMINAL QIO ACCOUNTING ENABLED BEQ 5$ ; IF EQ 0, NO MOV U.QIO1(R0),STRTTM+20 ; GET LOW ORDER QIO COUNT MOV U.QIO2(R0),STRTTM+22 ; GET HIGH ORDER QIO COUNT CLR U.QIO1(R0) ; NULL OUT OLD DATA CLR U.QIO2(R0) ; NULL OUT OLD DATA 5$: ; REF LABLE .ENDC ; .END Q$$CNT MOV U.RNCT(R0),STRTTM+24 ; GET RUN COUNT FOR CO: CLR U.RNCT(R0) ; NULL OUT OLD DATA CLR U.TIC1(R0) ; RESET CPU TIME COUNTERS CLR U.TIC2(R0) ; " CALL SEND ; SEND THE DATA, DON'T WORRY IF WE CAN'T 10$: RETURN ; ; ; MARKTM ; MRKAST: DIR$ #UNSTOP ; LETS UNSTOP US INC AGAIN ; TELL THE TASK TO LOOP AFTER SENDING DATA TST (SP)+ ; CLEAN OF EFN MASK WORD FROM STACK DIR$ #ASTEXT ; EXIT AST ROUTINE .SBTTL INTERCEPT TABLES ; ; INTERCEPT ADDRESS TABLE FOR COUNTING CLOCK TICS ; KADDTB: .WORD UPTIM ; 0, CLOCK ISR INTERCEPT ADDRES KINTTB: MOV #$TKPS+2,R4 ; 0, CLOCK ISR INSTRUCTION TO REPLACE KOFFTB: .WORD TIMINT-CODST ; 0, CLOCK INTERCEPT OFFSET INTO NODE ; ; INTERCEPT ADDRESS TABLE FOR QIO COUNTING ; QADDTB: ; JGD4 .IFDF A$$TRP&R$$DER ; With XDT in system ; JGD4 .WORD $IOFIN+12 ; 1, I/O COUNT INTERCEPT ADDRESS (XDT) ; JGD4 .IFF ; With no XDT ; JGD4 .WORD $IOFIN ; 1, I/O COUNT INTERCEPT ADDRESS ; JGD4 .ENDC ; ; JGD4 QINTTB: MOV I.IOSB+4(R3),R2 ; 1, I/O COUNT INSTRUCTION TO REPLACE ;**-1 QOFFTB: .WORD QIOCNT-CODST ; 1, COUNT QIO INTERCEPT OFFSET INTO NODE ; ; INTERCEPT ADDRESS TABLE FOR COUNTING CHECKPOINT REQUESTS ; CADDTB: .WORD $ICHKP ; 2, CHECKPOINT REQUEST INTERCEPT ADDRESS CINTTB: BIT #TS.OUT,T.STAT(R1) ; 2, CHECKPOINT REQUEST INSTRUCTION TO REPLACE COFFTB: .WORD CKPCNT-CODST ; 2, COUNT CHECKPOINT REQUESTS INTERCEPT OFFSET INTO NODE ; ; INTERCEPT ADDRESS TABLE FOR COUNTING SHUFFLER REQUESTS ; SADDTB: .WORD $EXRQN ; 3, INTERCEPT POINT FOR SHR AND LDR REQUESTS IN GENERAL SINTTB: BIT #T2.STP*2!T2.STP,T.ST2(R0) ; 3, SHF AND LDR REQUEST INSTRUCTION TO REPLACE SOFFTB: .WORD SHFCNT-CODST ; 3, COUNT SHUFFLER REQUESTS INTERCEPT OFFSET INTO NODE ; ; INTERCEPT ADDRESS TABLE FOR COUNTING RUN REQUESTS ; RADDTB: .WORD $TSKRP+32 ; 4, RUN REQUEST INTERCEPT ADDRESS RINTTB: BIT #T3.REM,T.ST3(R0) ; 4, RUN REQUEST INSTRUCTION TO REPLACE ROFFTB: .WORD RUNCNT-CODST ; RUN REQUEST INTERCEPT OFFSET INTO NODE .SBTTL CLEAN -- RESTORE EXEC TO PRE-INTERCEPT CONDITION ; ; CLEAN: ; REF LABLE CALL $SWSTK,125$ ; SWITCH TO SYSTEM STATE MOV CODNOD,R5 ;; GET CODE NODE ADDRESS MOV #KADDTB,R0 ;; POINT TO ADDRESS OF EXEC LOCATION IN TDSCH WE STOLE MOV #KINTTB,R1 ;; GET ADDRESS OF INTERCEPTED INSTRUCTION TABLE MOV (R0),R3 ;; GET INTERCEPT ADDRESS BIS #340,PS ;;; RAISE PRIORITY TO 7(INHIBIT CLOCK INTERRUPTS) MOV (R1)+,(R3)+ ;;; MOVE IN FIRST PART OF INSTRUCTION(2 WORD) MOV (R1),(R3) ;;; AND SECOND PART BACK INTO EXEC CLRB PS ;; SET PRIORITY BACK TO ZERO ; RESTORE $IOFIN TO PRISTINE CONDITION(QIO COUNTING) BIT #2,$ACMSK ;; IS QIO ACCOUNTING ACTIVE BEQ 110$ ;; IF EQ 0, NO, SKIP MOV #QADDTB,R0 ;; GET ADDRESS OF EXEC LOCATION WE STOLE MOV #QINTTB,R1 ;; GET ADDRESS OF INTERCEPTED INSTRUCTION TABLE MOV (R0),R3 ;; GET INTERCEPT ADDRESS MOV (R1)+,(R3)+ ;; MOVE FIRST PART OF INSTRUCTION MOV (R1),(R3) ;; AND SECOND PART OF INSTRUCTION BACK INTO EXEC 110$: ;; REF LABLE ; IF GLOBAL ACCOUNTING WAS SELECTED, ; 1. RETURN CHECKPOINT REQUEST INSTRUCTION INTO EXEC ; 2. RETURN SHUFFLER REQUEST INSTRUCTION INTO EXEC BIT #10,$ACMSK ;; IS GLOBAL STATISTICS COUNTING ENABLED BEQ 112$ ;; IF EQ 0, NO MOV #CADDTB,R0 ;; GET ADDRESS OF EXEC LOCATION WE STOLE MOV #CINTTB,R1 ;; GET ADDRESS OF INTERCEPTED INSTRUCTION CALL MOVIT ;; MOVE IN THE OLD INSTRUCTION MOV #SADDTB,R0 ;; GET ADDRESS OF EXEC LOCATION WE STOLE MOV #SINTTB,R1 ;; GET ADDRESS OF INTERCEPTED INSTRUCTION CALL MOVIT ;; MOVE IN THE OLD INSTRUCTION MOV #RADDTB,R0 ;; GET ADDRESS OF LOC WE STOLE MOV #RINTTB,R1 ;; GET ADDRESS OF INTERCEPTED INSTRUCTION TO PUTBACK CALL MOVIT ;; MOV IN THE OLD INSTRUCTION 112$: ;; REF LABLE .IF NDF M$$MGE CMP #CODST,R5 ;; IS THIS CODE IN A NODE? BEQ 125$ ;; NO. DON'T RETURN IT .ENDC ;; NDF M$$MGE MOV R5,R0 ;; RETRIEVE THE NODE ADDRESS MOV #CODSIZ,R1 ;; GET THE SIZE OF IT CALLR $DEACB ;; AND RETURN IT TO THE POOL 125$: RETURN ;+ ; MOVIT -- MOVE BACK INTO THE EXEC A 3 WORD INSTRUCTION ;- MOVIT: MOV (R0),R3 ;; GET INTERCEPT ADDRESS MOV (R1)+,(R3)+ ;; MOVE IN FIRST WORD OF INSTRUCTION MOV (R1)+,(R3)+ ;; MOVE IN SECOND WORD OF INSTRUCTION MOV (R1),(R3) ;; MOVE IN THIRD WORD OF INSTRUCTION RETURN ;; .PAGE .SBTTL PRINTING ROUTINES ; ; ; THIS ROUTINE WILL PRINT AN ASCIZ LINE ; PNTLIN: MOV #-1,R1 ; GET COUNT INITIALIZED MOV R0,QIODPB+Q.IOPL ; STORE BUFFER ADDRESS 170$: INC R1 ; INCREMENT COUNTER TSTB (R0)+ ; IS THIS THE LAST BYTE? BNE 170$ ; NO. KEEP GOING MOV R1,QIODPB+Q.IOPL+2 ; STORE BYTE COUNT DIR$ #QIODPB RETURN ; ; START OF CLOCK TIC INTERCEPT CODE ; .DSABL LSB .ENABL LSB CODST=. ; ; THIS THE CLOCK ISR. IT MERELY KEEP COUNT IN TICKS AND CONTINUES ; TIMINT: .IF DF T$$ACC ; IF TASK ACCOUNTING IS SUPPORTED ADD #1,@#$TIMEX ; ADD IN ONE TIC ADC @#$TIMEX+2 ; AND CARRY IF NEED BE .ENDC ; .END T$$ACC CMP #$HEADR,@#$RQSCH; ARE WE POINTING AT THE NULL TASK BEQ 10$ ; IF EQ, YES INC @#$UTLIZ ; SHOW CPU BUSY THIS TIC ; JGD2 MOV R0,-(SP) ; SAVE R0 MOV @#$TKTCB,R0 ; GET TASK HEADER MOV T.UCB(R0),R0 ; GET UCB ADDRESS ADD #1,U.TIC1(R0) ; ADD IN A TIC ADC U.TIC2(R0) ; AND ADD CARY MOV (SP)+,R0 ; RESTORE R0 BR 15$ ; RETURN VIA COMMON CODE 10$: ADD #1,NULTIM+2 ; YES, COUNT AS NULL TIME ADC NULTIM ; AND HIGH ORDER PART 15$: MOV #$TKPS+2,R4 ;* RESTORE THE INTERCEPTED INSTRUCTION RETURN ; BACK TO CLOCK ISR ; ; THIS SECTION OF PIC CODE IS JUMPED TO IMMEDIATELY AFTER $IOFIN ; (IN IOSUB) AND IS USE TO KEEP COUNT OF THE TOTAL SYSTEM QIO'S, ; QIO'S FOR EACH USER, AND TASK QIO'S QIOCNT: ADD #1,@#$QIOCT ; ADD ONE ADC @#$QIOCT+2 ; AND ADD THE CARRY IF NEEDED .IF DF Q$$CNT ; IF USER QIO ACCOUNTING A SYSGEN OPTION BIT #1000,@#$ACMSK ; IS TERMINAL QIO ACCOUNTING ENABLED BEQ 40$ ; IF NE NO MOV R0,-(SP) ; SAVE R0 MOV I.TCB(R3),R0 ; GET THE IO PACKETS TCB ADDRESS .IF DF T$$ACC ; IS TASK ACCOUNTING SELECTED BIT #2000,@#$ACMSK ; IS TASK ACCOUNTING SELECTED BEQ 35$ ; IF EQ NO MOV R1,-(SP) ; WE HAD BEST SAVE R1 FOR LATER CALL MATCH ; IS THERE A MATCH OF TCB ADDRESS WITH NODE ADDRESS BEQ 30$ ; IF EQ, NO ADD #1,B.QIO1(R1) ; ADD ONE TO QIO COUNT ADC B.QIO2(R1) ; AND ADD IN CARRY TO HIVAL 30$: MOV (SP)+,R1 ; RESTORE R1 35$: ; REF LABLE .ENDC ; .END T$$ACC MOV T.UCB(R0),R0 ; GET OUR UCB ADDRESS ADD #1,U.QIO1(R0) ; ADD ONE TO TERMINAL UCB COUNTER ADC U.QIO2(R0) ; ADD CARRY IF NEEDED MOV (SP)+,R0 ; RESTORE R0 .ENDC ; .END Q$$CNT 40$: MOV I.IOSB+4(R3),R2 ;* RESTORE THE INTERCEPTED INSTRUCTION RETURN ; BACK INTO THE EXEC ; ; INTERCEPT CODE FOR COUNTING CHECKPOINT REQUESTS ; NOTE THAT THE INSTRUCTION BEING INTERCEPTED IS A 3 WORD ; INSTRUCTION ; CKPCNT: ; REF LABLE ADD #1,@#$CKPRQ ; ADD ONE BIT #TS.OUT,T.STAT(R1) ;* TASK BEING READ IN RETURN ; BACK INTO EXEC ; ; INTERCEPT CODE FOR COUNTING THE NUMBER OF SHUFFLER REQUESTS ; AND LOAD REQUESTS, AND REQUESTS TO RUN A TASK IN GENERAL. ; NOTE THIS DOES NOT COUNT HOW MANY TIMES A TASK IS ACTUALLY RUN, ; BUT, RATHER THE NUMBER OF TIMES THE TASKS ARE REQUESTED TO RUN. ; LACK OF MEMORY, ETC. CAN BLOCK A TASK FROM EXECUTION. ; HOWEVER, FOR MOST NORMAL SITUATIONS $LDCNT ; AND $RUNCT SHOULD PROVIDE A GOOD MEASURE OF THE NUMBER OF TIMES ; A TASK IS REQUESTED TO BE LOADED INTO CORE, AND THE NUMBER OF TIMES ; IN CORE TASKS ARE REQUESTED TO RUN. WHEN $SHFRQ BECOMES >0, THESE ; FIGURES PROVIDE SOME MEASURE OF HOW MUCH THRASHING ABOUT THE EXEC IS ; HAVING TO DO TO TRY AND SATISFY ALL REQUESTS BY USERS AND ITSELF FOR ; TASKS TO HAVE ACCESS TO MEMORY SPACE AND CPU TIME. NOTE, THE ; INSTRUCTION BEING INTERCEPTED IS A 3 WORD INSTRUCTION. ; THIS CODE IS INSERTED INTO REQSB, RIGHT AFTER $EXQRN SHFCNT: ; REF LABLE 12$: CMP R0,@#$SHFPT ; ARE WE POINTING TO THE SHUFFLER BNE 20$ ; IF NE , NO, DON'T COUNT ADD #1,@#$SHFRQ ; ADD ONE 20$: CMP R0,@#$LDRPT ; ARE WE POINT TO THE LOADER BNE 25$ ; IF NE, NO DON'T COUNT ADD #1,@#$LDRQ ; ADD ONE TO COUNT 25$: CLC ; CLEAR CARRY(JUST IN CASE) ** BIT #T2.STP*2!T2.STP,T.ST2(R0) ;* EXECUTE INTERCEPTED INSTRUCTION RETURN ; BACK INTO EXEC ; INTERCEPT CODE TO COUNT RUN REQUESTS. BECAUSE OF WHERE THIS CODE IS ; PLACED IN THE EXEC(AFTER $TSKRQ IN REQSB), RUN REQUESTS WILL NOT ; BE COUNTED IF THE TASK IS ALREADY ACTIVE(IE A TASK BEING REACTIVATED ; AFTER A MARKTIME, OR IF THE TASK IS FIXED, OR BEING FIXED. RUNCNT: ADD #1,U.RNCT(R2) ; ADD ONE TO TERMINAL UCB RUN COUNTER ADD #1,@#$RUNRQ ; ADD ONE TO GLOBAL RUN COUNTER CLC ; CLEAR CARRY TO BE SAFE BIT #T3.REM,T.ST3(R0) ;* EXECUTE INTERCEPTED INSTRUCTION RETURN ; ; THIS ROUTINE SEARCHES FOR A MATCH IN THE ACCOUNTING LIST ; ; INPUTS: ; ; R0 -- CONTAINS TCB ADDRESS TO SCAN FOR ; ; PRINTS: ; ; Z=1 -- NO ENTRY FOUND ; ; Z=0 -- FOUND ENTRY ; R0 -- TCB ADDRESS ; R1 -- ACCOUNTING BLOCK ADDRESS ; R2 -- PREVIOUS ENTRY IN LIST ; .IF DF T$$ACC ; IF TASK ACCOUNTING DEFINED MATCH: ; REF LABLE MOV @#$TKBLK,R2 ; GET ADDRESS OF TASK ACCOUNTING NODE LIST HEAD BEQ 230$ ; IF EQ 0, NO ADDRESS IN NODE(HELP???) 210$: MOV (R2),R1 ; GET NEXT ENTRY ADDRESS BEQ 230$ ; IF = 0, THEN NO MATCH CMP R0,2(R1) ; DOES THIS ONE MATCH? BEQ 220$ ; YES. GIVE IT BACK TO USER MOV R1,R2 ; NO. CONTINUE LOOKING BR 210$ ; TRY AGAIN 220$: CLZ ; LET USER KNOW THAT WE GOT ONE 230$: RETURN ; AND RETURN .ENDC ; END T$$ACC ; ; DATA BASES ; NULTIM: .WORD 0,0 ; NULL TIME ; ; END OF CODE (IN NODE) ; CODEN=. ; SIZE OF ALL CODE ; CODSIZ=CODEN-CODST .END START