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.
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.
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';
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.
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;
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.
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;
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.
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.
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:
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;
USE DT.COMMANDS(PLICALL)
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 '
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.
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.