.title MWAIT System display of wait reasons .ident "X01.06" ;+ ; MWAIT ; ; Author : Nick de Smith ; Creation date : 09-Dec-91 ; ; Description ; ; This program runs in EXEC mode to try to determine the reason for ; a process being in a wait state. All the event flag information is ; displayed, as is the process state. MWAIT tries to get the best detail ; it can on process state, including JIB, RSN and MWAIT states. ; MWAIT also displays the delta time since the process last entered a ; wait state. This can be very useful for detecting dormant processes, ; and processes that should be dormant and are not. ; ; Notes ; ; For various reasons, mainly concerned with ensuring that MWAIT returns ; something, and because it only uses read access, MWAIT does not use any ; locking on the memory databases it accesses and does not use $GETJPI. ; This means that there is a finite possibility of MWAIT getting an ; ACCVIO. This is no cause for concern - it will not blow away your ; system, just re-run the program. If the process is jammed in an MWAIT, ; it isn't going anywhere anyway! Note that if MWAIT gets an exception ; in EXEC mode, it will cause the current (not the target) process to ; terminate. This should never happen, but if it is a problem, run ; MWAIT in a sub-process. ; ; Edit Edit date By Why ; 06 07-Feb-92 NMdS Cosmetic change in some strings (a conditional ; $FAO was wrong - did not affect data). ; Add in driver offset PC resolution. ; 05 14-Jan-92 NMdS Add more detail on process modes and AST modes. ; Change the CHME/CHMK vector location code to do ; a search, rather than relying on fixed offsets. ; 04 09-Jan-92 NMdS Enhance messaging. Add action routines for ; some of the RWAST causes. ; 03 06-Jan-92 NMdS Change mutex references to be .WEAK ; Added in RWMBX decoding. ; 02 23-Dec-91 NMdS Change way RWAST_TABLE is re-vectored to account ; for CHME/CHMK routines. Add in current priority. ; Improve messaging. Many other minor changes. ; 01 09-Dec-91 NMdS New ;- .link "SYS$SYSTEM:SYS.STB" /SELECTIVE_SEARCH ; Save typing later .library "SYS$LIBRARY:LIB.MLB" .library "SYS$DISK:[]MWAIT_MACROS.MLB" $cebdef ; Common event flag wait definitions $ddbdef ; Device data block definitions $dispdef ; Change mode dispatcher offsets $dptdef ; Driver prologue table offsets $dscdef ; VMS descriptors $irpdef ; I/O request packet offsets $jibdef ; Job Information Block $ldrimgdef ; Loadable image block definitions $opdef ; Define VAX instructions $pcbdef ; Process Control Block Definitions $phddef ; Process header definitions $psldef ; Processor status longword definitions $rsndef ; Resource name definitions $sfdef ; Saved frame offsets $splcoddef ; Spinlock index definitions $ssdef ; Define system statii $statedef ; Define SCH$C_xxxx $ucbdef ; Unit control block offsets .psect $RWDATA pic, noshr, rd, wrt, noexe, byte EPID_DESC: ; Descriptor for ascii PID .ascid - - ; 15 characters E_ARGS: .long 1 ; $CMEXEC argument list EPID: .blkl 1 ; Target PID CMOD$AR_EXEC_DISPATCH_VECTOR: ; Address of CHME dispatch vector .blkl 1 CMOD$AR_KERNEL_DISPATCH_VECTOR: ; Address of CHMK dispatch vector .blkl 1 .psect $RODATA pic, shr, rd, nowrt, noexe, byte MAX_VECTOR_OFFSET = ^x2000 ; Maximum offset in EXCEPTION to vectors KERNEL_CHECK: .long 0, 0, ^x00010008 ; CHMK vector check values EXEC_CHECK: .long 0, 0, ^x0002000C ; CHME vector check values EXCEPTION: ; Name of CHMx handling exec image .ascic "EXCEPTION.EXE" PID_PROMPT: ; Prompt to use for PID .ascid -_Target PID: - TITLE: .ascid " Pid User name Process name Prior State Port" FAOCTL: .ascid "!XL !12AF !15AF !5 !6AC!+ !AC" FAOCTL_SMP: .ascid "!XL !12AF !15AF !5 !3AC !2UB !AC" EFAOCTL:.word 20$-10$, 0 .address 10$ 10$: .ascii "!_Wait mask: !XL!0UL!1%C (all EFNs)!%F," .ascii " Cluster: !UB, Time: !%D!/" .ascii "!_0: !XL, 1: !XL" 20$: CEFAOCTL: .ascid "!_!UB: !XL (!%I!AF)" ; ; This $FAO control string assumes that the addresses of the saved register ; values in a process header (PHD) are in the contiguous order shown below. ; PHDCTL: .word 20$-10$, 0 .address 10$ 10$: .ascii " Process registers:!/" .ascii "!_!_R0: !XL!_R1: !XL!_R2: !XL!_R3: !XL!/" .ascii "!_!_R4: !XL!_R5: !XL!_R6: !XL!_R7: !XL!/" .ascii "!_!_R8: !XL!_R9: !XL!_R10: !XL!_R11: !XL!/" .ascii "!_!_AP: !XL!_FP: !XL!_PC: !XL!_PSL: !XL" 20$: NULL_USERNAME: .byte ^a' '[JIB$S_USERNAME] BATCH_MODE: .ascic "-Bat-" NETWORK_MODE: .ascic "-Net-" DETACH_MODE: .ascic "-Det-" SUB_MODE: .ascic "-Sub-" ; This table corresponds to the order for SCH$C_xxx values for process states ; from $STATEDEF SCH_TABLE: SCH ; Table of known system resource wait states. The table is in the order ; of the RSN$_xxx values from $RSNDEF RSN_TABLE: SCH ; Table of known JIB wait states. These states are contained in JIB$B_FLAGS. ASSUME JIB$M_BYTCNT_WAITERS eq 1 ASSUME JIB$M_TQCNT_WAITERS eq 2 JIB_TABLE: SCH ; Table of AST names vs. bits set (<0> = K, <1> = E, <2> = S, <3> = U) AST_TABLE: SCH <(none),K,E,KE,S,KS,ES,KES,U,KU,EU,KEU,SU,KSU,ESU,KESU> ; Processor mode table - corresponds to PSL$C_xxx values MODE_TABLE: SCH ; Table of known system MUTEXs and strings to display. ; This is taken from table 8.3 in the V5.2 IDSM. MTX_TABLE: MTX <- ,,,- ,,,- ,,,- ,,,- ,<-1,MWAIT?>,- > ; Table of known address ranges within the VMS executive where an RSN$_ASTWAIT ; can be generated. ; The RWPC macro takes up to 5 arguments: ; ; RWPC base, length, text [,action] [, backup] ; ; base Name of executive vectored routine in which the ASTWAIT occurs. ; length Number of bytes offset from the start of the routine to allow ; as the waiting PC. ie. the ASTWAIT must occur within this number ; of bytes from the start of the routine. ; text Message to display if a match is found. ; action Action routine number to call to fill in detail. ; backup Number of bytes preceding the start of the routine to allow in ; the "hit range". This is required in some cases where code has ; been placed prior to the start of the routine to overcome branch ; range restrictions (EXE$QIO does this). ; RWAST_TABLE: RWPC EXE$CANCEL , <^x1E0>,- , 1 RWPC EXE$DASSGN , <^x100>,- , 2 RWPC EXE$DELPRC , <^x120>, , 0 RWPC EXE$DELPRC , <^x180>, , 0 RWPC EXE$QIO , <^x100>,- , 3, <^x50> RWPC EXE$SUSPND , <^x1A0>, , 0 RWPC EXE$PSCAN_LOCKCTX, <^x80>, , 0 RWPC MMG$DELPAG , <^x700>, , 0 RWPC EXE$SNGLEQUOTA , <^x100>,- , 4 RWPC 0 , <0 >, ; Define local storage requirements ; The following data structure is allocated on the stack and referenced with R6. MWAIT_C_FAOBUFLEN = 512 $defini MWAIT $def MWAIT_Q_FAODSC .blkq 1 $def MWAIT_T_FAOBUF .blkb $def MWAIT_L_FLAGS .blkl 1 ; Program status flags _vield MWAIT,0,<- ,- ; Title has been displayed ,- ; Set if CHMx vectors looked for ,- ; CHMK vectors found ,- ; CHME vectors found ,- ; Process is in RWAST ,- ; Process is in RWMBX ,- ; Process is in a JIB related wait > $def MWAIT_R_PRMLST ; $FAOL parameter list $def MWAIT_L_EPID .blkl 1 ; EPID of process $def MWAIT_S_USERNAME .blkl 1 ; JIB$S_USERNAME $def MWAIT_T_USERNAME .blkl 1 ; => JIB$T_USERNAME $def MWAIT_S_LNAME .blkl 1 ; = PCB$T_LNAME<0:8> (namelength) $def MWAIT_T_LNAME .blkl 1 ; => PCB$T_LNAME (process name) $def MWAIT_L_PRI .blkl 1 ; 31 - PCB$B_PRI $def MWAIT_L_PRIB .blkl 1 ; 31 - PCB$B_PRIB $def MWAIT_T_STATE .blkl 1 ; => ascic state string $def MWAIT_L_CPU_ID .blkl 1 ; PCB$L_CPU_ID if state=CUR $def MWAIT_T_TERMINAL .blkl 1 ; => PCB$T_TERMINAL $def MWAIT_Q_WAITIME .blkq 1 ; Delta wait time $def MWAIT_R_EFNLST ; $FAOL parameter list $def MWAIT_L_EFWM .blkl 1 ; PCB$L_EFWM $def MWAIT_L_ALL .blkl 1 ; .TRUE. if waiting on all EFNs $def MWAIT_L_WEFC .blkl 1 ; PCB$B_WEFC (0..3) $def MWAIT_L_WAITIME .blkl 1 ; => MWAIT_Q_WAITIME $def MWAIT_L_EFCS .blkl 1 ; PCB$L_EFCS (0..31) $def MWAIT_L_EFCU .blkl 1 ; PCB$L_EFCU (32..63) $def MWAIT_K_LENGTH ; Length of local memory $defend MWAIT .subtitle MWAIT Determine process wait reasons ;+ ; MWAIT ; ; Description: ; ; Main entry point for the MWAIT utility. ; Ask for the EPID of the process to examine and call the main code. ; ; If an explicit PID is given, we examine just that PID. If no PID ; is specified, we scan the entire PCB vector table for all valid PIDs. ; ; Inputs: ; ; Target EPID or 0 from the user. ; ; Outputs: ; ; Notes: ; ;- .psect $CODE pic, shr, rd, nowrt, exe, gbl .entry MWAIT,^m<> pushab EPID_DESC ; Where to return PID text pushab PID_PROMPT ; Prompt to use for input pushab EPID_DESC ; Returned resultant length calls #3, g^LIB$GET_FOREIGN ; Get the PID text blbc r0, 05$ ; Check for errors pushab EPID ; Where to return binary PID pushab EPID_DESC ; Ascii PID text calls #2, g^OTS$CVT_TZ_L ; Convert the PID to binary blbc r0, 05$ ; Check for errors $cmexec_s routin=10$,- arglst = E_ARGS ; Argument list 05$: ret 10$: .word ^m movab g^EXE$SIGTORET, SF$A_HANDLER(fp) ; Set up our exception handler subl #MWAIT_K_LENGTH, sp ; Get some space for required data movl sp, r6 ; We will use R6 as or data pointer movab MWAIT_Q_FAODSC(r6), r0 ; R0 => FAO descriptor movab MWAIT_T_FAOBUF(r6), DSC$A_POINTER(r0) movb #DSC$K_DTYPE_T, DSC$B_DTYPE(r0) movb #DSC$K_CLASS_S, DSC$B_CLASS(r0) clrl MWAIT_L_FLAGS(r6) ; Say nothing has happened movl 4(ap), r0 ; R0 = PID to look for beqlu 15$ ; No PID, so do the lot. jsb g^EXE$EPID_TO_PCB ; Convert R0(pid) to R0(PCB) beqlu 14$ ; PID is not valid pushl r0 ; Save the PCB address pushaq TITLE ; Display title calls #1, g^LIB$PUT_OUTPUT blbc r0, 100$ ; Exit if any error calls #1, DISPLAY_PROCESS ; Display this process ret 14$: movzwl #SS$_NONEXPR, r0 ret 15$: movl #0, r10 ; R10 = Process index 20$: movl @SCH$GL_PCBVEC[r10], r4 ; R4 => Next PCB cmpl r4, g^SCH$AR_NULLPCB ; If it is the NULL process... beqlu 40$ bbss #MWAIT_V_TITLE, MWAIT_L_FLAGS(r6), 30$ pushaq TITLE ; Display title calls #1, g^LIB$PUT_OUTPUT 30$: pushl r4 ; Stack the PCB address calls #1, DISPLAY_PROCESS ; Display this process blbc r0, 100$ ; Exit if any error 40$: incl r10 ; Move to next table entry cmpl r10, g^SCH$GL_MAXPIX ; At the end of the table yet? blssu 20$ ; Get next process... movzwl #SS$_NORMAL, r0 ; Return success to caller 100$: ret .subtitle DISPLAY_PROCESS Display information on one process ;+ ; DISPLAY_PROCESS ; ; Description: ; ; Display the information that we can given the passed PCB. ; ; Inputs: ; ; R4 => PCB of process ; R6 => Internal stack ; ; Outputs: ; ; R0 = VMS status value ; ; Notes: ; ;- .enable local_block .entry DISPLAY_PROCESS, ^m movl 4(ap), r4 ; R4 => process's PCB movl PCB$L_JIB(r4), r5 ; R5 => process's JIB movl PCB$L_EPID(r4), MWAIT_L_EPID(r6) movl #JIB$S_USERNAME, MWAIT_S_USERNAME(r6) movab NULL_USERNAME, MWAIT_T_USERNAME(r6) ; Assume no username tstl r5 ; Any JIB? beqlu 10$ ; branch if none movab JIB$T_USERNAME(r5), MWAIT_T_USERNAME(r6) ; Save username 10$: movzbl PCB$T_LNAME(r4), MWAIT_S_LNAME(r6) ; Process name length movab PCB$T_LNAME+1(r4), MWAIT_T_LNAME(r6) ; Process name subb3 PCB$B_PRI(r4), #31, MWAIT_L_PRI(r6) ; Current priority subb3 PCB$B_PRIB(r4), #31, MWAIT_L_PRIB(r6) ; Base priority ; ; Determine the process's state. ; ; 1. If the process is NOT in MWAIT, just display the appropriate wait state ; 2. If it is MWAIT, look at the contents of EFWM... ; a. If EFWM is a small integer, less than RSN$_MAX, display the ; corresponding resource wait string. ; b. If EFWM contains the address of the JIB, then JIB$B_FLAGS contains ; bits saying what process resource is being waited for. Display ; the corresponding string (TQE,BYTCNT or a combination). ; c. Check the table of known MUTEXs to see if we have a match. If ; one is found (from IDSM table 8.3), then display a nice string. ; d. Display "MWAIT?" - an unknown MWAIT. The mutex address will be ; shown in the wait mask field. ; bicl #, MWAIT_L_FLAGS(r6) ; Say its not waiting (yet) movzwl PCB$W_STATE(r4), r0 ; R0 = Process state cmpl r0, #SCH$C_MWAIT ; Is it a mutex wait? beqlu 20$ ; Yes! movl SCH_TABLE[r0], MWAIT_T_STATE(r6) ; Set the description brb 29$ 20$: movl PCB$L_EFWM(r4), r0 ; R0 = resource status cmpl r0, #RSN$_MAX ; Is it a resource wait? bgtru 22$ movl RSN_TABLE[r0], MWAIT_T_STATE(r6) ; Set the description cmpl r0, #RSN$_ASTWAIT ; Was it RWAST? bnequ 21$ ; Nope bisl #MWAIT_M_RWAST, MWAIT_L_FLAGS(r6) ; Remember this for later brb 29$ 21$: cmpl r0, #RSN$_MAILBOX ; Was it RWMBX? bnequ 29$ ; Nope bisl #MWAIT_M_RWMBX, MWAIT_L_FLAGS(r6) ; Remember this for later brb 29$ 22$: cmpl r5, r0 ; Is it the JIB address? bnequ 24$ bisl #MWAIT_M_JIBWAIT, MWAIT_L_FLAGS(r6) ; Remember this for later movzbl JIB$B_FLAGS(r5), r0 ; R0 = JIB wait flags... bicl #^c, r0 movl JIB_TABLE[r0], MWAIT_T_STATE(r6) ; Set the description brb 29$ 24$: moval MTX_TABLE, r3 ; R3 => mutex list table 25$: movq (r3)+, r1 ; R1 = Mutex, R2 => descrip cmpl r1, #-1 ; At the end? beqlu 26$ ; Yes cmpl r0, r1 ; Does the mutex match? bnequ 25$ ; Try next one if not 26$: movl r2, MWAIT_T_STATE(r6) ; Set process state 29$: ; ; Check for terminal name. ; a) If name was returned, use that name ; b) If batch mode then say -Bat- ; c) If network process then say -Net- ; d) If process has an owner then say -Sub- ; e) If all the above fail, say -Det- ; tstb PCB$T_TERMINAL(r4) ; Any terminal name? beqlu 30$ movab PCB$T_TERMINAL(r4), MWAIT_T_TERMINAL(r6) ; Terminal name brb 37$ 30$: bbc #PCB$V_BATCH, PCB$L_STS(r4), 32$ ; Batch mode? movab BATCH_MODE, MWAIT_T_TERMINAL(r6) ; -Bat- brb 37$ 32$: bbc #PCB$V_NETWRK, PCB$L_STS(r4), 34$ ; Network connect object movab NETWORK_MODE, MWAIT_T_TERMINAL(r6) ; -Net- brb 37$ 34$: tstl PCB$L_OWNER(r4) ; If =0 then detached, else sub bnequ 36$ ; Branch if subprocess movab DETACH_MODE, MWAIT_T_TERMINAL(r6) ; -Det- brb 37$ 36$: movab SUB_MODE, MWAIT_T_TERMINAL(r6) ; -Sub- 37$: movaq FAOCTL, r1 ; R1 => standard $FAO control string cmpw PCB$W_STATE(r4), #SCH$C_CUR ; Is this a current process? bnequ 39$ bbc #SMP$V_ENABLED, g^SMP$GL_FLAGS, 39$ ; Is it an SMP system? movl PCB$L_CPU_ID(r4), MWAIT_L_CPU_ID(r6) ; Yes, so save the CPU id movaq FAOCTL_SMP, r1 ; R1 => SMP $FAO control string 39$: movab MWAIT_Q_FAODSC(r6), r0 ; R0 => FAO descriptor movw #MWAIT_C_FAOBUFLEN, DSC$W_LENGTH(r0) $FAOL_S ctrstr=(r1) , - outlen=MWAIT_Q_FAODSC(r6), - ; Format output outbuf=MWAIT_Q_FAODSC(r6), - prmlst=MWAIT_R_PRMLST(r6) blbcw r0, 100$ pushaq MWAIT_Q_FAODSC(r6) ; Display output calls #1, g^LIB$PUT_OUTPUT ; Now handle the event flag clusters and wait masks movl PCB$L_EFWM(r4), MWAIT_L_EFWM(r6) ; Event flag wait mask movzwl PCB$W_STATE(r4), r0 ; R0 = Process state cmpw r0, #SCH$C_CEF ; For EFN waits, negate wait mask beqlu 40$ cmpw r0, #SCH$C_LEF beqlu 40$ cmpw r0, #SCH$C_LEFO bnequ 45$ 40$: mcoml MWAIT_L_EFWM(r6), MWAIT_L_EFWM(r6) ; Make mask into set of bits 45$: extzv #PCB$V_WALL, #1, PCB$L_STS(r4), MWAIT_L_ALL(r6) ; .TRUE. if wait is for all movzbl PCB$B_WEFC(r4), MWAIT_L_WEFC(r6) ; Set the EFN cluster number movaq MWAIT_Q_WAITIME(r6), MWAIT_L_WAITIME(r6) ; Set the time buffer subl3 PCB$L_WAITIME(r4), g^EXE$GL_ABSTIM_TICS, r0 ; R0 = Waitime in 10ms ticks emul r0, #100000, #0, MWAIT_Q_WAITIME(r6) ; Make into an ABS time mcoml MWAIT_Q_WAITIME(r6), MWAIT_Q_WAITIME(r6) ; ...and now a DELTA mcoml MWAIT_Q_WAITIME+4(r6), MWAIT_Q_WAITIME+4(r6) movl PCB$L_EFCS(r4), MWAIT_L_EFCS(r6) ; 0: System EFN mask movl PCB$L_EFCU(r4), MWAIT_L_EFCU(r6) ; 1: User EFN mask movab MWAIT_Q_FAODSC(r6), r0 ; R0 => FAO descriptor movw #MWAIT_C_FAOBUFLEN, DSC$W_LENGTH(r0) $FAOL_S ctrstr=EFAOCTL, - outlen=MWAIT_Q_FAODSC(r6), - ; Format output outbuf=MWAIT_Q_FAODSC(r6), - prmlst=MWAIT_R_EFNLST(r6) blbcw r0, 100$ pushaq MWAIT_Q_FAODSC(r6) ; Display output calls #1, g^LIB$PUT_OUTPUT movl PCB$L_EFC2P(r4), r1 ; Is there an EFC #2 ? beqlu 50$ movzbl CEB$T_EFCNAM(r1), r2 movab CEB$T_EFCNAM+1(r1), r3 $printf CEFAOCTL, 2, CEB$L_EFC(r1), CEB$L_UIC(r1), r2, r3 50$: movl PCB$L_EFC3P(r4), r1 ; Is there an EFC #3 ? beqlu 55$ movzbl CEB$T_EFCNAM(r1), r2 movab CEB$T_EFCNAM+1(r1), r3 $printf CEFAOCTL, 3, CEB$L_EFC(r1), CEB$L_UIC(r1), r2, r3 55$: bbc #PCB$V_PHDRES, PCB$L_STS(r4), 60$ ; Only check PHD if resident movl PCB$L_PHD(r4), r5 ; R5 => PHD extzv #PSL$V_CURMOD, #PSL$S_CURMOD, PHD$L_PSL(r5), r1 ; R1 = Processor mode extzv #PSL$V_PRVMOD, #PSL$S_PRVMOD, PHD$L_PSL(r5), r2 ; R2 = Previous mode printf ,- MODE_TABLE[r1], MODE_TABLE[r2] 60$: movzbl PCB$B_ASTACT(r4), r1 ; R1 = AST active mask movzbl PCB$B_ASTEN(r4), r2 ; R2 = AST enabled mask subl3 PCB$L_ASTQFL(r4), PCB$L_ASTQBL(r4), r3 ; .TRUE. if ASTs waiting printf ,- AST_TABLE[r1], AST_TABLE[r2], r3 bbc #MWAIT_V_RWAST, MWAIT_L_FLAGS(r6), 80$ jsb g^PROCESS_RWAST ; Deal with RWAST states 80$: bbc #MWAIT_V_RWMBX, MWAIT_L_FLAGS(r6), 85$ jsb g^PROCESS_RWMBX ; Deal with RWMBX states 85$: bbc #MWAIT_V_JIBWAIT, MWAIT_L_FLAGS(r6), 90$ jsb g^PROCESS_JIBWAIT ; Deal with JIB wait states 90$: movzwl #SS$_NORMAL, r0 ; Return success to caller 100$: ret .disable local_block .subtitle PROCESS_RWAST Analyse RWAST condition ;+ ; PROCESS_RWAST ; ; Description: ; ; This routine is called when a process that is in RWAST is scanned. ; The code attempts to determine the state of the various critical ; quotas and system flags. ; ; Inputs: ; ; R4 => PCB ; R6 => Internal stack ; ; Outputs: ; ; R0 = VMS status value ; ; Notes: ; ;- .enable local_block PROCESS_RWAST: bbc #PCB$V_SSRWAIT, PCB$L_STS(r4), 1$ ; print < Process resource wait is DISABLED.> 1$: bbs #PCB$V_SSRWAIT, PCB$L_STS(r4), 2$ ; print < Process resource wait is ENABLED.> 2$: bbc #PCB$V_DELPEN, PCB$L_STS(r4), 3$ ; Process being deleted? print < Process is marked for deletion.> 3$: tstw PCB$W_DIOCNT(r4) ; Any DIO left? bnequ 10$ print < Process has no DIO left (DIOCNT zero).> 10$: tstw PCB$W_BIOCNT(r4) ; Any BIO left? bnequ 20$ print < Process has no BIO left (BIOCNT zero).> 20$: subw3 PCB$W_DIOCNT(r4), PCB$W_DIOLM(r4), r1 ; Any DIO outstanding? beqlu 30$ printf ,- r1, PCB$W_DIOCNT(r4), PCB$W_DIOLM(r4) 30$: subw3 PCB$W_BIOCNT(r4), PCB$W_BIOLM(r4), r1 ; Any BIO outstanding? beqlu 40$ printf ,- r1, PCB$W_BIOCNT(r4), PCB$W_BIOLM(r4) 40$: tstw PCB$W_PRCCNT(r4) ; Any sub-processes? beqlu 50$ printf ,- PCB$W_PRCCNT(r4) 50$: tstb PCB$B_DPC(r4) ; Any delete pending count? beqlu 60$ printf ,- PCB$B_DPC(r4) 60$: tstw PCB$W_ASTCNT(r4) ; Any ASTs left? bnequ 70$ print < Process has no AST entries left (ASTCNT zero).> 70$: ; ; If the process header is resident, we can check the PHD$W_ASTLM (initial ; AST count) field to see if there are outstanding ASTs. ; We can also dump the saved registers, and therefore get the saved PC from ; the process. With that, we can attempt to locate the routine in the executive ; that started the wait. ; bbs #PCB$V_PHDRES, PCB$L_STS(r4), 75$ ; Only check PHD if resident print < Process header is not resident.> brw 100$ 75$: movl PCB$L_PHD(r4), r5 ; R5 => PHD subw3 PCB$W_ASTCNT(r4), PHD$W_ASTLM(r5), r1 ; Any ASTs outstanding? beqlu 80$ printf ,- r1, PCB$W_ASTCNT(r4), PHD$W_ASTLM(r5) 80$: movab MWAIT_Q_FAODSC(r6), r0 ; R0 => FAO descriptor movw #MWAIT_C_FAOBUFLEN, DSC$W_LENGTH(r0) $FAOL_S ctrstr=PHDCTL, - outlen=MWAIT_Q_FAODSC(r6), - ; Format output outbuf=MWAIT_Q_FAODSC(r6), - prmlst=PHD$L_R0(r5) ; Assumption here is that all registers in the PHD are contiguous blbcw r0, 100$ pushaq MWAIT_Q_FAODSC(r6) ; Display output calls #1, g^LIB$PUT_OUTPUT ; Print the register dump blbcw r0, 100$ ; ; Attempt to locate the change mode (CHMx) vectors inside of VMS ; bbs #MWAIT_V_CMVECT, MWAIT_L_FLAGS(r6), 85$ jsb g^LOCATE_CMVECT ; Find the change mode vectors 85$: ; ; Scan the table of places in the executive where RSN$_ASTWAIT can be generated. ; We check a range of addresses from a table of the form: ; ; .address descriptive-text ; .long EXE$xxxx ; Base address ; .long value ; Offset within which RWAST will occur ; .long backoff ; Offset before base address to allow ; .long action ; Action routine number ; ... ; .address text ; Default text to use for PC evaluation ; .long 0, 0 ; End of table ; moval RWAST_TABLE, r0 ; R0 => start of RWAST PC table 90$: movl (r0)+, r3 ; R3 => text string movq (r0)+, r1 ; R1 = start, R2 = end movq (r0)+, r7 ; R7 = backoff, R8 = action number tstl r1 ; Any start address? beqluw 98$ ; Check for end of table ; ; Now we have to decode the real address of the selected code path in the ; executive. ; There are 4 forms we check for (as all code is vectored): ; ; 1. Ordinary (base image vectored) system routine. ; Format: jmp @#address ; We check for this first as "@#" has a value of ^x9F, which ; is an illegal value for an entry mask (for which <13:12> must be zero). ; ; 2. Base vectored system service routine. ; Format: .word ^m<> ; Entry mask ; jmp @#address ; ; 3./4. Change mode entries (system services). ; Format: .word ^m<> ; Entry mask ; chme/k #index ; cmpw (r1), #OP$_JMP!<^x9F00> ; Check for JMP @# bnequ 91$ ; Branch if not this movl 2(r1), r1 ; R1 = real routine address (skip JMP @#) brw 97$ 91$: cmpw 2(r1), #OP$_JMP!<^x9F00> ; Check for entry mask + JMP @# bnequ 92$ ; Branch if not this movl 4(r1), r1 ; R1 = real routine address (skip JMP @#) brw 97$ ; ; Can't do CHMx vector resolution without the vectors. ; 92$: cmpw 2(r1), #OP$_CHME!<^x8F00> ; Check for entry mask + CHME #nnnn bnequ 94$ ; Branch if not this bbs #MWAIT_V_CHME, MWAIT_L_FLAGS(r6), 93$ brb 90$ 93$: movzwl 4(r1), r1 ; R1 = CHME code movaq @CMOD$AR_EXEC_DISPATCH_VECTOR[r1], r1 ; R1 => dispatch vector for real code movl DISP_A_SERVICE_ROUTINE(r1), r1 ; R1 => Real routine address brb 97$ 94$: cmpw 2(r1), #OP$_CHMK!<^x8F00> ; Check for entry mask + CHMK #nnnn bnequ 96$ ; Branch if not this bbs #MWAIT_V_CHMK, MWAIT_L_FLAGS(r6), 95$ brb 90$ 95$: movzwl 4(r1), r1 ; R1 = CHME code movaq @CMOD$AR_KERNEL_DISPATCH_VECTOR[r1], r1 ; R1 => dispatch vector for real code movl DISP_A_SERVICE_ROUTINE(r1), r1 ; R1 => Real routine address brb 97$ 96$: printf <%MWAIT-W, Illegal vector (not known format) !XL:!/!_'!AS'>,- (r1), r3 brw 90$ 97$: ;printf , r3, r1 addl2 r1, r2 ; R2 = real end address subl2 r7, r1 ; Allow backoff if required ;printf < Range is !XL - !XL>, r1, r2 cmpl PHD$L_PC(r5), r1 ; In range? blssuw 90$ ; Branch if not cmpl PHD$L_PC(r5), r2 ; In range? bgtruw 90$ ; Branch if not 98$: tstl r1 ; Was any address actually found? bnequ 99$ ; Yes, so display it jsb CHECK_DRIVERS ; Check for code being in a driver ; ; Now add in detail of the states to save some work with SDA. ; 99$: case r8, <1000$,1010$,1020$,1030$,1040$,1050$> ; Action 0 - do nothing 1000$: $printf (r3) ; Print the message brw 100$ ; Action 1 - R5 = UCB, R6 = CCB, R7 = channel 1010$: mnegw PHD$L_R7(r5), r7 ; R7 = Real channel number movl PHD$L_R5(r5), r5 ; R5 => device UCB movl UCB$L_DDB(r5), r2 ; R2 => device DDB movab DDB$T_NAME(r2), r2 ; R2 => device name string $printf (r3), r2, UCB$W_UNIT(r5), r7 ; Display formatted details brw 100$ ; Action 2 - R5 = channel, R6 = CCB 1020$: movzwl PHD$L_R5(r5), R7 ; R7<0:15> = Channel number $printf (r3), r7 ; Display formatted details brw 100$ ; Action 3 - R4 = PCB, R5 = UCB, R7 = CCB 1030$: movl PHD$L_R5(r5), r5 ; R5 => device UCB movl UCB$L_DDB(r5), r2 ; R2 => device DDB movab DDB$T_NAME(r2), r2 ; R2 => device name string $printf (r3), r2, UCB$W_UNIT(r5) ; Display formatted details brw 100$ ; Action 4 - R1 = quantity, R2 = address, R3 = width(bits) 1040$: movl PHD$L_R2(r5), r2 ; R2 => Quota field extzv #0, PHD$L_R3(r5), (r2), r1 ; R1 = Current quota value $printf (r3), PHD$L_R1(r5), r1 ; Display formatted details ; ; Check for a few common addresses to isolate the problem ; movaw PCB$W_DIOCNT(r4), r1 ; R1 => DIO count field cmpl PHD$L_R2(r5), r1 ; Is this the one? bnequ 1042$ ; No, its not printf ,- PCB$W_DIOCNT(r4) brw 100$ ; Exit 1042$: movaw PCB$W_BIOCNT(r4), r1 ; R1 => BIO count field cmpl PHD$L_R2(r5), r1 ; Is this the one? bnequ 1044$ ; No, its not printf ,- PCB$W_BIOCNT(r4) brw 100$ ; Exit 1044$: movaw PCB$W_ASTCNT(r4), r1 ; R1 => AST count field cmpl PHD$L_R2(r5), r1 ; Is this the one? bnequ 1046$ ; No, its not printf ,- PCB$W_ASTCNT(r4) brb 100$ ; Exit 1046$: brb 100$ ; Action 5 - Identified driver, R1 => DPT 1050$: subl3 r1, PHD$L_PC(r5), r2 ; R2 = offset within driver movab DPT$T_NAME(r1), r1 ; R1 => driver name string printf , r1, r2 ; brb 100$ 100$: rsb .disable local_block .subtitle CHECK_DRIVERS Check if PC lies in a driver ;+ ; CHECK_DRIVERS ; ; Description: ; ; This routine scans the table of device drivers to see if the PC ; we want to match lies in the driver. ; ; Inputs: ; ; R3 => Default text ; R4 => PCB of process ; R5 => PHD of process ; R6 => Internal stack ; ; Outputs: ; ; R1/R2 trashed ; R3 => Default text ; R4 => PCB of process ; R5 => PHD of process ; R6 => Internal stack ; R8 = 0 (no driver match found) ; = 5 (match found...) ; R1 => DPT of located driver ; ; Notes: ; ;- .enable local_block CHECK_DRIVERS: clrl r8 ; Assume nothing found movl g^IOC$GL_DPTLIST, r1 ; R1 => first driver DPT 10$: movzwl DPT$W_SIZE(r1), r2 ; R2 = length of driver addl2 r1, r2 ; R2 => end of driver ;movab DPT$T_NAME(r1), r7 ;printf , r7, r1, r2 cmpl PHD$L_PC(r5), r1 ; In range? blssu 20$ ; Branch if not cmpl PHD$L_PC(r5), r2 ; In range? bgequ 20$ ; Branch if not movl #5, r8 ; R8 = DPT format index brb 100$ ; ...and exit 20$: movl DPT$L_FLINK(r1), r1 ; R1 => next DPT in list cmpl r1, #IOC$GL_DPTLIST ; At end yet? bnequ 10$ ; Go round if not at end 100$: rsb .disable local_block .subtitle PROCESS_RWMBX Analyse RWMBX condition ;+ ; PROCESS_RWMBX ; ; Description: ; ; This routine is called when a process that is in RWMBX is scanned. ; The code attempts to determine the identification of the mailbox ; or device causing the problem. ; ; Inputs: ; ; R4 => PCB ; R6 => Internal stack ; ; Outputs: ; ; R0 = VMS status value ; ; Notes: ; ;- .enable local_block PROCESS_RWMBX: bbc #PCB$V_SSRWAIT, PCB$L_STS(r4), 1$ ; print < Process resource wait is DISABLED.> 1$: bbs #PCB$V_SSRWAIT, PCB$L_STS(r4), 2$ ; print < Process resource wait is ENABLED.> 2$: bbc #PCB$V_DELPEN, PCB$L_STS(r4), 3$ ; Process being deleted? print < Process is marked for deletion.> 3$: movl PCB$L_PHD(r4), r0 ; R0 => PHD movl PHD$L_R3(r0), r3 ; R3 => IRP for write request movl PHD$L_R5(r0), r5 ; R5 => UCB for mailbox movl UCB$L_DDB(r5), r2 ; R2 => device DDB movab DDB$T_NAME(r2), r2 ; R2 => device name string mnegw IRP$W_CHAN(r3), r1 ; R1 = Channel number printf ,- IRP$W_BCNT(r3), r2, UCB$W_UNIT(r5), r1 printf ,- UCB$W_MSGCNT(r5), UCB$W_INIQUO(r5), UCB$W_BUFQUO(r5) 100$: rsb .disable local_block .subtitle PROCESS_JIBWAIT Analyse a JIB wait condition ;+ ; PROCESS_JIBWAIT ; ; Description: ; ; This routine is called when a process that is in a JIB quota related ; wait is scanned. ; The code attempts to determine the state of the various critical ; quotas and system flags. ; ; Inputs: ; ; R4 => PCB ; R6 => Internal stack ; ; Outputs: ; ; R0 = VMS status value ; ; Notes: ; ;- .enable local_block PROCESS_JIBWAIT: bbc #PCB$V_SSRWAIT, PCB$L_STS(r4), 1$ ; print < Process resource wait is DISABLED.> 1$: bbs #PCB$V_SSRWAIT, PCB$L_STS(r4), 2$ ; print < Process resource wait is ENABLED.> 2$: bbc #PCB$V_DELPEN, PCB$L_STS(r4), 3$ ; Process being deleted? print < Process is marked for deletion.> 3$: movl PCB$L_JIB(r4), r3 ; R3 => JIB beqluw 100$ ; Paranoia! bbc #JIB$V_BYTCNT_WAITERS, JIB$B_FLAGS(r3), 10$ print < Job buffered I/O count exhausted> clrl r1 ; Assume we cannot find size bbc #PCB$V_PHDRES, PCB$L_STS(r4), 05$ ; Only check PHD if resident movl PCB$L_PHD(r4), r1 ; R1 => PHD movl PHD$L_R1(r1), r1 ; R1 = amount requested 05$: printf ,- r1, JIB$L_BYTCNT(r3), JIB$L_BYTLM(r3) 10$: tstw JIB$W_FILCNT(r3) ; Any files left? bnequ 20$ print < Job open file count exhausted (FILCNT zero)> 20$: bbc #JIB$V_TQCNT_WAITERS, JIB$B_FLAGS(r3), 30$ printf ,- JIB$W_TQCNT(r3), JIB$W_TQLM(r3) 30$: tstl JIB$L_PGFLCNT(r3) ; Any page file space? bnequ 40$ print < Job page file space exhausted (PGFLCNT zero)> 40$: 100$: rsb .disable local_block .subtitle LOCATE_CMVECT Locate the change mode vectors ;+ ; LOCATE_CMVECT ; ; Description: ; ; This routine is called to locate the change mode (CHMx) vectors in ; the appropriate executive loadable image (EXCEPTION.EXE). ; The loadable image list is scanned to locate EXCEPTION.EXE. When found, ; we search through the "read-only" non-paged pool base for the ; "signature" of the CHME and CHMK vectors. We do a search because this ; code must be able to work with any version of VMS V5, and the previous ; method of using fixed offsets was a loser! ; ; Once we have the addresses of the CHME and CHMK vectors, we can get the ; address of the *real* routines from DISP_A_SERVICE_ROUTINE ; ; We have to use the change mode vectors to locate the real code for ; system services as the CHMx numbers are allocated dynamically as the ; services are loaded, and it is therefore uncertain which entry a ; particular service will use. ; ; Inputs: ; ; R6 => Internal stack ; ; Outputs: ; ; R8-R11 destroyed ; ; MWAIT_M_CMVECT is set in MWAIT_L_FLAGS. ; MWAIT_M_CHMK is set if we located the change mode to kernel vector ; MWAIT_M_CHME '' '' '' '' '' '' '' '' '' executive '' ; ; CMOD$AR_KERNEL_DISPATCH_VECTOR CHMK dispatch vector address ; CMOD$AR_EXEC_DISPATCH_VECTOR CHME '' '' '' ; ; Notes: ; ;- .enable local_block LOCATE_CMVECT: bisl #MWAIT_M_CMVECT, MWAIT_L_FLAGS(r6) ; Say we have looked bicl #, MWAIT_L_FLAGS(r6) movab EXCEPTION, r8 ; R8 => exception image name movzbl (r8)+, r9 ; R9 = image name length movaq g^LDR$GQ_IMAGE_LIST, r10 ; R10 => list header movl r10, r11 ; R11 => list header ASSUME LDRIMG$L_FLINK eq 0 10$: movl LDRIMG$L_FLINK(r10), r10 ; R10 => 1st loadable image block cmpl r10, r11 ; End of list? bnequ 20$ printf <%MWAIT-W, Failed to locate image !AC>, EXCEPTION brw 100$ 20$: cmpb r9, LDRIMG$B_IMGNAMLEN(r10) ; Same length? bnequ 10$ ; No cmpc3 r9, (r8), LDRIMG$T_IMGNAM(r10) ; Same text? bnequ 10$ ; Branch if no movl LDRIMG$L_NONPAG_R_BASE(r10), r11 ; R11 => Base of area to search moval MAX_VECTOR_OFFSET(r11), r9 ; R9 => end of range to search 25$: cmpl r11, r9 ; At end yet? bgtru 30$ ; Branch if yes cmpl KERNEL_CHECK, (r11) ; Check that its the right vector bnequ 27$ cmpl KERNEL_CHECK+4, 4(r11) bnequ 27$ cmpl KERNEL_CHECK+8, 8(r11) bnequ 27$ movl r11, CMOD$AR_KERNEL_DISPATCH_VECTOR ;printf , r11 bisl #MWAIT_M_CHMK, MWAIT_L_FLAGS(r6) ; Say we found the CHMK vector brb 40$ 27$: addl2 #4, r11 ; No match, try next longword brb 25$ 30$: printf <%MWAIT-W, CHMK vector not found> 40$: movl LDRIMG$L_NONPAG_R_BASE(r10), r11 45$: cmpl r11, r9 ; At end yet? bgtru 50$ ; Branch if yes cmpl EXEC_CHECK, (r11) ; Check that its the right vector bnequ 47$ cmpl EXEC_CHECK+4, 4(r11) bnequ 47$ cmpl EXEC_CHECK+8, 8(r11) bnequ 47$ movl r11, CMOD$AR_EXEC_DISPATCH_VECTOR ;printf , r11 bisl #MWAIT_M_CHME, MWAIT_L_FLAGS(r6) ; Say we found the CHME vector brb 100$ 47$: addl2 #4, r11 ; No match, try next longword brb 45$ 50$: printf <%MWAIT-W, CHME vector not found> 100$: rsb .disable local_block .end MWAIT