; Diskstat.Mar Version 2.2 July, 1989 ; ; Disk Extent Display Program ; ; This program is designed to help the system manager determine which ; files are potential sources of excessive I/O. Window turns occur ; whenever access is required to a portion of a file not in the current ; window. If the portion of the file desired is not found in the current ; header then an extra disk read must be performed to fetch the new ; header. In addition, if large blocks of data are required it is ; possible that portions of the block are not contiguous. Thus an I/O ; request must be split into separate read requests. All of these factors ; cause additional I/O overhead. ; ; This program displays the number of extents in each file. If a file ; extends to multiple headers, the headers are presented in order. If ; multiple headers for a file are encountered, all headers used by the ; file are displayed, regardless of whether an individual header has ; less than the minimum number of extents. ; ; The INDEXF.SYS disk file is normally examined sequentially. Large ; chunks of data are fetched at a time and then are examined one block ; at a time. This was required to reduce the I/O overhead to a manage- ; able level. If multiple headers are found the index file is opened ; again for single block reads in random order. The additional overhead ; for these operations is negligible given the low rate at which this ; occurs. ; ; ; Notes: ; This program must be linked with COMMAND.OBJ, which provides the ; command line parsing routines. It must be assembled with Lib.Mlb ; in Sys$Library. ; ; .enable suppression ; Modules in Sys$Library:Lib.Mlb $HM2DEF ; Indexf.Sys home block definitions $FH2DEF ; Indexf.Sys file header definitions $FI2DEF ; Indexf.Sys file ident area definitions $FM2DEF ; Indexf.Sys file map area definitions ; Modules in Sys$Library:Starlet.Mlb $DCDEF ; Device class definitions $DVIDEF ; $GETDVI definitions $FIBDEF ; File info block definitions $IODEF ; I/O mode definitions $RMSDEF ; RMS definitions $SSDEF ; System return status defs ; Local macro definitions .macro chk_status ,?NEXT blbs r0,NEXT $exit_s r0 NEXT: .endm chk_status .macro do_output descr pushaq descr calls #1,g^lib$put_output chk_status .endm do_output .psect data long,noexe,pic MAXBLOCKS = 512 ; Max number of blocks to attempt BUFFERSIZE = MAXBLOCKS * 512 ; Size of block buffer BUFFERMASK = BUFFERSIZE * -2 ; Buffer offset mask (complemented) buffer: .blkb 512*MAXBLOCKS*2 ; Use dual buffers block: .blkb 512 ; File header buffer for RMS nam: $NAM fab: $FAB FAC = ,- FNA = disk_name,- NAM = nam,- DNM = <[000000]Indexf.Sys>,- SHR = rab: $RAB BKT = 2,- FAB = fab,- ROP = ,- UBF = block,- USZ = 512 head1: .ascid / INDEX FILE INFORMATION/ head2: .ascid / File id. UIC Rec. Exts. Blocks Filename/ record: .ascid /!AS (!5UW,!5UW,!2UB) [!5OW,!5OW] !3UW !3UL !6UL !AS!AS/ totals: .ascid / Disk !AS contains !SL files, !SL with !SL or more extents./ start_reverse: .ascid // end_reverse: .ascid // current_mode: .long end_reverse disk_prompt: .ascid /Disk name ? / extent_prompt: .ascid /Minium number of extents to display [8] ? / file_descr: .ascid /[000000]Indexf.Sys/ name_descr: .long 20 ; Filename descriptor .long 0 ; - always used name2_descr: .long 66 ; Filename descriptor .long 0 ; - used if name >= 20 bytes extents: .long 8 ; Min num of extents to display over_extents: .long 0 ; Num files exceeding extents disk_descr: .blkl 1 ; Disk name .long disk_name disk_name: .blkb 30 item_descr: .blkl 1 ; Input data string .long item_string item_string: .blkl 40 output_descr: .blkl 1 ; Output data string .long output_string output_string: .blkl 100 force_prompt: .long 0 ; If set, Lib$Get_Foreign uses prompt file_offset: .blkl 1 ; Offset of first file id num_files: .long 0 ; Number of files examined devclass: .long 0 ; Class of selected device (ie. disk) devtype: .long 0 ; Device type (ie. RA82) dvi_list: .word 4 ; Argument list used by $GETDVI .word DVI$_DEVCLASS .long devclass .long 0 .word 4 .word DVI$_DEVTYPE .long devtype .long 0 .long 0 fao_block: .long 14 ; Number of args .long record ; Format string .long output_descr ; Output length address .long output_descr ; Output string descriptor fao_arg1: .long 0 ; Video mode string address fao_arg2: .long 0 ; File id number fao_arg3: .long 0 ; File sequence number fao_arg4: .long 0 ; File relative volume number fao_arg5: .long 0 ; UIC group number fao_arg6: .long 0 ; UIC member number fao_arg7: .long 0 ; File header number fao_arg8: .long 0 ; Number of map pointers fao_arg9: .long 0 ; Total blocks in file .long name_descr ; First part of file name .long name2_descr ; Second part of file name last_byte: .long 0 ; Address of last byte + 1 last_read: .long 0 ; Return status of last file fetch iosb: .long 0,0 ; I/O status block fib: .blkl FIB$K_DIRDATA fib_descr: .long FIB$K_DIRDATA .long fib buffer_size: .long 128 ; Size of read operation qio_block: .long 12 ; $QIO argument block .long 1 ; Use event flag 1 channel: .long 0 ; Load this during $assign operation mode: .long IO$_ACCESS!IO$M_ACCESS .long iosb .long 0 .long 0 p1: .long fib_descr p2: .long 0 p3: .long 0 p4: .long 0 p5: .long 0 p6: .long 0 .psect diskstat pic,exe,long,shr .entry begin,^m<> MIN_EXTENTS: pushl force_prompt pushaq extent_prompt ; Get minimum number of extents pushaq item_descr ; to display. If the result is calls #3,GET_COMMAND ; NUL then use the default tstw item_descr ; If no data then use default beql GET_DISK pushaq item_descr pushal extents ; Otherwise convert data to calls #2,CVT_TO_INT ; binary form blbs r0,GET_DISK movl #1,force_prompt ; Illegal number - try again brb MIN_EXTENTS GET_DISK: pushl force_prompt pushaq disk_prompt ; Get device to examine pushaq disk_descr calls #3,GET_COMMAND tstw disk_descr ; If device is NUL then exit bneq CHK_COLON $exit_s #1 CHK_COLON: locc #^A/:/,disk_descr,disk_name ; Make sure device name is tstb r0 ; terminated with a colon bneq DO_FILENAME movb #^A/:/,(r1) incw disk_descr DO_FILENAME: movb disk_descr,fab+FAB$B_FNS ; Load disk name size $open FAB = fab ; Open INDEXF.SYS file for chk_status ; random access and connect $connect RAB = rab ; to it chk_status do_output head1 ; Print header lines do_output head2 $read RAB = rab ; Read home block chk_status ; Compute the offset to the first file header. Add the Index File Bitmap ; Virtual Block Number and the Index File Bitmap Size from the Home Block ; to get the offset of the first record of the index file. movzwl block+HM2$W_IBMAPVBN,r11 ; Get offset to first file id addw2 block+HM2$W_IBMAPSIZE,r11 subl3 #1,r11,file_offset ; Save offset-1 for later $assign_s - ; Try opening channel to device devnam = disk_descr,- ; using specified disk name chan = channel chk_status $getdviw_s - chan = channel,- ; Use current device itmlst = dvi_list chk_status movw nam+NAM$W_FID_NUM,fib+FIB$W_FID_NUM ; Copy file id to the movw nam+NAM$W_FID_SEQ,fib+FIB$W_FID_SEQ ; file info block movw nam+NAM$W_FID_RVN,fib+FIB$W_FID_RVN callg qio_block,SYS$QIOW ; Do initial access chk_status cmpl #DC$_DISK,devclass ; Is device a disk ? bneq LOAD_NAME ; If not, can't be DSA cmpl #DT$_RA80,devtype ; Is it a DSA disk ? bgtr LOAD_NAME ; If so, use default size movl #MAXBLOCKS,buffer_size LOAD_NAME: movl #IO$_READVBLK,mode ; Set read access mode moval buffer,p1 ; Set buffer address mull3 #512,buffer_size,p2 ; Set buffer size movl r11,p3 ; Starting VBN ; There are two ways of determining whether a file header is used. The ; most correct way is to look at the index file bitmap. Unfortunately, ; this must be read each time a block of data is fetched or it requires ; that the index file be locked against writing by other users. Neither ; of these alternatives is desirable. We have noticed that a valid ; header has certain other characteristics. These characteristics are ; examined in the CHECK_HDR section. ; ; Once the header is determined to be valid it is checked to see if the ; extent threshhold is execeeded and/or if it has multiple headers. In ; either case the header data is printed. If there are multiple headers ; the data is printed in reverse video. ; ; If multiple headers are found then the headers are fetched via random ; file access. After all subheaders have been examined reverse video is ; disabled and normal sequential reading is continued. ; ; After several tests, we found that the bottleneck in program exexution ; is the rate at which data can be fetched from the disks. To reduce ; this problem it was decided that two buffers should be used. This ; allows the program to use the data in one buffer while the other ; buffer is being filled. More than two buffers could be used, but it ; was decided that the additional complexity didn't provide sufficient ; gain to make it worthwhile. clrl last_read callg qio_block,Sys$Qio ; Fill first buffer chk_status moval 2*BUFFERSIZE+buffer,last_byte ; Set up as if one buffer has movl last_byte,r8 ; been examined and the other ; is being filled READ_BUFFER: cmpl #SS$_ENDOFFILE,last_read ; Read end-of-file already? bneq DO_READ ; If not, continue brw WRAP_UP ; Else finish routine DO_READ: $waitfr_s efn = #1 ; Wait for $QIO to complete movzwl iosb,last_read ; Save completion status ashl #-9,iosb+2,r9 ; Update starting position for addl2 r9,p3 ; the next $QIO addl2 #BUFFERSIZE,p1 ; Try going to next buffer bicl2 #BUFFERMASK,p1 ; Offset is MOD BUFFERSIZE bicl2 #BUFFERMASK,r8 ; Change current buffer addl3 r8,iosb+2,last_byte ; Save block end + 1 subl2 #512,r8 ; Allow for pre-add callg qio_block,Sys$Qio ; Read new blocks chk_status CHECK_BLOCK: addl2 #512,r8 ; Point to next page cmpl r8,last_byte ; At end of buffer ? bneq CHECK_HDR brw READ_BUFFER ; Try reading file CHECK_HDR: cmpb #^x28,FH2$B_IDOFFSET(r8) ; Id area offset is 40 words bneq CHECK_BLOCK ; Anything else is invalid tstw FH2$W_FID_NUM(r8) ; File id = 0 is not valid beql CHECK_BLOCK tstw FH2$W_SEG_NUM(r8) ; Skip nonzero segment - read bneq CHECK_BLOCK ; after root segment found tstw FH2$W_EX_FIDNUM(r8) ; Highlight multiple headers beql DO_BLOCK movaq start_reverse,current_mode DO_BLOCK: incl num_files ; Update #files examined jsb GET_EXTENTS ; Get #extents this record addl2 r0,over_extents ; Count files exceeding thresh blbs r0,TEST_EXTENTS ; 1 = exceeded threshhold tstw FH2$W_EX_FIDNUM(r8) ; Multiple headers ? beql CHECK_BLOCK ; Look at next header TEST_EXTENTS: do_output output_descr ; Send record to terminal tstw FH2$W_EX_FIDNUM(r8) ; Multiple headers ? beql CHECK_BLOCK ; If not, get next file header pushl r8 ; Save current position NEW_BLOCK: movzwl FH2$W_EX_FIDNUM(r8),rab+RAB$L_BKT moval block,r8 addl2 file_offset,rab+RAB$L_BKT ; Load new block number in RAB $read RAB = rab ; and read new block jsb GET_EXTENTS ; Get #extents this record do_output output_descr moval block,r8 ; Use RMS buffer tstw FH2$W_EX_FIDNUM(r8) ; Get extension file id bneq NEW_BLOCK movl (sp)+,r8 ; Point to $QIO buffer movaq end_reverse,current_mode ; Disable reverse video brw CHECK_BLOCK ; Get next file header WRAP_UP: $disconnect RAB = rab ; Close current record $close FAB = fab ; Close file $qiow_s chan = channel,- ; Clear current file access func = #IO$_DEACCESS chk_status $dassgn_s - ; Free channel chan = channel do_output end_reverse ; Make sure reverse video is ; disabled movw #100,output_descr ; Construct summary message $fao_s CTRSTR = totals,- OUTBUF = output_descr,- OUTLEN = output_descr,- P1 = #disk_descr,- P2 = num_files,- P3 = over_extents,- P4 = extents chk_status do_output output_descr ret ; ; Read the map pointer list for the current block and determine how ; many extents are referenced by this header. Print the results and ; update the cumulative statistics. ; ; GET_EXTENTS: movzbl FH2$B_MPOFFSET(r8),r1 ; Map area offset movzbl FH2$B_MAP_INUSE(r8),r2 ; Map words in use addb2 r1,r2 movaw (r8)[r1],r1 ; Starting map address movaw (r8)[r2],r2 ; Final map address clrl r4 ; Clear initial count clrl r5 ; Total blocks found clrl r6 ; Number of extents found TEST_MAP: addl2 r4,r5 ; Update total blocks so far cmpl r1,r2 ; End of map area ? bgeq EXTENT_DONE incl r6 extzv #FM2$V_FORMAT,#FM2$S_FORMAT,(r1),r3 ; Extract map pointer type beql FORMAT_00 bbc #1,r3,FORMAT_01 blbc r3,FORMAT_10 FORMAT_11: extzv #0,#14,(r1),r4 ; Word: Type, High blk count ashl #14,r4,r4 ; Word: Low block count movw 2(r1),r4 ; Lword: LBN addl2 #FM2$K_LENGTH3,r1 brb TEST_MAP FORMAT_10: extzv #0,#14,(r1),r4 ; Word: Type, blocks addl2 #FM2$K_LENGTH2,r1 ; Lword: LBN brb TEST_MAP FORMAT_01: movzbl (r1),r4 ; Word: Type, high LBN, blocks addl2 #FM2$K_LENGTH1,r1 ; Word: Low LBN brb TEST_MAP FORMAT_00: decl r6 ; Don't count these packets addl2 #FM2$K_LENGTH0,r1 ; Word: Type, placement brb TEST_MAP ; Ignore - usage unknown EXTENT_DONE: addl2 r6,r5 ; Actual blocks in each extent ; is one greater than is shown tstw FH2$W_SEG_NUM(r8) ; Always do output for bneq EXTENT_OUT ; multiple headers cmpl r6,extents ; More than selected extents ? bgeq EXTENT_OUT ; Then output data clrl r0 ; Extents in range rsb EXTENT_OUT: clrw name2_descr moval 80+FI2$T_FILENAME(r8),name_descr+4 locc #^A/ /,#20,80+FI2$T_FILENAME(r8); Is filename block full ? subw3 r0,#20,name_descr cmpb r0,#20 bleq BLD_OUTPUT locc #^A/ /,#66,80+FI2$T_FILENAMEXT(r8) ; Find 1st space subl3 r0,#66,name2_descr ; put length in descriptor moval 80+FI2$T_FILENAMEXT(r8),name2_descr+4 BLD_OUTPUT: movw #80,output_descr ; Set max string length movl current_mode,fao_arg1 movw FH2$W_FID_NUM(r8),fao_arg2 movw FH2$W_FID_SEQ(r8),fao_arg3 movb FH2$B_FID_RVN(r8),fao_arg4 movw FH2$W_UICGROUP(r8),fao_arg5 movw FH2$W_UICMEMBER(r8),fao_arg6 movw FH2$W_SEG_NUM(r8),fao_arg7 movl r6,fao_arg8 movl r5,fao_arg9 callg fao_block,SYS$FAO chk_status movl #1,r0 ; Extents exceeded threshold rsb .end begin