; ****************** BUCKET FILE BLOCK I/O ROUTINES ************************** ; ; ; Subroutines to read in and write out a Bucket File. The PME_OPEN subroutine ; reads in the input bucket file and zeroes all the counts therein (if zeroing ; is requested) and the PME_CLOSE subroutine creates and writes out a new bucket ; file. The PME_INAME and PME_ONAME subroutines can optionally be called to ; specify the file names of the input and output files--if they are not called, ; a default file name is supplied instead. These routines are documented in ; more detail below. Bucket files are read and written with RMS Block I/O. ; ; ; Written by Bert Beander, March, 1979. .TITLE PMEBLKIO Performance Measurement and Evaluation .IDENT /V01-01/ .LIBRARY "PMEDEFS.MLB" ; Default macro library PMEDEFS ; Define all Bucket File tags $FABDEF ; Define all FAB$ tags $RABDEF ; Define all RAB$ tags $DSCDEF ; Define all DSC$ tags ; Globally defined data items--the bucket file buffer base address, the group ; record base address, and the default counter address. ; .PSECT PME_DATA,NOEXE,LONG PME_BASE:: .LONG 0 ; Holds address of the start of the in- ; memory copy of the bucket file .LONG 0 ; Required by $EXPREG system service PME_GRPADDR:: .LONG 0 ; Address of first Bucket Group Record PME_DEFCNTR:: .LONG 0 ; Address of default counter for ; out-of-range PC values ; FABs, RABs, and other data areas ; DEFNAME: PMEDEFNAME ; Pure copy of the default file name DEFSIZE = .-DEFNAME ; Size of default file name SENTINEL: .ASCII "**" ; Sentinel at start of bucket file .ALIGN LONG ; Longword align the FABs and RABs INFAB: $FAB FNA=INFNAME,FNS=DEFSIZE,DNM=<.PME>,FAC= INRAB: $RAB FAB=INFAB,BKT=1,ROP=BIO OUTFAB: $FAB FNA=OUTFNAME,FNS=DEFSIZE,DNM=<.PME>,FAC=,MRS=512 OUTRAB: $RAB FAB=OUTFAB,BKT=1,ROP=BIO INFNAME: PMEDEFNAME ; File name of input bucket file .BLKB MAXNAMSIZ-DEFSIZE ; OUTFNAME: PMEDEFNAME ; File name of output bucket file .BLKB MAXNAMSIZ-DEFSIZE ; HDRBUF: .BLKB HDRRECSIZ ; Buffer to hold Bucket File Header NAMSIZ: .WORD 0 ; Temporary for holding file name size ; Macro to branch to a specified location if R0 contains an error status ; .MACRO BRERR BRADDR,?LOCAL ; BLBS R0,LOCAL ; Branch around BRW if status is good BRW BRADDR ; On bad status, go to BRADDR LOCAL: ; Generated local label .ENDM BRERR ; .PSECT PME_CODE,EXE,NOWRT,LONG,PIC ; ************* PME_OPEN: READ IN AN EXISTING BUCKET FILE ******************** ; ; ; Subroutine to read in a bucket file. It can be called in two ways: ; ; CALL PME_OPEN ; ; CALL PME_OPEN(BUFFER,MAXSIZ,CLRFLG) ; ; This causes the input bucket file (whose name was specified by default or ; by a previous call on PME_IFILE) to be opened, checked for validity, read ; into memory, and closed. A buffer for the file contents is created dynam- ; ically unless the BUFFER parameter is specified. (If the bucket file size ; exceeds MAXSIZ, the first longword in BUFFER is zeroed and PME_OPEN returns ; without reading in the bucket file.) All bucket counts in the file are then ; cleared to zeroes if the CLRFLG parameter is .TRUE. and the HDRFLG_CLR bit in ; the file Header Record is set. If either CLRFLG or HDRFLG_CLR is not set, the ; bucket counts are left untouched. If no parameters are passed to PME_OPEN, ; the CLRFLG parameter is assumed to be .TRUE.. Certain global data items are ; also primed for use by the PME_COUNT and PME_COUNTER routines. ; ; Note: MAXSIZ is the size of BUFFER in longwords. ; ; ; Open the bucket file and connect the RAB to the FAB ; .ENTRY PME_OPEN,^M ; Read Bucket File entry point $OPEN FAB=INFAB ; Open the input bucket file BRERR INERROR ; Check for error $CONNECT RAB=INRAB ; Connect the RAB to the FAB BRERR INERROR ; ; Read in the Bucket File Header Record and check it for validity ; MOVAL HDRBUF,INRAB+RAB$L_UBF ; Set the buffer address in the RAB MOVW #HDRRECSIZ,INRAB+RAB$W_USZ ; Set the buffer length $READ RAB=INRAB ; Read the Bucket File Header Record BRERR INERROR ; CMPL HDRBUF+HDRSENT,SENTINEL ; Check the sentinel in the first BNEQ INERROR2 ; quadword to make sure this CMPL HDRBUF+HDRSENT+4,SENTINEL+4 ; is a valid bucket file BNEQ INERROR2 ; MOVL HDRBUF+HDRNXTPTR,R0 ; R0 = the total file size in bytes CMPL R0,#HDRRECSIZ ; Make sure the file size is in the BLEQ INERROR2 ; valid range--else error out CMPL R0,#65535 ; BGTR INERROR2 ; MOVW R0,INRAB+RAB$W_USZ ; Set the file size in the RAB BRB AROUND1 ; Branch around the branch to the INERROR2: BRW INERROR3 ; BRW--this prevents branch AROUND1: ; byte displacement overflows ; See if the caller is supplying the bucket file buffer. If so, save its ; address in PME_BASE and check out its size. ; TSTB (AP) ; Did the user pass any parameters? BEQL GETMEM ; If not, we get our own memory MOVL 4(AP),PME_BASE ; He did--get the address of his buffer MULL3 @8(AP),#4,R0 ; Compute his buffer size in bytes CMPL R0,HDRBUF+HDRNXTPTR ; Is it big enough for this bucket file? BGEQ READ ; If yes, go to read in the file CLRL @PME_BASE ; No--zero first longword of his buffer CLRL PME_BASE ; Forget his buffer's address RET ; Return--no more can be done ; Get enough memory for the whole bucket file from $EXPREG and save the address ; of that space in PME_BASE. ; GETMEM: ADDL2 #511,R0 ; R0 = the number of 512-byte pages DIVL2 #512,R0 ; needed to hold the bucket file $EXPREG_S PAGCNT=R0,RETADR=PME_BASE ; Get the necessary memory BRERR INERROR ; Make sure it worked ; Now read the whole bucket file into the memory we just got. After that, ; close the bucket file. ; READ: MOVL PME_BASE,INRAB+RAB$L_UBF; Fill address of the buffer into RAB $READ RAB=INRAB ; Read in the whole bucket file BRERR INERROR ; $CLOSE FAB=INFAB ; Close the bucket file BRERR INERROR ; ; Loop through the Bucket Group Records to set ABSTBLPTR (the absolute address ; of the corresponding index table) in each such record. ; MOVL PME_BASE,R2 ; R2 = the file header base address ADDL3 HDRBGRPTR(R2),R2,R0 ; R0 = addr of first Bucket Group Record 10$: ADDL3 PME_BASE,RELTBLPTR(R0),ABSTBLPTR(R0) ; Set group's ABSTBLPTR ADDL2 #GRPRECSIZ,R0 ; Go to next Bucket Group Record TSTL BUCKETSIZE(R0) ; If this is a real Bucket Group Record, BNEQ 10$ ; loop to initialize it ; If the HDRFLG_CLR bit is set and the CLRFLG parameter allows it, clear all ; bucket counts to zeroes. ; TSTB (AP) ; Is there a CLRFLG parameter? BEQL 20$ ; If not, we are allowed to clear counts BLBC @12(AP),40$ ; CLRFLG is there--go to 40$ if .FALSE. 20$: BITL #HDRFLG_CLR,HDRFLAGS(R2); Is zeroing of counts requested? BEQL 40$ ; If not, branch around to 40$ CLRL HDRDEFCNTR(R2) ; Clear the default counter CLRL HDRNUMRUNS(R2) ; Clear the number of runs contributing ; counts to this bucket file ADDL3 HDRNAMPTR(R2),R2,R1 ; R1 = pointer to end of Bucket Records ADDL3 HDRBRECPTR(R2),R2,R0 ; R0 = pointer to start of Bucket Records 30$: CMPL R0,R1 ; Are we at end of Bucket Records yet? BGEQ 40$ ; If yes, go to 40$ CLRL COUNT(R0) ; Clear the count in this Bucket Record ADDL2 #BKTRECSIZ,R0 ; Go to next Bucket Record BRB 30$ ; ; Set the address of the bucket file header's default counter in the ; global PME_DEFCNTR and set the address of the first Bucket Group Record ; in the global PME_GRPADDR. (These globals are used by the PME_COUNT ; and PME_COUNTER subroutines.) Then return to the caller. ; 40$: MOVAB HDRDEFCNTR(R2),PME_DEFCNTR ; Set default counter's address ADDL3 HDRBGRPTR(R2),R2,PME_GRPADDR ; Set up Group Record pointer RET ; Return ; Handle error conditions encountered in PME_INIT. ; INERROR: $EXIT_S CODE=R0 ; Just stop right here--this gets the ; status code in R0 printed out INERROR3: BPT ; Bad Bucket File Header--just stop $EXIT ; We should never get here ; ************* PME_CLOSE: CREATE AND WRITE A NEW BUCKET FILE *************** ; ; ; Subroutine to write out a new bucket file. It can be called two ways: ; ; CALL PME_CLOSE ; ; CALL PME_CLOSE(BUFFER) ; ; This causes a new bucket file, whose name is given by default or by a ; previous call to PME_ONAME, to be created. If BUFFER is not specified, ; the contents of the buffer created by or passed to PME_OPEN is then ; written out to the new bucket file and the file is closed. If BUFFER ; is specified, the contents of that buffer, which is assumed to have the ; standard bucket file format, is written out instead. ; ; ; See if the caller is passing the BUFFER parameter. If so, pick up the ; address of that buffer and store it in PME_BASE. ; .ENTRY PME_CLOSE,^M ; Write Bucket File entry point TSTB (AP) ; Did the user pass any parameters? BEQL 15$ ; If not, go to 15$ MOVL 4(AP),PME_BASE ; But if so, pick up the buffer address 15$: ; and stuff it into PME_BASE ; Fix up the Bucket File Header Record--fill in the sentinel value and ; increment the number of runs which have tallied PC values in it. ; MOVL PME_BASE,R2 ; R2 = base address of buffer MOVQ SENTINEL,HDRSENT(R2) ; Fill the magic sentinel value into ; the Bucket File Header INCL HDRNUMRUNS(R2) ; Increment the number of runs which ; have added counts to this file ; Set the buffer address and size in the RAB and set the allocation size in ; the FAB. Also make sure the buffer size is in the valid range. ; MOVL HDRNXTPTR(R2),R0 ; R0 = the buffer size in bytes CMPL R0,#HDRRECSIZ ; Make sure this buffer size is BLEQ OUTERROR2 ; within the valid range CMPL R0,#65535 ; BGTR OUTERROR2 ; MOVL R2,OUTRAB+RAB$L_RBF ; Set buffer address in the RAB MOVW R0,OUTRAB+RAB$W_RSZ ; Set buffer size in the RAB ADDL2 #511,R0 ; Compute the number of 512-byte blocks DIVL3 #512,R0,OUTFAB+FAB$L_ALQ; to allocate and store in the FAB BRB AROUND2 ; Branch around BRW instruction--this OUTERROR2: BRW OUTERROR3 ; prevents branch byte displace- AROUND2: ; ment overflows ; Now create the new bucket file, connect the RAB to the FAB, write out the ; contents of the bucket file, and close the file. Then return to the caller. ; $CREATE FAB=OUTFAB ; Create the new bucket file BRERR OUTERROR ; Make sure it worked $CONNECT RAB=OUTRAB ; Connect the RAB to the FAB BRERR OUTERROR ; $WRITE RAB=OUTRAB ; Write out the bucket file contents BRERR OUTERROR ; $CLOSE FAB=OUTFAB ; Close the new bucket file BRERR OUTERROR ; RET ; All done--now return ; Handle output error conditions. ; OUTERROR: $EXIT_S CODE=R0 ; Just stop right here--this gets the ; status code in R0 printed out OUTERROR3: BPT ; Bad bucket file length--just stop $EXIT ; We should never get here ; **************** PME_INAME AND PME_ONAME: SET FILE NAME ******************** ; ; ; These two entry points allow the user to specify the file names to be used ; for the input and output bucket files. These routines are called as follows: ; ; CALL PME_INAME(INFILENAME) ; ; CALL PME_ONAME(OUTFILENAME) ; ; where INFILENAME and OUTFILENAME are character strings, passed by descriptor, ; containing the desired file names. These routines simply copy the passed ; file name to a local buffer and strip off any trailing blanks. No other ; checking is done on the file name strings. If the passed string is completely ; empty (all blanks) or if these routines are not called in the first place, the ; file name "PMEFILE" is supplied by default. ; ; ; Subroutine to specify the name of the input bucket file. ; .ENTRY PME_INAME,^M ; Set Input File Name entry point MOVAB INFNAME,R6 ; R6 = address of local fname buffer MOVAB INFAB,R7 ; R7 = address of input FAB BRB GETSTR ; Go to process name string ; Subroutine to specify the name of the output bucket file. ; .ENTRY PME_ONAME,^M ; Set Output File Name entry point MOVAB OUTFNAME,R6 ; R6 = address of local fname buffer MOVAB OUTFAB,R7 ; R7 = address of output FAB ; Copy the passed file name string to our local file name buffer ; GETSTR: MOVL 4(AP),R1 ; R1 = address of string descriptor MOVW DSC$W_LENGTH(R1),R0 ; R0 = the string length CMPW R0,#MAXNAMSIZ ; If the length exceeds the size of our BLEQ 10$ ; local buffer, truncate it MOVW #MAXNAMSIZ,R0 ; 10$: MOVW R0,NAMSIZ ; Save the name size MOVC3 R0,@DSC$A_POINTER(R1),(R6) ; Copy the file name to local buffer ; (this clobbers registers R0-R5) ; Now scan the name string backwards to strip off any trailing blanks ; MOVW NAMSIZ,R0 ; R0 = the file name length again ADDL3 R0,R6,R1 ; R1 = pointer to the end of the string 20$: CMPB -(R1),#^A" " ; Scan backwards for the first non-blank BNEQ 30$ ; or the start of the string SOBGTR R0,20$ ; 30$: MOVB R0,FAB$B_FNS(R7) ; Set the file name size in the FAB ; If the string turned out to be empty (zero length), fill in our default ; file name (PMEFILE) instead. Then return to the caller. ; TSTW R0 ; Is the string empty? BGTR 40$ ; If not, go to 40$ and return MOVC3 #DEFSIZE,DEFNAME,(R6) ; But if so, fill in the default file MOVB #DEFSIZE,FAB$B_FNS(R7) ; name and its size 40$: RET ; Return .END