Debugging is hard work, the right tools make it easier
Debugging is a deductive process which programmers use to explore the causes of a program failure (bug). Programmers make inferences about the bug based on conditions in the data and software at the time the error occurs. The AS/400 supplies many tools that you can use in the debugging process including joblogs, dumps, journals and commands. In this article, I will discuss several debugging commands and how they may become part of an effective programming strategy.
As I started to work on this article, I asked a colleague what tricks he used in debugging. He assured me that his programs had no bugs and therefore he didn't know much about the AS/400's debugging facility. I reminded him that I had done some work in his shop.
No one likes to admit that their program has a bug. I remember all the things that used to go through my mind when one of my programs didn't work: the compiler's broken, someone's changed my program, there must be a bad chip in main memory. But, after disproving all my theories, I invariably conceded to what I thought was a programmer's last resort--the debugging facility.
The AS/400 debug commands create an environment for running a specific program or a set of programs. When the programs are run, they will pause (or break) at specific points during the processing. When the program breaks, selected variables can be displayed and even modified. Trace parameters can be set up to track the sequence of program execution. You can use this information to determine which section of code caused an error.
The debug environment consists of commands to set initial parameters and start the debug process; commands to modify break points, variables to be displayed and trace conditions; and commands to end the debug session. See 1 for a complete list of commands.
The debug environment consists of commands to set initial parameters and start the debug process; commands to modify break points, variables to be displayed and trace conditions; and commands to end the debug session. See Figure 1 for a complete list of commands.
STRDBG Command
Before faulty programs can have their execution and variable values reviewed, a debugging session must first be invoked with the Start Debug (STRDBG) command. STRDBG allows the entry of up to 10 program names to debug. The default program, if not explicitly defined, will be the first program name in the list of programs; all other debug commands will then default to this program if not otherwise specified. I'll cover the tracing parameters (maximum trace statements and trace full option) later.
Production file updates merit our major concern at this point. When STRDBG's UPDPROD default of *NO is used, attempts to update data in production libraries will cause a system error message, updates to test libraries created with TYPE(*TEST) will not. Remember, though, that source files in production libraries cannot be modified when UPDPROD(*NO) is specified. This means the system won't let you change the program without ending the debug session.
After the debug environment is set up, you can add or remove programs from the list of programs being debugged with the Add Program (ADDPGM) and Remove Program (RMVPGM) commands. Additionally, you can modify any of the STRDBG options with the Change Debug (CHGDBG) command at any point in a debug session. The debug session is ended explicitly with the End Debug (ENDDBG) command, or implicitly when you signoff.
Knows All, Tells All
Break points are the powerhouse of the AS/400 debug facility; they provide the crystal ball into program execution and variable content. You set break points by specifying the program source statement number on the Add Break Point (ADDBKP) command. The decimal point is not used for the STMT parameter of ADDBKP; i.e., statement 50.00 would be entered as 5000. The values of up to 10 variables can be displayed during a break point by listing their names within the PGMVAR parameter of ADDBKP. When a program executing in debug mode hits a break point, the system pauses the program and displays the break point screen (2). From this display, you can review the values of variables requested for that break point, abort the program with F3 or invoke command entry with F10.
Break points are the powerhouse of the AS/400 debug facility; they provide the crystal ball into program execution and variable content. You set break points by specifying the program source statement number on the Add Break Point (ADDBKP) command. The decimal point is not used for the STMT parameter of ADDBKP; i.e., statement 50.00 would be entered as 5000. The values of up to 10 variables can be displayed during a break point by listing their names within the PGMVAR parameter of ADDBKP. When a program executing in debug mode hits a break point, the system pauses the program and displays the break point screen (Figure 2). From this display, you can review the values of variables requested for that break point, abort the program with F3 or invoke command entry with F10.
You can set break points at statement numbers for calculation, input and output specifications. The standard RPG processing points (i.e., *GETIN, *TERM, etc.) can also be used to set a break. A break point set at *TERM, for instance, will allow you to view the final value of pertinent program variables before the program terminates--without having to find the last source statement number required by ADDBKP.
Remember, as you add break points, the program breaks before the statement actually executes. Also, if you add a break point for a statement that is already defined as a break point, the break point is redefined based on the parameters of the new command.
Conditional Break Points
Break points make debugging easier but sometimes they cause excessive pauses in program execution. The Break Point Condition parameter (BKPCOND) of ADDBKP lets you set break points to occur only when a program variable meets the criterion specified. You can use BKPCOND to set conditions for *PGMVAR1 through *PGMVAR10 defined by ADDBKP. *PGMVAR1 to 10 refer to the position within the list for the PGMVAR keyword. The following, for example, would cause a break to occur only when the hours worked by an employee exceeded regular time:
ADDBKP STMT(18800) + PGMVAR(rate hours) + BKPCOND(*PGMVAR2 *GT 40)
The SKIP parameter of ADDBKP lets you set the statement to break only after it has executed a specified number of times. There are many uses for this parameter, although, most often I use it to test the final execution of statements within DO loops.
ADDBKP STMT(141100) + PGMVAR(itrrte) + SKIP(100)
Working With Variables
When program execution pauses and displays the break point screen, the state of the displayed program variables typically leads to more questions. You might, for instance, want to view the values of variables not on the current break point display. To do this, you can invoke command entry with F10 and enter the Display Program Variable (DSPPGMVAR) command. From the command line, you can modify the contents of variables with the Change Program Variable (CHGPGMVAR) command. There may be circumstances where a bug causes the program to abort. After the cause of the error is found, you could use CHGPGMVAR to correct the condition and continue the test of the program. You might even use CHGPGMVAR to cause error conditions! Sometimes duplicating a production bug with input data can be difficult. By setting a break point at a statement that occurs before a questionable section of code and then changing pertinent variables during the pause, you can reenact the condition of the production bug. You may want to use a split screen if you have a multiple session terminal or use function keys to toggle back and forth between the source code and the break point screen.
Always Look Behind You--To See Where You've Been
The AS/400 trace facility allows you to review the processing path followed during execution of a program. This can help you to isolate sections of code that may be causing an error or to determine whether all possible program branches have been tested. The Add Trace (ADDTRC) command signals the debug environment to accumulate trace data on the next run. TRACE can be activated for an entire program or for groups of statements (up to five groups). The ADDTRC command allows you to specify up to 10 program variables which will be recorded in the trace data. By specifying *CHG or *ALWAYS on the OUTVAR parameter of the ADDTRC command, you tell trace to record the values of selected program variables on every reference, or only when the variables are modified.
The STRDBG command has two parameters that relate to tracing: TRCFULL (Trace Full) and MAXTRC (Maximum Trace statements). TRCFULL specifies what happens to the trace data when it exceeds MAXTRC's number of maximum statements to trace. TRCFULL can be set to *WRAP, or *STOPTRC. *WRAP overlays trace data when MAXTRC is exceeded, *STOPTRC pauses the program allowing the programmer to access the command entry screen and review the trace data before continuing. Setting ADDTRC's Trace Full parameter to *STOPTRC works best when you set MAXTRC to a number of statements that you can comfortably review at each pause.
One interesting trick is to invoke step mode by setting MAXTRC to one and TRCFULL to *STOPTRC. The program will then break after each statement. Setting ADDTRC's TRCFULL to *WRAP is useful for reviewing the last group of statements processed before a break point or program termination. The breadth of trace can also be controlled by specifying statement ranges in the ADDTRC command rather than by using the default of tracing all statements. Most often, though, I simply set the maximum trace to the highest value allowed (32,767) which normally allows the program to run in its entirety so that I can review the complete execution path of a program.
The problem with the trace facility is that the results are a bland list of statement numbers (displayed or printed with the DSPTRCDTA command). Following the execution path of program statements with a trace listing is tedious. For this reason, the trace facility of the AS/400 has largely been ignored by the programming community. I have found, however, that you can fairly easily follow trace data using the split screen of SEU to browse spool file QPDBGDSP (trace spool file). The job name for QPDBGDSP is usually your display name. One last point: trace data will continue to accumulate during each debug session until it is cleared with either the Clear Trace Data (CLRTRCDTA) command or the CLEAR(*YES) option on DSPTRCDTA.
Debugging Batch Programs
Prior to V1R3, debugging batch programs presented a dilemma. You don't want to run a CL program that takes several minutes before calling the buggy program and you might not enjoy waiting while thousands of records are processed before a break point is hit. So, you take the effort to create or copy a subset of the data to a test library and create a CL driver that skips unnecessary program calls. With batch debugging--available in V1R3--you can simply let the batch job run and work on something else until the batch job reaches a break point. Also, since the batch job does not tie up your session the way an interactive debug session does, you can watch the job execute using the Work with Submitted Jobs (WRKSBMJOB) command. The break point screen will interrupt whatever else you have on your screen when a break point is reached.
Batch debugging requires the addition of two simple commands to the debug process: Start Service Job (STRSRVJOB) and End Service Job (ENDSRVJOB). To begin batch debugging, first submit the program to a batch job queue, on hold. The job does not have to be held to start a service job on it--you just don't want the buggy program to pass the problem area before you start debugging it. The faulty program may be nested in a CL program or just a straight call-- whatever the application demands.
Second, start a service job for the submitted job. If the job name is unique, it only need be entered as the STRSRVJOB parameter; otherwise, the full NUMBER/USER/JOB qualification is required:
STRSRVJOB JOB(123456/ + DENONCOURT/BUGGYJOB)
Third, enter the STRDBG command with the appropriate parameters specified. And, for the final step in starting a batch debug session, release the held job. When OS/400 first starts a batch job that is being serviced and debugged, it sends a display to the interactive session servicing the job:
Start Service Job
System: AS400
Job: BUGGYJOB User: DENONCOURT Number: 123456 The serviced job has been released from the job queue. Press Enter to start the job or F10 to enter debug commands for that job.
Press Enter to Continue
F10=Command Entry
By gaining access to the command entry screen with F10, you can enter any of the debug commands covered earlier, then return to the STRSVCJOB screen and press enter to allow the system to start the batch job. From this point on, the debug session will function similar to a standard debug session with the same break point displays and entry of further debugging commands. The only difference is that your interactive session is not tied up with the running of the debugged program.
When a serviced job has completed, OS/400 will send a message to your display confirming the end of the serviced job. At this point, you should end both the debug session and the service job with ENDDBG and ENDSRVJOB. To debug another batch job, the STRSRVJOB and STRDBG commands must be reentered since the debug environment cannot carry across multiple jobs.
Debugging Other Interactive Sessions
STRSRVJOB also allows you to debug interactive jobs other than your own. Haven't you ever had a call from an irate user who, as you held your phone six inches from your ear, complained about strange program results or perhaps an apparent runaway job. There was no way, before V1R3, to see the values of program variables or to find out what section of code the program was executing. Dumps, which do list current variable values and the last executed statement, can only be generated if a dump request is coded into the program or if the job has a system error allowing you to select a D for Dump. The only recourse was to end the job and attempt to duplicate the bug in a test debug environment.
Now we can get into the guts of faulty interactive programs and watch execution of statements and display variables. Here's how it works. First, start a service job over the interactive session; you can use WRKSBSJOB QINTER to get the job name, user profile name and job number. Then, start the debug session and begin adding break points and displaying variables. By servicing and debugging the interactive job, you can resolve the production bug while the error condition still exists--without even leaving your seat.
ZZ Top, ZZ BIN and ZZ BOUT
You may be wondering what ZZ Top or his apparent cousins, BIN and BOUT, have to do with debugging but, with the exception of ZZ Top, ZZ BIN and ZZ BOUT can become useful when debugging. Sometimes we would like to see the value of a field in a database file but the field is not used anywhere in the program. Database fields not directly referenced cannot be displayed in an RPG debug session, just as they do not list in a dump. We might, for instance, need to know the full key of a invalid record, the key fields of which are not all described within the program. By viewing the internal program I/O buffers, ZZnnBIN and ZZnnBOUT (nn is the relative position of file declarations), with DSPPGMVAR, we can see the value of records currently in the program. The best bet is to use the *HEX format option of the DSPPGMVAR so that you may see both the hex value, for packed data, and character values for zoned and character (3). With a file layout and a little effort, you'll be able to see the value of any field in an input or output buffer. I have not tested this method for other languages but it will probably work.
You may be wondering what ZZ Top or his apparent cousins, BIN and BOUT, have to do with debugging but, with the exception of ZZ Top, ZZ BIN and ZZ BOUT can become useful when debugging. Sometimes we would like to see the value of a field in a database file but the field is not used anywhere in the program. Database fields not directly referenced cannot be displayed in an RPG debug session, just as they do not list in a dump. We might, for instance, need to know the full key of a invalid record, the key fields of which are not all described within the program. By viewing the internal program I/O buffers, ZZnnBIN and ZZnnBOUT (nn is the relative position of file declarations), with DSPPGMVAR, we can see the value of records currently in the program. The best bet is to use the *HEX format option of the DSPPGMVAR so that you may see both the hex value, for packed data, and character values for zoned and character (Figure 3). With a file layout and a little effort, you'll be able to see the value of any field in an input or output buffer. I have not tested this method for other languages but it will probably work.
Debugging Simplified
Hopefully all the debugging techniques I've discussed will make debugging easier for you and you will use the facilities more often. But don't just use them to see why bad programs don't work--use them to see how good programs work. The AS/400 debugging aids can sometimes be a more expedient method of doing code walkthroughs and testing of internal routines than laborious desk checking.
The AS/400 provides a decent set of utilities for debugging programs, however these utilities are somewhat cumbersome to use because they do not provide a complete integrated debugging environment. But, IBM has given us all the hooks and switches required to develop such an integrated environment ourselves. My RexxDebugger, found in another article in this issue, allows you to view source, add break points, and display and change RPG program variables; all on the same screen--no function keying or hot keying between screens required. It was quick and easy to write using REXX and saves lots of time in the debugging process.
Debugging Simplified
Figure 1 Debug commands
Figure 1: Debug Commands Session Commands ADDPGM Adds up to 10 programs to the debug session CHGDBG Changes any of the STRDBG parameters during a debug session DSPDBG Displays the current invocation stack and programs being debugged ENDDBG Terminates the Debug session, removing all breakpoints and traces ENDSRVJOB Ends a service job STRDBG Starts a debug session with up to 10 programs to debug STRSRVJOB Starts a service job over a batch job or other interactive job so that you can debug that job from your interactive session RMVPGM Removes a program from the list of debugged programs Trace Commands ADDTRC Adds traces for up to five ranges, with optional variables to break CLRTRCDTA Clears any accumulated trace data DSPTRC Displays all trace ranges in effect DSPTRCDTA Displays the accumulated list of program statements executed and within the trace ranges specified with ADDTRC RMVTRC Removes all traces or a range Break Point Commands ADDBKP Adds a break point by source statement number with options of 10 variables to display during a break, one conditional value, and the number of times to execute this statement before a break CHGPGMVAR Changes the value of a debugged program's variable DSPPGMVAR Display the value of a debugged program's variable RMVBKP Removes an existing break point
Debugging Simplified
Figure 2 Display breakpoint
Figure 2: Display Breakpoint Statement/Instruction . . . . . . . . . : 114400 /050D Program . . . . . . . . . . . . . . . . : FAULTYPGM Recursion level . . . . . . . . . . . . : 1 Start Position . . . . . . . . . . . . : 1 Format . . . . . . . . . . . . . . . . : *HEX Length . . . . . . . . . . . . . . . . : *DCL Variable . . . . . . . . . . . . . . . : ITMNBR Type . . . . . . . . . . . . . . . . : CHARACTER Length . . . . . . . . . . . . . . . : 15 *...+....1....+....2....+....3....+....4....+....5 '1234567890ABCDE' Variable . . . . . . . . . . . . . . . : *IN91 Type . . . . . . . . . . . . . . . . : CHARACTER Length . . . . . . . . . . . . . . . : 1 *...+....1....+....2....+....3....+....4....+....5 + Press Enter to continue. F3=Exit program F10=Command entry
Debugging Simplified
Figure 3 DSPPGMVAR for ZZ BIN
Figure 3: DSPPGMVAR for ZZ BIN Program . . . . . . . . . . . . . . . . : FAULTYPGM Recursion level . . . . . . . . . . . . : 1 Start Position . . . . . . . . . . . . : 1 Format . . . . . . . . . . . . . . . . : *HEX Length . . . . . . . . . . . . . . . . : *DCL Variable . . . . . . . . . . . . . . . : ZZ03BIN Type . . . . . . . . . . . . . . . . : CHARACTER Length . . . . . . . . . . . . . . . : 122 * . . . + . . . . 1 . . . . + . *...+....1....+. 40404040D4C1D9E84040404040404040 ' MARY ' 40404040D4C1D9E84040404040404040 ' ' 4040C7C2C5C1D3C54040404040404040 ' GBEALE ' 40404040404040404040404040404040 ' ' 40404040404040404040404040404040 ' ' 4040404040D44BC44BD4C4C4F0F2F8F0 ' M.D.MDD0280' F8F5404040D7F0F1C90920101F000000 '85 P01U ' 40404040D4C1D9E84040404040404040 ' C '
LATEST COMMENTS
MC Press Online