Build your own integrated debugger using REXX
The AS/400 provides a baseline of debugging functions that is considered standard across many platforms. OS/400 allows us to suspend program execution when a particular statement is executed or a particular variable is altered and, again, resume execution of the program. We can display the flow of a program with trace listings of statements executed. Contents of variables can be examined and, if you choose, modified. Through the use of the AS/400 debugging commands (see my "Debugging Simplified" page 20 of this issue) we have a window into the actual processing of faulty programs to assist us in investigating and resolving the cause of program failure.
But the AS/400 lacks the integrated debugging environment that programmers on other platforms have the benefit of. Typically, an integrated debugging environment uses a source level debugger that combines the viewing of program source code with the simple invocation of the standard debugging functions. AS/400 programmers, on the other hand, must mimic this process with a source listing or SEU session while they use an interactive session to enter lengthy and cumbersome commands.
The Solution
OS/400 does, however, provide all the hooks and switches required to develop an integrated debugging environment. In fact, several third-party software firms are already marketing source level debuggers for the AS/400. But why not develop your own? Well, how about the time and effort involved to create screens and programs sophisticated enough to handle source level debugging. The solution--use REXX. REXX, which uses the Extended Program Monitor's (EPM) screen processor, does most of the screen input/output for you; display files are not used. EPM's screen I/O was originally developed for use with AS/400 BASIC, Pascal and C and supports the SAA standard use of F3, F9, F21, Roll-up, and Roll-down keys--no programming required. REXX can be used to read and write to the EPM screen with simple commands such as SAY and PULL. Users can then roll back and forth through the session output, retrieve previously entered commands with F9 or exit with F3 in a fashion similar to the command entry screen.
Using REXX, I have developed a working model of a source level debugger. This article will cover how RexxDebugger, as I called my debugger, works as well as providing some tips for its use. I call RexxDebugger a model of a debugger only because it is easily modified for your own requirements and, using it as a prototype, you will gain an insight into how to write your own debugger in CLP and a HLL.
Let's Try It Out
Before jumping into a discussion of the inner workings of RexxDebugger, let's try it out. Create the objects in Figures 1-7 (beginning on page 50) according to the instructions at the bottom of each figure. To set up the RexxDebugger environment, we run the Start RexxDebugger (STRREXDBG) command. You must enter the qualified name of the RPG program being debugged in the PGM parameter. The UPDPROD parameter determines whether production files can be updated; it defaults to *YES.
8 contains a simple RPG program (ORDLST) we suspect has a bug. Let's debug it with RexxDebugger. The ORDLST program as listed cannot be compiled but we are going to pretend that it is. Just follow the test as if you were at a workstation and use the figures as reference. First, we execute the STRREXDBG command as follows:
Figure 8 contains a simple RPG program (ORDLST) we suspect has a bug. Let's debug it with RexxDebugger. The ORDLST program as listed cannot be compiled but we are going to pretend that it is. Just follow the test as if you were at a workstation and use the figures as reference. First, we execute the STRREXDBG command as follows:
STRREXDBG PGM(ORDLST)
Next, we simply call the program with the following command:
CALL PGM(ORDLST)
Before any statements are processed, RexxDebugger gains control of the screen and, after the REXX interpreter flashes "REXX interpreter starting" for a few seconds, the following prompt is displayed:
Start of terminal session. Program initialization. Set breakpoints. > Ready for RexxDebugger command. Type HELP for assistance. ________________________________________________________________________ F3=End of File F9=Retrieve F21=Extend line
Suppose we want to find out in what statements ORDDTL appears. We can scan the source member in its entirety by running RexxDebugger's FIND command, as follows:
Start of terminal session. Program initialization. Set breakpoints. > Ready for RexxDebugger command. Type HELP for assistance. find orddtl Start of terminal session. 100 FORDDTL IF E K DISK 1900 C ORDKEY SETLLRORDDTL 2100 C ORDKEY READERORDDTL 91 Press ENTER to end terminal session. F3=End of File F9=Retrieve F21=Extend line
The text line "Press ENTER to end terminal session" is from the REXX interpreter telling us that a REXX procedure has completed; press Enter every time you see this prompt. With the results of the find, we now know that the Order Detail file (ORDDTL) is processed at statements 100, 1900 and 2100.
Let's now list the source member starting at statement 2100. Run the RexxDebugger GOTO 21 command (which moves the pointer to statement 2100), followed by the LIST 10 command (which lists 10 lines of code, starting at statement 2100):
100 FORDDTL IF E K DISK 1900 C ORDKEY SETLLRORDDTL 2100 C ORDKEY READERORDDTL 91 Press ENTER to end terminal session. End of terminal session. > Ready for RexxDebugger command. Type HELP for assistance. goto 21 > Ready for RexxDebugger command. Type HELP for assistance. list 10 Start of terminal session. 002100 C ORDKEY READERORDDTL 91 002200 C *IN91 IFEQ '0' 002300 C ORDQTY MULT PRICE CHRGE 92 002400 C EXCPTLINOUT 002500 C END 002600 C END 002700 * 002800 C SETON LR 002900 C RETRN 003000 * Press ENTER to end terminal session. F3=End of File F9=Retrieve F21=Extend line
As you can see, the EPM screen handler puts lines at the bottom of the screen and, as the screen fills up, rolls lines to the top of the screen. Similar to the command entry screen, the EPM screen allows you to roll back and forth through prior screens for information that has rolled off the top of the screen.
To find out why no detail records are being processed, we can set break points at the SETLL command and at the IFEQ statement following the read. To do this, cursor up to line 1900, press F9 to retrieve and press Enter. RexxDebugger then adds a break point for the retrieved statement number. To set a break point at line 2200, again, cursor to that line, hit F9, and press Enter. You'll see the following:
list 10 Start of terminal session. 002100 C ORDKEY READERORDDTL 91 002200 C *IN91 IFEQ '0' 002300 C ORDQTY MULT PRICE CHRGE 92 002400 C EXCPTLINOUT 002500 C END 002600 C END 002700 * 002800 C SETON LR 002900 C RETRN 003000 * Press ENTER to end terminal session. End of terminal session. > Ready for RexxDebugger command. Type HELP for assistance. 1900 C ORDKEY SETLLRORDDTL Break point added at statement:1900 > Ready for RexxDebugger command. Type HELP for assistance. 002200 C *IN91 IFEQ '0' Break point added at statement:2200 > Ready for RexxDebugger command. Type HELP for assistance. _______________________________________________________________________ F3=End of File F9=Retrieve F21=Extend line
Now it's possible to display program variables. Let's look at ORDNO and PARTNO by running RexxDebugger's DISP command: DISP ORDNO PARTNO
> Ready for RexxDebugger command. Type HELP for assistance. 1900 C ORDKEY SETLLRORDDTL Break point added at statement:1900 > Ready for RexxDebugger command. Type HELP for assistance. 002200 C *IN91 IFEQ '0' Break point added at statement:2200 > Ready for RexxDebugger command. Type HELP for assistance. Press ENTER to end terminal session. End of terminal session. Start of terminal session. Break point hit at source line number 1900 > Ready for RexxDebugger command. Type HELP for assistance. disp ordno partno Start of terminal session. ORDNO ' 12345' PARTNO ' ' Press ENTER to end terminal session. End of terminal session. > Ready for RexxDebugger command. Type HELP for assistance. _______________________________________________________________________ F3=End of File F9=Retrieve F21=Extend line
Here, then, is the problem: the part number is blank and the subsequent Read Equal will fail. The program should set PARTNO to a valid value for that order before positioning the file or it should use only the order number as the key. Let's test our theory by modifying PARTNO to be a good number now, since statement 1900 will not execute until after ORDLST resumes. Type the following RexxDebugger command: CHANGE PARTNO 54321. After RexxDebugger accepts the change command, press Enter to resume execution of the buggy program. When the program hits line 2200, RexxDebugger, again, gains control and informs you of what statement number you are at. Looking at: "Break Point hit at source line number 2200," we realize that we have forgotten what statement 2200 was. Rolling back the EPM, we can see the section of code and decide to list the value for *IN91, the READE end of file indicator, and two of the order detail fields with: DISPLAY *IN91 ORDQTY PRICE. RexxDebugger then displays:
PARTNO ' ' Press ENTER to end terminal session. End of terminal session. > Ready for RexxDebugger command. Type HELP for assistance. change partno 54321 > Ready for RexxDebugger command. Type HELP for assistance. Press ENTER to end terminal session. End of terminal session. Start of terminal session. Break point hit at source line number 2200 > Ready for RexxDebugger command. Type HELP for assistance. disp *in91 ordqty price Start of terminal session. *IN91 '0' ORDQTY ' 12' PRICE ' 2.98' Press ENTER to end terminal session. End of terminal session. > Ready for RexxDebugger command. Type HELP for assistance. _______________________________________________________________________ F3=End of File F9=Retrieve F21=Extend line
Hey, we've got a hit, and we've proved our theory about setting PARTNO before the SETLL. Let's remove the break points with the REMOVE *ALL, and allow the program to continue without pause by pressing Enter until RexxDebugger returns control.
So, How Does It Work?
The big hook into the RexxDebugger utility is in the REX001CL CL (2) program where it sets the first break point to be at program initialization time (*INIT). The CL Add Breakpoint (ADDBKP) command has a parameter, BKPPGM, which allows you to specify the name of a program to receive control when a break point is hit. For our purposes, that program is the CL program-- REX001CLA (3). REX001CLA accepts the parameters that OS/400 passes, informing us of the breaking (sic) program, the recursion level, the break point statement number and the machine instruction number. REX001CLA then retrieves the source file name, and source and object library of the program and, after concatenating them into one field, passes them to the REXX procedure--REX001RX (4).
The big hook into the RexxDebugger utility is in the REX001CL CL (Figure 2) program where it sets the first break point to be at program initialization time (*INIT). The CL Add Breakpoint (ADDBKP) command has a parameter, BKPPGM, which allows you to specify the name of a program to receive control when a break point is hit. For our purposes, that program is the CL program-- REX001CLA (Figure 3). REX001CLA accepts the parameters that OS/400 passes, informing us of the breaking (sic) program, the recursion level, the break point statement number and the machine instruction number. REX001CLA then retrieves the source file name, and source and object library of the program and, after concatenating them into one field, passes them to the REXX procedure--REX001RX (Figure 4).
The REXX procedure, REX001RX, is the powerhouse of our debugging utility and yet, it is fairly short. REX001RX begins by parsing the passed parameters, displaying the break point statement number and setting the program source relative record number equal to the break point. Next, REX001RX prompts us for the RexxDebugger debugging commands. These commands are processed until we enter the QUIT command or resume execution of the program by pressing Enter without the entry of a command.
The command processing portion of RexxDebugger is controlled with the REXX case construct: SELECT, WHEN and OTHERWISE. Each iteration of the main processing loop prompts us with the REXX SAY command and receives our response with the PULL command. Whatever we type, or retrieve with F9, drops through the SELECT statement until a WHEN clause is true. Each of these WHEN clauses use the REXX WORD function to parse the first word of the input value, IN, and compare it to one of the RexxDebugger commands (see the RexxDebugger Command Help display at the beginning of this article for a list of available commands). If none of the WHEN clauses is true, then the OTHERWISE clause executes sending the invalid entry message to the EPM.
For brevity, and also because REXX code is very easy to read, I will explain only the more interesting sections of RexxDebugger's code: LIST, FIND, DISPLAY, GOTO and F9 for ADDBKP. The LIST RexxDebugger command displays source statements on the EPM session screen. With REXX, we have a problem reading from a source file because REXX does not directly support file processing. But, the EPM does support redirection of the standard input file, STDIN. To do this piping, RexxDebugger invokes a second REXX procedure, REX001RXB (6) passing the member name, source file, source library, the relative position of the file and the number of lines to list. REX001RXB starts by overriding the standard input file to the source member, positioning the file at the passed relative record number. The routine then reads the source file with the PARSE command and lists the records out to the EPM with the SAY command.
For brevity, and also because REXX code is very easy to read, I will explain only the more interesting sections of RexxDebugger's code: LIST, FIND, DISPLAY, GOTO and F9 for ADDBKP. The LIST RexxDebugger command displays source statements on the EPM session screen. With REXX, we have a problem reading from a source file because REXX does not directly support file processing. But, the EPM does support redirection of the standard input file, STDIN. To do this piping, RexxDebugger invokes a second REXX procedure, REX001RXB (Figure 6) passing the member name, source file, source library, the relative position of the file and the number of lines to list. REX001RXB starts by overriding the standard input file to the source member, positioning the file at the passed relative record number. The routine then reads the source file with the PARSE command and lists the records out to the EPM with the SAY command.
The REX001RX procedure's variable, SRCRRN (source relative record number), bears some consideration because it is heavily used. When OS/400 hands over control to RexxDebugger, it passes the statement number of the break point. RexxDebugger receives the statement number as SRCRRN. When the LIST command is used, it will list statements starting at the break point number. The GOTO command, however, allows you to change this variable so that subsequent use of LIST will display source from a different statement number. For smooth operation, the sequence numbers of your program should be resequenced after each modification and before recompilation; otherwise, the SRCRRN will be off by the number of source statements with decimal values.
The RexxDebugger FIND command--the sister operation to LIST--will display all the occurrences of the string entered. After entry of the FIND, RexxDebugger invokes OS/400's FNDSTRPDM command to print the matching statements and then start another REXX procedure, REX001RXA (5). REX001RXA copies the spool file output from FNDSTRPDM to a work file, PRT132, and then overrides EPM's standard input file to PRT132. REX001RXA reads through all the spool file records parsing out and listing to the EPM all the occurrences of the string. Try that in RPG or CL.
The RexxDebugger FIND command--the sister operation to LIST--will display all the occurrences of the string entered. After entry of the FIND, RexxDebugger invokes OS/400's FNDSTRPDM command to print the matching statements and then start another REXX procedure, REX001RXA (Figure 5). REX001RXA copies the spool file output from FNDSTRPDM to a work file, PRT132, and then overrides EPM's standard input file to PRT132. REX001RXA reads through all the spool file records parsing out and listing to the EPM all the occurrences of the string. Try that in RPG or CL.
To display the value of variables, RexxDebugger uses another REXX procedure that operates similar to REX001RXA's copying and parsing of a spool file. The section of code that processes the RexxDebugger DISPLAY command into the AS/400 DSPPGMVAR command builds the DSPPGMVAR command by looping through the words in the input string, IN, until no more variable names are in the string or the limit of 10 is reached. As each variable is parsed from IN, it is concatenated to the RexxDebugger command variable DSPVAR. When DSPVAR is constructed, its value contains the OS/400 command DSPPGMVAR with variables. DSPPGMVAR is executed by REXX on the RexxDebugger statement line where DSPVAR appears (roughly comparable to QCMDEXC). This dynamic creation of command statements adds inherent power to the ability of the REXX interpreter.
As we learned earlier, the adding of break points is easily done by retrieving RPG source statements previously listed to the EPM with the F9 key and pressing Enter. The SELECT statement of RexxDebugger knows that an Add Break Point request has been entered WHEN the first word of the input string is a number. You can also add a break point by typing in a valid statement number and pressing Enter. The input statement number is then converted from a string variable to a numeric variable by assigning itself, multiplied by one to the variable STMT. The OS/400 Add Break Point (ADDBKP) command is then processed; note that the break point program parameter of ADDBKP is set to the CL program, REX001CLA so that RexxDebugger will again receive control when the new break point is hit.
There are several other RexxDebugger commands that we have not mentioned as they are fairly self-explanatory and are covered in RexxDebugger's help section. The BROWSE command, for instance, can be used as a high-speed method of viewing and scanning long source members since--due to the nature of an interpreter--the REXX FIND procedure is a little slow. The STEP command is another useful RexxDebugger function; it allows you to enter a statement range to break on. Every statement number, evenly divisible by 100, between the entered range will then be added as a break point.
RexxDebugger's Downfall
REXX, because it is interpretive, is slow. Additionally, to read from different files, RexxDebugger requires the invocation of other REXX procedures. Another problem is that RexxDebugger requires resequencing of source members after modification and before recompilation because the debugger retrieves statements by relative record number. Your EPM screen also gets cluttered with commands, source and EPM messages.
Nevertheless, if you consider the time required to do the variety of functions directly available from within RexxDebugger, you might discover the comparable AS/400 functions take even more time. More time to flip between the multitude of screens, print new source listings, invoke SEU and scribble notes. RexxDebugger, by integrating the debugging process, streamlines the effort.
But then, if top speed is a requirement, I did say RexxDebugger was just a working model. RexxDebugger can be converted to an HLL with subfile processing, split screens and windows; although, you will miss the string handling capabilities of REXX. Or, you might also consider purchasing one of the third party debugging products available.
For Best Use...
In the meantime, until you write or purchase a faster integrated debugger, there a few tips on making the best use of RexxDebugger. First of all, think in terms of code fragments. List sections of pertinent code to the EPM, not long portions. Also, to get started, use the browse option to quickly scan for those pertinent sections of code and then return to RexxDebugger, GOTO the statement number of interest and LIST the number of lines of interest. Have some fun customizing RexxDebugger, too; if you find yourself going to the command entry screen and doing the same function--add it to RexxDebugger.
Editor's Note: RexxDebugger works for RPG programs only, although it will work for CL programs with slight modifications. In most other languages, the compiler assigns statement numbers that are different from the source line numbers--COBOL and PL/I are two such languages. Because RexxDebugger depends on their equality to do its work, RexxDebugger cannot be used for such programming languages.
Sidebar: RexxDebugger Commands =================== REXX DEBUGGER HELP ========================== BROWSE: Displays the physical file member of the source being debugged (DSPPFM). Useful for lengthy source members. CHANGE variable value: Changes the value of a variable. CLEAR: Clears the screen. DISPLAY variable1-10: Displays up to 10 program variables. ENTER: Resumes program execution. FIND string: Searches for and displays all occurrences of a string with no embedded spaces. GOTO rrn: Set source relative record number (see LIST). LIST number: List source lines from last breakpoint, last statement listed, or from rrn set with GOTO. QCMD: The "backdoor" to the Command Entry panel. QUIT: Aborts program. REMOVE breakpoint: Removes a previously set breakpoint. STEP fromstmt tostmt: Set breakpoints on all statements between two statement numbers. To set a breakpoint, move cursor to line LISTed and press F9 or enter entire 6-digit statement number, including all leading zeros and omitting the decimal point. > Ready for RexxDebugger command. Type HELP for assistance. _______________________________________________________________________ F3=End of File F9=Retrieve F21=Extend line
Interactive RPG/400 Source Level Debugger
Figure 1 Command STRREXDBG
STRREXDBG: CMD PROMPT('Start REXX Debugger') PARM KWD(PGM) TYPE(Q1) MIN(1) PROMPT('Program') PARM KWD(UPDPROD) TYPE(*CHAR) LEN(4) RSTD(*YES) + DFT(*YES) VALUES(*YES *NO) PROMPT('Update + production files') Q1: QUAL TYPE(*NAME) LEN(10) MIN(1) QUAL TYPE(*NAME) LEN(10) DFT(*LIBL) + SPCVAL((*LIBL)) PROMPT('Library')
Interactive RPG/400 Source Level Debugger
Figure 2 CL program REX001CL
REX001CL: + PGM PARM(&QPGM &UPDPROD) DCL VAR(&PGM) TYPE(*CHAR) LEN(10) DCL VAR(&PGMLIB) TYPE(*CHAR) LEN(10) DCL VAR(&QPGM) TYPE(*CHAR) LEN(20) DCL VAR(&UPDPROD) TYPE(*CHAR) LEN(4) CHGVAR VAR(&PGM) VALUE(%SST(&QPGM 1 10)) CHGVAR VAR(&PGMLIB) VALUE(%SST(&QPGM 11 10)) CHKOBJ OBJ(&PGMLIB/&PGM) OBJTYPE(*PGM) MONMSG MSGID(CPF9801) EXEC(DO) SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGDTA('Program' *BCAT + &PGM *BCAT 'not found in' *BCAT &PGMLIB) MSGTYPE(*ESCAPE) RETURN ENDDO MONMSG MSGID(CPF9810) EXEC(DO) SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGDTA('Program + library' *BCAT &PGMLIB *BCAT 'not found') MSGTYPE(*ESCAPE) RETURN ENDDO STRDBG PGM(&PGMLIB/&PGM) UPDPROD(&UPDPROD) ADDBKP STMT(*INIT) BKPPGM(*CURLIB/REX001CLA) OVRPRTF FILE(QPUOPRTF) HOLD(*YES) OVRPRTF FILE(QPDBGDSP) HOLD(*YES) CRTPF FILE(QTEMP/PRT132) RCDLEN(132) MONMSG MSGID(CPF0000) ENDPGM
Interactive RPG/400 Source Level Debugger
Figure 3 CL program REX001CLA
REX001CLA: + PGM PARM(&PGM &RECURS &STMT &MI) DCL VAR(&MI) TYPE(*CHAR) LEN(5) DCL VAR(&OBJLIB) TYPE(*CHAR) LEN(10) DCL VAR(&PGM) TYPE(*CHAR) LEN(10) DCL VAR(&RECURS) TYPE(*CHAR) LEN(5) DCL VAR(&REXPRM) TYPE(*CHAR) LEN(60) DCL VAR(&SRCF) TYPE(*CHAR) LEN(10) DCL VAR(&SRCFLIB) TYPE(*CHAR) LEN(10) DCL VAR(&STMT) TYPE(*CHAR) LEN(10) RTVOBJD OBJ(&PGM) OBJTYPE(*PGM) RTNLIB(&OBJLIB) SRCF(&SRCF) + SRCFLIB(&SRCFLIB) CHGVAR VAR(%SST(&REXPRM 1 10)) VALUE(&PGM) CHGVAR VAR(%SST(&REXPRM 12 10)) VALUE(&OBJLIB) CHGVAR VAR(%SST(&REXPRM 23 10)) VALUE(&SRCFLIB) CHGVAR VAR(%SST(&REXPRM 34 10)) VALUE(&SRCF) CHGVAR VAR(%SST(&REXPRM 45 10)) VALUE(&STMT) STRREXPRC SRCMBR(REX001RX) SRCFILE(MAGAZINE/PROPOSED) PARM(&REXPRM) ENDPGM
Interactive RPG/400 Source Level Debugger
Figure 4 REXX procedure REX001RX
arg pgm olib slib sfil bkp if datatype(bkp) = 'CHAR' then do rrn = 1 say 'Program initialization. Set brakpoints.' end else do rrn = trunc(bkp/100) say 'Breakpoint hit at source line number' bkp end do until word(in,1) = 'QUIT' say '> Ready for RexxDebugger command. Type HELP for assistance.' pull in select when word(in,1) = '' then return when word(in,1) = 'BROWSE' then 'dsppfm file('slib'/'sfil') mbr('pgm')' when word(in,1) = 'CHANGE' then 'chgpgmvar pgmvar('word(in,2)') value('word(in,3)')' when word(in,1) = 'CLEAR' then do 21 say ' ' end when word(in,1) = 'DISPLAY' then do dspvar = 'dsppgmvar pgmvar(' do loop = 2 to 11 if word(in,loop) = '' then leave dspvar = dspvar || '(' || word(in,loop) || ') ' end dspvar = dspvar || ') output(*print)' dspvar 'strrexprc srcmbr(rex001rxc) srcfile(magazine/proposed)' end when word(in,1) = 'FIND' then do find = substr(in,6,24) 'fndstrpdm string('find') file('slib'/'sfil') ', 'mbr('pgm') option(*none) prtrcds(*all)' 'strrexprc srcmbr(rex001rxa) srcfile(magazine/proposed)' find = '' end when word(in,1) = 'GOTO' then rrn = word(in,2) when word(in,1) = 'HELP' then call display_help when word(in,1) = 'LIST' then do list = word(in,2) if list = '' then list = 10 'strrexprc srcmbr(rex001rxb) parm('''pgm slib sfil rrn , list''') srcfile(magazine/proposed)' rrn = rrn + list end when word(in,1) = 'QCMD' then 'call qcmd' when word(in,1) = 'QUIT' then do say 'Aborting REXX Debugger.' 'rmvbkp *all' 'addbkp stmt(*init) bkppgm(rex001cla)' say 'Breakpoints removed except for *INIT.' return end when word(in,1) = 'REMOVE' then do rmvbkp = 'rmvbkp stmt(' do loop = 2 to 11 if word(in,loop) = '' then leave rmvbkp = rmvbkp || '(' || word(in,loop) || ') ' end rmvbkp = rmvbkp || ') ' rmvbkp say rmvbkp end when word(in,1) = 'STEP' then do fromstmt = word(in,2) * 1 tostmt = word(in,3) * 1 stmt = fromstmt do loop = fromstmt to tostmt by 10 'addbkp stmt('stmt') bkppgm(rex001cla)' stmt = stmt * 10 end end when datatype(word(in,1)) = 'NUM' then do stmt = word(in,1) * 1 'addbkp stmt('stmt') bkppgm(rex001cla)' say 'Breakpoint added at statement: 'stmt'.' end otherwise say in' is an invalid entry. Try again.' end end return display_help: say '=================== REXX DEBUGGER HELP ==========================' say 'BROWSE: Displays the physical file member of the source being' say ' debugged (DSPPFM). Useful for lengthy source members.' say 'CHANGE variable value: Changes the value of a variable.' say 'CLEAR: Clears the screen.' say 'DISPLAY variable1-10: Displays up to 10 program variables.' say 'ENTER: Resumes program execution.' say 'FIND string: Searches for and displays all occurrences of a' say ' string with no embedded spaces.' say 'GOTO rrn: Set source relative record number (see LIST).' say 'LIST number: List source lines from last breakpoint, last' say ' statement listed, or from rrn set with GOTO.' say 'QCMD: The "backdoor" to the Command Entry panel.' say 'QUIT: Aborts program.' say 'REMOVE breakpoint: Removes a previously set breakpoint.' say 'STEP fromstmt tostmt: Set breakpoints on all statements between' say ' two statement numbers.' say ' ' say 'To set a breakpoint, move cursor to line LISTed and press F9' say ' or enter entire 6-digit statement number, including all' say ' leading zeros and omitting the decimal point.' return
Interactive RPG/400 Source Level Debugger
Figure 5 REXX procedure REX001RXA
'cpysplf file(qpuoprtf) tofile(prt132) splnbr(*last)' 'dltsplf file(qpuoprtf) splnbr(*last)' 'ovrdbf stdin prt132' do until word(in,1) = 'SEQNBR' pull in if in = '' then do say 'No matches found.' exit end end do until word(in,1) = 'NUMBER' | in = '' pull in if datatype(word(in,1)) = 'NUM' then say substr(in,1,70) end return
Interactive RPG/400 Source Level Debugger
Figure 6 REXX procedure REX001RXB
arg mbr lib srcf n roll 'ovrdbf file(stdin) tofile('lib'/'srcf') mbr('mbr') position(*rrn ', n')' do roll parse linein src if src = '' then leave say substr(src,1,6) substr(src,13,60) end
Interactive RPG/400 Source Level Debugger
Figure 7 REXX procedure REX001RXC
'cpysplf file(qpdbgdsp) tofile(prt132) splnbr(*last)' 'dltsplf file(qpdbgdsp) splnbr(*last)' 'ovrdbf stdin prt132' do until word(in,1) = 'VARIABLE' pull in if in = '' then do say 'Variable not found.' exit end end do forever field = substr(in,45,10) do until word(in,1) = 'TYPE' pull in end if substr(in,47,6) = 'PACKED' then loop = 2 if substr(in,47,5) = 'ZONED' then loop = 2 if substr(in,47,9) = 'CHARACTER' then loop = 3 do loop pull in end do until word(in,1) = 'VARIABLE' if substr(in,1,3) = ' ' then say field' 'substr(in,4,60) field = ' ' pull in if substr(in,52,5) = 'E N D' | in = '' then exit end end return
Interactive RPG/400 Source Level Debugger
Figure 8 Sample RPG program ORDLST
Figure 8: Sample RPG Program ORDLST 100 FORDDTL IF E K DISK 200 FQSYSPRT O F 132 OF PRINTER 300 * 400 C *ENTRY PLIST 500 C PARM ORD@@ 600 C PARM PART@@ 700 * 800 C *LIKE DEFN ORDNO ORD@@ 900 C *LIKE DEFN PARTNO PART@@ 1000 * 1100 C ORDKEY KLIST 1200 C KFLD ORDNO 1300 C KFLD PARTNO 1400 * 1500 C EXCPTLINHDR 1600 * 1700 C Z-ADDORD@@ ORDNO 1800 * 1900 C ORDKEY SETLLRORDDTL 2000 C *IN91 DOUEQ'1' 2100 C ORDKEY READERORDDTL 91 2200 C *IN91 IFEQ '0' 2300 C ORDQTY MULT PRICE CHRGE 92 2400 C EXCPTLINOUT 2500 C END 2600 C END 2700 * 2800 C SETON LR 2900 C RETRN 3000 * 3100 OQSYSPRT E 1 LINHDR 3200 O 'Order' 3300 O + 1 'Part Number' 3400 O 34 'Price' 3500 O 41 'Quantity' 3600 O 54 'Charge' 3700 O 65 'Due Date' 3800 O E 1 LINOUT 3900 O ORDNO 4000 O PARTNO + 1 4100 O PRICE J + 1 4200 O ORDQTYZ + 1 4300 O CHRGE J + 1 4400 O DUEDTEY + 1
LATEST COMMENTS
MC Press Online