Chapter 6. Interactive Program Debug.

Overview.

Again, before giving our views, this is what the book teaches us :

The debug facility permits interactively controlled execution of a program.

Changing the TRACE action to one with a prefix ? (for example, TRACE ?A) turns on interactive debug and indicates to the user that interactive debug is active.  Further TRACE instructions in the program are ignored, and the language processor pauses after nearly all instructions that are traced at the console (see the following for the exceptions).  When the language processor pauses, indicated by a VM Read; or unlocking of the keyboard, three debug actions are available :

  1. Entering a null line (with no characters, even blanks) makes the language processor continue execution until the next pause for debug input.  Repeatedly entering a null line, therefore, steps from pause point to pause point.  For TRACE ?A, for example, this is equivalent to single-stepping through the program. 
  2. Entering an equal sign (=) with no blanks makes the language processor re-execute the clause last traced.  For example: if an IF clause is about to take the wrong branch, you can change the value of the variable(s) on which it depends, and then re-execute it. 
    Once the clause has been re-executed, the language processor pauses again.
  3. Anything else entered is treated as a line of one or more clauses, and processed immediately (that is, as though DO; line ; END; had been inserted in the program).  The same rules apply as in the INTERPRET instruction (for example, DO-END constructs must be complete). 

    If an instruction has a syntax error in it, a standard message is displayed and you are prompted for input again.  Similarly all the other SIGNAL conditions are disabled while the string is processed to prevent unintentional transfer of control. 

    During execution of the string, no tracing takes place, except that not zero return codes from host commands are displayed.  Host commands are always executed (that is, are not affected by the prefix ! on TRACE instructions), but the variable RC is not set. 

    Once the string has been processed, the language processor pauses again for more debug input unless a TRACE instruction was entered.  In this latter case, the language processor immediately alters the tracing action (if necessary) and then continues executing until the next pause point (if any).  To alter the tracing action (from All to Results, for example) and then re-execute the instruction, you must use the built-in function TRACE().  For example, CALL TRACE I changes the trace action to "I" and allows re-execution of the statement after which the pause was made.  Interactive debug is turned off, when it is in effect, if a TRACE instruction uses a prefix, or at any time when a TRACE Off or TRACE with no options is entered. 

Exceptions: Some clauses cannot safely be re-executed, and therefore, the language processor does not pause after them, even if they are traced.

These are:

Examples of interactive tracing.

In this example, we trace the procedure interactively (?i).  We use the possibility to interact with the procedure at each statement:

» ttest ?i
     8 *-* do i=1 to 4
       >L>   "1"
       >L>   "4"
       +++ Interactive trace.  TRACE OFF to end debug, ENTER to continue. +++
» say i
1
»
     9 *-*  i=i+1
       >V>    "1"
       >L>    "1"
       >O>    "2"
» =
       *-*  i=i+1
       >V>    "2"
       >L>    "1"
       >O>    "3"
» say i
3
» i=2
»
    10 *-* end
     8 *-* do i=1 to 4
     9 *-*  i=i+1
       >V>    "3"
       >L>    "1"
       >O>    "4"
    10 *-* end
     8 *-* do i=1 to 4
    11 *-* call subrout
    13 *-*  SUBROUT:
» trace off
Value of i= 5
       +++ Interactive trace.  TRACE OFF to end debug, ENTER to continue. +++
» trace off
Ready; T=0.01/0.03 11:51:12

We see that we can:

  1. ask for the actual value of a variable at any moment (say i).
  2. re-execute the last statement (=) which in this particular case effectively modifies the value of the variable i
  3. modify the value of a variable (i=2), which effectively changes the behavior of the procedure.
  4. change the tracing (trace off).
  5. set trace off in subroutines doesn't reset tracing for the mainline.

Question 13

We just saw that you can query the value of a variable during interactive trace.  Suppose now that the variable has leading and trailing blanks.  The trailing blanks will not be visible unless you find a way to make them visible.  Do you have a solution to that little problem ?

We can in fact issue any command that we could have coded in the procedure itself.  It is as if the interpreter waits for the next statement to come from the console instead of the file.  The commands you issue have thus to conform the REXX coding rules (REXX is in control).  Let's look at another example, where we issue a CMS command:

» ttest ?i
     8 *-* do i=1 to 4
       >L>   "1"
       >L>   "4"
       +++ Interactive trace.  TRACE OFF to end debug, ENTER to continue. +++
» 'TYPE PROFILE EXEC A'
/* PROFILE */
trace off
'ACCESS .TC.TCVM1.INSTRUCTORS C'
'CP LINK FSCIPOE 193 193 RR'
'ACCESS 193 E'
exit
»
     9 *-*  i=i+1
       >V>    "1"
       >L>    "1"
       >O>    "2"
» 'RENAME PROFILE EXEC A1 = = A'i
» do 2
       +++ Interactive trace.  Error 14: Incomplete DO/SELECT/IF. +++
» do 2;say 'Hello';end
Hello
Hello
»
    10 *-* end
» trace off
Ready; T=0.01/0.02 16:53:22

We first issued the CMS TYPE PROFILE EXEC command, which is executed immediately, resulting in the output at the terminal.

Later, we issue the RENAME command, but we use the variable i in the command.  REXX interprets this statement before passing it to CMS, so the resulting command is RENAME PROFILE EXEC A1 = = A2 as i had the value 2 at that moment. 

Then we issued the do 2 command and REXX refused.  Indeed, we have to give valid and complete REXX instructions, so we corrected the error by issuing the do 2;say 'Hello';end statement. 

Finally, we turned tracing off. 

We didn't use arrays in our little test procedure, but the technique of issuing a DO-block is useful when you want to inspect the values of all (or part) of your elements in your array during interactive debug. 

To visualize this, let's give a simple example.  The procedure has initialized an array a. with all numbers from 1 to 10 before interactive trace was started...

     8 *-* factorial=1
       >>>   "1"
       +++ Interactive trace.  TRACE OFF to end debug, ENTER to continue. +++
»
     9 *-* do i=1 to 10
       >>>   "1"
       >>>   "10"
»
    10 *-*  factorial=factorial*a.i
       >>>    "1"
»
    11 *-*  say 'Factorial of' i 'is' format(factorial,5)
       >>>    "Factorial of 1 is     1"
Factorial of 1 is     1
    12 *-* end
     9 *-* do i=1 to 10
»
    10 *-*  factorial=factorial*a.i
       >>>    "2"
»
    11 *-*  say 'Factorial of' i 'is' format(factorial,5)
       >>>    "Factorial of 2 is     2"
Factorial of 2 is     2
    12 *-* end
     9 *-* do i=1 to 10
»
    10 *-*  factorial=factorial*a.i
       >>>    "6"
»
    11 *-*  say 'Factorial of' i 'is' format(factorial,5)
       >>>    "Factorial of 3 is     6"
Factorial of 3 is     6
» say 'counter ='i;do j=i to 10;say a.j;end
counter =3
3
4
5
6
7
8
9
10
» exit
Ready; T=0.02/0.06 12:25:11

In the middle of the loop we issue
say 'counter ='i;do j=i to 10;say a.j;end

We ask for the current value of the loop counter (when tracing large loops, you may have forgotten at which iteration you were), and want to display the coming elements in the array.

Remark also that a simple exit is correctly interpreted by REXX and immediately canceled our procedure.

To end this discussion, let's give another 'real-life' example where the possibility to enter CMS commands proved to be very useful.  The RACF utility procedure that allows to copy the RACF database from 9335 (FBA) to 9345 (CKD) disks failed due to a bug.  The procedure was started again, now using interactive trace (TS), as this is easier to debug than to XEDIT the file and to try to understand the logic.  Also, by effectively running the procedure, you can see the real values the variables get during execution and see only those parts of the procedure that get executed. 

This is what we got (this is not the real console listing):

   ...
   745 *-* "OSRUN ICHUT400 PARM='"stparms"'"
       >>> "OSRUN ICHUT400 PARM='INDD1 OUTDD1'"
DMSOPN036E OPEN ERROR 4 ON INDD1
» 'Q FILEDEF'
 ... shows INDD1 is still defined ...
 ... as we know RACF's habits a bit, we checked accessed disks ...
» 'Q DISK'
 ... olala, the input disk (INDD1), at address 200 isn't accessed anymore...
» 'ACCESS 200 R'
» =
   745 *-* "OSRUN ICHUT400 PARM='"stparms"'"
       >>> "OSRUN ICHUT400 PARM='INDD1 OUTDD1'"
ICHMSGxxx COPY of 200 to 400 completed successfuly
» trace off
Ready;

Thanks to the interactive tracing, we were able to correct the environment for the procedure (ACCESS command), and to re-execute the failing instruction.

Of course, you have to modify the source if you want to avoid the same error again in later runs.  Commands issued during interactive trace don't change the EXEC file on the disk !

This concludes the chapter on Interactive Tracing.  Chapter 7 will study how tracing can be controlled.