.TITLE DISK_FRAGMENTATION_STATISTICS .SUBTITLE DECLARE CONSTANTS AND MACROS .ENABLE DEBUG ; DISK FRAGMENTATION DISPLAY PROGRAM ; MICHAEL N. LEVINE ; CODE 3514 ; NAVAL WEAPONS CENTER ; CHINA LAKE ; CA ; (619)939-2465 AVN 437-2465 $DVIDEF $DCDEF ; MACRO TO CONVERT A LONG WORD BINARY NUMBER TO AN UNSIGNED INTEGER STRING .MACRO GENSTR SIZE,LOC,VAR MOVL SIZE,DESCRIPTER MOVAL LOC,DESCRIPTER+4 $FAO_S CTRSTR,,DESCRIPTER,VAR .ENDM .MACRO GENPCT VAR,LOC,DIVISOR,MULT,?AAA,?BBB,?CCC,?DDD,?EEE CVTLD VAR,TEMP BEQL DDD MULD2 TEN_K,TEMP .IF NB MULT CVTLD MULT,TEMP_2 MULD2 TEMP_2,TEMP .ENDC CVTLD DIVISOR,TEMP_2 BNEQ EEE CLRF TEMP BRB DDD EEE: DIVD2 TEMP_2,TEMP ADDD2 HALF,TEMP DDD: CVTDL TEMP,TEMP MOVL #5,DESCRIPTER MOVAL LOC,DESCRIPTER+4 $FAO_S CTRSTR_2,,DESCRIPTER,TEMP MOVC3 #2,LOC+3,LOC+4 MOVB #^A/./,LOC+3 CMPB #^A/ /,LOC+5 BNEQ AAA MOVB #^A/0/,LOC+5 AAA: CMPB #^A/ /,LOC+4 BNEQ BBB MOVB #^A/0/,LOC+4 BBB: CMPB #^A/ /,LOC+2 BNEQ CCC MOVB #^A/0/,LOC+2 CCC: .ENDM ; MACRO TO PRINT OUT A STRING .MACRO PUTSTR SIZE,LOC MOVL SIZE,DESCRIPTER MOVAL LOC,DESCRIPTER+4 CALLG OUTPUT_ARG,G^LIB$PUT_OUTPUT .ENDM .PAGE .SUBTITLE DATA STORAGE .PSECT PURE_DATA,RD,NOWRT,SHR,NOEXE,LONG,GBL,CON ; ; ARGUMENT LIST FOR ERROR MESSAGE THAT SELECTED DEVICE IS NOT A DISK ; NOT_DISK_ARG: .LONG 1 .ADDRESS NOT_DISK_STRING NOT_DISK_STRING: .ASCID /Specified device is not a disk/ ; ; ARGUMENT LIST FOR LIB$GET_FOREIGN ; GET_ARG: .LONG 4 .ADDRESS DEVICE_NAME .ADDRESS PROMPT .ADDRESS LENGTH .ADDRESS FORCE PROMPT: .ASCID /Device ? / ; ; ITEM LIST FOR THE $GETDVI CALL ; ITEM_LIST: .WORD 4,DVI$_CLUSTER .ADDRESS CLUSTER_SIZE .LONG 0 .WORD 4,DVI$_DEVCLASS .ADDRESS CLASS .LONG 0 .WORD 4,DVI$_MAXBLOCK .ADDRESS MAXBLOCK .LONG 0 .WORD 4,DVI$_FREEBLOCKS .ADDRESS FREE .LONG 0 .LONG 0 ; ; LIST OF BOUNDRY VALUES FOR BUCKETS. THE FIRST MUST BE "1" AND THE ; LAST "-1", TO CHANGE THE BUCKET BOUNDRY VALUES AND NUMBER OF BUCKETS, ; JUST CHANGE THE LIST BELOW. THE PROGRAM IS WRITTEN TO TAKE CARE ; OF THE DETAILS.-- THE LIST MUST BE MONOTONICALLY INCREASING. ; BUCKET_SIZE_LIST: .LONG 1,5,10,25,50,75,100,250,500,750,1000,2500,5000,7500 .LONG 10000,25000,50000,75000,100000,250000,500000,750000 .LONG 1000000,2500000,5000000,7500000,10000000 .LONG -1 NUMBER_OF_BUCKETS = <<<.-BUCKET_SIZE_LIST>/4>-1> ; ; MAP THE BITS IN EACH 32 BIT WORD ; BIT_MAP:.LONG ^X1,^X2,^X4,^X8,^X10,^X20,^X40,^X80,^X100,^X200,^X400,^X800 .LONG ^X1000,^X2000,^X4000,^X8000,^X10000,^X20000,^X40000,^X80000 .LONG ^X100000,^X200000,^X400000,^X800000 .LONG ^X1000000,^X2000000,^X4000000,^X8000000 .LONG ^X10000000,^X20000000,^X40000000,^X80000000 ; ; STATISTICS CONSTANTS ; TEN_K: .DOUBLE 10000.0 HALF: .DOUBLE 0.5 .PAGE .PSECT IMPURE_DATA,RD,WRT,NOSHR,NOEXE,QUAD,GBL,CON ; ; ARGUMENT LIST FOR LIB$PUT_OUTPUT ; OUTPUT_ARG: .LONG 1,DESCRIPTER ; ; FORMAT LIST FOR $FAO_S ; CTRSTR: .ASCID /!10UL/ CTRSTR_2:.ASCID /!5UL/ ; ; GENERIC STRING DESCRIPTER ; DESCRIPTER: .LONG 0,0 ; ; ASSORTED TEXT LINES AWAITING INSERTION OF INTEGER STRINGS FOR OUTPUT ; LINE_1: .ASCII /Disk size in blocks / LINE_1_DATA: .ASCII / / LINE_2: .ASCII /Number of free blocks / LINE_2_DATA: .ASCII / / LINE_3: .ASCII /Cluster size in blocks / LINE_3_DATA: .ASCII / / LINE_4: .ASCII /Largest free space in clusters / LINE_4_DATA: .ASCII / / LINE_9: .ASCII /Fragmentation factor (0-100%) / LINE_9_A:.ASCII / % / LINE_5: .ASCII / / LINE_6: .ASCII /Free area size in clusters Count of Total / .ASCII / % Disk % Free/ LINE_6_A:.ASCII / Fragments Clusters/ .ASCII / Space Space / LINE_7: .ASCII / -/ LINE_7_B:.ASCII / / LINE_7_A: .ASCII / (/ LINE_7_C:.ASCII ? /? LINE_7_D:.ASCII ? %/? LINE_7_E:.ASCII / %)/ LINE_8: .ASCII / Total / LINE_8_A:.ASCII / (/ LINE_8_B:.ASCII ? /? LINE_8_C:.ASCII ? %/? LINE_8_D:.ASCII / %)/ ; ; BUCKETS FOR COUNT OF NUMBER OF FRAGMENTS OF IN GIVEN SIZE RANGE ; BUCKET_BRIGADE: .BLKL NUMBER_OF_BUCKETS ; ; TOTAL NUMBER OF CLUSTERS IN A GIVEN BUCKET AND TOTAL FOUND (LAST SLOT) ; TOTALS: .BLKL NUMBER_OF_BUCKETS+1 ; ; TOTAL NUMBER OF FRAGMENTS FOUND ; TOTAL_FRAGS: .LONG 0 ; ; TEMPORARY STORAGE ; TEMP: .LONG 0,0 TEMP_2: .LONG 0,0 ; ; LAST BLOCK NUMBER IN SYS$DISK:[000000]BITMAP.SYS READ IN ; BLOCK_NUMBER: .LONG 0 .ALIGN QUAD ; ; BIT MAP BLOCK ; BLOCK: .BLKB 512 ; ; LONGEST FRAGMENT FOUND ; LONGEST:.LONG 0 ; ; WHAT DOES THE SYSTEM SAY THE NUMBER OF FREE BLOCKS IS ; FREE: .LONG 0 ; ; DISK SIZE IN BLOCKS ; MAXBLOCK: .LONG 0 ; ; WHAT TYPE OF DEVICE IS THIS (SHOULD BE A DISK) ; CLASS: .LONG 0 ; ; GET DISK CLUSTER SIZE ; CLUSTER_SIZE: .LONG 0 FRAGMENTATION_FACTOR: .LONG 0 ; ; FORCE THE PROMPT IF NO DEVICE SPECIFIED ON COMMAND LINE ; FORCE: .LONG 0 ; ; LENGTH OF INPUT STRING ; LENGTH: .LONG 0 ; ; SPECIFIED DEVICE STRING STORED HERE ; DEVICE_NAME: .ASCID / / ; ; IO STATUS BLOCK ; IOSB: .LONG 0,0 ; ; THIS IS THE FILE WANTED FOR THE BIT MAP ; DEFAULT_NAME: .ASCII /SYS$DISK:[000000]BITMAP.SYS/ DFLT_NAM_SIZ=.-DEFAULT_NAME ; ; DEFINE THE FAB AND RAB FORTHE BIT MAP FILE ; .ALIGN LONG FAB_1: $FAB ALQ=0,- DEQ=0,- DNA=DEFAULT_NAME,DNS=DFLT_NAM_SIZ,- FAC=,- FNA=,FNS=0,- SHR= .ALIGN LONG RAB_1: $RAB BKT=1,- FAB=FAB_1,- ROP=,- UBF=BLOCK,USZ=512 .ALIGN LONG FAB_2: $FAB ALQ=0,- DEQ=0,- DNA=DEFAULT_NAME,DNS=DFLT_NAM_SIZ,- FAC=,- FNA=,FNS=0,- FOP=,SHR= .ALIGN LONG RAB_2: $RAB BKT=1,- FAB=FAB_2,- ROP=,- UBF=BLOCK,USZ=512 .PAGE .SUBTITLE CODE .SUBTITLE INITIALIZATION .PSECT CODE,RD,NOWRT,SHR,EXE,LONG,GBL,CON .ENTRY START,0 ; ; GET THE DEVICE NAME ; CALLG GET_ARG,G^LIB$GET_FOREIGN CMPW #SS$_NORMAL,R0 BEQL 1$ $EXIT_S R0 1$: MOVL LENGTH,DEVICE_NAME ; ; GET INFORMATION ON SPECIFIED DEVICE ; $GETDVI_S #1,,DEVICE_NAME,ITEM_LIST,IOSB CMPW #SS$_NORMAL,R0 BEQL 2$ $EXIT_S R0 2$: $WAITFR_S #1 CMPW #SS$_NORMAL,IOSB BEQL 3$ CVTWL IOSB,R0 $EXIT_S R0 ; ; MUST BE A DISK ; 3$: CMPL #DC$_DISK,CLASS BEQL 103$ CALLG NOT_DISK_ARG,G^LIB$PUT_OUTPUT $EXIT_S ; ; OPEN THE BITMAP FILE TWICE ; THE FIRST TIME WITH NOSHARE OPTION TO FORCE THE ; BLOCKS OF THE BITMAP CACHED BY THE ACP TO BE ; WRITTEN OUT, AND THE TIME SECOND WITH SHARE OPTION ; TO ALLOW OHTER PEOPLE TO USE THE DISK ; 103$: MOVB LENGTH,FAB_1+FAB$B_FNS MOVB LENGTH,FAB_2+FAB$B_FNS $OPEN FAB=FAB_1 BLBS R0,4$ $EXIT_S R0 4$: $CONNECT RAB=RAB_1 BLBS R0,5$ $EXIT_S R0 5$: $DISCONNECT RAB=RAB_1 $CLOSE FAB=FAB_1 $OPEN FAB=FAB_2 BLBS R0,1004$ $EXIT_S R0 1004$: $CONNECT RAB=RAB_2 BLBS R0,1005$ $EXIT_S R0 1005$: ; ; INIT ALL INTERNAL VARIBALES AND ARRAYS ; CLRL LONGEST MOVC5 #0,LONGEST,#0,#,BUCKET_BRIGADE MOVC5 #0,LONGEST,#0,#<*4>,TOTALS CLRL TOTAL_FRAGS ; ; BIT MAP STARTS IN LBN 2 ; MOVL #2,BLOCK_NUMBER MOVL #2,RAB_1+RAB$L_BKT MOVL #2,RAB_2+RAB$L_BKT ; ; READ IN FIRST BLOCK OF BITMAP AN INIT ALL REGISTERS ; $READ RAB=RAB_2 BLBS R0,6$ $EXIT_S R0 6$: CLRL R11 ;OFFSET INTO BITMAP BLOCK MOVL #32,R8 ;BITS PER WORD CLRL R6 ;OFFSET INTO BIT MASK ARRAY MOVL BLOCK(R11),R9 ;GET FIRST WORD IN BLOCK ADDL2 #4,R11 ;OFFSET TO NEXT WORD NEEDED CLRL R10 ;INIT FRAGMENT SIZE COUNT .PAGE .SUBTITLE ACCUMULATE DATA LOOP: BITL BIT_MAP[R6],R9 ;IS BIT SET SAYING IN USE BEQL NOT_SET ;BIT CLEAR-CLUSTER NO IN USE INCL R10 ;BUMP CLUSTER SIZE COUNT BRW END_LOOP ;GO TO NEXT CLUSTER FOR CHECK NOT_SET: ;BIT NOT SET TSTL R10 ;DOES IT SIGNAL END OF FRAGMENT BEQL END_LOOP ;NO CLRL R7 ;FIND BUCKET TO PUT COUNT IN BUCKET_LOOP: CMPL [R7],R10 ;CMP CLUSTER SIZE TO BUCKET BGTRU 1$ ;FOUND BUCKET BRACKETING SIZE INCL R7 ;NO-SKIP TO NEXT BUCKET BRW BUCKET_LOOP ;AND TEST IT 1$: INCL BUCKET_BRIGADE[R7] ;INC BUCKET COUNT INCL TOTAL_FRAGS ;IN TOTAL FRAGMENT COUNT ADDL2 R10,TOTALS[R7] ;INC BUCKET TOTAL CLUSTERS ADDL2 R10,TOTALS+;INC TOTAL FREE CLUSTERS CMPL R10,LONGEST ;SEE IF THIS LONGEST BLEQ 2$ ;NO MOVL R10,LONGEST ;YES-UPDATE LONGEST 2$: CLRL R10 ;RE-INIT SIZE OF FRAGMENT END_LOOP: INCL R6 ;BUMP BIT MASK POINTER SOBGTR R8,LOOP ;DEC COUNTER AND LOOP IF DONE CMPL #512,R11 ;SEE IF DONE WITH BITMAP BLOCK BGTR 1$ ;NO-SKIP NEXT INCL BLOCK_NUMBER ;SET UP READ OF NEXT BLOCK MOVL BLOCK_NUMBER,RAB_2+RAB$L_BKT $READ RAB=RAB_2 BLBS R0,2$ CMPL #RMS$_EOF,R0 ;WAS ERROR E.O.F BEQL BIT_MAP_IN ;YES-DONE-GO DO PRINTOUT $EXIT_S R0 2$: CLRL R11 ;INIT AS NEEDED FOR NEW BLOCK 1$: MOVL BLOCK(R11),R9 ;GET NEXT WORD AND INIT ADDL2 #4,R11 CLRL R6 MOVL #32,R8 BRW LOOP ;CONTINUE LOOP .PAGE .SUBTITLE CLEAN UP AND GEN OUTPUT BIT_MAP_IN: ;BIT MAP COMPLETLY INPUT TSTL R10 ;SEE IF LAST FRAGMENT IN BEQL CLOSE_DOWN_FILE ;NO-END OF DISK IN USE CLRL R7 ;FIND BUCKET TO PUT IT IN BUCKET_LOOP_2: CMPL [R7],R10 ;COMPARE TO BUCKET LIMITS BGTRU 1$ ;FOUND BUCKET INCL R7 ;CHECK NEXT BUCKET BRW BUCKET_LOOP_2 ;REPEAT TILL DONE 1$: INCL BUCKET_BRIGADE[R7] ;INC BUCKET FRAGMENT COUNT INCL TOTAL_FRAGS ;INC TOTAL FRAGMENT COUNT ADDL2 R10,TOTALS[R7] ;GET BUCKET TOTAL CLUSTERS ADDL2 R10,TOTALS+;GET TOTAL CLUSTERS CMPL R10,LONGEST ;CHECK IF LONGEST BLEQ CLOSE_DOWN_FILE ;NO MOVL R10,LONGEST ;YES-SAY SO CLOSE_DOWN_FILE: $DISCONNECT RAB=RAB_2 ;CLOSE BITMAP FILE $CLOSE FAB=FAB_2 ; ; FORMAT RESULTS FOR OUTPUT ; FIRST PUT OUT THE GENERAL LIST OF DATA ALWAYS DONE ; PLUS FIXED HEADERS ; GENSTR #14,LINE_1_DATA,MAXBLOCK GENSTR #14,LINE_2_DATA,FREE GENSTR #14,LINE_3_DATA,CLUSTER_SIZE GENSTR #14,LINE_4_DATA,LONGEST GENPCT TOTAL_FRAGS,LINE_9_A,> PUTSTR #46,LINE_1 PUTSTR #46,LINE_2 PUTSTR #46,LINE_3 PUTSTR #46,LINE_4 PUTSTR #46,LINE_9 PUTSTR #1,LINE_5 PUTSTR #77,LINE_6 PUTSTR #77,LINE_6_A ; ; INIT LOOP TO OUTPUT COLLECTED BUCKET STATS ; MOVL #NUMBER_OF_BUCKETS,R11 ;GET NUMBER OF BUCKETS CLRL R10 ;OFFSET INTO BUCKET ARRAYS 1$: TSTL BUCKET_BRIGADE[R10] ;SEE IF BUCKET EMPTY BNEQ 4$ ;NOT EMPTY BRW 3$ ;EMPTY-SKIP THIS BUCKET 4$: GENSTR #14,LINE_7,BUCKET_SIZE_LIST[R10];FILL IN BUCKET SIZE LIMITS SUBL3 #1,[R10],R0 GENSTR #14,LINE_7_B,R0 GENSTR #14,LINE_7_A,BUCKET_BRIGADE[R10];FILL IN FRAGMENT COUNT GENSTR #14,LINE_7_C,TOTALS[R10] ;FILL IN CLUSTER COUNT GENPCT TOTALS[R10],LINE_7_D,MAXBLOCK,CLUSTER_SIZE;% OF DISK GENPCT TOTALS[R10],LINE_7_E,>;% OF FREE PUTSTR #78,LINE_7 3$: INCL R10 ;NEXT BUCKET DECL R11 ;DEC COUNTER BLEQ 2$ ;IF DONE BRW 1$ ;NO-LOOP 2$: GENSTR #14,LINE_8_A,TOTAL_FRAGS ;GEN AND OUTPUT DISK TOTALS GENSTR #14,LINE_8_B,TOTALS[R10] GENPCT TOTALS[R10],LINE_8_C,MAXBLOCK,CLUSTER_SIZE GENPCT TOTALS[R10],LINE_8_D,TOTALS[R10] PUTSTR #78,LINE_8 $EXIT_S .END START