GT.M provides the error handling facilities described in the M standard. In addition, GT.M provides a number of extensions for error handling. Both are discussed in the following sections. The following table summarizes some of the tools, which are then described in more detail within the context of various techniques and examples.
The value of $ECODE is a string that may reflect multiple error conditions. As long as no error has occured, the value of $ECODE is equal to the empty string.
$ECODE contains a list of errors codes for "active" errors - the error conditions which are not yet resolved. If there are no active errors, $ECODE contains the empty string. The value of $ECODE can be SET.
The most recent error in $ECODE appears first, the oldest last. If the error is defined by the M standard, the code starts with an "M", GT.M error codes including those provided by OS services start with "Z", and application defined codes must start with "U". Every code is separated by a coma (,) and there is always a coma at the beginning and at the end of a list. GT.M provided codes are those reported in $ZSTATUS, interpreted by $ZMESSAGE() and recognized as arguments to ZMESSAGE command. When GT.M supplies a standard error code in $ECODE, it also supplies a corresponding 'Z' code.
Example (setting $ECODE):
SET $ECODE="" ;sets $ECODE to the empty string
SET $ECODE=",M20," ;an ANSI M standardized error code
SET $ECODE=",U14," ;user defined error code
SET $PIECE($ECODE,",",2)="Z3," ;insert a non-ANSI error code
SET $PIECE($ECODE,",",$LENGTH($ECODE,",")+1)="An..," ;append
Standard Error processing affects the flow of control in the following manner. Detection of an error causes GOTO implicit sub-routine. When $ECODE="", the implicit subroutine is $ETRAP and QUIT:$QUIT "" QUIT. Otherwise the implicit subroutine is $ETRAP followed by TROLLBACK:$TLEVEL and then QUIT:$QUIT "" QUIT.
The QUIT command behaves in a special fashion while the value of $ECODE is non-empty. If a QUIT command is executed that returns control to a less nested level than the one where the error occurred, and the value of $ECODE is still non-empty, first all normal activity related to the QUIT command occurs (especially the unstacking of NEWed variables) and then the current value of $ETRAP is executed. Note that, if $ETRAP had been NEWed at the current or intervening level, the unstacked value of $ETRAP is executed.
SETting $ECODE to an invalid value is an error. SETting $ECODE to a valid error behaves like detection of error. SETting $ECODE="" does not cause a change in the flow, but effects $STACK(), subsequent $QUITs and errors.
![[Note]](../common/images/note.jpg) | Note |
---|
To force execution of an error trap or to flag a user-defined error ("U" errors), make the value of $ECODE non-empty: SET $ECODE=",U13-User defined error trap," |
![[Note]](../common/images/note.jpg) | Note |
---|
The value of $ECODE provides information about errors that have occurred since the last time it was reset to an empty string. In addition to the information in this variable, more detailed information can be obtained from the intrinsic function $STACK. For more information, see the section on a??$STack()a??. |
When you need to set up a stratified scheme where one level of subroutines use one error trap setting and another more nested subroutine uses a different one; the more nested subroutine must NEW $ETRAP. When $ETRAP is NEWed, its old value is saved and copied to the current value. A subsequent SET $ETRAP=<new-value> then establishes the error trapping code for the current execution level.
The QUIT command that reverts to the calling routine causes the NEWed values to be unstacked, including the one for $ETRAP.
If an error occurs while executing at the current execution level (or at an execution level farther from the initial base stack frame), GT.M executes the code from the current $ETRAP. Unless a GOTO or ZGOTO in $ETRAP or any code it invokes redirects the flow of execution, when the execution of the $ETRAP code completes, control reverts to the implicit QUIT command, which returns to the routine that invoked the code that encountered the error. At this time, the QUIT reinstates any prior value of $ETRAP.
While at the more nested execution level(s), if an error occurs, GT.M executes the code from the current $ETRAP. After the QUIT to a less nested level, GT.M invokes the code from the now current $ETRAP. The current $ETRAP may be different from the $ETRAP at the time of the error due to unstacking. This behavior continues until one of the following possible situations occur:
When dealing with stratified error trapping, it is important to be aware of two additional intrinsic variables: $STACK and $ESTACK. The values of both of these variables indicate the current execution level. The value of $STACK is an "absolute" value that counts from the start of the GT.M process, whereas the value of $ESTACK restarts at zero (0) each time $ESTACK is NEWed.
It is often beneficial to NEW both $ETRAP and $ESTACK a the same time.
If, at the time of any error, the value of $ZTRAP is non-empty, GT.M uses the $ZTRAP contents to direct execution of the next action.Refer to the $ZTRAP section in ChapterA 8: a??Intrinsic Special Variablesa??.
By default, execution proceeds as if the next instruction to be executed were the first one on "the next line", and the code on that next line would be the same as the text in the value of $ZTRAP. Unless $ZTRAP or any code it invokes issues a GOTO or ZGOTO, after GT.M has executed the code in $ZTRAP, GT.M attempts to execute the line with the error again. When a value is assigned to $ZTRAP, the new value replaces the previous value. If the value of $ETRAP is a non-empty one, $ETRAP is implicitly NEWed, and the value of $ETRAP becomes equal to the empty string; this ensures that at most one of $ETRAP and $ZTRAP is not the empty string. If the environment variable gtm_ztrap_new evaluates to Boolean TRUE (case insensitive string "TRUE", or case insensitive string "YES", or a non-zero number), $ZTRAP is NEWed when $ZTRAP is SET; otherwise $ZTRAP is not stacked when it is SET.
Other than the default behavior, $ZTRAP settings are controlled by the environment variable gtm_ztrap_form as described in the following table.
Although the "adaptive" and "popadaptive" behaviors permit mixing of two behaviors based on the current value of $ZTRAP, the $ZTRAP behavior type is selected at process startup from gtm_ztrap_form and cannot be modified during the life of the process.
![[Note]](../common/images/note.jpg) | |
---|
Like $ZTRAP values, invocation of device EXCEPTION values, with the exception noted, follow the pattern specified by the current gtm_ztrap_form setting. |
The activation of $ETRAP and $ZTRAP are the same, however there are a number of differences in their subsequent behavior.
For subsequent errors the then current $ZTRAP is invoked, while with $ETRAP, behavior is controlled by the state of $ECODE. This means that when using $ZTRAP, it is important to change $ZTRAP, possibly to the empty string, at the beginning of the action in order to protect against recursion caused by any errors in $ZTRAP itself or in the code it invokes.
If there is no explicit or implicit GOTO or ZGOTO in the action, once a $ZTRAP action completes, execution resumes at the beginning of the line where the error occurred, while once a $ETRAP action completes, there is an implicit QUIT. This means that $ZTRAP actions that are not intended to permit a successful retry of the failing code should contain a GOTO, or more typically a ZGOTO. In contrast, $ETRAP actions that are intended to cause a retry must explicitly reinvoke the code where the error occurred.
For QUITs from the level at which an error occurred, $ZTRAP has no effect, where $ETRAP behavior is controlled by the state of $ECODE. This means that to invoke an error handler nested at the lower level, $ZTRAP actions need to use an explicit ZMESSAGE command, while $ETRAP does such invocations implicitly unless $ECODE is SET to the empty string.
It is important to be aware of which of the trap mechanisms is in place to avoid unintended interactions, and aware of which conditions may cause a switch-over from one mode of error handling to the other.
When a SET command assigns a value to either $ZTRAP or $ETRAP, GT.M examines the value of the other error handling variable. If the other value is non-empty, GT.M executes an implicit NEW command that saves the current value of that variable, and then assigns that variable to the empty string, then makes the requested assignment effective.
For example, re-setting $ETRAP is internally processed as:
NEW:$LENGTH($ZTRAP) $ZTRAP $ETRAP SET $ETRAP=code
Whereas, SET $ZTRAP=value is internally processed as:
NEW:$LENGTH($ETRAP) $ETRAP SET:$LENGTH($ETRAP)="" SET $ZTRAP=value
Note that, after saving the prior value, GT.M ensures the superseded $ETRAP or $ZTRAP implicitly gets the value of the empty string. As a result, at most one of the two error handling mechanisms can be effective at any given point in time.
If an error handling procedure was invoked through the $ETRAP method, and the value of $ECODE is non-empty when QUITing from the level of which the error occurred, the behavior is to transfer control to the error handler associated with the newly unstacked level. However, if the QUIT command at the end of error level happens to unstack a saved value of $ZTRAP (and thus cause the value of $ETRAP to become empty), the error handling mechanism switches from $ETRAP-based to $ZTRAP-based.
![[Note]](../common/images/note.jpg) | Note |
---|
At the end of an error handling procedure invoked through $ZTRAP, the value of $ECODE is not examined, and this value (if any) does not cause any transfer to another error handling procedure. However, if not cleared it may later trigger a $ETRAP unstacked by a QUIT. |
Making a choice between the two mechanisms for error handling is mostly a matter of compatibility. If compatibility with existing GT.M code is important, and that code happens to use $ZTRAP, then $ZTRAP is the best effort choice. If compatibility with code written in MUMPS dialects from other vendors is important, then $ETRAP or a non-default form of $ZTRAP probably is the better choice.
When no pre-existing code exists that favors one mechanism, the features of the mechanisms themselves should be examined.
Almost any effect that can be achieved using one mechanism can also be achieved using the other. However, some effects are easier to achieve using one method, and some are easier using with the other.
If the mechanisms are mixed, or there is a desire to refer to $ECODE in an environment using $ZTRAP, it is recommended to have $ZTRAP error code SET $ECODE="" at some appropriate time, so that $ECODE does not become cluttered with errors that have been successfully handled.
![[Note]](../common/images/note.jpg) | Note |
---|
A device EXCEPTION gets control after a non-fatal device error and $ETRAP/$ZTRAP get control after other non-fatal errors. |
With $ZTRAP: If $ZSTATUS[...
With $ETRAP: If $ECODE[...
![[Note]](../common/images/note.jpg) | Note |
---|
The value of $ZSTATUS reflects only the most recent error, while the value of $ECODE is the cumulative list of all errors since its value was explicitly set to empty. Both values are always maintained and can be used with either mechanism. |
$ETRAP and $ZTRAP offer many features for catching, recognizing, and recovering from errors. Code within an error processing subroutines may cause its own errors and these need to be processed without causing an infinite loop (where an error is caught, which, while being processed causes another error, which is caught, and so on).
During the debugging phase, such loops are typically the result of typographical errors in code. Once these typographical errors are corrected, the risk remains that an error trapping subroutine was designed specifically to deal with an expected condition; such as the loss of a network connection. This then creates an unexpected error of its own, such as:
![[Note]](../common/images/note.jpg) | Note |
---|
It is important to remain aware of any issues that may arise within an error trapping procedure, and also of the conditions that might cause the code in question to be invoked. |
$ETRAP is recursively invoked if it invokes a GOTO or a ZGOTO and the error condition persists in the code path and the code SETs $ECODE="". $ZTRAP is recursively invoked if the error condition persists in the code path.
When GT.M encounters an error in the operation of an I/O device, GT.M executes the EXCEPTION deviceparameter for the OPEN/USE/CLOSE commands. An EXCEPTION deviceparameter specifies an action to take when an error occurs in the operation of an I/O device. The form of the EXCEPTION action is subject to the gtm_ztrap_form setting described for $ZTRAP, except that there is never any implicit popping with EXCEPTION actions. If a device has no current EXCEPTION, GT.M uses $ETRAP or $ZTRAP to handle an error from that device.
GT.M provides the option to:
An EXCEPTION based on an error for the device applies only to that device, and provides a specific error handler for a specific I/O device.
The CTRAP deviceparameter for USE establishes <CTRL-n> where 0<=n<=31 as an interrupting signal. When GT.M encounters <CTRL-n> with CTRAP enabled, GT.M executes the EXCEPTION deviceparamenter, or, $ETRAP or $ZTRAP if the device has no current EXCEPTION. <CTRL-C> is unique among <CTRL> characters, in that the OS recognizes it as an out-of-band signal and delivers it immediately; GT.M recognizes other <CTRL> character when they appear duing a READ.
Example:
GTM>ZPRINT ^EP12
EP12 WRITE !,"THIS IS ",$TEXT(+0)
SET $ECODE="";this only affects $ETRAP
SET $ETRAP="GOTO ET"
;N $ZT S $ZT="W !,"CAN'T TAKE RECIPROCAL OF 0"",*7"
USE $P:(EXCEPTION="D BYE":CTRAP=$C(3))
WRITE !,"TYPE <CTRL-C> TO STOP"
LOOP FOR DO
. READ !,"TYPE A NUMBER: ",X
. WRITE ?20,"HAS RECIPROCAL OF: ",1/X
. QUIT
ET . WRITE !,"CAN'T TAKE RECIRPOCAL OF 0",*7
. SET $ECODE=""
QUIT
BYE WRITE !,"YOU TYPED <CTRL-C> YOU MUST BE DONE!"
USE $P:(EXCEPTION="":CTRAP="")
WRITE !,"$ZSTATUS=",$ZSTATUS
ZGOTO 1
GTM>DO ^EP12
THIS IS EP12
TYPE <CTRL-C> TO STOP
TYPE A NUMBER: 1 HAS RECIPROCAL OF: 1
TYPE A NUMBER: 2 HAS RECIRPOCAL OF: .5
TYPE A NUMBER: 3 HAS RECIPROCAL OF: .33333333333333
TYPE A NUMBER: 4 HAS RECIPROCAL OF: .25
TYPE A NUMBER: HAS RECIPROCAL OF:
CAN'T TAKE RECIPROCAL OF 0
TYPE A NUMBER:
YOU TYPED <CTRL-C> YOU MUST BE DONE!
$ZSTATUS=150372498,LOOP+1^EP12,%GTM-E-CTRAP,Character trap $C(3) encountered
GTM>
This routine prompts the user to enter a number at the terminal. If the user enters a zero, GT.M encounters an error and executes $ETRAP (or $ZTRAP). The action specified reports the error and returns to prompt the user to enter a number. With $ZTRAP, this is very straightforward. With $ETRAP, some care is required to get the code to resume at the proper place. The CTRAP deviceparameter establishes <CTRL-C> as a trap character. When GT.M encounters a <CTRL-C>, GT.M executes the EXCEPTION string whcih transfers control to the label BYE. At the label BYE, the routine terminates execution with an error message. Using the EXCEPTION deviceparameter with CTRAP generally simplifies $ETRAP or $ZTRAP handling.
$ZSTATUS allows the routine to find out which trap character GT.M encountered. When a routine has several character traps set, $ZSTATUS provides useful information for identifying which character triggered the trap, and thereby allows a custom response to a specific input.