Command-line Routine Debugging
This chapter describes the Caché techniques for testing and debugging Caché applications. Its topics include:
An important part of application development is routine debugging: the testing and correcting of program code. Caché gives you two ways to debug your routines:
Use the BREAK command in routine code to suspend execution and allow you to examine what is happening.
Use the ZBREAK command to invoke the Caché Debugger to interrupt execution and allow you to examine both code and variables.
Caché includes the ability to suspend a routine and enter a shell that supports full debugging capabilities, as described in this chapter. Caché also includes a secure debug shell, which has the advantage of ensuring that users are prevented from exceeding or circumventing their assigned privileges.
Secure Debug Shell
The secure debug shell helps better control access to sensitive data. It is an environment that allows users to perform basic debugging, such as stepping and displaying variables, but does not allow them to do anything that changes the execution path or results of a routine. This protects against access that can lead to issues such as manipulation, malicious role escalation, and the injection of code to run with higher privileges.
By default, users at the debug prompt maintain their current level of privileges. To enable the secure shell for the debug prompt and thereby restrict the commands that the user may issue, you must enable the secure debug shell for that user.
If enabled for the current user, the secure debug shell starts when a BREAK command is executed, a breakpoint or watchpoint is encountered, or an uncaught error is issued.
Within the secure debug shell, the user cannot invoke:
Any command that can modify a variable.
Any function that can modify a variable.
Any command that can call other routines.
Any command that affects the flow of the routine or the environment.
Within the secure debug shell, when a user attempts to invoke a restricted command or function, Caché throws a <COMMAND> or <FUNCTION> error, respectively.
Restricted Commands and Functions
This section lists the restricted activities within the secure debug shell:
Restricted ObjectScript Commands
The following are the restricted ObjectScript commands for the secure debug shell:
GOTO with an argument
user commands except ZW and ZZDUMP
Restricted ObjectScript Functions
The following are the restricted ObjectScript functions for the secure debug shell:
$DATA(,var) — two-argument version only
$ORDER(,,var) — three-argument version only
$QUERY(,,var) — three-argument version only
any extrinsic function
Restricted Object Constructions
No method or property references are allowed. Property references are restricted because they could invoke a propertyGet method. Some examples of the object method and property syntax constructions that are restricted are:
Even without passing a variable by reference, a method can modify public variables. Since a property reference could invoke a propGet method, no property access is allowed.
Restricted MultiValue Commands
The following are the restricted MultiValue commands for the secure debug shell:
Debugging with the Caché Debugger
The Caché Debugger lets you test routines by inserting debugging commands directly into your routine code. Then, when you run the code, you can issue commands to test the conditions and the flow of processing within your application. Its major capabilities are:
Set breakpoints with the ZBREAK command at code locations and take specified actions when those points are reached.
Set watchpoints on local variables and take specified actions when the values of those variables change.
Interact with Caché during a breakpoint/watchpoint in a separate window.
Trace execution and output a trace record (to the Terminal or other device) whenever the path of execution changes.
Display the execution stack.
Run an application on one device while debugging I/O goes to a second device. This enables full screen Caché applications to be debugged without disturbing the application’s terminal I/O.
Using Breakpoints and Watchpoints
The Caché Debugger provides two ways to interrupt program execution:
A breakpoint is a location in a Caché routine that you specify with the ZBREAK command. When routine execution reaches that line, Caché suspends execution of the routine and, optionally, executes debugging actions you define. You can set breakpoints in up to 20 routines. You can set a maximum of 20 breakpoints within a particular routine.
A watchpoint is a variable you identify in a ZBREAK command. When its value is changed with a SET or KILL command, you can cause the interruption of routine execution and/or the execution of debugging actions you define within the ZBREAK command. You can set a maximum of 20 watchpoints.
Breakpoints and watchpoints you define are not maintained from one session to another. Therefore, you may find it useful to store breakpoint/watchpoint definitions in a routine or XECUTE command string so it is easy to reinstate them between sessions.
Establishing Breakpoints and Watchpoints
You use the ZBREAK command to establish breakpoints and watchpoints.
|location||Required. Specifies a code location (that sets a breakpoint) or local or system variable (which sets a watchpoint). If the location specified already has a breakpoint/watchpoint defined, the new specification completely replaces the old one.|
|action||Optional — Specifies the action to take when the breakpoint/watchpoint is triggered. For breakpoints, the action occurs before the line of code is executed. For watchpoints, the action occurs after the command that modifies the local variable. Actions may be upper- or lowercase, but must be enclosed in quotation marks.|
|condition||Optional — Specifies an expression that will be evaluated when the breakpoint/watchpoint is triggered. The expression must be surrounded by quotation marks. If condition is false, the action will not be carried out and the execute_code will not be executed. If condition is not specified, the default is true.|
|execute_code||Optional — Specifies ObjectScript code to be executed if condition is true. The code must be surrounded by quotation marks if it is a literal. This code is executed before the action being carried out. Before the code is executed, the value of the $TEST special system variable is saved. After the code has executed, the value of $TEST as it existed in the program being debugged is restored.|
Using ZBREAK with a ? (question mark) displays help.
Setting Breakpoints with Code Locations
You specify code locations as a routine line reference that you can use in a call to the $TEXT function. A breakpoint occurs whenever execution reaches this point in the code, before the execution of the line of code. If you do not specify a routine name, Caché assumes the reference is to the current routine.
Argumentless GOTO in Breakpoint Execution Code
An argumentless GOTO is allowed in breakpoint execution code. Its effect is equivalent to executing an argumentless GOTO at the debugger BREAK prompt and execution proceeds until the next breakpoint.
For example, if the routine you are testing is in the current namespace, you can enter location values such as these:
|label^rou||Break before the line at the line label label in the routine rou.|
|label+3^rou||Break before the third line after the line label label in routine rou.|
|+3^rou||Break before the third line in routine rou.|
If the routine you are testing is currently loaded in memory (that is, an implicit or explicit ZLOAD was performed), you can use location values such as these:
|label||Break before the line label at label.|
|label+3||Break before the third line after label.|
|+3||Break before the third line.|
Setting Watchpoints with Local and System Variable Names
Local variable names cause a watchpoint to occur in these situations:
When the local variable is created
When a SET command changes the value of the local variable
When a KILL command deletes the local variable
Variable names are preceded by an asterisk, as in *a.
If you specify an array-variable name, the Caché Debugger watches all descendant nodes. For instance, if you establish a watchpoint for array a, a change to a(5) or a(5,1) triggers the watchpoint.
The variable need not exist when you establish the watchpoint.
You can also use the following special system variables:
Action Argument Values
The following table describes the values you can use for the ZBREAK action argument.
|"B"||Default, except if you include the "T" action, then you must also explicitly include the "B" action, as in ZBREAK *a:"TB", to actually cause a break. Suspends execution and displays the line at which the break occurred along with a caret (^) indicating the point in the line. It then displays the Terminal prompt and allows interaction. Execution resumes when you issue an argumentless GOTO command.|
|"L"||Same as "B", except GOTO initiates single-step execution, stopping at the beginning of each line. When a DO command, user-defined function, or XECUTE command is encountered, single-step mode is suspended until that command or function completes.|
|"L+"||Same as "B", except GOTO initiates single-step execution, stopping at the beginning of each line. DO commands, user-defined functions, and XECUTE commands do not suspend single-step mode.|
|"S"||Same as "B", except GOTO initiates single-step execution, stopping at the beginning of each command. When a DO command, user-defined function, FOR command, or XECUTE command is encountered, single-step mode is suspended until that command or function completes.|
|"S+"||Same as "B", except GOTO initiates single-step execution, stopping at the beginning of each command. DO commands, user-defined functions, FOR commands, and XECUTE commands do not suspend single-step mode.|
|"T"||Can be used together with any other argument. Outputs a trace message to the trace device. This argument works only after you have set tracing to be ON with the ZBREAK /TRACE:ON command, described later. The trace device is the principal device unless you define it differently in the ZBREAK /TRACE command. If you use this argument with a breakpoint, you see the following message: TRACE: ZBREAK at label2^rou2. If you use this argument with a watchpoint, you see a trace message that names the variable being watched and the command being acted upon. In the example below, the variable a was being watched. It changed at the line test+1 in the routine test. TRACE: ZBREAK SET a=2 at test+1^test. If you include the "T" action, you must also explicitly include the "B" action as in ZBREAK *a:"TB", to have an actual break occur.|
|"N"||Take no action at this breakpoint/watchpoint. The condition expression is always evaluated and determines if the execute_code is executed.|
The following example establishes a watchpoint that suspends execution whenever the local variable a is killed. No action is specified, so "B" is assumed.
The following example illustrates the above watchpoint acting on a direct mode Caché command (rather than on a command issued from within a routine). The caret (^) points to the command that caused execution to be suspended:
USER>KILL a KILL a ^ <BREAK> USER 1s0>
The following example establishes a breakpoint that suspends execution and sets single-step mode at the beginning of the line label2^rou.
The following example shows how the break would appear when the routine is run. The caret (^) indicates where execution was suspended.
USER>DO ^rou label2 SET x=1 ^ <BREAK>label2^rou USER 2d0>
In the following example, a breakpoint at line label3^rou does not suspend execution, because of the "N" action. However, if x<1 when the line label3^rou is reached, then flag is SET to x.
ZBREAK label3^rou:"N":"x<1":"SET flag=x"
The following example establishes a watchpoint that executes the code in ^GLO whenever the value of a changes. The double colon indicates no condition argument.
ZBREAK *a:"N"::"XECUTE ^GLO"
The following example establishes a watchpoint that causes a trace message to display whenever the value of b changes. The trace message will display only if trace mode has been turned on with the ZBREAK /TRACE:ON command.
The following example establishes a watchpoint that suspends execution in single-step mode when variable a is set to 5.
When the break occurs in the following example, a caret (^) symbol points to the command that caused the variable a to be set to 5.
USER>DO ^test FOR i=1:1:6 SET a=a+1 ^ <BREAK> test+3^test USER 3f0>WRITE a 5
Disabling Breakpoints and Watchpoints
You can disable either:
Specific breakpoints and watchpoints
All breakpoints or watchpoints
Disabling Specific Breakpoints and Watchpoints
You can disable a breakpoint or watchpoint by preceding the location with a minus sign. The following command disables a breakpoint previously specified for location label2^rou:
A disabled breakpoint is “turned off”, but Caché remembers its definition. You can enable the disabled breakpoint by preceding the location with a plus sign. The following command enables the previously disabled breakpoint:
Disabling All Breakpoints and Watchpoints
You can disable all breakpoints or watchpoints by using the plus or minus signs without a location:
|ZBREAK -||Disable all defined breakpoints and watchpoints.|
|ZBREAK +||Enable all defined breakpoints and watchpoint.|
Delaying Execution of Breakpoints and Watchpoints
You can also delay the execution of a break/watch point for a specified number of iterations. You might have a line of code that appears within a loop that you want to break on periodically, rather than every time it is executed. To do so, establish the breakpoint as you would normally, then disable with a count following the location argument.
The following ZBREAK command causes the breakpoint at label2^rou to be disabled for 100 iterations. On the 101st time this line is executed, the specified breakpoint action occurs.
ZBREAK label2^rou ; establish the breakpoint ZBREAK -label2^rou#100 ; disable it for 100 iterations
A delayed breakpoint is not decremented when a line is repeatedly executed because it contains a FOR command.
Deleting Breakpoints and Watchpoints
You can delete individual break/watchpoints by preceding the location with a double minus sign; for example:
After you have deleted a breakpoint/watchpoint, you can only reset it by defining it again.
To delete all breakpoints, issue the command:
This command is performed automatically when a Caché process halts.
Single-step Breakpoint Actions
You can use single step execution to stop execution at the beginning of each line or of each command in your code. You can establish a single step breakpoint to specify actions and execution code to be executed at each step. Use the following syntax to define a single step breakpoint:
Unlike other breakpoints, ZBREAK $ does not cause a break, because breaks occur automatically as you single-step. ZBREAK $ lets you specify actions and execute code at each point where the debugger breaks as you step through the routine. It is especially useful in tracing executed lines or commands. For example, to trace executed lines in the application ^TEST:
USER>ZBREAK /TRACE:ON USER>BREAK "L+" USER>ZBREAK $:"T"
The "T" action specified alone (that is, without any other action code) suppresses the single step break that normally occurs automatically. (You can also suppress the single-step break by specifying the "N" action code — either with or without any other action codes.)
Establish the following single-step breakpoint definition if both tracing and breaking should occur:
You can control whether or not the "T" action of the ZBREAK command is enabled by using the following form of ZBREAK:
where state can be:
|ALL||Enables tracing of application by performing the equivalent of: ZBREAK /TRACE:ON[:device] BREAK "L+" ZBREAK $:"T"|
When device is used with the ALL or ON state keywords, trace messages are redirected to the specified device rather than to the principal device. If the device is not already open, Caché attempts to open it as a sequential file with WRITE and APPEND options.
When device is specified with the OFF state keyword, Caché closes the file if it is currently open.
ZBREAK /TRACE:OFF does not delete or disable the single-step breakpoint definition set up by ZBREAK /TRACE:ALL, nor does it clear the “L+” single stepping set up by ZBREAK /TRACE:ALL. You must also issue the commands ZBREAK --$ and BREAK "C" to remove the single stepping; alternatively, you can use the single command BREAK "OFF" to turn off all debugging for the process.
Tracing messages are generated at breakpoints associated with a “T” action. With one exception, the trace message format is as follows for all breakpoints:
Trace: ZBREAK at line_reference
where line_reference is the line reference of the breakpoint.
The trace message format is slightly different for single step breakpoints when stepping is done by command:
Trace: ZBREAK at line_reference source_offset
where line_reference is the line reference of the breakpoint and source_offset is the 0-based offset to the location in the source line where the break has occurred.
Operating System Notes:
Windows — Trace messages to another device are supported on Windows platforms for terminal devices connected to a COM port, such as COM1:. You cannot use the console or a terminal window. You can specify a sequential file for the trace device
UNIX® — To send trace messages to another device on UNIX® platforms:
Log in to /dev/tty01.
Verify the device name by entering the tty command:
$ tty /dev/tty01Copy code to clipboard
Issue the following command to avoid contention for the device:
$ exec sleep 50000Copy code to clipboard
Return to your working window.
Start and enter Caché.
Issue your trace command:
ZBREAK /T:ON:"/dev/tty01"Copy code to clipboard
Run your program.
If you have set breakpoints or watchpoints with the “T” action, you see trace messages appear in the window connected to /dev/tty01.
Trace Message Format
If you set a code breakpoint, the following message appears:
Trace: ZBREAK at label2^rou2
If you set a variable watchpoint, one of the following messages appears:
Trace: ZBREAK SET var=val at label2^rou2 Trace: ZBREAK SET var=Array Val at label2^rou2 Trace: ZBREAK KILL var at label2^rou2
var is the variable being watched.
val is the new value being set for that variable.
If you issue a NEW command, you receive no trace message. However, the trace on the variable is triggered the next time you issue a SET or KILL on the variable at the NEW level. If a variable is passed by reference to a routine, then that variable is still traced, even though the name has effectively changed.
INTERRUPT Keypress and Break
Normally, pressing the interrupt key sequence (typically CTRL-C) generates a trapable (<INTERRUPT>) error. To set interrupt processing to cause a break instead of an <INTERRUPT> error, use the following ZBREAK command: ZBREAK /INTERRUPT:Break
This causes a break to occur when you press the INTERRUPT key even if you have disabled interrupts at the application level for the device.
If you press the INTERRUPT key during a read from the terminal, you may have to press RETURN to display the break-mode prompt. To reset interrupt processing to generate an error rather than cause a break, issue the following command: ZBREAK /INTERRUPT:NORMAL
Displaying Information About the Current Debug Environment
To display information about the current debug environment, including all currently defined break or watchpoints, issue the ZBREAK command with no arguments.
The argumentless ZBREAK command describes the following aspects of the debug environment:
Whether CTRL-C causes a break
Whether trace output specified with the "T" action in the ZBREAK command displays
The location of all defined breakpoints, with flags describing their enabled/disabled status, action, condition and executable code
All variables for which there are watchpoints, with flags describing their enabled/disabled status, action, condition and executable code
Output from this command is displayed on the device you have defined as your debug device, which is your principal device unless you have defined the debug device differently with the ZBREAK /DEBUG command described in the Using the Debug Device section.
The following table describes the flags provided for each breakpoint and watchpoint:
|Identification of break/watch point||Line in routine for breakpoint. Local variable for watchpoint.|
|F:||Flag providing information about the type of action defined in the ZBREAK command.|
|S:||Number of iterations to delay execution of a breakpoint/watchpoint defined in a ZBREAK - command.|
|C:||Condition argument set in ZBREAK command.|
|E:||Execute_code argument set in ZBREAK command.|
The following table describes how to interpret the F: value in a breakpoint/watchpoint display. The F: value is a list of the applicable values in the first column.
|E||Breakpoint or watchpoint enabled|
|D||Breakpoint or watchpoint disabled|
|B||Perform a break|
|L||Perform an "L"|
|L+||Perform an "L+"|
|S||Perform an "S"|
|S+||Perform an "S+"|
|T||Output a Trace Message|
When you first enter Caché and use ZB, the output is as follows:
USER>ZBREAK BREAK: No breakpoints No watchpoints
Trace execution is OFF
There is no break if CTRL-C is pressed
No break/watchpoints are defined
Display When Breakpoints and Watchpoints Exist
This example shows two breakpoints and one watchpoint being defined:
USER>ZBREAK +3^test:::"WRITE ""IN test""" USER>ZBREAK -+3^test#5 USER>ZBREAK +5^test:"L" USER>ZBREAK -+5^test USER>ZBREAK *a:"T":"a=5" USER>ZBREAK /TRACE:ON USER>ZBREAK BREAK: TRACE ON +3^test F:EB S:5 C: E:"WRITE ""IN test""" +5^test F:DL S:0 C: E: a F:ET S:0 C:"a=5" E:
The first two ZBREAK commands define a delayed breakpoint; the second two ZBREAK commands define a disabled breakpoint; the fifth ZBREAK command defines a watchpoint. The sixth ZBREAK command enables trace execution. The final ZBREAK command, with no arguments, displays information about current debug settings.
In the example, the ZBREAK display shows that:
Tracing is ON
There is no break if CTRL-C is pressed.
The output then describes the two breakpoints and one watchpoint:
The F flag for the first breakpoint equals “EB” and the S flag equals 5, which means that a breakpoint will occur the fifth time the line is encountered. The E flag displays executable code, which will run before the Caché Terminal prompt for the break is displayed.
The F flag for the second breakpoint equals “DL”, which means it is disabled, but if enabled will break and then single-step through each line of code following the breakpoint location.
The F flag for the watchpoint is “ET”, which means the watchpoint is enabled. Since trace execution is ON, trace messages will appear on the trace device. Because no trace device was defined, the trace device will be the principal device.
The C flag means that trace is displayed only when condition is true.
Using the Debug Device
The debug device is the device where:
The ZBREAK command displays information about the debug environment.
The Caché Terminal prompt appears if a break occurs.
On Windows platforms, trace messages to another device are supported only for terminal devices connected to a COM port, such as COM1:
When you enter Caché, the debug device will automatically be set to your principal device. At any time, debugging I/O can be sent to an alternate device with the command: ZBREAK /DEBUG:"device".
There are also operating-system-specific actions that you can take.
On UNIX® systems, to cause the break to occur on the tty01 device, issue the following command:
When a break occurs, because of a CTRL-C or to a breakpoint or watchpoint being triggered, it appears in the window connected to the device. That window becomes the active window.
If the device is not already open, an automatic OPEN is performed. If the device is already open, any existing OPEN parameters are respected.
If the device you specify is not an interactive device (such as a terminal), you may not be able to return from a break. However, the system does not enforce this restriction.
Caché Debugger Example
First, suppose you are debugging the simple program named test shown below. The goal is to put 1 in variable a, 2 in variable b, and 3 in variable c.
test; Assign the values 1, 2, and 3 to the variables a, b, and c SET a=1 SET b=2 SET c=3 KILL a WRITE "in test, at end" QUIT
However, when you run test, only variables b and c hold the correct values:
USER>DO ^test in test, at end USER>WRITE b=2 c=3 USER>
The problem in the program is obvious: variable a is KILLed on line 4. However, assume you need to use the debugger to determine this.
You can use the ZBREAK command to set single-stepping through each line of code ("L" action) in the routine test. By a combination of stepping and writing the value of a, you determine that the problem lies in line 4:
USER>NEW USER 1S1>ZBREAK BREAK No breakpoints No watchpoints USER 1S1>ZBREAK ^test:"L" USER 1S1>DO ^test SET a=1 ^ <BREAK>test+1^test USER 3d3>WRITE a <UNDEFINED>^test USER 3d3>GOTO SET b=2 ^ <BREAK>test+2^test USER 3d3>WRITE a 1 USER 3d3>GOTO SET c=3 KILL a WRITE "in test, at end" ^ <BREAK>test+3^test USER 3d3>WRITE a 1 USER 3d3>GOTO in test, at end QUIT ^ <BREAK>test+4^test USER 3d3>WRITE a WRITE a ^ <UNDEFINED>^test USER 3d3>GOTO USER 1S1>
You can now examine that line and notice the KILL a command. In more complex code, you might now want to single-step by command ("S" action) through that line.
If the problem occurred within a DO, FOR, or XECUTE command or a user-defined function, you would use the "L+" or "S+" actions to single-step through lines or commands within the lower level of code.
Understanding Caché Debugger Errors
The Caché Debugger flags an error in a condition or execute argument with an appropriate Caché error message.
If the error is in the execute_code argument, the condition surrounds the execute code when the execute code is displayed before the error message. The condition special variable ($TEST) is always set back to 1 at the end of the execution code so that the rest of the debugger processing code works properly. When control returns to the routine, the value of $TEST within the routine is restored.
Suppose you issue the following ZBREAK command for the example program test:
USER>ZBREAK test+1^test:"B":"a=5":"WRITE b"
In the program test, variable b is not defined at line test+1, so there is an error. The error display appears as follows:
IF a=5 XECUTE "WRITE b" IF 1 ^ <UNDEFINED>test+1^test
If you had not defined a condition, then an artificial true condition would be defined before and after the execution code; for example:
USER>IF 1 WRITE b IF 1
Debugging With BREAK
Caché includes three forms of the BREAK command:
BREAK without an argument inserted into routine code establishes a breakpoint at that location. When encountered during code execution this breakpoint suspend execution and returns to the Terminal prompt.
BREAK with a letter string argument establishes or deletes breakpoints at that enable stepping through code on a line-by-line or command-by-command basis.
The BREAK command with an integer argument enables or disables CTRL-C user interrupts. (Refer to the BREAK command for further details.)
Using Argumentless BREAK to Suspend Routine Execution
To suspend a running routine and return the process to the Terminal prompt, enter an argumentless BREAK into your routine at points where you want execution to temporarily stop.
When Caché encounters a BREAK, it takes the following steps:
Suspends the running routine
Returns the process to the Terminal prompt. From the Terminal prompt you can issue ObjectScript commands, modify data, and execute further routines or subroutines, even those with errors or additional BREAKs.
To resume execution at the point at which the routine was suspended, issue an argumentless GOTO command.
You may find it useful to specify a postconditional on an argumentless BREAK command so that you can rerun the same code simply by setting the postconditional variable rather than having to change the routine. For example, you may have the following line in a routine:
You can then set the variable debug to suspend the routine and return the job to the Terminal prompt or clear the variable debug to continue running the routine.
For further details, refer to Command Postconditional Expressions in this manual.
Using Argumented BREAK to Suspend Routine Execution
You do not have to place argumentless BREAK commands at every location where you want to suspend your routine. Caché provides several argument options that allow you to step through the execution of the code. You can step through the code by single steps (BREAK “S”) or by command line (BREAK “L”). For a full list of these letter code arguments, see the BREAK command.
One difference between BREAK “S” and BREAK “L” is that many command lines consist of more than one step. This is not always obvious. For example, the following are all one line (and one ObjectScript command), but each is parsed as two steps: SET x=1,y=2, KILL x,y, WRITE “hello”,!, IF x=1,y=2.
Both BREAK “S” and BREAK “L” ignore label lines, comments, and TRY statements (though both break at the closing curly brace of a TRY block). BREAK “S” breaks at a CATCH statement (if the CATCH block is entered); BREAK “L” does not.
When a BREAK returns the process to the Terminal prompt, the break state is not stacked. Thus you can change the break state and the new state remains in effect when you issue an argumentless GOTO to resume the executing routine.
Caché stacks the break state whenever a DO, XECUTE, FOR, or user-defined function is entered. If you choose BREAK "C" to turn off breaking, the system restores the break state at the end of the DO, XECUTE, FOR, or user-defined function. Otherwise, Caché ignores the stacked state.
Thus if you enable breaking at a low subroutine level, breaking continues after the routine returns to a higher subroutine level. In contrast, if you disable breaking at a low subroutine level that was in effect at a higher level, breaking resumes when you return to that higher level. You can use BREAK "C-" to disable breaking at all levels.
You can use BREAK “L+” or BREAK “S+” to enable breaking within a DO, XECUTE, FOR, or a user-defined function.
You can use BREAK “L-” to disable breaking at the current level but enables line breaking at the previous level. You can use BREAK “S-” to disable breaking at the current level but enables single-step breaking at the previous level.
Shutting Off Debugging
To remove all debugging that has been established for a process, use the BREAK "OFF" command. This command removes all breakpoints and watchpoints and turns off stepping at all program stack levels. It also removes the association with the debug and trace devices, but does not close them.
Invoking BREAK "OFF" is equivalent to issuing the following set of commands:
ZBREAK /CLEAR ZBREAK /TRACE:OFF ZBREAK /DEBUG:"" ZBREAK /ERRORTRAP:ON BREAK "C-"
Terminal Prompt Shows Program Stack Information
When a BREAK command suspends execution of a routine or when an error occurs, the program stack retains some stacked information. When this occurs, a brief summary of this information is displayed as part of the Terminal prompt ( namespace> ). For example, this information might take the form: USER 5d3>, where:
|5||Indicates there are five stack levels. A stack level can be caused by a DO, FOR, XECUTE, NEW, user-defined function call, error state, or break state.|
|d||Indicates that the last item stacked is a DO.|
|3||Indicates there are 3 NEW states, parameter passing, or user-defined functions on the stack. This value is a zero if no NEW commands, parameter passing, or user-defined functions are stacked.|
Terminal prompt letter codes are listed in the following table.
In the following example, command line statements are shown with their resulting Terminal prompts when adding stack frames:
USER>NEW USER 1S1>NEW USER 2N1>XECUTE "NEW WRITE 123 BREAK" <BREAK> USER 4x1>NEW USER 5B1>BREAK <BREAK> USER 6N2>
You can unwind the program stack using QUIT 1. The following is an example of Terminal prompts when unwinding the stack:
USER 6f0>QUIT 1 /* an error occurred in a FOR loop. */ USER 5x0>QUIT 1 /* the FOR loop was in code invoked by XECUTE. */ USER 4f0>QUIT 1 /* the XECUTE was in a FOR loop. */ USER 3f0>QUIT 1 /* that FOR loop was nested inside another FOR loop. */ USER 2d0>QUIT 1 /* the DO command was used to execute the program. */ USER 1S0>QUIT 1 /* sign on state. */ USER>
FOR Loop and WHILE Loop
You can use either a FOR or a WHILE to perform the same operation: loop until an event (usually a counter increment) causes execution to break out of the loop. However, which loop construct you use has consequences for performing single-step (BREAK "S+" or BREAK "L+") debugging on the code module.
A FOR loop pushes a new level onto the stack. A WHILE loop does not change the stack level. When debugging a FOR loop, popping the stack from within the FOR loop (using BREAK "C" GOTO or QUIT 1) allows you to continue single-step debugging with the command immediately following the end of the FOR command construct. When debugging a WHILE loop, issuing a using BREAK "C" GOTO or QUIT 1 does not pop the stack, and therefore single-step debugging does not continue following the end of the WHILE command. The remaining code executes without breaking.
Resuming Execution after a BREAK or an Error
When returned to the Terminal prompt after a BREAK or an error, Caché keeps track of the location of the command that caused the BREAK or error. Later, you can resume execution at the next command simply by entering an argumentless GOTO at the Terminal prompt:
By typing a GOTO with an argument, you can resume execution at the beginning of another line in the same routine with the break or error, as follows:
USER 4f0>GOTO label3
You can also resume execution at the beginning of a line in a different routine:
USER 4f0>GOTO label3^rou
Alternatively, you may clear the program stack with an argumentless QUIT command:
USER 4f0>QUIT USER>
The following routines are used in the examples below.
MAIN ; 03 Jan 2019 11:40 AM SET x=1,y=6,z=8 DO SUB1 WRITE !,"sum=",sum QUIT
SUB1 ; 03 Jan 2019 11:42 AM SET sum=x+y+z QUIT
With BREAK "L", breaking does not occur in the routine SUB1.
USER>BREAK "L" USER>DO ^MAIN SET x=1,y=6,z=8 ^ <BREAK>MAIN+1^MAIN USER 2d0>GOTO DO ^SUB1 WRITE !,"sum=",sum ^ <BREAK>MAIN+2^MAIN USER 2d0>GOTO sum=15 QUIT ^ <BREAK>MAIN+3^MAIN USER 2d0>GOTO USER>
With BREAK "L+", breaking also occurs in the routine SUB1.
USER>BREAK "L+" USER>DO ^MAIN SET x=1,y=6,z=8 ^ <BREAK>MAIN+1^MAIN USER 2d0>GOTO DO ^SUB1 WRITE !,"sum=",sum ^ <BREAK>MAIN+2^MAIN USER 2d0>GOTO SET sum=x+y+z ^ <BREAK>SUB1+1^SUB1 USER 3d0>GOTO QUIT ^ <BREAK>SUB1+2^SUB1 USER 3d0>GOTO sum=15 QUIT ^ <BREAK>MAIN+3^MAIN USER 2d0>GOTO USER>
The NEW Command at the Terminal Prompt
The argumentless NEW command effectively saves all symbols in the symbol table so you can proceed with an empty symbol table. You may find this command particularly valuable when you are at the Terminal prompt after an error or BREAK.
To run other routines without disturbing the symbol table, issue an argumentless NEW command at the Terminal prompt. The system then:
Stacks the current frame on the program stack.
Returns the Terminal prompt for a new stack frame.
USER 4d0>NEW USER 5B1>DO ^%T 3:49 PM USER 5B1>QUIT 1 USER 4d0>GOTO
The 5B1> prompt indicates that the system has stacked the current frame entered through a BREAK. The 1 indicates that a NEW command has stacked variable information, which you can remove by issuing a QUIT 1. When you wish to resume execution, issue a QUIT 1 to restore the old symbol table, and a GOTO to resume execution.
Whenever you use a NEW command, parameter passing, or user-defined function, the system places information on the stack indicating that later an explicit or implicit QUIT at the current subroutine or XECUTE level should delete certain variables and restore the value of others.
You may find it useful to know if any NEW commands, parameter passing, or user-defined functions have been executed (thus stacking some variables), and if so, how far back on the stack this information resides.
The QUIT Command at the Terminal Prompt
From the Terminal prompt you can remove all items from the program stack by entering an argumentless QUIT command:
USER 4f0>QUIT USER>
To remove only a couple of items from the program stack (for example, to leave a currently executing subroutine and return to a previous DO level), use QUIT with an integer argument. QUIT 1 removes the last item on the program stack, QUIT 3 removes the last three items, and so forth, as illustrated below:
USER 9f0>QUIT 3 USER 6d0>
Caché Error Messages
Caché displays error messages within angle brackets, as in <ERROR>, followed by a reference to the line that was executing at the time of the error and by the routine. A caret (^) separates the line reference and routine. Also displayed is the intermediate code line with a caret character under the first character of the command executing when the error occurred. For example:
SET x=y+3 DO ^ABC ^ <UNDEFINED>label+3^rou
This error message indicates an <UNDEFINED> error (that refers to the variable y) in line label+3 of routine rou. At this point, this message is also the value of the special variable $ZERROR.
Using %STACK to Display the Stack
You can use the %STACK utility to:
Display the contents of the process execution stack.
Display the values of local variables, including values that have been “hidden” with the NEW command or through parameter passing.
You execute %STACK by entering the following command:
As shown in this example, the %STACK utility displays the current process stack without variables.
Level Type Line Source 1 SIGN ON 2 DO ~DO ^StackTest 3 NEW ALL/EXCL NEW (E) 4 DO TEST+1^StackTest SET A=1 ~DO TEST1 QUIT ;level=2 5 NEW NEW A 6 DO TEST1+1^StackTest ~DO TEST2 ;level = 3 7 ERROR TRAP SET $ZTRAP="TrapLabel^StackTest" 8 XECUTE TEST2+2^StackTest ~XECUTE "SET A=$$TEST3()" 9 $$EXTFUNC ^StackTest ~SET A=$$TEST3() 10 PARAMETER AA 11 DIRECT BREAK TEST3+1^StackTest ~BREAK 12 DO ^StackTest ~DO ^%STACK
Under the current execution stack display, %STACK prompts you for a Stack Display Action. You can get help by entering a question mark (?) at this prompt. You can exit %STACK by pressing the Return key at this prompt.
Displaying the Process Execution Stack
Depending on what you enter at the Stack Display Action prompt, you can display the current process execution stack in four forms:
Without variables, by entering *F
With a specific local variable, by entering *V
With all local variables, by entering *P
With all local variables, preceded by a list of process state variables, by entering *A
%STACK then displays the Display on Device prompt, enabling you to specify where you want this information to go. Press the Return key to display this information to the current device.
Displaying the Stack without Variables
The process execution stack without variables appears when you first enter the %STACK utility or when you type *F at the Stack Display Action prompt.
Displaying the Stack with a Specific Variable
Enter *V at the Stack Display Action prompt. This will prompt you for the name(s) of the local variable(s) you want to track through the stack. Specify a single variable or a comma-separated list of variables. It returns the names and values of all local variables. In the following example, the variable e is being tracked and the display is sent to the Terminal by pressing Return
Stack Display Action: *V Now loading variable information ... 2 done. Variable(s): e Display on Device: <RETURN>
Displaying the Stack with All Defined Variables
Enter *P at the Stack Display Action prompt to see the process execution stack together with the current values of all defined local variables.
Displaying the Stack with All Variables, including State Variables
Enter *A at the Stack Display Action prompt to display all possible reports. Reports are issued in the following order:
Process state intrinsic variables
Process execution stack with the names and values of all local variables
Understanding the Stack Display
Each item on the stack is called a frame. The following table describes the information provided for each frame.
|Level||Identifies the level within the stack. The oldest item on the stack is number 1. Frames without an associated level number share the level that first appears above them.|
|Type||Identifies the type of frame on the stack, which can be: DIRECT BREAK: A BREAK command was encountered that caused a return to direct mode. DIRECT CALLIN: A Caché process was initiated from an application outside of Caché, using the Caché call-in interface. DIRECT ERROR: An error was encountered that caused a return to direct mode. DO: A DO command was executed. ERROR TRAP: If a routine sets $ZTRAP, this frame identifies the location where an error will cause execution to continue. FOR: A FOR command was executed. NEW: A NEW command was executed. If the NEW command had arguments, they are shown. SIGN ON: Execution of the Caché process was initiated. XECUTE: An XECUTE command was executed. $$EXTFUNC: A user-defined function was executed.|
|Line||Identifies the ObjectScript source line associated with the frame, if available, in the format label+offset^routine.|
|Source||Shows the source code for the line, if it is available. If the source is too long to display in the area provided, horizontal scrolling is available. If the device is line- oriented, the source wraps around and continued lines are preceded with “...”.|
The following table shows whether level, line, and source values are available for each frame type. A "No" under Level indicates that the level number is not incremented and no level number appears in the display.
|DIRECT CALL IN||Yes||No||No|
|ERROR TRAP||No||No||No, but the new $ZTRAP value is shown.|
|NEW||No||No||Shows the form of the NEW (inclusive or exclusive) and the variables affected.|
|PARAMETER||No||No||Shows the formal parameter list. If a parameter is passed by reference, shows what other variables point to the same memory location.|
|* The LINE value is blank if these are invoked from the Terminal prompt.|
Moving through %STACK Display
If a %STACK display fills more than one screen, you see the prompt “-- more --” in the bottom left corner of the screen. At the last page, you see the prompt “-- fini --”. Type ? to see key presses you use to maneuver through the %STACK display.
- - - Filter Help - - - <space> Display next page. <return> Display one more line. T Return to the beginning of the output. B Back up one page (or many if arg>1). R Redraw the current page. /text Search for \qtext\q after the current page. A View all the remaining text. Q Quit. ? Display this screen # specify an argument for B, L, or W actions. L set the page length to the current argument. W set the page width to the current argument.
You enter any of the commands listed above whenever you see the “-- more --” or “-- fini --” prompts.
For the B, L and W commands, you enter a numeric argument before the command letter. For instance, enter 2B to move back two pages, or enter 20L to set the page length to 20 lines.
Be sure to set your page length to the number of lines which are actually displayed; otherwise, when you do a page up or down, some lines may not be visible. The default page length is 23.
Displaying Variables at Specific Stack Level
To see the variables that exist at a given stack frame level, enter ?# at the “Stack Display Action” prompt, where # is the stack frame level. The following example shows the display if you request the variables at level 1.
Stack Display Action: ?1 The following Variables are defined for Stack Level: 1 E Stack Display Action:
Displaying Stack Levels with Variables
You can display the variables defined at all stack levels by entering ?? at the “Stack Display Action” prompt. The following example shows a sample display if you select this action.
Stack Display Action: ?? Now loading variable information ... 19 Base Stack Level: 5 A Base Stack Level: 3 A B C D Base Stack Level: 1 E Stack Display Action:
Displaying Process State Variables
To display the process state variables, such as $IO, enter *S at the “Stack Display Action” prompt. You will see these defined variables (Process State Intrinsics) as listed in the following table:
|Process State Intrinsics||Documentation|
|$D =||$DEVICE special variable|
|$EC = ,M9,||$ECODE special variable|
|$ES = 4||$ESTACK special variable|
|$ET =||$ETRAP special variable|
|$H = 64700,50668||$HOROLOG special variable|
|$I = |TRM|:|5008||$IO special variable|
|$J = 5008||$JOB special variable|
|$K = $c(13)||$KEY special variable|
|$P = |TRM|:|5008||$PRINCIPAL special variable|
|$Roles = %All||$ROLES special variable|
|$S = 268315992||$STORAGE special variable|
|$T = 0||$TEST special variable|
|$TL = 0||$TLEVEL special variable|
|$USERNAME = glenn||$USERNAME special variable|
|$X = 0||$X special variable|
|$Y = 17||$Y special variable|
|$ZA = 0||$ZA special variable|
|$ZB = $c(13)||$ZB special variable|
|$ZC = 0||$ZCHILD special variable|
|$ZE = <DIVIDE>||$ZERROR special variable|
|$ZJ = 5||$ZJOB special variable|
|$ZM = RY\Latin1\K\UTF8\||$ZMODE special variable|
|$ZP = 0||$ZPARENT special variable|
|$ZR = ^||a||$ZREFERENCE special variable|
|$ZS = 262144||$ZSTORAGE special variable|
|$ZT =||$ZTRAP special variable|
|$ZTS = 64700,68668.58||$ZTIMESTAMP special variable|
|$ZU(5) = USER||$NAMESPACE|
|$ZU(12) = c:\intersystems\cache\mgr\||NormalizeDirectory()|
|$ZU(18) = 0||Undefined()|
|$ZU(20) = USER||UserRoutinePath()|
|$ZU(23,1) = 5|
|$ZU(34) = 0|
|$ZU(39) = USER||SysRoutinePath()|
|$ZU(55) = 0||LanguageMode()|
|$ZU(56,0) = $Id: //dev/2017.2.1/kernel/common/src/crtnbuf.c#1 $ 0|
|$ZU(56,1) = 1349|
|$ZU(61) = 16|
|$ZU(61,30,n) = 262160|
|$ZU(67,10,$J) = 1||JobType|
|$ZU(67,11,$J) = glenn||UserName|
|$ZU(67,12,$J) = TRM:||ClientNodeName|
|$ZU(67,15,$J) = 127.0.0.1||ClientIPAddress|
|$ZU(67,4,$J) = 0^0^0||State|
|$ZU(67,5,$J) = %STACK||Routine|
|$ZU(67,6,$J) = USER||NameSpace|
|$ZU(67,7,$J) = |TRM|:|5008||CurrentDevice|
|$ZU(67,8,$J) = 923||LinesExecuted|
|$ZU(67,9,$J) = 46||GlobalReferences|
|$ZU(68,1) = 0||NullSubscripts()|
|$ZU(68,21) = 0||SynchCommit()|
|$ZU(68,25) = 0|
|$ZU(68,27) = 1|
|$ZU(68,32) = 0||ZDateNull()|
|$ZU(68,34) = 1||AsynchError()|
|$ZU(68,36) = 0|
|$ZU(68,40) = 0||SetZEOF()|
|$ZU(68,41) = 1|
|$ZU(68,43) = 0||OldZU5()|
|$ZU(68,5) = 1||BreakMode()|
|$ZU(68,6) = 0|
|$ZU(68,7) = 0||RefInKind()|
|$ZU(131,0) = MYCOMPUTER|
|$ZU(131,1) = MYCOMPUTER:CACHE|
|$ZV = Cache for Windows (x86-64) 2017.2.1 (Build 801_1U) Mon Mar 12 2018 22:47:10 EST||$ZVERSION special variable|
Printing the Stack and/or Variables
When you select the following actions, you can choose the output device:
*V after selecting the variables you want to display.
Other Debugging Tools
There are also other tools available to aid in the debugging process. These include:
Error Trap Utilities — %ETN and %ERN
Displaying References to an Object with $SYSTEM.OBJ.ShowReferences
To display all variables in the process symbol table that contain a reference to a given object, use the ShowReferences(oref) method of the %SYSTEM.OBJ class. The oref is the OREF (object reference) for the given object. For details on OREFs, see the section “OREF Basics” in the “Working with Registered Objects” chapter of Using Caché Objects.
Error Trap Utilities
The error trap utilities, %ETN and %ERN, help in error analysis by storing variables and recording other pertinent information about an error.
%ETN Application Error Trap
You may find it convenient to set the error trap to execute the utility %ETN on an application error. This utility saves valuable information about the job at the time of the error, such as the execution stack and the value of variables. This information is saved in the application error log, which you can display with the %ERN utility or view in the Management Portal on the View Application Error Log page (System Operation, System Logs, Application Error Log).
Use the following code to set the error trap to this utility:
In a procedure, you cannot set $ZTRAP to an external routine. Because of this restriction, you cannot use ^%ETN in procedures (including class methods that are procedures). However, you can set $ZTRAP to a local label that calls %ETN.
When an error occurs and you call the %ETN utility, you see a message similar to the following message:
Error has occurred: <SYNTAX> at 10:30 AM
Because %ETN ends with a HALT command (terminates the process) you may want to set the %ETN error trap only if the routine is used in Application Mode. When an error occurs at the Terminal prompt, it may be useful for the error to be displayed on the Terminal and go into the debugger prompt to allow for immediate analysis of the error. The following code sets an error trap only if Caché is in Application Mode:
%ERN Application Error Report
The %ERN utility examines application errors recorded by the %ETN error trap utility. For information on using %ERN, refer to Using %ERN to View Application Error Logs in the “Error Processing” chapter of this manual.
In the following code, a ZLOAD of the routine REPORT is issued to illustrate that by loading all of the variables with “*LOAD” and then loading the routine, you can recreate the state of the job when the error occurred except that the program stack, which records information about DOs, etc., is empty.
USER>DO ^%ERN For Date: 4/30/2018 3 Errors Error: ?L 1) "<DIVIDE>zMyTest+2^Sample.MyStuff.1" at 10:27 am. $I=|TRM|:|10044 ($X=0 $Y=17) $J=10044 $ZA=0 $ZB=$c(13) $ZS=262144 ($S=268242904) WRITE 5/0 2) <SUBSCRIPT>REPORT+4^REPORT at 03:16 pm. $I=|TRM|:|10044 ($X=0 $Y=57) $J=10044 $ZA=0 $ZB=$c(13) $ZS=2147483647 ($S=2199023047592) SET ^REPORT(%DAT,TYPE)=I 3) <UNDEFINED>zMyTest+2^Sample.MyStuff.1 *undef" at 10:13 pm. $I=|TRM|:|12416 ($X=0 $Y=7) $J=12416 $ZA=0 $ZB=$c(13) $ZS=262144 ($S=268279776) WRITE undef Error: 2 2) <SUBSCRIPT>REPORT+4^REPORT at 03:16 pm. $I=|TRM|:|10044 ($X=0 $Y=57) $J=10044 $ZA=0 $ZB=$c(13) $ZS=2147483647 ($S=2199023047592) SET ^REPORT(%DAT,TYPE)=I Variable: %DAT %DAT="Apr 30 2018" Variable: TYPE TYPE="" Variable: *LOAD USER>ZLOAD REPORT USER>WRITE %DAT="Apr 30 2018" %DS="" %TG="REPORT+1" I=88 TYPE="" XY="SET $X=250 WRITE *27,*91,DY+1,*59,DX+1,*72 SET $X=DX,$Y=DY" USER>