Next: Index Up: No Title Previous: No Title

Global operational aids

ZPHASE - set program phase

Primarily to avoid recovery to 'next event' at the wrong moment, ZEBRA needs to know in which phase the user program is at any given moment. We distinguish three phases:

  1. during the initialization phase the user prepares the context for
  2. the operation phase of his program;
  3. during the termination phase accumulated results are output, files are closed, etc.
The user may subdivide the operation phase for his own purpose. With the change to 'termination' one may request ZPHASE to take action of tidying up the primary store, to make room for end-processing routines (like output of histograms) which may need a large amount of working memory.

CALL ZPHASE (JPH)

signals to Zebra a change of phase, preset by MZEBRA to 'initialization'.

Normal operation phase:  JPH .GE. 0

   Termination phase:       JPH .LT. 0

      JPH = -1:  reset the working space to be of zero length,
                  but leave the store as it is;

            -2:  reset the working space,
                  wipe all user short-range divisions;

            -3:  reset, wipe, and collapse upwards all short-range user
                  divisions to be of zero length, giving their space to
                  division 1 for use by termination routines; perform
                  clean-up garbage collection in all other divisions.

The program phase is recorded on the variable NQPHAS of ZSTATE and has the following significance :

NQPHAS =   0  initialisation phase
             >0  = MAX(JPH,1):  normal operation phase
             <0  = JPH: termination phase
If the user whishes to subdivide the operation phase, he can pick up the current state from this variable.

ZPHASE prints a log message at level -1 for major phase changes, and at level 2 for minor changes.

ZEND - normal program end

CALL ZEND

The routine ZEND, which has no parameters, is defined to be the entry-point for normal run termination. It is normally provided by the user to close files and print accumulated results. It is important that all closing down operations are done through this routine, if the user wants them to happen even in abnormal run termination. It would normally look like this:

SUBROUTINE ZEND
 +CDE, ZSTATE.

       CALL ZPHASE (-3)           start termination
       . . .                      any user termination code

       CALL MZEND
       IF (NQERR.NE.0)  CALL ABEND
       STOP
       END

MZEND is a little routine which prints statistics about the usage of all divisions.

NQERR in ZSTATE is zero for normal run termination, it is non-zero if ZEND has been reached via ZFATAL. On some machines the recovery system of the machine expects the user to re-enter it. The KernLib routine ABEND (Z 035) looks after this.

The Zebra system contains a little default routine ZEND which is the above without any user termination.

A user routine similar to ZEND is defined for taking over control of fatal error termination. It is called ZABEND and it is described in the next paragraph. This should perform in there any extra operations needed for fatal termination and then it should transfer to ZEND for closing down.

ZABEND - abnormal program end through ZFATAL

CALL ZFATAL

is defined to be the entry point for fatal run termination.

CALL ZFATAM (chMESSAGE)

is identical to ZFATAL, except that it prints a message, given in the parameter as a character string of not more than 30 characters, terminated by the character '.'.

These routines are supplied by the system; they are protected against recovery loops, and they must not be supplied by the user. They should only be called when the run cannot usefully continue. If the application program discovers such a fatal condition it too should call ZFATAL or ZFATAM, preceded with some diagnostic printing or with loading to IQUEST some clue to the trouble.

CALL ZABEND

and

CALL ZPOSTM chOPT

This routine receives control from ZFATAL to handle fatal run termination. This routine may be supplied by the user.

The Zebra system contains the standard routine as follows:

SUBROUTINE ZABEND
 +CDE, ZSTATE.

       CALL ZPOSTM ('TCWM.')
       IF (NQPHAS.LE.0)  CALL ABEND
       NQPHAS = -2
       CALL ZEND
       RETURN
       END

This is not just a dummy, it causes a post-mortem dump, including a subroutine trace-back (if possible), followed by any normal user output programmed in ZEND. Transfer to ZEND takes place only if the break-down happened during normal operation, but not if the program is still in the initialisation phase or if it is already under ZEND control.

The parameter to ZPOSTM is passed from there to DZSNAP to select the options for dumping the dynamic store, see section for details.

ZTELL - recover trouble through ZTELL-ZTELUS

During normal operation any request from the user for space with MZWORK, MZLIFT et al. is satisfied, after garbage collection if necessary and possible. If however the request cannot be satisfied, the normal course of the program must be broken. To deliver the user from the burden of checking for success after each space request, the garbage collector sends control to the user at the entry-point QNEXT (via ZTELL and the KERNLIB routine QNEXTE), where he can program the recovery of the problem. Normally this will be to skip the current event and to continue with processing the next one.

Other Zebra packages, apart from MZ, and maybe the user himself, have similar problems. Therefore a general trouble control routine ZTELL has been included into Zebra. This is a switching routine with several modes of continuation, one of which is to send control to QNEXT. ZTELL can also be called by the user program, thus:

CALL ZTELL (ID,IFLAG)

ID     is an integer between 101 and 999,
           ID's below 100 are reserved for system usage,
           ID=99 for 'no memory left' from MZGARB.

    IFLAG  is a flag indicating whether the calling code can
           accept a RETURN from ZTELL:
           = 0 ZTELL may return;
           = 1 the calling code is not capable to accept a RETURN;
           = 2 fatal error, the run must stop.

ZTELL prints a message, sets up a reasonable exit mode as a function of ID and IFLAG into the little labelled common ZTELLC and calls ZTELUS to give the user a chance to modify this mode. On the obligatory return from ZTELUS it takes the selected exit as follows:

COMMON /ZTELLC/ ID, MODE

    ID     is a copy of the first parameter to ZTELL;
    MODE   is the selected exit mode:
           = 0 RETURN to let the calling routine continue;
           = 1 CALL QNEXTE to enter QNEXT
           = 2 CALL ZFATAL
           = 3 CALL ZEND

The exit mode to QNEXT is enabled only if NQPHAS in ZSTATE is larger than zero, indicating that the program is in the normal operation phase. During the initialisation or the termination phase of the program transfer is to ZFATAL instead, to avoid a program crash to be 'recovered' into normal operation.

The pre-loading of MODE is MODE=IFLAG for user calls (ID>100); and for system calls (ID<100) it is as shown in the diagnostics chapter for ZTELL.

CALL ZTELUS

Here is an example of a ZTELUS which is happy with the default modes, except that it wants to go to ZEND for ID=8:

SUBROUTINE ZTELUS
      COMMON /ZTELLC/ ID,MODE

      IF (ID.EQ.8)  MODE=3
      RETURN
      END
The default subroutine ZTELUS on the library is a do-nothing dummy. The default subroutine QNEXT goes straight to ZFATAL.

CALL QNEXT

On most machines repeated recovery directly to QNEXT causes trouble with the Fortran trace-back and the subroutine stack. For this reason ZEBRA relies on the KERNLIB routine QNEXTE, which implies an organization for event processing as follows:

_________________________
|                         |          Program flow with QNEXT recovery
|    MAIN program         |
|                         |
|    CALL MZEBRA (0)      |
|    CALL MZSTOR (...)    |
|                         |
|    program              |                                _________
|    initialization       |                               |         |
|                         |                               |         |
|    CALL ZPHASE (0)      | first entry           recover |         |
|    CALL QNEXTE          | ------       _________       _|         |
|_________________________|      `----> |         |     |           |
                                        | routine |     |  routine  |
          <---------------------------< | QNEXTE  | <-- |  ZTELL    |
         |                              |_________|     |_          |
 ________|________________                                |         |
|                         |                               |         |
|    subr QNEXT           |                               |         |
|                         |                               |         |
| 11 CALL MZWIPE (0)      |       requests                |         |
|    read event           | ----------------------------> |  Zebra  |
|    IF (end) CALL ZEND   |                               |  system |
|    process event        | <---------------------------- |         |
|    output event         |               normal RETURN's |         |
|    GO TO 11             |                               |         |
|_________________________|                               |         |
         |                                                |         |
 ________|________________        __________              |         |
|                         |      |          |             |         |
|    subr ZEND            | <--- |  subr    | <---        |         |
|                         |      |  ZABEND  |     |      _|         |
|    CALL ZPHASE (-3)     |      |__________|     |     |           |
|                         |                       |     |  routine  |
|    program              |                       `---< |  ZFATAL   |
|    termination          |                             |_          |
|_________________________|                               |_________|
The initialisation part prepares the program to be ready for execution and then calls itself QNEXTE for entry to QNEXT to process the 'next event', being the first event in this case. QNEXT loops internally to process all events.

CALL's from the processing program to the Zebra system are normally satisfied, and control comes back to the user with normal RETURN. Abnormal returns are either via ZFATAL to ZEND, or straight to ZEND, or to QNEXT via QNEXTE.

The Fortran version of QNEXTE is a simple CALL QNEXT followed by RETURN. If necessary on a given computer, QNEXTE is a machine-language or a C routine to unwind to itself the Fortran trace-back and the subroutine stack. So, if the user wishes at some point to abandon himself the curent event and to go to the next one, he should CALL QNEXTE and not QNEXT.

QNEXT is a user routine to the KERNLIB routine QNEXTE and has thus the usual problem of user routines called from a library routine in that it must be loaded explicitely:

either:  compile it together with the other material
       or:  if it resides on a user library it must be
             INCLUDEd explicitly, for example on the VAX with
             $ LINK  ...   MYLIB/INC=QNEXT/LIB  ...

This flow-diagram is only an example for the most common case of actual usage of Zebra. If one's program is not of the event-processing type one has to look at QNEXTE/QNEXT from a different angle: program flow from MAIN has to go to QNEXTE to initialize for re-entry. Entry and all re-entries are then to QNEXT, which has to control the further program flow according to some flags, conveniently ID in ZTELLC and NQPHAS in ZSTATE. ID is not initialized by MZEBRA, it is only changed by ZTELL which copies its first parameter to ID. This number is an integer in the range 1 to 99 for calls from the Zebra system. NQPHAS is initialized to zero by MZEBRA, it is then changed only by the user (or the default ZABEND) either directly or with ZPHASE.

ZVERIF - check logical integrity of Zebra stores

CALL ZVERIF (IXDIV,IFLRTN,chTEXT)

with   IXDIV  1) the index of a single division to be verified;
              2) the index of the store to be verified;
                 = -1: all stores
                        (other negative values are reserved)

      IFLRTN  return flag, see below;

      chTEXT  identifying text to be printed if trouble.

This routine has two somewhat different modes of operation:

Normally ZVERIF checks all the data in and relevant to a complete store, or even several stores. It goes to ZFATAL if it finds trouble.

But if verification is for one single division only, only the banks of this division are checked and control is given back to the caller if the flag IFLRTN is non-zero, in which case the number of normally fatal conditions is delivered in IQUEST(1). This is intended to be used if one receives a data-structure in memory (with FZIN, say) which may be bad. Rather than letting the program run to crash one can realize the situation and wipe the division. This must be done with MZWIPE because this is the only way to get rid of the data without Zebra looking at them.

This routine inspects all link areas and all banks in the stores selected, performing the following checks:

(0)  process and store control tables intact;
    (1)  bank chaining intact;
    (2)  a non-zero structural link must point to a true bank address;
    (3)  consistency of "up" and "origin" links;
    (4)  a link in a bank in division A pointing to division B
         is checked for consistency with the cross-reference matrix;
    (5)  a non-zero reference link in a bank must not point
         into a reserve area;

ZVERIF is used by the automatic verification procedure ZVAUTO, see next section.

ZVAUTO - running with automatic verification

The routines of this complex are provided as the ultima ratio regis for finding program errors which destroy data in a Zebra store by faulty handling of links; they consume a lot of CPU time.

With automatic verification switched on calls to the Zebra system transfer to ZVAUTY, which handles the verification process:

The "verify identifier" (VID) is constructed by counting the number of entries to ZVAUTY. The VID is a two-word object, because 32 bits may not be sufficient, it identifies every call to Zebra, it gives us a handle on where we are in the job, and it is available to the user via the common ZVFAUT to be printed together with the event number, for example. ZVAUTY will remember separately the VID for the last garbage collection, for the last MZWIPE, and for the last FZIN, for printing on fatal error.

In "active mode" ZVAUTY will call ZVERIF for the stores selected by the user initially, causing a transfer to ZFATAL in case of trouble, which in turn will transfer control to the user by calling ZABEND, where he may code any display wanted.

In "dummy mode" the time-consuming calls to ZVERIF will not take place, but the VID is checked to see whether the "action threshold" defined by the user is reached. If so, the mode is switched to "active", and the log-level is set for all stores to be verified. From this point on one may get rather a lot of output, tracing the execution of the program step-by-step.

Suppose now one has a particular run, "run A" say, of a lengthy production job which collapses with the memory destroyed. The reason for this will be somewhere in the past. One could re-submit the job switching on true automatic verification right away, but this could be excessively time consuming and produce much too much output. If so, it is better to re-submit the job first with "dummy verification" switched on by inserting a call to ZVAUTO at the point where one wants to start verification, after the initialization phase for example, and with the connection routine ZVAUTX supplied, "run B" say. Dummy verification goes through the same motion as active verification, but it does not call ZVERIF and hence costs only little more time than run A. This run should also end up in ZFATAL, which will now print the VID's remembered for the last garbage collection, for the last wipe, for the last FZIN, and maybe other VID's entered by the user with ZVAUTU.

One can now re-submit the job, "run C" say, having changed the call to ZVAUTO by giving the action threshold, thereby defining the moment when active verification will start.

Automatic verification is initiated with

CALL ZVAUTO (MSTORE,IDVTH1,IDVTH2,LOGLEV)

with  MSTORE  a bit-by-bit word indicating the stores to be verified,
              store i = 0,1,2,... selected if bit (i+1) is set to one;
              if MSTORE = 0: all stores

    IDVTH1/2  the two-word VID at which true verification should start,
              give both words zero if dummy verification only
              ("run B" in the discussion above);
              give 0,-1 if active verification is to start right away;

      LOGLEV  set the log-level of the existing stores to be verified
              to this value at the moment of changing to active mode,
              give 2 for maximum logging.

To avoid loading the non-negligeable code of the ZVAUTO complex with normal production jobs, this trick is used: all relevant Zebra routines contain a conditional call to ZVAUTX (which does not take place if ZVAUTO has not been called), and the Zebra library contains a dummy routine ZVAUTX. But the true process is controlled by ZVAUTY, which is not normally loaded. The user makes the connection by supplying this routine:

SUBROUTINE ZVAUTX
      CALL ZVAUTY
      END
as part of the material to be compiled and linked.

In the list of "last VIDs remembered" there are 3 places for user triggered storage, which is done with

CALL ZVAUTU

CALL ZVAUTY

This will push the VID in position 2 to position 3, and the one in position 1 to position 2, entering the current VID into position 1. Thus, if one were to call ZVAUTU at the start of each event, one would see the start points of the last 3 events in a dump from ZFATAL.

The user has access to the verification parameters via this common:

COMMON /ZVFAUT/IQVID(2),IQVSTA,IQVLOG,IQVTHR(2),IQVREM(2,6)

           IQVID  the current VID

          IQVSTA  the verification status:
                    zero  automatic verifying not running
                      -1  dummy verification
                      +1  active verification

          IQVLOG  the log level to be set at the activation threshold
          IQVTHR  the threshold VID

     IQVREM(2,J)  VID remembered in position J,
                    J = 1  last garbage collection
                        2  last call to MZWIPE
                        3  last call to FZIN
                        4  last call to ZVAUTU
                        5  last-but-one call to ZVAUTU
                        6  last-but-two call to ZVAUTU

Examples:

Do not forget to supply the connection routine ZVAUTX !

      CALL ZVAUTO (7,0,0,0)

           to start a dummy verification run for store numbers 0, 1, 2;
           "run B" of the discussion above.

           Supposing that the ZFATAL output of this run tells us the
           VID of the last-but-one event, by changing to
 
      CALL ZVAUTO (7,0,123456,2)

           we may start "run C" to give detailed logging for the
           last two events.

      CALL ZVAUTO (1,0,-1,0)

           to start an active verification run for store number 0;
           one might do this on a limited data sample before going
           into production with a new program, just to verify that
           the program is alright.

References

Global CERN library references

/user/goossens/cnasall/cnasbibl,/user/goossens/cnasall/textproc}



Next: Index Up: No Title Previous: No Title


goossens@cern.ch