There are many ways to defeat CL program bugs.
Brief: In addition to using the system- supplied debugging commands, you may need to use some other techniques when working with CL programs. Because CL programs are frequently used to establish a job environment for other programs, the debugging effort focuses not so much on variables and flow of control, but rather on the overall job. This article describes the commands that can be used to debug CL programs.
In the March 1992 issue of Midrange Computing, Don Denoncourt presented a comprehensive review of system-supplied debugging commands. These commands include the session commands (STRDBG, ENDDBG, STRSRVJOB, ENDSRVJOB and others), the trace commands (ADDTRC, DSPTRC, etc.) and the breakpoint commands (ADDBKP, CHGPGMVAR, DSPPGMVAR and RMVBKP). While all of the commands and techniques described in that article are useful for debugging CL programs, some additional commands and techniques can be used.
CL programs are different from other high-level language (HLL) programs in that the CL program usually only sets the stage for other programs. Because of the limited I/O and computational capabilities of CL programs, doing any significant data processing in CL is just too difficult. But the work done in a CL program is just as vital as the programs that it calls. This work includes prompting for and preparing parameters, verifying authorities, allocating objects and performing file overrides, and controlling the processing ("flow of control") of the called programs. Bugs that occur while performing those functions may not be readily observable or correctable with the system-supplied debugging commands. In my experience, debugging CL programs often involves a post-mortem investigation-trying to figure out why things happened the way they did.
Breakpoints Are Allowed
In some cases, the IBM debugging commands will be enough to help you find and correct a bug. I'm merely suggesting that some are more effective than others in debugging CL, and some can be augmented by additional strategies presented here. Let's start with an assessment of the debugging facilities you already know about-the commands mentioned earlier.
I have not used the trace function with CL programs. Most CL programs are short and the flow of control is simply linear, so the trace is not very useful. But I have implemented the breakpoint function with success. I usually add breakpoints to a CL program so that I can check the results of a HLL program that has just completed before executing the next program.
On some occasions, I use breakpoints to display CL program variables. This is similar to breakpoint displays in other HLL programs, although specifying the CL program variable on the Add Breakpoint (ADDBKP) command is slightly more complicated. For example, to add a breakpoint at statement number 100.00 in a CL program and display CL program variables &FILE and &LIB, the ADDBKP command would look like this:
ADDBKP STMT(10000) + PGMVAR(('&FILE') ('&LIB'))
Each variable must be preceded by an ampersand and enclosed in single quotes. The other commands that use the PGMVAR parameter also require this format. For example, to use the Display Program Variable (DSPPGMVAR) or Change Program Variable (CHGPGMVAR) commands, you must specify the CL program variable with the ampersand and the single quotes.
Job Logs
When used properly, the job log is a valuable tool for debugging a CL program. In order to get the most benefit from the job log, you have to ensure that the commands processed by the CL program will be logged and that messages sent by the job are posted to the job log.
The logging of CL program commands to the job log is initially set by the LOG parameter on the Create CL Program (CRTCLPGM) command. The values for the parameter may be *JOB, *YES or *NO, with *JOB as the default. *JOB means that logging of CL commands is controlled by the setting of the job's logging flag when the program is run. *NO means that commands are never logged; *YES means they're always logged. To be sure that you always get logging information, use the *YES value when you compile a CL program.
The logging flag controls which messages sent during the job are logged to the job log. These messages include the CL commands in a CL program and messages sent as a result of executing those commands. The logging flag for the job is set either in the job description object (*JOBD) or with job control commands. The logging flag can be set in the job description using the LOG parameter of the Create Job Description (CRTJOBD) or Change Job Description (CHGJOBD) commands. The logging flag of the job description may be overridden by the value of the LOG parameter on the Submit Job (SBMJOB) or Change Job (CHGJOB) commands. IBM's choice of LOG as the keyword for all these commands is unfortunate, since its usage differs from that of the LOG parameter on the CRTCLPGM command.
Using the elements of the LOG parameter, you can control the extent and level of detail of messages that are recorded in the job log. The chart in 1 shows the three elements that are included in the LOG parameter and the meaning of each element. The default, LOG(4 00 *NOLIST), means that a job log is not produced if the job ends normally; but if the job ends abnormally, a complete job log is produced.
Using the elements of the LOG parameter, you can control the extent and level of detail of messages that are recorded in the job log. The chart in Figure 1 shows the three elements that are included in the LOG parameter and the meaning of each element. The default, LOG(4 00 *NOLIST), means that a job log is not produced if the job ends normally; but if the job ends abnormally, a complete job log is produced.
This is usually the preferred setting in a run-time environment. However, since most of my work is development, I have changed my setting to LOG(4 00 *SECLVL). That means that I always get the most extensive job log available. Granted, the second-level messages are sometimes quite lengthy, but I would rather see the long explanation in place than have to use the Work with Message Descriptions (WRKMSGD) command to look up the message text. It is important to note that the commands logged by a CL program may not include all of the commands in the program. Commands that are valid only in CL programs (such as DCL, CHGVAR, GOTO, IF and DO) are not logged.
When a job is started or submitted, the logging level parameter values are taken from the job description associated with the job. You can permanently change the job description values with the CHGJOBD command. For submitted jobs, you can override the job description value with the LOG parameter on the SBMJOB command. For jobs that are active or on the job queue, you can change the logging level with the CHGJOB command. If you are testing a program or suspect that an active program may end in error, you can set the logging level before or during the execution of the program to force the maximum amount of information to the job log.
Dumping a CL Program
As with other HLLs, you can request a formatted dump from a CL program. The dump request can be made explicitly or as the result of a response to an unmonitored escape message. In either case, the formatted dump is the same. To explicitly request a formatted dump, use the Dump CL Program (DMPCLPGM) command. This command, which can only be used in a CL program, has no parameters. To request a dump from an unmonitored escape message, use the "D" option.
The dump is quite simple, compared with dumps from other programs (for example, the RPG formatted dump). It consists of only two sections. The first lists the messages sent to the program's message queue. If the CL program logging is set to *YES, the CL commands that were executed are also listed. This is similar to the listing of commands that appears in a job log. The second part of the formatted dump lists the name, type, length and value of any CL program variables used in the program. This is especially helpful for debugging parameters passed to programs, since the message for a CALL command that includes parameters simply states that "the CALL command contains parameters."
The DMPCLPGM command is similar to using the breakpoint capabilities and displaying program variables. The command can be run at any point in a CL program. When run as a command, processing continues with the next statement following the command. When run as the result of an option "D" request of an inquiry message, processing terminates after producing the dump.
Displaying a Job
Perhaps one of the most useful commands for debugging a CL program is the Display Job (DSPJOB) command. This can be run either interactively while the job is active or as part of the CL program itself. If run as part of the program, you can indicate that all information about the job is to be printed, using this command:
DSPJOB JOB(*) OUTPUT(*PRINT) + OPTION(*ALL)
This includes all of the information that you can see on the interactive DSPJOB command, including job status, definition and run attributes, spool files associated with the job, the job log, program stack, job locks, library list, open files, file overrides, commitment control status and communications status. In my experience, the most important of these factors have been the program stack, job locks, library list, open files and file overrides.
File overrides are a frequent source of errors, the bug in this case not being a program failure, but a failure to select the right data. Faulty file overrides can lead to wild speculation about the source of the problem, as the program has apparently run without error, yet produced incorrect results.
Slightly different is the Display Overrides (DSPOVR) command. It can display overrides like DSPJOB OPTION(*FILOVR), but you also get to choose how you want to see them-merged or not. While DSPJOB always presents file overrides separately (not merged), DSPOVR gives you the option to merge them, which is of great help. It also lets you direct output to the printer if you prefer.
Other things to look for in the DSPJOB output are the job description used, job switches and the program stack. The program stack will tell you if the programs that were called are the ones that you expected. Using the library list part of the DSPJOB output, you can determine if you have multiple occurrences of a program that may be causing the problem.
Consider using the Work with Job (WRKJOB) command instead of DSPJOB in your CL programs (if they run interactively). WRKJOB has all the functions of DSPJOB but, in addition, provides a command line. When the CL program reaches the WRKJOB command, the execution of the program stops. As soon as you exit the WRKJOB panel (with Enter, F3 or F12), the CL program continues executing. Another tactic, detailed in "Display Program Messages," MC, September 1992, is designed specifically for debugging CL programs. The DSPPGMMSG command lets you see the messages that have accumulated in a program message queue-a task that no built-in OS/400 command addresses.
Coding for Debugging
There is a simple technique that you can add to your CL programs to ensure that you get debugging information in the event of a program failure. The technique is illustrated in the CL Command Reference manual, and also shown here in 2. The key to this technique is in the global Monitor Message (MONMSG) command, which monitors for the function check message CPF9999. If a function check occurs in the program (if you have any unmonitored escape messages), control branches to the statement labeled DUMP. At that statement, you then run the DSPJOB and DMPCLPGM commands. In most cases, the information from those two commands will be sufficient to help you find the CL program bug.
There is a simple technique that you can add to your CL programs to ensure that you get debugging information in the event of a program failure. The technique is illustrated in the CL Command Reference manual, and also shown here in Figure 2. The key to this technique is in the global Monitor Message (MONMSG) command, which monitors for the function check message CPF9999. If a function check occurs in the program (if you have any unmonitored escape messages), control branches to the statement labeled DUMP. At that statement, you then run the DSPJOB and DMPCLPGM commands. In most cases, the information from those two commands will be sufficient to help you find the CL program bug.
Craig Pelkie can be contacted through Midrange Computing.
References
AS/400 CL Programmer's Guide
(SC41-8077-01, CD ROM QBKA7101)
AS/400 CL Reference (SC41-0030-01,
CD ROM QBKA8201)
Debugging CL Programs
Figure 1 LOG Parameter Values (CRTJOBD, CHGJOBD, SBMJOB, CH
Element Value Description 0 No data is logged. 1 All messages sent to job's external message queue with severity greater than or equal to specified message severity. 2 All information from level 1. All commands and commands being logged from a CL program for which messages are issued with a severity greater than or equal to specified message severity. 3 All information from level 1. All commands and commands being logged from a CL program. All messages associated with a command or commands being logged from a CL program that result in a high-level message with a severity greater than or equal to specified message severity. 4 All commands and commands being logged from a CL program. All messages with a severity code greater than or equal to specified message severity, including trace messages. 00-99 Lowest severity level that causes an error message to be logged. A severity level of 0 (default value) causes all messages to be logged. *NOLIST No job log is produced for jobs that end normally. If the job ends abnormally (end-of-job code 20 or higher), a job log is produced. *MSG First-level message text is written to the job log.** *SECLVL First- and second-level message text is written to the job log.** Example: LOG(4 00 *SECLVL) **Job log produced.
Debugging CL Programs
Figure 2 Adding Debugging Code to a CL Program
CLPGM: PGM DCL ... DCL ... MONMSG MSGID(CPF9999) EXEC(GOTO CMDLBL(DUMP)) (other parts of the CL program here) RETURN DUMP: DSPJOB JOB(*) OUTPUT(*PRINT) OPTION(*ALL) DMPCLPGM ENDPGM
LATEST COMMENTS
MC Press Online