Error Actions

In the following examples (and the previous one as well), $ETRAP and $ZTRAP in most cases have similar behavior. The most prominent difference is that, when $ETRAP is active, $ECODE determines whether or not a second error in an M stack level triggers an immediate implicit QUIT from that level. For additional information, see the sections on $ECODE and $ETRAP in Chapter 8: “Intrinsic Special Variables. Because of the effect of $ECODE on the processing flow when $ETRAP is active, there is a benefit to including appropriate $ECODE maintenance in $ZTRAP related code, so that things stay well behaved when the two mechanisms are intemixed. Other differences are discussed in some of the examples.

Break on an Error

When $ZTRAP is set to a BREAK command and an error occurs, GT.M puts the process into Direct Mode. The default for $ZTRAP is a BREAK command. When developing a program, $ZTRAP="BREAK" allows you to investigate the cause of the error from Direct Mode. For information on GT.M debugging tools, see Chapter 4: “Operating and Debugging in Direct Mode.

Example:

GTM>zprint ^EP1
EP1    WRITE !,"THIS IS "_$TEXT(+0) 
       KILL A 
BAD    WRITE A 
       WRITE !,"THIS IS NOT DISPLAYED" 
       QUIT 
 
GTM>do ^EP1
THIS IS EP1%GTM-E-UNDEF, Undefined local variable: A
                At M source location BAD^EP1
GTM>ZSHOW
BAD^EP1    ($ZTRAP)
    (Direct mode) 
+1^GTM$DMOD    (Direct mode) 
GTM>QUIT
GTM>ZSHOW
EP1+1^EP1    (Direct mode) 
+1^GTM$DMOD    (Direct mode) 
GTM>

Because by default $ETRAP="" and $ZTRAP="B", this example does not explicitly set either $ETRAP or $ZTRAP. When the routine encounters an error at BAD^EP1, GT.M initiates Direct Mode. The ZSHOW displays the M stack that has, at the bottom, the base Direct Mode frame and, at the top, EP1 with a notation that $ZTRAP has been invoked. The QUIT command at the prompt removes EP1 from the stack.

To prevent a program such as a production image from accessing Direct Mode, assign an action other than "BREAK" to $ETRAP or $ZTRAP. The following sections discuss various alternative values for $ETRAP or $ZTRAP.

In order to prevent inappropriate access to Direct Mode, eliminate all BREAKs from the production code. If the code contains BREAK commands, the commands should be subject to a postconditional flag that is only turned on for debugging. ZBREAK serves as an alternative debugging tool that effects only the current process and lasts only for the duration of an image activation.

Unconditional Transfer on an Error

The GOTO command instructs GT.M to transfer execution permanently to another line within the routine or to another routine. When stopping to investigate an error is undesirable, use the GOTO command in $ETRAP or $ZTRAP to continue execution at some other point.

Example:

GTM>ZPRINT ^EP2
EP2     WRITE !,"THIS IS "_$TEXT(+0) 
        SET $ECODE=""        ;this affects only $ETRAP 
        SET $ETRAP="GOTO ET"        ;this implicitly stacks $ZTRAP 
        ;N $ZT S $ZT="GOTO ET"  ;would give a similar result 
        DO SUB1 
        WRITE !,"THIS IS THE END" 
        QUIT 
SUB1    WRITE !,"THIS IS SUB1" 
        DO SUB2 
        QUIT 
SUB2    WRITE !,"THIS IS SUB2" 
        KILL A 
BAD     WRITE A 
        WRITE !,"THIS IS NOT DISPLAYED" 
        QUIT 
ET      ;SET $ZTRAP=""         ;if using $ZTRAP to prevent recursion 
        WRITE !,"CONTINUING WITH ERROR TRAP AFTER AN ERROR" 
        WRITE !,"$STACK: ",$STACK 
        WRITE !,"$STACK(-1): ",$STACK(-1) 
        WRITE !,"$ZLEVEL: ",$ZLEVEL 
        FOR I=$STACK(-1):-1:1 DO 
        . WRITE !,"LEVEL: ",I 
        . SET K=10 
        . FOR J="PLACE","MCODE","ECODE" DO 
        . . WRITE ?K," ",J,": ",$STACK(I,J) 
        . . SET K=K+20 
        WRITE !,$ZSTATUS,!  
        ZSHOW "S" 
        SET $ECODE=""        ;this affects only $ETRAP 
        QUIT 
 
GTM>do ^EP2
THIS IS EP2
THIS IS SUB1
THIS IS SUB2
CONTINUING WITH ERROR TRAP AFTER AN ERROR
$STACK: 3
$STACK(-1): 3
$ZLEVEL: 4
LEVEL: 3   PLACE: BAD^EP2      MCODE: BAD     WRITE A  ECODE: ,M6,Z150373850,
LEVEL: 2   PLACE: SUB1+1^EP2   MCODE:         DO SUB2  ECODE: 
LEVEL: 1   PLACE: EP2+4^EP2    MCODE:         DO SUB1  ECODE: 
150373850,BAD^EP2,%GTM-E-UNDEF, Undefined local variable: A
ET+12^EP2
SUB1+1^EP2
EP2+4^EP2
+1^GTM$DMOD    (Direct mode) 
THIS IS THE END
GTM>

This routine specifies a GOTO command transferring execution to the ET label when an error occurs. The $ZLEVEL special variable contains an integer indicating the M stack level.

The ZGOTO command is similar to the GOTO command, however, the ZGOTO allows the removal of multiple levels from the program stack. ZGOTO can ensure that execution returns to a specific point, such as a menu.

Example:

GTM>ZPRINT ^EP3
EP3     ;
MENU    WRITE !,"THIS IS MENU IN ",$TEXT(0) 
        SET $ECODE=""        ;this affects only $ETRAP 
        SET $ETRAP="SET $ECODE="""" ZGOTO 2" 
        ;N $ZT S $ZT="ZGOTO 2" ;would give a similar result 
        DO SUB1 
        WRITE !,"`MENU' AFTER $ETRAP" 
        WRITE !,"$STACK: ",$STACK 
        WRITE !,"$ZLEVEL: ",$ZLEVEL 
        QUIT 
SUB1    WRITE !,"THIS IS SUB1" 
        DO SUB2 
        WRITE !,"THIS IS SKIPPED BY ZGOTO" 
        QUIT 
SUB2    WRITE !,"THIS IS SUB2" 
        KILL A 
BAD     WRITE A 
        WRITE !,"THIS IS NOT DISPLAYED" 
        QUIT 
 
GTM>do ^EP3
THIS IS MENU IN 
THIS IS SUB1
THIS IS SUB2
`MENU' AFTER $ETRAP
$STACK: 1
$ZLEVEL: 2

This routine instructs GT.M to reset the execution to level 2 if it encounters an error. GT.M removes all intermediate levels.

In general, coding ZGOTO level information based on $ZLEVEL provides a more robust technique than the "hard-coding" shown in the previous example.

Example:

GTM>ZPRINT ^EP4
EP4     WRITE !,"THIS IS "_$TEXT(+0) 
        SET $ECODE=""        ;this affects only $ETRAP 
        DO MAIN 
        WRITE !,"THIS IS ",$TEXT(+0)," AFTER THE ERROR" 
        WRITE !,"$ZLEVEL: ",$ZLEVEL 
        QUIT 
MAIN    WRITE !,"THIS IS MAIN" 
        WRITE !,"$ZLEVEL: ",$ZLEVEL 
        SET $ETRAP="ZGOTO "_$ZLEVEL_":ET" 
        ;N $ZT S $ZT="ZGOTO "_$ZLEVEL_":ET ;alternative 
        DO SUB1 
        QUIT 
SUB1    WRITE !,"THIS IS SUB1" 
        WRITE !,"$ZLEVEL: ",$ZLEVEL 
        DO SUB2 
        QUIT 
SUB2    WRITE !,"THIS IS SUB2" 
        WRITE !,"$ZLEVEL :",$ZLEVEL 
        KILL A 
BAD     WRITE A 
        WRITE !,"THIS IS NOT DISPLAYED" 
        QUIT 
ET     ;SET $ZTRAP="" ;if using $ZTRAP to prevent recursion 
        WRITE !,"CONTINUING WITH ERROR TRAP AFTER AN ERROR" 
        WRITE !,"$STACK: ",$STACK 
        WRITE !,"$STACK(-1): ",$STACK(-1) 
        WRITE !,"$ZLEVEL: ",$ZLEVEL 
        FOR I=$STACK(-1):-1:1 DO 
        . WRITE !,"LEVEL: ",I 
        . SET K=10 
        . FOR J="PLACE","MCODE","ECODE" DO 
        . . WRITE ?K," ",J,": ",$STACK(I,J) 
        . . SET K=K+20 
        WRITE !,$ZSTATUS,!
        ZSHOW "S" 
        SET $ECODE=""        ;this affects only $ETRAP 
        QUIT 
 
GTM>do ^EP4
THIS IS EP4
THIS IS MAIN
$ZLEVEL: 3
THIS IS SUB1
$ZLEVEL: 4
THIS IS SUB2
$ZLEVEL :5
CONTINUING WITH ERROR TRAP AFTER AN ERROR
$STACK: 2
$STACK(-1): 4
$ZLEVEL: 3
LEVEL: 4   PLACE: BAD^EP4      MCODE: BAD     WRITE A  ECODE: ,M6,Z150373850,
LEVEL: 3   PLACE: SUB1+2^EP4   MCODE:         DO SUB2  ECODE: 
LEVEL: 2   PLACE: MAIN+4^EP4   MCODE:         DO SUB1  ECODE: 
LEVEL: 1   PLACE: EP4+2^EP4    MCODE:         DO MAIN  ECODE: 
150373850,BAD^EP4,%GTM-E-UNDEF, Undefined local variable: A
ET+12^EP4
EP4+2^EP4
+1^GTM$DMOD    (Direct mode) 
THIS IS EP4 AFTER THE ERROR
$ZLEVEL: 2
GTM>        

This routine sets $ETRAP or $ZTRAP to a ZGOTO specifying the current level. When the routine encounters an error at label BAD, GT.M switches control to label ET at the level where $ETRAP (or $ZTRAP) was established. At this point in the execution, ET replaces SUB1+2^EP4 as the program stack entry for the level specified, that is, $ZLEVEL=3. The QUIT command then returns control to the level where $ZLEVEL=2.

Setting $ZTRAP for Each Level

The command NEW $ETRAP or NEW $ZTRAP stacks the current value of $ETRAP or $ZTRAP respectively and, in the case of $ZTRAP, sets the value equal to the empty string. Normally, a SET $ETRAP or $ZTRAP immediately follows a NEW $ETRAP or $ZTRAP. When GT.M encounters a QUIT command that leaves a level where $ETRAP or $ZTRAP had been NEWed, GT.M deletes any value set to the ISV after the NEW command and restores the value that the ISV held previous to the NEW. NEW $ETRAP or $ZTRAP enables the construction of error handlers corresponding to the nesting of routines. A SET $ETRAP or $ZTRAP implicitly NEWs the other variable if it does not already have the value of the empty string. This enables the interleaving of $ETRAP and $ZTRAP at different levels, although (as mentioned above) such interleaving requires that $ZTRAP handlers deal appropriately with $ECODE.

Example:

GTM>ZPRINT ^EP5
EP5     WRITE !,"THIS IS "_$TEXT(+0)
        SET $ECODE="";this affects only $ETRAP
        WRITE !,"STARTING $ETRAP: ",$ETRAP
        WRITE !,"STARTING $ZTRAP: ",$ZTRAP
        DO SUB1
        WRITE !,"ENDING $ETRAP: ",$ETRAP
        WRITE !,"ENDING $ZTRAP: ",$ZTRAP
        QUIT
MAIN    WRITE !,"THIS IS MAIN"
        WRITE !,"$ZLEVEL: ",$ZLEVEL
        DO SUB1
        QUIT
SUB1    WRITE !,"THIS IS SUB1"
        NEW $ETRAP SET $ETRAP="GOTO ET1"
        ;NEW $ZTRAP SET $ZTRAP="GOTO ET1" ;alternative
        WRITE !,"$ETRAP FOR SUB1: ",$ETRAP
        KILL A
BAD     WRITE A
        WRITE !,"THIS IS NOT DISPLAYED"
        QUIT
ET1     WRITE !,"ERROR TRAP 1"
        WRITE !,"$ETRAP AFTER THE TRAP: ",$ETRAP
        WRITE !,"$ZTRAP AFTER THE TRAP: ",$ZTRAP
        SET $ECODE="";this affects only $ETRAP
        QUIT
 
GTM>do ^EP5
THIS IS EP5
STARTING $ETRAP: 
STARTING $ZTRAP: B
THIS IS SUB1
$ETRAP FOR SUB1: GOTO ET1
ERROR TRAP 1
$ETRAP AFTER THE TRAP: GOTO ET1
$ZTRAP AFTER THE TRAP: 
ENDING $ETRAP: 
ENDING $ZTRAP: B
GTM>

At SUB1, this routine NEWs $ETRAP and assigns it a value, which implicitly NEWs $ZTRAP. When the routine encounters an error at the SUB1 level, GT.M transfers control to label ET1 without modifying the value of $ETRAP or $ZTRAP. When the routine encounters a QUIT command in routine ET1, GT.M transfers control to the command after the DO that invoked ET1 and restores $ETRAP or $ZTRAP to the values they held before the NEW and the SET.

[Note] Note

If the transfer to ET1 was accomplished with a ZGOTO that reduced the stack level, after the trap, $ETRAP would have the value of the empty string and $ZTRAP would be "B".

Nested Error Handling

$ETRAP or $ZTRAP set to a DO command instructs GT.M to transfer execution temporarily to another line within this or another routine when it encounters an error. A QUIT command within the scope of the DO transfers control back to the code specified by the $ETRAP or $ZTRAP. When the code in the ISV terminates due to an explicit or implicit QUIT, the behavior of $ETRAP and $ZTRAP is different. When $ETRAP is in control, the level at which the error occurred is removed, and control returns to the invoking level. When $ZTRAP contains code, execution picks up at the beginning of the line with the error. A DO command within $ZTRAP is normally used for I/O errors that an operator may resolve, because a DO command permits re-execution of the line containing the error.

Example:

GTM>ZPRINT ^EP6
EP6     WRITE !,"THIS IS "_$TEXT(+0)
        NEW
        NEW $ZTRAP SET $ZTRAP="DO ET"
        SET (CB,CE)=0
BAD     SET CB=CB+1 WRITE A SET CE=CE+1
        WRITE !,"AFTER SUCCESSFUL EXECUTION OF BAD:",!
        SET A="A IS NOT DEFINED"
        ZWRITE
        QUIT
ET      W !,"CONTINUING WITH ERROR TRAP AFTER AN ERROR",!
        ZWRITE
        SET A="A IS NOW DEFINED"
GTM>do ^EP6
THIS IS EP6
CONTINUING WITH ERROR TRAP AFTER AN ERROR
CB=1
CE=0
A IS NOW DEFINED
AFTER SUCCESSFUL EXECUTION OF BAD:
A="A IS NOT DEFINED"
CB=2
CE=1
GTM>

This example sets $ZTRAP to a DO command. When the routine encounters an error in the middle of the line at label BAD, GT.M transfers control to label ET. After QUITting from routine ET, GT.M returns control to the beginning of the line at label BAD.

Example:

GTM>ZPRINT ^EP6A
EP6A    WRITE !,"THIS IS "_$TEXT(+0) 
        NEW 
        NEW $ETRAP SET $ETRAP="GOTO ET" 
        SET (CB,CE)=0 
BAD     SET CB=CB+1 WRITE A SET CE=CE+1 
        WRITE !,"AFTER SUCCESSFUL EXECUTION OF BAD:",! 
        ZWRITE 
        QUIT 
ET      W !,"CONTINUING WITH ERROR TRAP AFTER AN ERROR",! 
        ZWRITE 
        SET A="A IS NOW DEFINED" 
        SET RETRY=$STACK($STACK,"PLACE") 
        SET $ECODE="" 
        GOTO @RETRY 
 
GTM>DO ^EP6A
THIS IS EP6A
CONTINUING WITH ERROR TRAP AFTER AN ERROR
CB=1
CE=0
A IS NOW DEFINED
AFTER SUCCESSFUL EXECUTION OF BAD:
A="A IS NOW DEFINED"
CB=2
CE=1
RETRY="BAD^EP6A"
GTM>

This routine is an example of how $ETRAP handling can be coded to perform the same kind of resumtion of the original execution stream that occurs by default with $ZTRAP when there is no unconditional transfer of control.

Terminating Execution on an Error

If both $ETRAP and $ZTRAP are set to the empty string upon encountering an error, the current level is discarded and the error is reissued at the invoking level. When already at the lowest M stack level, GT.M terminates routine execution and returns control to the shell level. If $ZTRAP is used exclusively, $ZTRAP="" suppresses the unstacking of NEWed values of $ZTRAP associated with lower levels. $ETRAP values are always unstacked, however if the lowest level $ETRAP is the empty string (which it is by default when GT.M starts), GT.M performs the same termination as it does with $ZTRAP. These terminations with both ISVs empty provides a mechanism for returning to the shell with a status message when GT.M encounters an error.

Example:

GTM>ZPRINT ^EP7
EP7     WRITE !,"THIS IS ",$TEXT(+0)
        SET $ECODE="";this only affects $ETRAP
        SET $ETRAP="",$ZTRAP=""
        KILL A
BAD     WRITE A
        WRITE !,"THIS IS NOT DISPLAYED"
        QUIT
 
GTM>do ^EP7
THIS IS EP7
%GTM-E-UNDEF, Undefined local variable: A
%GTM-I-RTSLOC, At M source location BAD^EP7
$ 

GT.M issues a message describing the M error and releases control to the shell.

When the action specified by $ZTRAP results in another run-time error before changing the value of $ZTRAP, the routine may iteratively invoke $ZTRAP until a stack overflow terminates the GT.M image. SETting $ZTRAP="" at the beginning of error processing ensures that this type of infinite loop does not occur. Because $ETRAP is implicitly followed by a QUIT it does not have the tendency to recurse. While $ETRAP is resistant to recursion, it is not completely immune, because a GOTO or a ZGOTO within the same level can evade the implicit QUIT. $ETRAP error handling involving errors on more than one stack level can also be induced to recurse if $ECODE is inappropriately cleared before the errors at all levels have been properly dealt with.

Example:

GTM>ZPRINT ^EP8
EP8     WRITE !,"THIS IS ",$TEXT(+0)
        NEW $ZTRAP SET $ZTRAP="DO ET"
        KILL A
BAD     WRITE A
        WRITE !,"THIS IS NOT DISPLAYED"
        QUIT
ET      WRITE 2/0
        QUIT
 
GTM>DO ^EP8
THIS IS EP8
%GTM-E-STACKCRIT, Stack space critical
%GTM-E-ERRWZTRAP, Error while processing $ZTRAP
GTM>

When the routine encounters an error at label BAD, GT.M transfers control to label ET. When the routine encounters an error at label ET, it recursively does ET until a stack overflow condition terminates the GT.M image.

A set $ZTRAP="" command as soon as the program enters an error-handling routine prevents this type of "infinite" recursion.

GTM>zprint ^EP8A
EP8A    WRITE !,"THIS IS ",$TEXT(+0)
        SET $ECODE=""
        SET $ZTRAP="",$ETRAP="DO ET"
        KILL A
BAD     WRITE A
        WRITE !,"THIS IS NOT DISPLAYED"
        QUIT
ET      WRITE !,"CONTINUING WITH ERROR TRAP AFTER AN ERROR"
        ZSHOW "S"
        WRITE !,"HERE COMES AN ERROR IN THE TRAP CODE"
        WRITE 2/0
        QUIT
 
GTM>DO ^EP8A
THIS IS EP8A
CONTINUING WITH ERROR TRAP AFTER AN ERRORET+1^EP8A
BAD^EP8A    ($ZTRAP)
+1^GTM$DMOD    (Direct mode) 
HERE COMES AN ERROR IN THE TRAP CODE
%GTM-E-DIVZERO, Attempt to divide by zero
GTM>

This demonstrates how $ETRAP behavior in this circumstance is more appropriate. Note that the $ZTRAP="" at the lowest level, prevents exection from returning to Direct Mode when the initial value of $ZTRAP ("B") is unstacked; this step takes $ZTRAP out of the equation and should be part of initialization when the intention is to use $ETRAP exclusively.

Example:

GTM>ZPRINT ^EP9
EP9     WRITE !,"THIS IS ",$TEXT(+0)
        SET $ZTRAP="DO ET"
        KILL A
BAD     WRITE A
        WRITE !,"THIS IS NOT DISPLAYED"
        QUIT
ET      SET $ZT=""
        WRITE !,"THIS IS THE ERROR TRAP"
ERROR   WRITE !,"HERE COMES AN ERROR IN THE ERROR TRAP"
        WRITE 2/0
        QUIT
 
GTM>DO ^EP9
THIS IS EP9
THIS IS THE ERROR TRAP
HERE COMES AN ERROR IN THE ERROR TRAP
%GTM-E-DIVZERO, Attempt to divide by zero
%GTM-I-RTSLOC,                 At M source location ERROR+1^EP9
$

This routine sets the value of $ZTRAP to null as soon as the program enters the error handler. This insures program termination when an error occurs in the error handler.

Setting $ZTRAP to Other Actions

The QUIT, HALT and ZHALT commands also serve as useful $ETRAP or $ZTRAP actions.

The QUIT command terminates execution at that invocation level.

Example:

GTM>zprint ^EP10
EP10    WRITE !,"THIS IS ",$TEXT(+0)
        SET $ECODE="";this affects only $ETRAP
        S $ET="S $EC="""" Q" ;this implicitly stacks $ZTRAP
        ;N $ZT S $ZT="QUIT" ;would give a similar result
        DO SUB1
        QUIT
SUB1    WRITE !,"THIS IS SUB1"
        DO SUB2
        WRITE !,"THIS IS SUB1 AFTER THE ERROR WAS 'IGNORED'"
        QUIT
SUB2    WRITE !,"THIS IS SUB2"
        KILL A
BAD     WRITE A
        WRITE !,"THIS IS NOT DISPLAYED"
        QUIT
 
GTM>do ^EP10
THIS IS EP10
THIS IS SUB1
THIS IS SUB2
THIS IS SUB1 AFTER THE ERROR WAS 'IGNORED'
GTM>

This routine sets $ETRAP or $ZTRAP to the QUIT command. When the routine encounters an error at label BAD, GT.M executes the active error handling ISV. The QUIT command terminates execution of SUB2 and transfers execution back to SUB1. The WRITE displays the error message using the $ZSTATUS special variable. Because the default behavior is to QUIT after $ETRAP code completes, this technique is mostly useful with $ETRAP as a place holder to avoid the $ETRAP="" semantics when there is no action to take at the current level. With $ZTRAP, where the default behavior is to resume execution at the beginning the line that triggered the error, the QUIT is more than a placeholder.

The HALT command terminates routine execution and returns control to the shell level. Setting $ETRAP="HALT" or $ZTRAP="HALT" is similar to setting the ISV to the empty string except that the "HALT" code does not pass the error condition code back to the shell. After a HALT, $? contains zero (0).

ZHALT acts like HALT but takes and argument, which GT.M passes back to the OS shell. Note that UNIX shells typically limit return codes to a byte, so they may truncate the value of the ZHALT argument.

Example:

GTM>ZPRINT ^EP11
EP11    WRITE !,"THIS IS ",$TEXT(+0)
        SET $ECODE="";this affects only $ETRAP
        SET $ETRAP="HALT";this implicitly stacks $ZTRAP
        ;SET $ZTRAP="HALT";would give a similar result
        KILL A
BAD     WRITE !,A
        WRITE !,"THIS IS NOT DISPLAYED"
        QUIT
        
GTM>DO ^EP11
THIS IS EP11
$ 

Summary of $ETRAP & $ZTRAP Error-Handling Options

Summary of Error-Handling Options

ERROR-HANDLING FEATURE

DESCRIPTION AND POSSIBLE USES

$ETRAP="BREAK"

$ZTRAP="BREAK"

Returns to Direct Mode upon encountering an error that enables interactive debugging to determine the nature of the error.

$ETRAP="GOTO.."

$ZTRAP="GOTO.."

Transfers control upon encountering an error and allows for continuation of execution after the error. Use with an error handling routine that may record or report an error.

$ETRAP="ZGOTO.."

$ZTRAP="ZGOTO.."

Similar to GOTO, but additionally allows for removal of levels from the stack. Use to allow recovery to specific point, such as a menu.

NEW $ETRAP

NEW $ZTRAP

NEW $ETRAP stacks the old value but does not change the current value, while NEW $ZTRAP stacks the old value and sets the current value to the empty string. Usually followed by a SET $ETRAP or SET $ZTRAP. After a QUIT from a given level, GT.M restores the value held prior to the NEW. Use to enable different methods of error handling at different levels within an application.

$ETRAP="DO..."

Transfers execution temporarily to another label upon encountering an error. After return from a DO, GT.M QUITs from the stack level at which the error occured. Whether control returns to the invoking code or to the trap handler at the less nested level, depends on the value of $ECODE.

$ZTRAP="DO..."

Transfers execution temporarily to another label upon encountering an error. When GT.M returns from a DO and completes the $ZTRAP action, execution continues at the beginning of the line containing the error and re-executes the entire line containing the error. Use with I/O device errors where operator may intervene to correct the error condition.

$ZTRAP=""

Returns to shell with the Status Code and terminates execution. If SET in error handling routines, prevents infinite loops. Prevents access to Direct Mode. Use in production code when the invoking shell needs to test $?.

$ETRAP="SET $ECODE="""""

$ZTRAP="QUIT"

Terminates execution at that level upon encountering an error, and returns to the invocation level at the point immediately following the invocation. Use to ignore errors on a particular level and continue executing.

$ZTRAP="HALT"

Returns to the shell as if normal termination occurred. Avoids access to Direct Mode. Use in production code when the invoking shell does not need to examine the exit status of the GT.M process.

Errors in $ZTRAP

If $ZTRAP contains invalid source code, GT.M displays an error message and puts the process into Direct Mode.

If the action specified by $ZTRAP results in another run-time error before changing the value of $ZTRAP, it may result in a loop that iteratively invokes $ZTRAP until a stack overflow terminates the GT.M image. Keep $ZTRAP simple and take special care to debug exception handling.

[Note] Note

An error in $ETRAP code triggers an implicit TROLLBACK:$TLEVEL QUIT:$QUIT "" QUIT.

Recording Information about Errors

GT.M provides a number of standard features and extensions to examine and record information about an error condition.

The extensions are:

  • ZSHOW

  • ZWRITE

  • $ECODE

  • $STACK

  • $STACK()

  • $ZSTATUS

  • $ZJOBEXAM()

  • $ZLEVEL

The ZSHOW command displays information about the current M environment. A ZSHOW argument may contain an expression that contains codes selecting one or more types of information for output.

A: selects auto-relink information

B: selects ZBREAK information

C: provides the list of loaded external call packages and their routines. ZSHOW "C" does not report packages that are accessible but have not been accessed by the process.

D: selects open device information

G: selects global statistic information

I: selects intrinsic special variables

L: selects locks held by the process

R: selects the M stack but with routine hashes

S: selects the M stack

V: selects local variables

*: selects all possible ZSHOW information except A.

A ZSHOW with no argument displays the M stack on the current device. It lists the program stack from initiation to the current execution level.

The ZWRITE command prints the current value of defined variables. ZWRITE provides a tool for examining or saving variable context. ZWRITE and ZSHOW can only display the current local variables, not any local variable states that have been protected by NEW commands, or appearance in an invoked formallist. A WRITE may also display current global variables.

The $ECODE special variable contains a M standardized/user defined/GT.M specific error code.

The $STACK special variable contains the current level of M execution stack depth.

The $STACK() function returns strings describing aspects of the execution environment.

The $ZLEVEL special variable maintains an integer that indicates the level of nesting of DO and XECUTE commands. $ZLEVEL always contains an integer count of the number of levels displayed by issuing a ZSHOW "S" in that context.

The $ZJOBEXAM() function returns a string indicating the full path to the file where it stored a process context dump.

The $ZSTATUS special variable records the error condition code and location of the last error condition during execution.

For I/O operations, GT.M uses the $ZA, $ZB and $ZEOF special variables. $ZA contains a status determined by the last read on the current device.

To simplify record keeping, an application may set $ZTRAP to an error-handling routine that records information about an error. The next section provides an example of a routine ERR.m that does this.

Program to Record Information on an Error using $ZTRAP

GTM>ZPRINT ^ERR
ERR0;;RECORD CONTENT OF AN ERROR
;
RECORD  SET $ZTRAP="GOTO OPEN"
        ZSHOW "*":^ERR($J,$H)
        GOTO LOOPT;$H might change
LOOPV   ZSHOW "V":^ERR($J,$H,"VL",$ZLEVEL)
LOOPT   IF $ZLEVEL>1 ZGOTO $ZLEVEL-1:LOOPV
STACK   SET $ZTRAP="GOTO WARN"
        SET %ERRVH=$H;can cause error if memory low
        SET ^ERR($J,%ERRVH,"$STACK")=$STACK
        SET ^ERR($J,%ERRVH,"$STACK",-1)=$STACK(-1)
        FOR %ERRVI=$STACK(-1):-1:1 DO
        . SET %ERRVK=""
        . FOR %ERRVJ="PLACE","MCODE","ECODE" DO
        . . SET %ERRVK=%ERRVK_$STACK(%ERRVI,%ERRVJ)_"|~|"
        . SET ^ERR($J,%ERRVH,"$STACK",%ERRVI)=%ERRVK
        GOTO WARN
OPEN    SET $ZTRAP="GOTO OPEN1"
        SET %ERRIO=$IO,%ERRZA=$ZA,%ERRZB=$ZB,%ERRZE=$ZEOF
        SET %ERRVF="REC.ERR"
        SET %ERRVF=$ZDATE($H,"YEARMMDD2460SS")_"_"_$J_".ERR"
        OPEN %ERRVF:NEWVERSION
        USE %ERRVF
        S $ZT="S $ZT="" G WARN"" U $P:(NOCENA:CTRAP="""") G STAC"
        ZSHOW "*"
        KILL %ERRVF,%ERRIO,%ERRZA,%ERRZB,%ERRZE
        GOTO LOOPU
LOOPF   WRITE !,"LOCAL VARIABLES FOR ZLEVEL: ",$ZLEVEL,!
        ZWRITE
LOOPU   IF $ZLEVEL>1 ZGOTO $ZLEVEL-1:LOOPF
        WRITE !
STAC    SET $ZTRAP="GOTO WARN"
        WRITE !,"PROGRAM STACK: ",!
        WRITE !,"$STACK: ",$STACK,!
        WRITE !,"$STACK(-1): ",$STACK(-1),!
        FOR %ERRVI=$STACK(-1):-1:1 DO
        . WRITE !,"LEVEL: ",%ERRVI
        . SET %ERRVK=10
        . FOR %ERRVJ="PLACE","MCODE","ECODE" DO
        .. W ?%ERRVK,"",%ERRVJ,":",$STACK(%ERRVI,%ERRVJ)
        .. SET %ERRVK=%ERRVK+20
        CLOSE $IO
WARN    SET $ZTRAP="GOTO FATAL"
        IF $P=$I SET %ERRIO=$IO,%ERRZA=$ZA,%ERRZB=$ZB,%ERRZE=$ZEOF
        USE $P:(NOCENABLE:CTRAP="":EXCEPTION="")
        WRITE !,"YOU HAVE ENCOUNTERED AN ERROR"
        WRITE !,"PLEASE NOTIFY JOAN Q SUPPORT PERSON",!
FATAL   SET $ZTRAP=""
        ZM +$P($ST($ST(-1),"ECODE"),"Z",2)

The routine sets $ZTRAP to a sequence of values so that, in the event of an error various fallback actions are taken. If a STACKCRIT error occurs, GT.M makes a small amount of space for error handling. However, if the error handler uses up significant amounts of space by nesting routines or manupulating local variables, the error handler may cause another STACKCRIT error. In this case, it is possible for the error handling to loop endlessly, therefore this routine changes $ZTRAP so that each error moves the routine closer to completion.

First it attempts to store the context information in the global ^ERR. The LOOPV-LOOPT code records the invocation levels using the ZSHOW command. This technique addresses the situation where the application program defines or NEWs local variables for each level. The code executes a pass through the loop for each instance where the value of $ZLEVEL is greater than one (1). For each pass, ERR.M decrements the value of $ZLEVEL with the ZGOTO. When the value of $ZLEVEL reaches one (1), the code at, and following, the STACK label stores the error context available in the $STACK() function.

If there is a problem with storing any of this information, ^ERR attempts to store the context information in a file in the current default working directory. If it uses a file, in order to (at the label OPEN), record information about I/O operations, on the current device at the time of the error, the error handler SETs local variables to the values of the device specific I/O special variables $IO, $ZA, $ZB and $ZEOF before opening the log file.

The routine OPENs the log file with a name made up of the date and $JOB of the process. The NEWVERSION deviceparameter instructs GT.M to create a new version of the file. The LOOPF-LOOPU code records the invocation levels using the ZWRITE command in a manner analogous to that described above. If an error occurs trying to write to the file, $ZTRAP USEs the principal device and transfers control to the STAC label in an attempt to provide a minimal error context on the user terminal. The code at and following the STAC label records the error context available in the $STACK() function.

At the label WARN, the routine attempts to notify the user that an error has occurred and who to notify.

At the label FATAL, the ZMESSAGE command resignals the error. Because (with proper setup) $ETRAP and $ZTRAP are now null, GT.M releases control of the process to the host shell. In this example, the user never has access to Direct Mode.

Example:

GTM>zprint ^EP13
EP13    WRITE !,"THIS IS ",$TEXT(+0)
        SET $ZTRAP="GOTO NODB"
        KILL ^ERR
NODB    SET $ECODE="";this affects only $ETRAP
        ;S $ET="GOTO ^ERR";this implicitly stacks $ZTRAP
        N $ZT S $ZT="GOTO ^ERR" ;gives similar result
        DO SUB1
        WRITE !,"THIS IS THE END"
        QUIT
SUB1    WRITE !,"THIS IS SUB1"
        NEW
        SET (A,B,C)=$ZLEVEL
        DO SUB2
        QUIT
SUB2    WRITE !,"THIS IS SUB2"
        NEW
        SET (B,C,D)=$ZLEVEL
        DO SUB3
        QUIT
SUB3    WRITE !,"THIS IS SUB3"
        NEW
        SET (A,C,D)=$ZLEVEL
        DO BAD
BAD     NEW (A)
        SET B="BAD"
        WRITE 1/0
        WRITE !,"THIS IS NOT DISPLAYED"
        QUIT
 
GTM>do ^EP13
THIS IS EP13
THIS IS SUB1
THIS IS SUB2
THIS IS SUB3
YOU HAVE ENCOUNTERED AN ERROR
PLEASE NOTIFY JOAN Q SUPPORT PERSON

Example EP13 uses the error recording routine by setting $ZTRAP="GOTO ^ERR". When the routine encounters an error at label BAD, GT.M transfers control to routine ERR. Afterwards the ^ERR global would have contents like:

GTM>zwrite ^ERR
^ERR(13258,"64813,17382","$STACK")=0
^ERR(13258,"64813,17382","$STACK",-1)=5
^ERR(13258,"64813,17382","$STACK",1)="NODB+3^EP13|~|        DO SUB1|~||~|"
^ERR(13258,"64813,17382","$STACK",2)="SUB1+3^EP13|~|        DO SUB2|~||~|"
^ERR(13258,"64813,17382","$STACK",3)="SUB2+3^EP13|~|        DO SUB3|~||~|"
^ERR(13258,"64813,17382","$STACK",4)="SUB3+3^EP13|~|        DO BAD|~||~|"
^ERR(13258,"64813,17382","$STACK",5)="BAD+2^EP13|~|        WRITE 1/0|~|,M9,Z150373210,|~|"
^ERR(13258,"64813,17382","D",1)="/dev/pts/0 OPEN TERMINAL NOPAST NOESCA NOREADS TYPE WIDTH=165 LENG=48 "
^ERR(13258,"64813,17382","G",0)="GLD:*,REG:*,SET:77,KIL:3,GET:0,DTA:0,ORD:0,ZPR:0,QRY:0,LKS:0,LKF:0,CTN:0,DRD:3,DWT:0,NTW:77,NTR:5,NBW:85,NBR:170,NR0:0,NR1:0,NR2:0,N
R3:0,TTW:0,TTR:0,TRB:0,TBW:0,TBR:0,TR0:0,TR1:0,TR2:0,TR3:0,TR4:0,TC0:0,TC1:0,TC2:0,TC3:0,TC4:0,ZTR:0,DFL:0,DFS:0,JFL:0,JFS:0"
^ERR(13258,"64813,17382","G",0,1)=",JBB:0,JFB:0,JFW:0,JRL:0,JRP:0,JRE:0,JRI:0,JRO:0,JEX:0,DEX:0,CAT:80,CFE:0,CFS:0,CFT:0,CQS:0,CQT:0,CYS:0,CYT:0,BTD:6"
^ERR(13258,"64813,17382","G",1)="GLD:/home/gtc_twinata/staff/nitin/a.gld,REG:DEFAULT,SET:79,KIL:4,GET:0,DTA:0,ORD:0,ZPR:0,QRY:0,LKS:0,LKF:0,CTN:79,DRD:3,DWT:0,NTW:79
,NTR:6,NBW:87,NBR:178,NR0:0,NR1:0,NR2:0,NR3:0,TTW:0,TTR:0,TRB:0,TBW:0,TBR:0,TR0:0,TR1:0,TR2:0,TR3:0,TR4:0,TC0:0,TC1:0,TC2:0,"
^ERR(13258,"64813,17382","G",1,1)="TC3:0,TC4:0,ZTR:0,DFL:0,DFS:0,JFL:0,JFS:0,JBB:0,JFB:0,JFW:0,JRL:0,JRP:0,JRE:0,JRI:0,JRO:0,JEX:0,DEX:0,CAT:83,CFE:0,CFS:0,CFT:0,CQS
:0,CQT:0,CYS:0,CYT:0,BTD:6"
^ERR(13258,"64813,17382","I",1)="$DEVICE="""""
^ERR(13258,"64813,17382","I",2)="$ECODE="",M9,Z150373210,"""
^ERR(13258,"64813,17382","I",3)="$ESTACK=5"
^ERR(13258,"64813,17382","I",4)="$ETRAP="""""
^ERR(13258,"64813,17382","I",5)="$HOROLOG=""64813,17382"""
^ERR(13258,"64813,17382","I",6)="$IO=""/dev/pts/0"""
^ERR(13258,"64813,17382","I",7)="$JOB=13258"
^ERR(13258,"64813,17382","I",8)="$KEY="""""
^ERR(13258,"64813,17382","I",9)="$PRINCIPAL=""/dev/pts/0"""
^ERR(13258,"64813,17382","I",10)="$QUIT=0"
^ERR(13258,"64813,17382","I",11)="$REFERENCE=""^ERR(13258,""""64813,17382"""",""""I"""",10)"""
^ERR(13258,"64813,17382","I",12)="$STACK=5"
^ERR(13258,"64813,17382","I",13)="$STORAGE=2147483647"
^ERR(13258,"64813,17382","I",14)="$SYSTEM=""47,gtm_sysid"""
^ERR(13258,"64813,17382","I",15)="$TEST=1"
^ERR(13258,"64813,17382","I",16)="$TLEVEL=0"
^ERR(13258,"64813,17382","I",17)="$TRESTART=0"
^ERR(13258,"64813,17382","I",18)="$X=12"
^ERR(13258,"64813,17382","I",19)="$Y=6"
^ERR(13258,"64813,17382","I",20)="$ZA=0"
^ERR(13258,"64813,17382","I",21)="$ZALLOCSTOR=780808"
^ERR(197306,"64341,39400","I",21)="$ZAUDIT=0"
^ERR(13258,"64813,17382","I",22)="$ZB="""""
^ERR(13258,"64813,17382","I",23)="$ZCHSET=""M"""
^ERR(13258,"64813,17382","I",24)="$ZCLOSE=0"
^ERR(13258,"64813,17382","I",25)="$ZCMDLINE="""""
^ERR(13258,"64813,17382","I",26)="$ZCOMPILE="""""
^ERR(13258,"64813,17382","I",27)="$ZCSTATUS=0"
^ERR(13258,"64813,17382","I",28)="$ZDATEFORM=0"
^ERR(13258,"64813,17382","I",29)="$ZDIRECTORY=""/path/to/the/current/directory"""
^ERR(13258,"64813,17382","I",30)="$ZEDITOR=0"
^ERR(13258,"64813,17382","I",31)="$ZEOF=0"
^ERR(13258,"64813,17382","I",32)="$ZERROR=""Unprocessed $ZERROR, see $ZSTATUS"""
^ERR(13258,"64813,17382","I",33)="$ZGBLDIR=""/path/to/the/global/directory"""
^ERR(13258,"64813,17382","I",34)="$ZHOROLOG=""64813,17382,175283,14400"""
^ERR(13258,"64813,17382","I",35)="$ZICUVER="""
^ERR(13258,"64813,17382","I",36)="$ZININTERRUPT=0"
^ERR(13258,"64813,17382","I",37)="$ZINTERRUPT=""IF $ZJOBEXAM()"""
^ERR(13258,"64813,17382","I",38)="$ZIO=""/dev/pts/0"""
^ERR(13258,"64813,17382","I",39)="$ZJOB=0"
^ERR(13258,"64813,17382","I",40)="$ZKEY="""""
^ERR(13258,"64813,17382","I",41)="$ZLEVEL=6"
^ERR(13258,"64813,17382","I",42)="$ZMAXTPTIME=0"
^ERR(13258,"64813,17382","I",43)="$ZMODE=""INTERACTIVE"""
^ERR(13258,"64813,17382","I",44)="$ZONLNRLBK=0"
^ERR(13258,"64813,17382","I",45)="$ZPATNUMERIC=""M"""
^ERR(13258,"64813,17382","I",46)="$ZPIN=""/dev/pts/0"""
^ERR(13258,"64813,17382","I",47)="$ZPOSITION=""RECORD+1^ERR"""
^ERR(13258,"64813,17382","I",48)="$ZPOUT=""/dev/pts/0"""
^ERR(13258,"64813,17382","I",49)="$ZPROMPT=""GTM>"""
^ERR(13258,"64813,17382","I",50)="$ZQUIT=0"
^ERR(13258,"64813,17382","I",51)="$ZREALSTOR=802648"
^ERR(13258,"64813,17382","I",52)="$ZRELDATE=""20180614 00:33"""
^ERR(13258,"64813,17382","I",53)="$ZROUTINES="". /usr/lib/fis-gtm/V6.3-007_x86_64 /usr/lib/fis-gtm/V6.3-007_x86_64/plugin/o(/usr/lib/fis-gtm/V6.3-007_x86_64/plugin/r)"""
^ERR(13258,"64813,17382","I",54)="$ZSOURCE="""""
^ERR(13258,"64813,17382","I",55)="$ZSTATUS=""150373210,BAD+2^EP13,%GTM-E-DIVZERO, Attempt to divide by zero"""
^ERR(13258,"64813,17382","I",56)="$ZSTEP=""B"""
^ERR(13258,"64813,17382","I",57)="$ZSTRPLLIM=0"
^ERR(13258,"64813,17382","I",58)="$ZSYSTEM=0"
^ERR(13258,"64813,17382","I",59)="$ZTIMEOUT=-1"
^ERR(13258,"64813,17382","I",60)="$ZTDATA=0"
^ERR(13258,"64813,17382","I",61)="$ZTDELIM="""""
^ERR(13258,"64813,17382","I",62)="$ZTEXIT="""""
^ERR(13258,"64813,17382","I",63)="$ZTLEVEL=0"
^ERR(13258,"64813,17382","I",64)="$ZTNAME="""""
^ERR(13258,"64813,17382","I",65)="$ZTOLDVAL="""""
^ERR(13258,"64813,17382","I",66)="$ZTRAP=""GOTO OPEN"""
^ERR(13258,"64813,17382","I",67)="$ZTRIGGEROP="""""
^ERR(13258,"64813,17382","I",68)="$ZTSLATE="""""
^ERR(13258,"64813,17382","I",69)="$ZTUPDATE="""""
^ERR(13258,"64813,17382","I",70)="$ZTVALUE="""""
^ERR(13258,"64813,17382","I",71)="$ZTWORMHOLE="""""
^ERR(13258,"64813,17382","I",72)="$ZUSEDSTOR=759855"
^ERR(13258,"64813,17382","I",73)="$ZUT=1528966182176530"
^ERR(13258,"64813,17382","I",74)="$ZVERSION=""GT.M V6.3-007 Linux x86_64"""
^ERR(13258,"64813,17382","I",75)="$ZYERROR="""""
^ERR(13258,"64813,17382","L",0)="MLG:0,MLT:0"
^ERR(13258,"64813,17382","R",1)="RECORD+1^ERR:e99b16e4f7e1112d058dc22cb53491fd"
^ERR(13258,"64813,17382","R",2)="SUB3+3^EP13:d9e026c6d14e42567d3e64eecd049726"
^ERR(13258,"64813,17382","R",3)="SUB2+3^EP13:d9e026c6d14e42567d3e64eecd049726"
^ERR(13258,"64813,17382","R",4)="SUB1+3^EP13:d9e026c6d14e42567d3e64eecd049726"
^ERR(13258,"64813,17382","R",5)="NODB+3^EP13:d9e026c6d14e42567d3e64eecd049726"
^ERR(13258,"64813,17382","R",6)="+1^GTM$DMOD    (Direct mode) "
^ERR(13258,"64813,17382","V",1)="A=5 ;*"
^ERR(13258,"64813,17382","V",2)="B=""BAD"""
^ERR(13258,"64813,17382","VL",3,"V",1)="A=3"
^ERR(13258,"64813,17382","VL",3,"V",2)="B=3"
^ERR(13258,"64813,17382","VL",3,"V",3)="C=3"
^ERR(13258,"64813,17382","VL",4,"V",1)="B=4"
^ERR(13258,"64813,17382","VL",4,"V",2)="C=4"
^ERR(13258,"64813,17382","VL",4,"V",3)="D=4"
^ERR(13258,"64813,17382","VL",5,"V",1)="A=5"
^ERR(13258,"64813,17382","VL",5,"V",2)="C=5"
^ERR(13258,"64813,17382","VL",5,"V",3)="D=5"