The record / replay mechanism is used in regression testing and in migration of Fortran codes.
FPT adds statements to the code to capture all of the data read in to a
program and all of the data written out. The changes are made in such a way that
the input data can be replayed into the program, replacing interactive
interfaces and file I/O.
If you have a program is controlled by a long interactive menu, testing is
tedious because the menu items must be entered by hand. The record / replay
mechanism allows them to be captured once and replayed automatically.
If you have a program which interacts with a large database file, the database
must be set to the same state every time testing is carried out. Record / replay
can replace the database by replaying the database inputs so that the program
receives the same input data every time.
Every Fortran logical unit, and every sub-progam and memory interface may be
selected for replay independently. You can replay the interactive I/O, for
example, without replaying some or all of the file I/O.
FPT instruments the user's code to capture and to replay:
File and terminal I/O
The arguments of interface routines
Interfaces to shared memory
For example:
Click here to see a code example
before modification.
Click here to see the changes which FPT makes.
The changes which FPT has made
FPT has inserted an INCLUDE statement for declaration of the variables used to control record and replay.
INCLUDE 'rr_cmn.fpi'
The control variables, RR_LUN_FOR_REPLAY, RR_OUTPUT_RECORD_F etc.
are in COMMON blocks. The file also contains declarations of the control functions such
as REPLAY_IO(N,UNIT). This function determines whether the unit is being replayed or not.
The OPEN statement has been enclosed in a test to find whether or not the unit, in this case unit 7, is being replayed.
IF (.NOT. REPLAY_IO(1,7)) THEN
OPEN (7,FILE='VISCOSITY.DAT',ACCESS='DIRECT',STATUS='OLD')
ENDIF
If unit 7 is replayed from the captured data, we don't need to open the file.
This means that the original files don't need to be present when the code is tested.
The read of the viscosity array has been enclosed in a test.
IF (REPLAY_IO(2,7)) THEN
READ (RR_LUN_FOR_REPLAY,*)RR_IOSTAT
READ (RR_LUN_FOR_REPLAY,*)VISCOSITY(I)
ELSE
READ (7,REC=I,IOSTAT=RR_IOSTAT)VISCOSITY(I)
ENDIF
If unit 7 is being replayed, VISCOSITY(I) is read from the replay file. If not, the original READ statement is executed.
FPT inserts code to capture the values from the viscosity file.
IF (RR_INPUT_RECORD_F) THEN
WRITE (RR_LUN_TO_RECORD_INPUT,*)RR_IOSTAT
WRITE (RR_LUN_TO_RECORD_INPUT,*)VISCOSITY(I)
ENDIF
The original READ statement had an END= destination. This has been replaced
by capturing the IOSTAT value in the READ. The IOSTAT is replayed to the
variable RR_IOSTAT and this is
used to control jumping to the error destination by the statements:
IF (RR_IOSTAT .NE. 0) THEN
GOTO 200
ENDIF
RR_IOSTAT is also replayed from
the recorded file, so that the recording behaves in exactly the same way as the
original code.
The WRITE statement to unit 1 is modified in a similar way. Once again, the
statement is enclosed in a test, and is not executed if unit 1 is being
replayed. The file does not need to exist during testing.
IF (.NOT. REPLAY_IO(3,1)) THEN
WRITE (1,100)
ENDIF
IF (RR_OUTPUT_RECORD_F) THEN
WRITE (RR_LUN_TO_RECORD_OUTPUT,100)
ENDIF
100 FORMAT (/,'Viscosity table read successfully')
Note that FPT captures the outputs from the program as well as the inputs. In
a migration project, the outputs may be compared on the old and new hosts as a
diagnostic tool.
The Extent of the Changes
FPT has re-written so much of this small subroutine that it is almost
unrecognisable. This is not typical. It has happened because the subroutine
contains almost nothing but I/O statements. Usually, the record / replay process
makes very few changes to a large program.
A Shared Memory Location
Many programs have interfaces which are shared memory locations. These may
also be instrumented for record and replay. In the code fragment below, VISBUFR is part of a memory block shared
between a simulation program and the visual scene generator.
C Only the visual system knows where the ground is
RADAR_ALT=-RVISBUF(3)
To instrument this interface, we need to tell FPT that the memory is shared.
This is done by annotating a declaration of VISBUFR. The annotation doesn't need to be in the main
code body. It can be written in a small template file which is used only by FPT.
The annotation would be, for example:
COMMON /VISBUF/ RVISBUF(20),IVISBUF(200)
C% Record/replay interface:: RVISBUF
The comment beginning C% has special meaning to FPT.
FPT then modifies the original statement to read:
C Only the visual system knows where the ground is
C RADAR_ALT=-RVISBUF(3)
IF (REPLAY_INTERFACE(1,'RVISBUF')) THEN
READ (RR_LUN_FOR_REPLAY,*)RVISBUF_TMP(3)
ELSE
RVISBUF_TMP(3)=RVISBUF(3)
ENDIF
IF (RR_INPUT_RECORD_F) THEN
WRITE (RR_LUN_TO_RECORD_INPUT,*)RVISBUF_TMP(3)
ENDIF
RADAR_ALT=-RVISBUF_TMP(3)
The original statement is commented-out. Note how replay of the specific
interface is controlled by the logical function REPLAY_INTERFACE(N,NAME).
FPT has added a new array, RVISBUF_TMP to the code. This has the same bounds and
type as the original array RVISBUF and is declared wherever it is needed.
A Sub-Program Interface
Programs may communicate with external devices or other programs through
sub-program arguments. To instrument these interfaces, FPT requires a
declaration that the sub-program is an interface, and needs either the code of
the sub-program, or a template which indicates the INTENT of the arguments -
whether they are inputs, outputs or both. Templates may be written in small
files outside the main body of the code, which are processed only by FPT. For
example:
C Template for MACRO routine TO_RADALT
C% Record/replay interface TO_RADALT
SUBROUTINE TO_RADALT(V,IADD,IE)
C% INTENT (IN) :: V
C% INTENT (IN) :: IADD
C% INTENT (OUT) :: IE
END
A call to TO_RADALT, for
example:
CALL TO_RADALT(RADAR_ALT,23,ISTAT)
is instrumented to read:
IF (REPLAY_INTERFACE(2,'TO_RADALT')) THEN
READ (RR_LUN_FOR_REPLAY,*)ISTAT
ELSE
CALL TO_RADALT(RADAR_ALT,23,ISTAT)
ENDIF
IF (RR_INPUT_RECORD_F) THEN
WRITE (RR_LUN_TO_RECORD_INPUT,*)ISTAT
ENDIF
IF (RR_OUTPUT_RECORD_F) THEN
WRITE (RR_LUN_TO_RECORD_OUTPUT,*)RADAR_ALT,23
ENDIF
|