Rational Developer for System z, Version 7.6

Debugging a PL/I program in full-screen mode

The descriptions of basic debugging tasks for PL/I refer to the following PL/I program.

Example: sample PL/I program for debugging

Refer to the following topics for more information related to the material discussed in this topic.

Example: sample PL/I program for debugging

The program below is used in various topics to demonstrate debugging tasks.

This program is a simple calculator that reads its input from a character buffer. If integers are read, they are pushed on a stack. If one of the operators (+ - * /) is read, the top two elements are popped off the stack, the operation is performed on them and the result is pushed on the stack. The = operator writes out the value of the top element of the stack to a buffer.

Before running PLICALC, you need to allocate SYSPRINT to the terminal by entering the following command:

ALLOC FI(SYSPRINT) DA(*) REUSE

Main program PLICALC

 plicalc: proc options(main);
 /*------------------------------------------------------------------*/
 /*                                                                  */
 /* A simple calculator that does operations on integers that        */
 /* are pushed and popped on a stack                                 */
 /*                                                                  */
 /*------------------------------------------------------------------*/
 dcl index  builtin;
 dcl length builtin;
 dcl substr builtin;
 /*                                                                  */
 dcl 1 stack,
       2 stkptr fixed bin(15,0) init(0),
       2 stknum(50) fixed bin(31,0);
 dcl 1 bufin,
       2 bufptr fixed bin(15,0) init(0),
       2 bufchr char (100) varying;
 dcl 1 tok char (100) varying;
 dcl 1 tstop char(1) init ('s');
 dcl 1 ndx fixed bin(15,0);
 dcl num      fixed bin(31,0);
 dcl i        fixed bin(31,0);
 dcl push entry external;
 dcl pop  entry returns (fixed bin(31,0)) external;
 dcl readtok entry returns (char (100) varying) external;
 /*------------------------------------------------------------------*/
 /* input  action:                                                   */
 /*    2      push 2 on stack                                        */
 /*    18     push 18                                                */
 /*    +      pop 2, pop 18, add, push result (20)                   */
 /*    =      output value on the top of the stack (20)              */
 /*    5      push 5                                                 */
 /*    /      pop 5, pop 20, divide, push result (4)                 */
 /*    =      output value on the top of the stack (4)               */
 /*------------------------------------------------------------------*/
 bufchr = '2 18 + = 5 / =';
 do while (tok ^= tstop);
   tok = readtok(bufin);          /* get next 'token' */
   select (tok);
     when (tstop)
       leave;
     when ('+') do;
       num = pop(stack);
       call push(stack,num);      /*   CALC1   statement */
     end;
     when ('-') do;
       num = pop(stack);
       call push(stack,pop(stack)-num);
     end;
     when ('*')
       call push(stack,pop(stack)*pop(stack));
     when ('/') do;
       num = pop(stack);
       call push(stack,pop(stack)/num); /*   CALC2   statement */
     end;
     when ('=') do;
       num = pop(stack);
       put list ('PLICALC: ', num) skip;
       call push(stack,num);
     end;
     otherwise do;/* must be an integer */
       num = atoi(tok);
       call push(stack,num);
     end;
   end;
 end;
 return;

TOK function

 atoi: procedure(tok) returns (fixed bin(31,0));
 /*------------------------------------------------------------------*/
 /*                                                                  */
 /* convert character string to number                               */
 /* (note: string validated by readtok)                              */
 /*                                                                  */
 /*------------------------------------------------------------------*/
   dcl 1 tok char (100) varying;
   dcl 1 num fixed bin (31,0);
   dcl 1 j fixed bin(15,0);
   num = 0;
   do j = 1 to length(tok);
     num = (10 * num) + (index('0123456789',substr(tok,j,1))-1);
   end;
   return (num);
 end atoi;
 end plicalc;

PUSH function

 push: procedure(stack,num);
 /*------------------------------------------------------------------*/
 /*                                                                  */
 /* a simple push function for a stack of integers                   */
 /*                                                                  */
 /*------------------------------------------------------------------*/
 dcl 1 stack connected,
       2 stkptr fixed bin(15,0),
       2 stknum(50) fixed bin(31,0);
 dcl num      fixed bin(31,0);
 stkptr = stkptr + 1;
 stknum(stkptr) = num; /*   PUSH1   statement */
 return;
 end push;

POP function

 pop: procedure(stack) returns (fixed bin(31,0));
 /*------------------------------------------------------------------*/
 /*                                                                  */
 /* a simple pop function for a stack of integers                    */
 /*                                                                  */
 /*------------------------------------------------------------------*/
 dcl 1 stack connected,
       2 stkptr fixed bin(15,0),
       2 stknum(50) fixed bin(31,0);
 stkptr = stkptr - 1;
 return (stknum(stkptr+1));
 end pop;

READTOK function

 readtok: procedure(bufin) returns (char (100) varying);
 /*------------------------------------------------------------------*/
 /*                                                                  */
 /* a function to read input and tokenize it for a simple calculator */
 /*                                                                  */
 /* action: get next input char, update index for next call          */
 /* return: next input char(s)                                       */
 /*------------------------------------------------------------------*/
 dcl length builtin;
 dcl substr builtin;
 dcl verify builtin;
 dcl 1 bufin connected,
       2 bufptr fixed bin(15,0),
       2 bufchr char (100) varying;
 dcl 1 tok char (100) varying;
 dcl 1 tstop char(1) init ('s');
 dcl 1 j fixed bin(15,0);
                                  /* start of processing */
 if bufptr > length(bufchr) then do;
   tok = tstop;
   return ( tok );
 end;
 bufptr = bufptr + 1;
 do while (substr(bufchr,bufptr,1) = ' ');
   bufptr = bufptr + 1;
   if bufptr > length(bufchr) then do;
     tok = tstop;
     return ( tok );
   end;
 end;
 tok = substr(bufchr,bufptr,1); /* get ready to return single char */
 select (tok);
   when ('+','-','/','*','=')
     bufptr = bufptr;
   otherwise do;                /* possibly an integer */
     tok = '';
     do j = bufptr to length(bufchr);
       if verify(substr(bufchr,j,1),'0123456789') ^= 0 then
         leave;
     end;
     if j > bufptr then do;
         j = j - 1;
       tok = substr(bufchr,bufptr,(j-bufptr+1));
       bufptr = j;
     end;
     else
       tok = tstop;
   end;
 end;
 return (tok);
 end readtok;

Refer to the following topics for more information related to the material discussed in this topic.

Halting when certain PL/I functions are called

This topic describes how to halt just before or just after a routine is called by using the AT CALL and AT ENTRY commands. The Example: sample PL/I program for debugging is used to describe these commands.

To use the AT CALL command, you must compile the calling program with the TEST compiler option.

To halt just before READTOK is called, enter the following command:

AT CALL READTOK ;

To use the AT ENTRY command, you must compile the called program with the TEST compiler option.

To halt just after READTOK is called, enter the following command:

AT ENTRY READTOK ;

To halt just after TOK is called and only when the parameter tok equals 2, enter the following command:

AT ENTRY TOK WHEN tok='2';

Identifying the statement where your PL/I program has stopped

If you have many breakpoints set in your program, enter the following command to have Debug Tool identify where your program has stopped:

QUERY LOCATION

The Debug Tool Log window displays something similar to the following example:

QUERY LOCATION ;
You are executing commands in the ENTRY READTOK breakpoint.
The program is currently entering block READTOK.

Modifying the value of a PL/I variable

To list the contents of a single variable, move the cursor to an occurrence of the variable name in the Source window and press PF4 (LIST). The value is displayed in the Log window. This is equivalent to entering LIST TITLED variable on the command line. For example, run the PLICALC program to the statement labeled  CALC1  by entering AT 22 ; GO ; on the Debug Tool command line. Move the cursor over NUM and press PF4 (LIST). The following appears in the Log window:

 LIST NUM ;
NUM =             18

To modify the value of NUM to 22, type over the NUM = 18 line with NUM = 22, press Enter to put it on the command line, and press Enter again to issue the command.

You can enter most PL/I expressions on the command line.

Now step into the call to PUSH by pressing PF2 (STEP) and step until the statement labeled  PUSH1  is reached. To view the attributes of variable STKNUM, enter the Debug Tool command:

DESCRIBE ATTRIBUTES STKNUM;

The result in the Log window is:

 ATTRIBUTES FOR STKNUM
   ITS ADDRESS IS  0003944C AND ITS LENGTH IS 200
     PUSH : STACK.STKNUM(50)  FIXED BINARY(31,0) REAL PARAMETER
         ITS ADDRESS IS  0003944C AND ITS LENGTH IS 4

You can list all the values of the members of the structure pointed to by STACK with the command:

LIST STACK;

with results in the Log window appearing something like this:

 LIST STACK ;
STACK.STKPTR =                 2
STACK.STKNUM(1) =              2
STACK.STKNUM(2) =             18
STACK.STKNUM(3) =         233864

  ·
  ·
  ·
STACK.STKNUM(50) = 121604

You can change the value of a structure member by issuing the assignment as a command as in the following example:

STKNUM(STKPTR) = 33;

Halting on a PL/I line only if a condition is true

Often a particular part of your program works fine for the first few thousand times, but it fails under certain conditions. You don’t want to just set a line breakpoint because you will have to keep entering GO.

Example: sample PL/I program for debugging

For example, in PLICALC you want to stop at the division selection only if the divisor is 0 (before the exception occurs). Set the breakpoint like this:

AT 31 DO; IF NUM ^= 0 THEN GO; END;

Line 31 is the statement labeled  CALC2 . The command causes Debug Tool to stop at line 31. If the value of NUM is not 0, the program continues. The command causes Debug Tool to stop on line 31 only if the value of NUM is 0.

Debugging PL/I when only a few parts are compiled with TEST

Example: sample PL/I program for debugging

Suppose you want to set a breakpoint at entry to subroutine PUSH. PUSH has been compiled with TEST, but the other files have not. Debug Tool comes up with an empty Source window. To display the compile units, enter the command:

LIST NAMES CUS

The LIST NAMES CUS command displays a list of all the compile units that are known to Debug Tool. If PUSH is fetched later on by the application, this compile unit might not be known to Debug Tool. If it is displayed, enter:

SET QUALIFY CU PUSH
AT ENTRY PUSH;
GO ;

If it is not displayed, set an appearance breakpoint as follows:

AT APPEARANCE PUSH ;
GO ;

You can also combine the breakpoints as follows:

AT APPEARANCE PUSH AT ENTRY PUSH; GO;

The only purpose for this appearance breakpoint is to gain control the first time a function in the PUSH compile unit is run. When that happens, you can set a breakpoint at entry to PUSH like this:

AT ENTRY PUSH;

Displaying raw storage in PL/I

You can display the storage for a variable by using the LIST STORAGE command. For example, to display the storage for the first 30 characters of STACK enter:

LIST STORAGE(STACK,30)

Refer to the following topics for more information related to the material discussed in this topic.

Getting a PL/I function traceback

Often when you get close to a programming error, you want to know how you got into that situation, and especially what the traceback of calling functions is. To get this information, issue the command:

LIST CALLS ;

Example: sample PL/I program for debugging

For example, if you run the PLICALC example with the commands:

AT ENTRY READTOK ;
GO ;
LIST CALLS ;

the Log window will contain something like:

At ENTRY IN PL/I subroutine READTOK.
From LINE 17.1 IN PL/I subroutine PLICALC.

which shows the traceback of callers.

Tracing the run-time path for PL/I code compiled with TEST

To trace a program showing the entry and exit points without changing the program, you can enter the commands described in step 2 by using a commands file or by entering the commands individually. To use a commands file, do the following steps:

  1. Create a PDS member with a name similar to the following name: userid.DT.COMMANDS(PLICALL)
  2. Edit the file or data set and add the following Debug Tool commands:
    SET PROGRAMMING LANGUAGE PLI ;
    DCL LVLSTR CHARACTER (50);
    DCL LVL FIXED BINARY (15);
    LVL = 0;
    AT ENTRY *
    DO;
    LVLSTR = ' ' ;
    LVL = LVL + 1 ;
    LVLSTR = 'ENTERING >' || %BLOCK;
    LIST UNTITLED ( LVLSTR ) ;
    GO ;
    END;
    AT EXIT *
    DO;
    LVLSTR = 'EXITING < ' || %BLOCK;
    LIST UNTITLED ( LVLSTR ) ;
    LVL = LVL - 1 ;
    GO ;
    END;
  3. Start Debug Tool.
  4. Enter the following command:
    USE DT.COMMANDS(PLICALL)
  5. Run your program sequence. Debug Tool displays the trace in the Log window.

For example, after you enter the USE command, you run the following program sequence:

*PROCESS MACRO,OPT(TIME); 
 *PROCESS S STMT TEST(ALL); 

 PLICALL: PROC OPTIONS (MAIN); 

 DCL PLIXOPT CHAR(60) VAR STATIC EXTERNAL 

 INIT('STACK(20K,20K),TEST'); 

 CALL PLISUB; 

 PUT SKIP LIST('DONE WITH PLICALL'); 

 PLISUB: PROC; 

 DCL PLISUB1 ENTRY ; 

 CALL PLISUB1; 

 PUT SKIP LIST('DONE WITH PLISUB '); 

 END PLISUB; 

 PLISUB1: PROC; 

 DCL PLISUB2 ENTRY ; 

 CALL PLISUB2; 

 PUT SKIP LIST('DONE WITH PLISUB1'); 

 END PLISUB1; 

 PLISUB2: PROC; 

 PUT SKIP LIST('DONE WITH PLISUB2'); 
 END PLISUB2; 
 END PLICALL; 

In the Log window, Debug Tool displays a trace similar to the following trace:

'ENTERING >PLICALL                                 '
'ENTERING >PLISUB                                  '
'ENTERING >PLISUB1                                 '
'ENTERING >PLISUB2                                 '
'EXITING < PLISUB2                                 '
'EXITING < PLISUB1                                 '
'EXITING < PLISUB                                  '
'EXITING < PLICALL                                 '

Finding unexpected storage overwrite errors in PL/I

During program run time, some storage might unexpectedly change its value and you want to find out when and where this happened. Consider the following example where the program changes more than the caller expects it to change.

2 FIELD1(2) CHAR(8);
2 FIELD2 CHAR(8);
 CTR = 3;         /* an invalid index value is set */
 FIELD1(CTR) = 'TOO MUCH';

Find the address of FIELD2 with the command:

DESCRIBE ATTRIBUTES FIELD2

Suppose the result is X'00521D42'. To set a breakpoint that watches for a change in storage values starting at that address for the next 8 bytes, issue the command:

AT CHANGE %STORAGE('00521D42'px,8)

When the program is run, Debug Tool halts if the value in this storage changes.

Halting before calling an undefined program in PL/I

Calling an undefined program or function is a severe error. To halt just before such a call is run, set this breakpoint:

AT CALL 0

When Debug Tool stops at this breakpoint, you can bypass the CALL by entering the GO BYPASS command. This allows you to continue your debug session without raising a condition.


Terms of use | Feedback

This information center is powered by Eclipse technology. (http://www.eclipse.org)