Debugging COBOL programs is a skill that separates productive programmers from those who spend days chasing problems that experienced developers resolve in minutes. The techniques differ dramatically from debugging in modern languages with...
In This Chapter
- Introduction: The Reality of COBOL Debugging
- 20.1 DISPLAY-Based Debugging
- 20.2 Debugging Lines: D in Column 7
- 20.3 Compiler Listing Analysis
- 20.4 Common COBOL Abends: Diagnosis and Resolution
- 20.5 Dump Analysis
- 20.6 IBM Debug Tool (IBM Debugger)
- 20.7 GnuCOBOL Debugging
- 20.8 Debugging COBOL-DB2 Programs
- 20.9 Unit Testing COBOL Programs
- 20.9 Debugging Common Problems
- 20.10 Debugging CICS Programs
- 20.11 Prevention Through Defensive Programming
- 20.12 Memory Analysis and Storage Dumps
- 20.13 Debugging Checklist
- Summary
- 20.14 Debugging Batch vs. Online Programs: Key Differences
- 20.15 Systematic Debugging Methodology
- Exercises
Chapter 20: Debugging Techniques and Tools
Introduction: The Reality of COBOL Debugging
Debugging COBOL programs is a skill that separates productive programmers from those who spend days chasing problems that experienced developers resolve in minutes. The techniques differ dramatically from debugging in modern languages with integrated development environments, step-through debuggers, and interactive REPLs. On a mainframe, your program may have run as a batch job at 2:00 AM, processing ten million records before failing at record 7,342,891. You have a dump, a sysout listing, and possibly a cryptic abend code. Your job is to figure out what went wrong, fix it, and resubmit before the morning production deadline.
This chapter covers the full spectrum of COBOL debugging, from simple DISPLAY-based techniques that work everywhere, through compiler-generated listings that reveal data layouts and cross-references, to interactive debuggers on both IBM mainframes and GnuCOBOL environments. It also covers the critical skill of dump analysis -- reading the hexadecimal memory contents that the operating system produces when a program abends. Finally, it addresses the modern practice of unit testing COBOL programs, which represents a shift from reactive debugging to proactive defect prevention.
Whether you are working on a z/OS mainframe with IBM Debug Tool, on a Linux workstation with GnuCOBOL and GDB, or analyzing a production dump at 3:00 AM with a cup of coffee and a hex conversion chart, this chapter gives you the tools and techniques you need.
20.1 DISPLAY-Based Debugging
The DISPLAY statement is the oldest, simplest, and most universally available debugging technique in COBOL. It writes output to the system console (SYSOUT on z/OS, standard output on GnuCOBOL). Despite its simplicity, experienced COBOL programmers use DISPLAY-based debugging extensively because it works in every environment, requires no special tools, and can be added to production programs with minimal risk.
Basic DISPLAY Debugging
2000-PROCESS-RECORD.
DISPLAY '>>> ENTERING 2000-PROCESS-RECORD'
DISPLAY ' RECORD KEY: ' WS-RECORD-KEY
DISPLAY ' AMOUNT: ' WS-AMOUNT
DISPLAY ' COUNTER: ' WS-RECORD-COUNT
PERFORM 2100-VALIDATE
DISPLAY ' AFTER VALIDATE, STATUS: '
WS-VALID-FLAG
PERFORM 2200-CALCULATE
DISPLAY ' AFTER CALCULATE, RESULT: '
WS-CALC-RESULT
DISPLAY '<<< EXITING 2000-PROCESS-RECORD'
.
Strategic DISPLAY Placement
Effective DISPLAY debugging requires strategic placement. Do not scatter DISPLAY statements randomly; instead, place them at decision points where the program's behavior might diverge from expectations:
* 1. At the start of every major paragraph
DISPLAY '=== 2000-PROCESS-TRANSACTION ==='
* 2. Before and after file I/O operations
DISPLAY 'ABOUT TO READ MASTER FILE'
READ MASTER-FILE INTO WS-MASTER-REC
DISPLAY 'READ STATUS: ' WS-MASTER-STATUS
* 3. Before and after CALL statements
DISPLAY 'CALLING EDITMOD WITH: ' WS-INPUT-DATA
CALL 'EDITMOD' USING WS-INPUT-DATA WS-OUTPUT-DATA
DISPLAY 'EDITMOD RETURNED RC=' RETURN-CODE
* 4. At conditional branch points
DISPLAY 'TRANS-TYPE=' WS-TRANS-TYPE
EVALUATE WS-TRANS-TYPE
WHEN 'A'
DISPLAY 'TAKING ADD PATH'
PERFORM 3100-ADD-RECORD
WHEN 'C'
DISPLAY 'TAKING CHANGE PATH'
PERFORM 3200-CHANGE-RECORD
WHEN 'D'
DISPLAY 'TAKING DELETE PATH'
PERFORM 3300-DELETE-RECORD
WHEN OTHER
DISPLAY 'UNEXPECTED TYPE: [' WS-TRANS-TYPE
']'
END-EVALUATE
* 5. Inside loops, with iteration count
DISPLAY 'LOOP ITERATION: ' WS-LOOP-COUNT
' KEY=' WS-CURRENT-KEY
Displaying Data in Multiple Formats
Sometimes you need to see the hexadecimal representation of a field to diagnose data corruption or encoding issues:
01 WS-DEBUG-HEX PIC X(40).
01 WS-HEX-DISPLAY.
05 WS-HEX-CHAR PIC X(02) OCCURS 20 TIMES.
* Show the raw content of a field
DISPLAY 'AMOUNT DISPLAY: [' WS-AMOUNT ']'
DISPLAY 'AMOUNT LENGTH: '
FUNCTION LENGTH(WS-AMOUNT)
* For IBM Enterprise COBOL, you can inspect individual
* bytes using reference modification:
DISPLAY 'BYTE 1: ' WS-AMOUNT(1:1)
DISPLAY 'BYTE 2: ' WS-AMOUNT(2:1)
Displaying Numeric Data for Debugging
Numeric fields in COBOL can behave unexpectedly when displayed, especially COMP-3 (packed decimal) and COMP (binary) fields. When debugging, you often need to see both the formatted value and the raw content:
* COMP-3 field: DISPLAY shows the numeric value
01 WS-AMOUNT PIC S9(7)V99 COMP-3 VALUE 12345.67.
DISPLAY 'AMOUNT (FORMATTED): ' WS-AMOUNT
* Shows: AMOUNT (FORMATTED): +0012345.67
* To see the raw bytes, use reference modification
* COMP-3 PIC S9(7)V99 occupies 5 bytes
DISPLAY 'AMOUNT (BYTE 1): '
WS-AMOUNT(1:1)
DISPLAY 'AMOUNT (BYTE 2): '
WS-AMOUNT(2:1)
* For binary (COMP) fields, DISPLAY converts to readable form
01 WS-COUNTER PIC S9(9) COMP VALUE 42.
DISPLAY 'COUNTER: ' WS-COUNTER
* Shows: COUNTER: +000000042
* When a numeric field contains invalid data (pre-S0C7),
* DISPLAYing it may itself cause an abend. To safely
* inspect suspicious data, MOVE it to an alphanumeric
* field first:
01 WS-SUSPECT-AMOUNT PIC S9(7)V99 COMP-3.
01 WS-RAW-BYTES PIC X(5).
01 WS-RAW-REDEF REDEFINES WS-RAW-BYTES
PIC S9(7)V99 COMP-3.
MOVE WS-SUSPECT-AMOUNT TO WS-RAW-BYTES
DISPLAY 'RAW CONTENT: [' WS-RAW-BYTES ']'
* This safely displays the bytes without interpreting
* them as packed decimal
Trace Table Technique
For programs where the sequence of paragraph execution matters, maintain a trace table in WORKING-STORAGE that records the last N paragraphs executed:
01 WS-TRACE-TABLE.
05 WS-TRACE-INDEX PIC 99 VALUE 0.
05 WS-TRACE-MAX PIC 99 VALUE 20.
05 WS-TRACE-ENTRY PIC X(30) OCCURS 20 TIMES
VALUE SPACES.
9500-TRACE-PARAGRAPH.
ADD 1 TO WS-TRACE-INDEX
IF WS-TRACE-INDEX > WS-TRACE-MAX
MOVE 1 TO WS-TRACE-INDEX
END-IF
MOVE WS-CURRENT-PARAGRAPH
TO WS-TRACE-ENTRY(WS-TRACE-INDEX)
.
9510-DUMP-TRACE.
DISPLAY '=== PARAGRAPH TRACE ==='
PERFORM VARYING WS-DUMP-IDX FROM 1 BY 1
UNTIL WS-DUMP-IDX > WS-TRACE-MAX
IF WS-TRACE-ENTRY(WS-DUMP-IDX) NOT = SPACES
DISPLAY WS-DUMP-IDX ': '
WS-TRACE-ENTRY(WS-DUMP-IDX)
END-IF
END-PERFORM
DISPLAY '=== END TRACE ==='
.
Call 9500-TRACE-PARAGRAPH at the entry of each major paragraph (with WS-CURRENT-PARAGRAPH set to the paragraph name). If the program abends or produces wrong results, the trace table shows the sequence of paragraphs that led to the problem. This is the COBOL equivalent of a stack trace in modern languages.
Conditional Debug Displays
In production programs, you often want debug displays that can be turned on and off without recompiling:
WORKING-STORAGE SECTION.
01 WS-DEBUG-MODE PIC 9 VALUE 0.
88 WS-DEBUG-ON VALUE 1.
88 WS-DEBUG-OFF VALUE 0.
01 WS-DEBUG-LEVEL PIC 9 VALUE 0.
88 WS-DEBUG-NONE VALUE 0.
88 WS-DEBUG-ERROR VALUE 1.
88 WS-DEBUG-INFO VALUE 2.
88 WS-DEBUG-TRACE VALUE 3.
PROCEDURE DIVISION.
0000-MAIN.
* Read debug level from a control card or parameter
ACCEPT WS-DEBUG-LEVEL FROM ENVIRONMENT
'DEBUG_LEVEL'
END-ACCEPT
IF WS-DEBUG-TRACE
DISPLAY 'DEBUG: TRACE MODE ENABLED'
END-IF
.
2000-PROCESS.
IF WS-DEBUG-TRACE
DISPLAY 'TRACE: ENTERING 2000-PROCESS'
DISPLAY 'TRACE: KEY=' WS-RECORD-KEY
' AMT=' WS-AMOUNT
END-IF
* ... processing logic ...
IF WS-DEBUG-INFO
DISPLAY 'INFO: PROCESSED RECORD '
WS-RECORD-COUNT
END-IF
.
20.2 Debugging Lines: D in Column 7
COBOL provides a built-in mechanism for debugging code that can be activated or deactivated at compile time without removing or commenting out the debug statements. Any line with the letter D in column 7 is treated as a debugging line.
How Debugging Lines Work
WORKING-STORAGE SECTION.
D 01 WS-DEBUG-COUNTER PIC 9(7) VALUE 0.
PROCEDURE DIVISION.
2000-PROCESS.
D ADD 1 TO WS-DEBUG-COUNTER
D DISPLAY 'DEBUG: RECORD ' WS-DEBUG-COUNTER
D ' KEY=' WS-RECORD-KEY
D ' AMOUNT=' WS-AMOUNT
PERFORM 2100-VALIDATE-RECORD
D DISPLAY 'DEBUG: AFTER VALIDATE, FLAG='
D WS-VALID-FLAG
IF WS-RECORD-VALID
PERFORM 2200-UPDATE-MASTER
END-IF
D DISPLAY 'DEBUG: EXITING 2000-PROCESS'
.
Activating Debugging Lines
On IBM Enterprise COBOL, debugging lines are controlled by the WITH DEBUGGING MODE clause in the SOURCE-COMPUTER paragraph:
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SOURCE-COMPUTER. IBM-370 WITH DEBUGGING MODE.
When WITH DEBUGGING MODE is specified, lines with D in column 7 are compiled as regular source code. When it is removed, those lines are treated as comments and generate no object code.
On GnuCOBOL, debugging lines are activated with the -fdebugging-line compiler option:
cobc -fdebugging-line -x program.cob
Limitations of Column 7 Debugging
The D-in-column-7 mechanism is part of the COBOL-85 standard but is considered archaic in COBOL 2002 and later. Modern COBOL standards prefer the >>D directive for debugging lines in free-format source:
>>D DISPLAY 'DEBUG: Processing record ' WS-COUNT
The mechanism has a significant limitation: it is an all-or-nothing switch. You cannot selectively enable some debugging lines and disable others. For more granular control, use the conditional DISPLAY approach described in Section 20.1.
20.3 Compiler Listing Analysis
The compiler listing is one of the most powerful debugging tools available to a COBOL programmer. When you compile a COBOL program, the compiler can produce a listing that contains the source code annotated with data definitions, cross-references, offset maps, and diagnostic messages. Learning to read this listing is essential for effective debugging.
Requesting a Listing
On IBM Enterprise COBOL, the listing is controlled by compiler options:
//COBOL EXEC PGM=IGYCRCTL,
// PARM='LIST,MAP,XREF,OFFSET,VBREF'
| Option | What It Produces |
|---|---|
| LIST | Assembler expansion and object code listing |
| MAP | Data Division map showing offsets and lengths |
| XREF | Cross-reference of data names and procedure names |
| OFFSET | Condensed verb listing with statement offsets |
| VBREF | Verb cross-reference (which verbs are used where) |
On GnuCOBOL, use the -t option for a listing file:
cobc -x -t listing.txt program.cob
The DATA MAP
The DATA MAP section of the listing shows every data item in your program with its offset from the start of its section, its length, its format, and its level number. This is critical for dump analysis because dump contents are displayed by offset from the start of the data area.
Example DATA MAP output:
DATA DIVISION MAP
DEFN DATA NAME LVL SOURCE HEX-DISP DEFINITION
NAME
0045 WS-COUNTERS 01 WS 00000000 GROUP
0046 WS-READ-COUNT 05 WS 00000000 BINARY PIC S9(9)
0047 WS-WRITE-COUNT 05 WS 00000004 BINARY PIC S9(9)
0048 WS-ERROR-COUNT 05 WS 00000008 BINARY PIC S9(9)
0050 WS-RECORD-AREA 01 WS 0000000C GROUP
0051 WS-CUST-ID 05 WS 0000000C DISPLAY PIC X(10)
0052 WS-CUST-NAME 05 WS 00000016 DISPLAY PIC X(30)
0053 WS-CUST-BALANCE 05 WS 00000034 COMP-3 PIC S9(7)V99
When you have a dump showing that the field at WORKING-STORAGE offset X'00000034' contains X'0000000C' (which is not valid COMP-3 data), you can use the DATA MAP to identify that field as WS-CUST-BALANCE and know immediately that an invalid numeric value caused an S0C7 abend.
The Cross-Reference (XREF)
The XREF listing shows every data name and procedure name with every line number where it is referenced, and whether each reference is a definition (D), a modification (M), or a reference (R):
CROSS-REFERENCE OF DATA NAMES
DATA NAME DEFN REFERENCE
WS-CUST-BALANCE 0053 0120M 0135 0148 0162M 0175
WS-CUST-ID 0051 0118 0122 0130 0155M
WS-CUST-NAME 0052 0119 0131 0156M
WS-EOF-FLAG 0040 0112M 0125 0128M
WS-READ-COUNT 0046 0115M 0170
The XREF is invaluable for several debugging tasks:
- Finding where a variable is modified: Look for references marked with M.
- Finding unused variables: Variables with only a D (definition) reference are never used.
- Tracing data flow: Follow a variable through all its references to understand how it gets its value.
The OFFSET Listing
The OFFSET listing shows the hexadecimal offset of each COBOL verb in the generated object code. This is critical for dump analysis because the Program Status Word (PSW) in a dump contains the instruction address where the abend occurred. By subtracting the program load point from the PSW address, you get the offset, which you can look up in the OFFSET listing to find the exact COBOL statement that caused the abend.
OFFSET LISTING
LINE # HEXLOC VERB
000120 000A2E MOVE
000121 000A36 READ
000122 000A54 IF
000125 000A78 ADD
000128 000A86 MOVE
000130 000A8E PERFORM
000135 000A96 COMPUTE
If the dump shows that the abend occurred at offset X'000A96', you look up this offset in the OFFSET listing and find that it corresponds to line 135, which is a COMPUTE statement. You then look at line 135 in the source listing and examine the variables involved.
20.4 Common COBOL Abends: Diagnosis and Resolution
Understanding common abend codes is fundamental to COBOL debugging. Each abend code tells a specific story about what went wrong. Here is a comprehensive guide to the abend codes most frequently encountered in COBOL programs.
S0C7: Data Exception
What it means: A decimal arithmetic or comparison instruction encountered data that is not in valid packed decimal (COMP-3) or zoned decimal format.
How to diagnose:
- Find the failing instruction address in the PSW (Program Status Word) from the dump.
- Subtract the program load point to get the offset.
- Look up the offset in the OFFSET listing to find the COBOL statement.
- Examine the variables used in that statement.
- In the dump, locate those variables using the DATA MAP offsets and examine their hexadecimal content.
Common scenarios and fixes:
* SCENARIO 1: Uninitialized COMP-3 variable
* The field contains X'0000' instead of X'0000000C'
01 WS-TOTAL PIC S9(5)V99 COMP-3.
* FIX: Always initialize
01 WS-TOTAL PIC S9(5)V99 COMP-3 VALUE 0.
* SCENARIO 2: Moving alphanumeric to numeric without check
MOVE WS-INPUT-FIELD TO WS-NUMERIC-FIELD
ADD WS-NUMERIC-FIELD TO WS-TOTAL
* FIX: Validate before using
IF WS-INPUT-FIELD IS NUMERIC
MOVE WS-INPUT-FIELD TO WS-NUMERIC-FIELD
ADD WS-NUMERIC-FIELD TO WS-TOTAL
ELSE
DISPLAY 'INVALID DATA: ' WS-INPUT-FIELD
END-IF
* SCENARIO 3: Record area reused after EOF
READ INPUT-FILE INTO WS-RECORD
AT END SET WS-EOF TO TRUE
END-READ
* If WS-EOF, WS-RECORD may contain partial/invalid data
* FIX: Only use WS-RECORD when read was successful
IF WS-NOT-EOF
ADD WS-RECORD-AMOUNT TO WS-TOTAL
END-IF
* SCENARIO 4: REDEFINES mismatch
01 WS-DATA-AREA.
05 WS-TEXT-FIELD PIC X(10) VALUE 'HELLO'.
05 WS-NUM-FIELD REDEFINES WS-TEXT-FIELD
PIC S9(7)V99 COMP-3.
* Using WS-NUM-FIELD in arithmetic will S0C7 because
* 'HELLO' is not valid COMP-3 data
S0C4: Protection Exception
What it means: The program attempted to access a memory address that is not allocated to the program or is outside the addressable range.
How to diagnose:
- Check the PSW for the failing instruction address.
- Check register contents in the dump -- a register used as a base address may contain an invalid value (X'00000000' is a common culprit indicating an uninitialized pointer).
- Check subscript/index values for table references.
- Check LINKAGE SECTION references to ensure data was actually passed.
Common scenarios and fixes:
* SCENARIO 1: Table subscript out of range
01 WS-TABLE.
05 WS-ITEM PIC X(20) OCCURS 100 TIMES.
01 WS-SUB PIC 9(4) VALUE 0.
* If WS-SUB = 0 or > 100, S0C4:
MOVE 'DATA' TO WS-ITEM(WS-SUB)
* FIX: Validate subscript
IF WS-SUB >= 1 AND WS-SUB <= 100
MOVE 'DATA' TO WS-ITEM(WS-SUB)
END-IF
* SCENARIO 2: LINKAGE SECTION reference without data
LINKAGE SECTION.
01 LS-PARAMETER PIC X(100).
PROCEDURE DIVISION USING LS-PARAMETER.
* If caller did not pass a parameter, accessing
* LS-PARAMETER will S0C4
* SCENARIO 3: Reference modification out of range
MOVE WS-DATA(WS-START:WS-LENGTH)
TO WS-OUTPUT
* If WS-START = 0 or WS-START + WS-LENGTH - 1 > length
* of WS-DATA, S0C4
* FIX: Validate reference modification values
IF WS-START >= 1 AND
(WS-START + WS-LENGTH - 1) <=
FUNCTION LENGTH(WS-DATA)
MOVE WS-DATA(WS-START:WS-LENGTH)
TO WS-OUTPUT
END-IF
* SCENARIO 4: CALL with wrong number of parameters
CALL 'SUBPROG' USING WS-PARM1
* But SUBPROG expects USING LS-P1 LS-P2 LS-P3
* Accessing LS-P2 or LS-P3 will likely S0C4
S0C1: Operation Exception
What it means: The CPU encountered an invalid machine instruction.
Common causes and fixes:
* SCENARIO 1: Falling through end of program
PROCEDURE DIVISION.
0000-MAIN.
PERFORM 1000-PROCESS
* Missing STOP RUN or GOBACK here!
* Execution continues into whatever follows the
* program's object code in memory
* FIX: Always end with STOP RUN or GOBACK
GOBACK
.
* SCENARIO 2: Dynamic CALL to non-existent program
MOVE 'BADPROG' TO WS-PROG-NAME
CALL WS-PROG-NAME USING WS-PARMS
* FIX: Use ON EXCEPTION
CALL WS-PROG-NAME USING WS-PARMS
ON EXCEPTION
DISPLAY 'PROGRAM NOT FOUND: '
WS-PROG-NAME
END-CALL
S0CB: Division Exception
What it means: A fixed-point divide produced a quotient too large for the register, or division by zero occurred.
* Prevention: Always check for zero divisor
IF WS-DIVISOR NOT = ZERO
DIVIDE WS-DIVIDEND BY WS-DIVISOR
GIVING WS-QUOTIENT
ON SIZE ERROR
DISPLAY 'OVERFLOW IN DIVISION'
END-DIVIDE
ELSE
DISPLAY 'ZERO DIVISOR DETECTED'
MOVE 0 TO WS-QUOTIENT
END-IF
S322: Job Step Timed Out
What it means: The job step exceeded its CPU time limit (TIME parameter on JOB or EXEC statement in JCL).
How to diagnose:
- Check for infinite loops by examining the last paragraph executed (shown in the dump or LE traceback).
- Check loop termination conditions.
- Check file I/O patterns for inefficiency.
* Common cause: PERFORM UNTIL with unreachable condition
2000-PROCESS-LOOP.
PERFORM UNTIL WS-DONE
PERFORM 2100-READ-NEXT
* BUG: WS-DONE is never set to TRUE!
PERFORM 2200-PROCESS
END-PERFORM
.
* FIX: Ensure the termination condition is reachable
2100-READ-NEXT.
READ INPUT-FILE
AT END
SET WS-DONE TO TRUE
NOT AT END
ADD 1 TO WS-READ-COUNT
END-READ
.
* Additional safety: Add a maximum iteration guard
01 WS-SAFETY-COUNTER PIC 9(9) COMP VALUE 0.
01 WS-MAX-ITERATIONS PIC 9(9) COMP VALUE 50000000.
2000-PROCESS-LOOP.
PERFORM UNTIL WS-DONE
ADD 1 TO WS-SAFETY-COUNTER
IF WS-SAFETY-COUNTER > WS-MAX-ITERATIONS
DISPLAY 'SAFETY LIMIT REACHED'
SET WS-DONE TO TRUE
ELSE
PERFORM 2100-READ-NEXT
IF NOT WS-DONE
PERFORM 2200-PROCESS
END-IF
END-IF
END-PERFORM
.
S806: Load Module Not Found
What it means: The system could not find the load module (program) specified in a dynamic CALL or LINK.
How to diagnose:
- Check the module name for typos (including trailing spaces).
- Check the STEPLIB/JOBLIB DD statements in the JCL.
- Verify the module has been compiled, linked, and placed in the load library.
* Prevention: Use ON EXCEPTION
CALL WS-MODULE-NAME USING WS-PARMS
ON EXCEPTION
DISPLAY 'MODULE NOT IN LOADLIB: '
WS-MODULE-NAME
INSPECT WS-MODULE-NAME
TALLYING WS-SPACE-COUNT
FOR TRAILING SPACES
DISPLAY 'NAME LENGTH (LESS TRAILING SPACES): '
(FUNCTION LENGTH(WS-MODULE-NAME)
- WS-SPACE-COUNT)
END-CALL
20.5 Dump Analysis
When a COBOL program abends on z/OS, the system produces a dump -- a snapshot of the program's memory at the time of failure. Reading a dump is an essential skill for COBOL programmers working on mainframes. While it can seem intimidating at first, the process follows a systematic procedure.
Components of a Dump
A typical SYSUDUMP or SYSABEND dump contains:
- Job and step information: Job name, step name, program name, abend code
- PSW (Program Status Word): Contains the address of the instruction that caused the abend
- General Purpose Registers (GPR): Register contents at the time of the abend
- Program storage: The hexadecimal contents of the program's code and data areas
- LE Traceback: A formatted traceback showing the chain of COBOL paragraphs executed (IBM Language Environment)
Step-by-Step Dump Analysis
Step 1: Identify the abend code and PSW
Look at the beginning of the dump for the completion code:
COMPLETION CODE - SYSTEM=0C7 USER=0000 REASON=00000000
PSW AT TIME OF ERROR 078D1000 80012A4E
The PSW address is X'80012A4E'. The high-order bit (8) indicates 31-bit addressing mode. The effective address is X'00012A4E'.
Step 2: Find the program entry point
Look for the program's entry point address in the dump or the LE traceback:
ENTRY POINT = 00012000
Step 3: Calculate the offset
Subtract the entry point from the PSW address:
00012A4E - 00012000 = 00000A4E
The failing instruction is at offset X'A4E' in the program.
Step 4: Look up the offset in the compiler listing
In your OFFSET listing:
LINE # HEXLOC VERB
000245 000A3C MOVE
000247 000A4E ADD <-- This is the failing statement
000249 000A5A IF
Line 247 is an ADD statement. Now you know which COBOL statement caused the S0C7.
Step 5: Examine the data
Use the DATA MAP to find the offsets of the variables used in line 247, then locate those offsets in the WORKING-STORAGE dump to see their hexadecimal contents.
LE Traceback
IBM Language Environment provides a formatted traceback that is much easier to read than a raw dump:
CEE3DMP V2 R1.0: Condition processing resulted in the
unhandled condition.
Information for enclave CUSTPROC
Traceback:
DSA Entry E Offset Statement
1 CUSTPROC 1 +00A4E 247
2 CUSTPROC 1 +00832 198 (PERFORM from 0000-MAIN)
3 IGZCFCC 1 +001A8
This tells you directly that the abend occurred at statement 247, which was called via a PERFORM at statement 198 from 0000-MAIN. This is far quicker than manual offset calculation.
Reading Hexadecimal Data
When examining dump data, you need to understand how COBOL data types appear in hexadecimal:
| COBOL Type | Example Value | Hex Representation |
|---|---|---|
| PIC X(5) VALUE 'HELLO' | HELLO | C8C5D3D3D6 (EBCDIC) |
| PIC 9(5) VALUE 12345 | 12345 | F1F2F3F4F5 (Zoned) |
| PIC 9(5) COMP VALUE 12345 | 12345 | 00003039 (Binary) |
| PIC S9(5) COMP-3 VALUE 12345 | 12345 | 12345C (Packed) |
| PIC S9(5) COMP-3 VALUE -12345 | -12345 | 12345D (Packed) |
| PIC X(5) VALUE SPACES | (spaces) | 4040404040 (EBCDIC) |
| PIC X(5) VALUE LOW-VALUES | (nulls) | 0000000000 |
Recognizing bad data in a dump:
Field at offset 0034: 00000000 0C
Expected COMP-3 PIC S9(7)V99: should end in C, D, or F
This looks like X'000000000C' which is valid COMP-3 for +0.00
Field at offset 0034: C8C5D3D3 D6
This is 'HELLO' in EBCDIC -- clearly not valid COMP-3!
This is the cause of the S0C7.
20.6 IBM Debug Tool (IBM Debugger)
IBM Debug Tool (now part of IBM Developer for z/OS) is an interactive source-level debugger for COBOL, PL/I, C/C++, and Assembler programs running on z/OS. It allows you to set breakpoints, step through code line by line, examine and modify variable values, and watch variables for changes -- capabilities that transform debugging from dump analysis to interactive investigation.
Compiling for Debug Tool
To use Debug Tool, you must compile your program with the TEST compiler option:
//COBOL EXEC PGM=IGYCRCTL,
// PARM='TEST(ALL,SYM,SEPARATE),SOURCE,LIST'
The TEST option parameters: - ALL: Generate debug information for all statements - SYM: Include symbolic variable names (required for variable display) - SEPARATE: Store debug information in a separate debug side file
Launching Debug Tool
Debug Tool can be launched in several modes:
Batch mode (most common for batch COBOL programs):
//CEEOPTS DD *
TEST(ALL,*,PROMPT,INSPPREF%USERID:.DBGTOOL.PREFS:*)
/*
//INSPLOG DD SYSOUT=*
Full-screen mode (via 3270 terminal):
//CEEOPTS DD *
TEST(ALL,*,PROMPT,VTAM%userid:*)
/*
Common Debug Tool Commands
Once Debug Tool is active, you can use commands interactively:
AT 247 -- Set breakpoint at line 247
AT ENTRY 2000-PROCESS -- Break at paragraph entry
AT CALL -- Break at every CALL statement
GO -- Run until next breakpoint
STEP -- Execute one statement
STEP 5 -- Execute five statements
LIST WS-AMOUNT -- Display variable value
LIST WS-RECORD-AREA -- Display group item
LIST TITLED (WS-COUNTERS) -- Display with field names
LIST STORAGE(WS-AMOUNT) 8 -- Display 8 bytes of storage
MOVE 100 TO WS-AMOUNT -- Change a variable value
SET WS-EOF TO TRUE -- Set a condition name
AT CHANGE WS-AMOUNT DO -- Watch for variable change
LIST WS-AMOUNT
LIST TITLED (WS-COUNTERS)
END-AT
QUERY LOCATION -- Show current location
QUERY QUALIFY -- Show current qualification
CLEAR AT * -- Remove all breakpoints
Debug Tool with Batch Programs
For batch programs, Debug Tool is typically used with a commands file that automates common debugging steps:
//INSPCMD DD *
AT ENTRY 2000-PROCESS
AT 247
GO;
LIST TITLED (WS-INPUT-RECORD);
LIST WS-CUST-BALANCE;
GO;
/*
20.7 GnuCOBOL Debugging
GnuCOBOL provides several debugging mechanisms that parallel the mainframe tools, adapted for Linux/Unix/Windows environments.
Compile-Time Debug Options
# Compile with debugging enabled
cobc -x -debug program.cob
# Compile with all checks (bounds checking, etc.)
cobc -x -debug -Wall -fcheck program.cob
# Generate a listing file
cobc -x -t listing.txt program.cob
# Compile with GDB debugging symbols
cobc -x -g program.cob
Runtime Debug Environment Variables
GnuCOBOL supports several environment variables that control runtime debugging:
# Enable runtime debugging
export COB_SET_DEBUG=Y
# Enable runtime tracing (output to stderr)
export COB_SET_TRACE=Y
# Enable paragraph-level tracing
export COB_TRACE_FILE=trace.log
# Enable core dumps on error
export COB_CORE_ON_ERROR=1
# Set dump output file
export COB_DUMP_FILE=dump.txt
Using GDB with GnuCOBOL
Since GnuCOBOL compiles COBOL to C and then to native machine code, you can use the GNU Debugger (GDB) to debug COBOL programs. Compile with -g to include debug symbols:
# Compile with GDB symbols
cobc -x -g -fdebugging-line program.cob
# Launch GDB
gdb ./program
Common GDB commands for COBOL debugging:
(gdb) break program.cob:247 # Break at line 247
(gdb) break 2000-PROCESS # Break at paragraph
(gdb) run # Start the program
(gdb) next # Step to next line
(gdb) step # Step into subprogram
(gdb) print WS-AMOUNT # Display variable
(gdb) print WS-RECORD-AREA # Display group
(gdb) set WS-AMOUNT = 100 # Change variable
(gdb) backtrace # Show call stack
(gdb) continue # Run to next breakpoint
(gdb) info locals # Show local variables
(gdb) quit # Exit GDB
GnuCOBOL Runtime Tracing Example
IDENTIFICATION DIVISION.
PROGRAM-ID. TRACEDEM.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SOURCE-COMPUTER. PC WITH DEBUGGING MODE.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-COUNTER PIC 9(3) VALUE 0.
01 WS-TOTAL PIC 9(7) VALUE 0.
01 WS-INPUT PIC 9(5).
PROCEDURE DIVISION.
D DISPLAY 'DEBUG MODE IS ACTIVE'
0000-MAIN.
PERFORM 1000-PROCESS 5 TIMES
DISPLAY 'TOTAL: ' WS-TOTAL
STOP RUN
.
1000-PROCESS.
ADD 1 TO WS-COUNTER
D DISPLAY 'ITERATION: ' WS-COUNTER
MOVE WS-COUNTER TO WS-INPUT
COMPUTE WS-TOTAL = WS-TOTAL +
(WS-INPUT * WS-INPUT)
D DISPLAY 'RUNNING TOTAL: ' WS-TOTAL
.
Running with tracing:
export COB_SET_TRACE=Y
export COB_TRACE_FILE=trace.log
./tracedem
The trace log shows each paragraph entry and exit:
Source: 'TRACEDEM.cob'
Program-Id: TRACEDEM Entry: 0000-MAIN
Program-Id: TRACEDEM Section: (None)
Program-Id: TRACEDEM Paragraph: 0000-MAIN
Program-Id: TRACEDEM Paragraph: 1000-PROCESS
Program-Id: TRACEDEM Paragraph: 1000-PROCESS Exit
Program-Id: TRACEDEM Paragraph: 1000-PROCESS
...
20.8 Debugging COBOL-DB2 Programs
COBOL programs that use embedded SQL introduce additional debugging challenges. SQL errors produce SQLCODEs rather than abend codes, and the interaction between the COBOL program and the DB2 engine creates failure modes that do not exist in pure COBOL programs.
SQLCODE-Based Debugging
The first step in debugging any DB2 error is to capture and display the SQLCODE, SQLSTATE, and SQLERRMC fields from the SQLCA:
9100-SQL-ERROR.
DISPLAY '*** DB2 ERROR ***'
DISPLAY 'SQLCODE: ' SQLCODE
DISPLAY 'SQLSTATE: ' SQLSTATE
DISPLAY 'SQLERRMC: ' SQLERRMC(1:SQLERRML)
DISPLAY 'SQLERRD(1):' SQLERRD(1)
DISPLAY 'SQLERRD(2):' SQLERRD(2)
DISPLAY 'SQLERRD(3):' SQLERRD(3)
DISPLAY 'SQLERRD(4):' SQLERRD(4)
DISPLAY 'SQLERRD(5):' SQLERRD(5)
DISPLAY 'SQLERRD(6):' SQLERRD(6)
DISPLAY 'PARAGRAPH: ' WS-CURRENT-PARAGRAPH
.
Common DB2 Debugging Scenarios
SQLCODE -818: Timestamp Mismatch
This error means the program's DBRM (produced by the DB2 precompile) does not match the bound package. The program was recompiled but not rebound, or vice versa.
Fix: Rebind the package using the current DBRM.
SQLCODE -805: Package Not Found
The program references a package that does not exist. This usually means the program was never bound, or it was bound to a different collection.
Fix: Bind the package, or verify the PLAN/COLLECTION that the program is using.
SQLCODE -904: Unavailable Resource
DB2 cannot access a required resource (tablespace, index, or the DB2 subsystem itself). The resource may be stopped, in a pending state, or unavailable due to a utility running against it.
Fix: Check the DB2 resource status with the DB2 DISPLAY commands.
Using DSNTEP2 for SQL Testing
Before embedding complex SQL in a COBOL program, test it interactively using DSNTEP2 (DB2's batch SQL processor) or SPUFI (SQL Processor Using File Input):
//DSNTEP2 EXEC PGM=IKJEFT01,DYNAMNBR=20
//STEPLIB DD DSN=DSN.V13R1.SDSNLOAD,DISP=SHR
//SYSTSPRT DD SYSOUT=*
//SYSPRINT DD SYSOUT=*
//SYSUDUMP DD SYSOUT=*
//SYSTSIN DD *
DSN SYSTEM(DB2P)
RUN PROGRAM(DSNTEP2) PLAN(DSNTEP2)
/*
//SYSIN DD *
SELECT CUST_ID, CUST_NAME, CUST_BALANCE
FROM CUSTOMER
WHERE CUST_STATUS = 'A'
ORDER BY CUST_NAME
FETCH FIRST 10 ROWS ONLY;
/*
20.9 Unit Testing COBOL Programs
Unit testing -- the practice of writing automated tests that verify individual program components in isolation -- has been standard practice in modern software development for decades. In the COBOL world, unit testing adoption has been slower, but several frameworks now make it practical.
cobol-check: A Unit Testing Framework for COBOL
cobol-check is an open-source unit testing framework designed specifically for COBOL. It allows you to write test cases that are embedded alongside or adjacent to your COBOL source code.
Installing cobol-check:
cobol-check runs on the JVM and can be used with GnuCOBOL. It is downloaded from its GitHub repository.
Writing a test:
Test cases are written in a COBOL-like syntax within special test source files:
* File: DATEUTIL.cut (COBOL Under Test)
TESTSUITE 'DATE VALIDATION TESTS'
TESTCASE 'VALID DATE RETURNS SUCCESS'
MOVE '20250115' TO WS-INPUT-DATE
PERFORM 1000-VALIDATE-DATE
EXPECT WS-DATE-VALID TO BE TRUE
EXPECT WS-RETURN-CODE TO EQUAL 0
TESTCASE 'INVALID MONTH RETURNS ERROR'
MOVE '20251315' TO WS-INPUT-DATE
PERFORM 1000-VALIDATE-DATE
EXPECT WS-DATE-VALID TO BE FALSE
EXPECT WS-RETURN-CODE TO EQUAL 8
TESTCASE 'FEBRUARY 29 IN LEAP YEAR IS VALID'
MOVE '20240229' TO WS-INPUT-DATE
PERFORM 1000-VALIDATE-DATE
EXPECT WS-DATE-VALID TO BE TRUE
TESTCASE 'FEBRUARY 29 IN NON-LEAP YEAR IS INVALID'
MOVE '20250229' TO WS-INPUT-DATE
PERFORM 1000-VALIDATE-DATE
EXPECT WS-DATE-VALID TO BE FALSE
Running tests:
# Run cobol-check
java -jar cobol-check.jar -p DATEUTIL
# Output:
# TESTSUITE: DATE VALIDATION TESTS
# PASS: VALID DATE RETURNS SUCCESS
# PASS: INVALID MONTH RETURNS ERROR
# PASS: FEBRUARY 29 IN LEAP YEAR IS VALID
# PASS: FEBRUARY 29 IN NON-LEAP YEAR IS INVALID
# 4 TESTS, 4 PASSED, 0 FAILED
IBM zUnit
For IBM Enterprise COBOL on z/OS, IBM provides zUnit (part of IBM Developer for z/OS), which allows writing and running unit tests for COBOL programs in a mainframe environment.
zUnit tests are written in COBOL and use IBM-provided copybooks for assertions:
IDENTIFICATION DIVISION.
PROGRAM-ID. TCALCPAY.
* zUnit test case for CALCPAY subprogram
DATA DIVISION.
WORKING-STORAGE SECTION.
COPY AZUNIT.
01 WS-HOURS PIC 9(3)V9 VALUE 40.0.
01 WS-RATE PIC 9(3)V99 VALUE 25.00.
01 WS-GROSS-PAY PIC 9(5)V99 VALUE 0.
01 WS-EXPECTED-PAY PIC 9(5)V99 VALUE 1000.00.
PROCEDURE DIVISION.
MOVE 'TEST REGULAR HOURS' TO AZ-TEST-NAME
CALL 'CALCPAY' USING WS-HOURS
WS-RATE
WS-GROSS-PAY
END-CALL
IF WS-GROSS-PAY = WS-EXPECTED-PAY
MOVE 'PASS' TO AZ-TEST-RESULT
ELSE
MOVE 'FAIL' TO AZ-TEST-RESULT
STRING 'EXPECTED ' WS-EXPECTED-PAY
' GOT ' WS-GROSS-PAY
DELIMITED BY SIZE
INTO AZ-TEST-MESSAGE
END-STRING
END-IF
CALL 'AZUNIT' USING AZ-TEST-RECORD
GOBACK
.
Test Driver Approach
Even without a formal testing framework, you can create simple test driver programs that call your subprograms with known inputs and verify the outputs:
IDENTIFICATION DIVISION.
PROGRAM-ID. TESTDRV.
*========================================================*
* TEST DRIVER FOR VALIDATECUST SUBPROGRAM *
*========================================================*
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-TEST-COUNT PIC 99 VALUE 0.
01 WS-PASS-COUNT PIC 99 VALUE 0.
01 WS-FAIL-COUNT PIC 99 VALUE 0.
01 WS-CUSTOMER-DATA.
05 WS-CUST-ID PIC X(10).
05 WS-CUST-NAME PIC X(30).
05 WS-CUST-BAL PIC S9(7)V99 COMP-3.
01 WS-EXPECTED-RC PIC S9(4) COMP.
01 WS-ACTUAL-RC PIC S9(4) COMP.
PROCEDURE DIVISION.
0000-MAIN.
DISPLAY '===== VALIDATECUST TEST SUITE ====='
* TEST 1: Valid customer
MOVE 'CUST000001' TO WS-CUST-ID
MOVE 'JOHN DOE' TO WS-CUST-NAME
MOVE 1000.00 TO WS-CUST-BAL
MOVE 0 TO WS-EXPECTED-RC
PERFORM 8000-RUN-TEST
* TEST 2: Blank customer ID
MOVE SPACES TO WS-CUST-ID
MOVE 'JANE DOE' TO WS-CUST-NAME
MOVE 500.00 TO WS-CUST-BAL
MOVE 8 TO WS-EXPECTED-RC
PERFORM 8000-RUN-TEST
* TEST 3: Negative balance (warning)
MOVE 'CUST000003' TO WS-CUST-ID
MOVE 'BOB SMITH' TO WS-CUST-NAME
MOVE -100.00 TO WS-CUST-BAL
MOVE 4 TO WS-EXPECTED-RC
PERFORM 8000-RUN-TEST
DISPLAY '============================='
DISPLAY 'TOTAL: ' WS-TEST-COUNT
' PASS: ' WS-PASS-COUNT
' FAIL: ' WS-FAIL-COUNT
DISPLAY '============================='
IF WS-FAIL-COUNT > 0
MOVE 8 TO RETURN-CODE
ELSE
MOVE 0 TO RETURN-CODE
END-IF
STOP RUN
.
8000-RUN-TEST.
ADD 1 TO WS-TEST-COUNT
DISPLAY 'TEST ' WS-TEST-COUNT ':'
CALL 'VALIDATECUST' USING WS-CUSTOMER-DATA
MOVE RETURN-CODE TO WS-ACTUAL-RC
IF WS-ACTUAL-RC = WS-EXPECTED-RC
ADD 1 TO WS-PASS-COUNT
DISPLAY ' PASS (RC=' WS-ACTUAL-RC ')'
ELSE
ADD 1 TO WS-FAIL-COUNT
DISPLAY ' FAIL - EXPECTED RC='
WS-EXPECTED-RC
' GOT RC=' WS-ACTUAL-RC
END-IF
.
20.9 Debugging Common Problems
Beyond the abend codes covered in Section 20.4, several categories of bugs recur frequently in COBOL programs. This section covers diagnosis and resolution of these common problems.
Problem: Wrong Results (No Abend)
The program completes successfully (return code 0) but produces incorrect output. These bugs are often harder to find than abends because there is no dump, no abend code, and no obvious failure point.
Diagnostic approach:
* Add checkpoints to trace data flow
2000-CALCULATE-INTEREST.
DISPLAY 'CHECKPOINT 1: PRINCIPAL='
WS-PRINCIPAL
' RATE=' WS-RATE
' DAYS=' WS-DAYS
COMPUTE WS-DAILY-RATE =
WS-RATE / 365
DISPLAY 'CHECKPOINT 2: DAILY-RATE='
WS-DAILY-RATE
COMPUTE WS-INTEREST =
WS-PRINCIPAL * WS-DAILY-RATE * WS-DAYS
DISPLAY 'CHECKPOINT 3: INTEREST='
WS-INTEREST
.
Common causes:
- Truncation: A receiving field is too small for the result.
- Missing ROUNDED: Decimal truncation instead of rounding.
- Wrong sign: Using unsigned PIC 9 when PIC S9 is needed.
- Implicit decimal alignment: Moving PIC 9(5)V99 to PIC 9(7) loses the decimal places.
- REDEFINES confusion: Reading one format, processing as another.
Problem: Records Missing from Output
Diagnostic approach:
* Add counters at every decision point
01 WS-DIAG-COUNTERS.
05 WS-TOTAL-READ PIC 9(7) VALUE 0.
05 WS-PASSED-EDIT PIC 9(7) VALUE 0.
05 WS-FAILED-EDIT PIC 9(7) VALUE 0.
05 WS-WRITTEN PIC 9(7) VALUE 0.
05 WS-SKIPPED PIC 9(7) VALUE 0.
3000-TERMINATE.
DISPLAY 'TOTAL READ: ' WS-TOTAL-READ
DISPLAY 'PASSED EDIT: ' WS-PASSED-EDIT
DISPLAY 'FAILED EDIT: ' WS-FAILED-EDIT
DISPLAY 'WRITTEN: ' WS-WRITTEN
DISPLAY 'SKIPPED: ' WS-SKIPPED
DISPLAY 'BALANCE CHECK: '
COMPUTE WS-BALANCE =
WS-TOTAL-READ -
WS-PASSED-EDIT - WS-FAILED-EDIT
IF WS-BALANCE NOT = 0
DISPLAY '*** RECORDS UNACCOUNTED FOR: '
WS-BALANCE ' ***'
END-IF
.
Problem: Infinite Loop (S322)
Diagnostic approach:
* Add a safety counter and periodic progress display
01 WS-LOOP-DIAG.
05 WS-ITERATION PIC 9(9) COMP VALUE 0.
05 WS-LAST-KEY PIC X(10) VALUE SPACES.
05 WS-DISPLAY-FREQ PIC 9(5) COMP VALUE 10000.
2000-PROCESS-LOOP.
PERFORM UNTIL WS-DONE
ADD 1 TO WS-ITERATION
IF FUNCTION MOD(WS-ITERATION,
WS-DISPLAY-FREQ) = 0
DISPLAY 'PROGRESS: ITERATION '
WS-ITERATION
' CURRENT KEY: ' WS-CURRENT-KEY
' LAST KEY: ' WS-LAST-KEY
END-IF
IF WS-CURRENT-KEY = WS-LAST-KEY
DISPLAY '*** KEY NOT ADVANCING: '
WS-CURRENT-KEY
DISPLAY '*** POSSIBLE INFINITE LOOP'
SET WS-DONE TO TRUE
END-IF
MOVE WS-CURRENT-KEY TO WS-LAST-KEY
PERFORM 2100-PROCESS-RECORD
PERFORM 2200-READ-NEXT
END-PERFORM
.
Problem: File I/O Errors After OPEN
* Comprehensive file open diagnostic
1100-OPEN-INPUT.
DISPLAY 'OPENING INPUT FILE...'
OPEN INPUT INPUT-FILE
DISPLAY 'OPEN STATUS: ' WS-INPUT-STATUS
EVALUATE WS-INPUT-STATUS
WHEN '00'
DISPLAY 'FILE OPENED SUCCESSFULLY'
WHEN '05'
DISPLAY 'WARNING: OPTIONAL FILE NOT PRESENT'
WHEN '35'
DISPLAY 'ERROR: FILE DOES NOT EXIST'
DISPLAY 'CHECK DD STATEMENT IN JCL'
SET WS-ABORT TO TRUE
WHEN '37'
DISPLAY 'ERROR: FILE DOES NOT SUPPORT '
'REQUESTED OPEN MODE'
SET WS-ABORT TO TRUE
WHEN '39'
DISPLAY 'ERROR: FILE ATTRIBUTES MISMATCH'
DISPLAY 'CHECK RECORD LENGTH AND FORMAT'
SET WS-ABORT TO TRUE
WHEN '41'
DISPLAY 'ERROR: FILE ALREADY OPEN'
SET WS-ABORT TO TRUE
WHEN '96'
DISPLAY 'ERROR: DD STATEMENT MISSING'
SET WS-ABORT TO TRUE
WHEN OTHER
DISPLAY 'UNEXPECTED STATUS: '
WS-INPUT-STATUS
SET WS-ABORT TO TRUE
END-EVALUATE
.
20.10 Debugging CICS Programs
CICS programs present unique debugging challenges because they run inside the CICS region, share memory with other tasks, and use pseudo-conversational programming where each user interaction creates a new task.
CEDF: The CICS Execution Diagnostic Facility
CEDF (pronounced "sed-f") is an interactive debugging tool built into CICS. It intercepts every EXEC CICS command and shows the programmer what the program is doing, step by step.
To activate CEDF for a terminal, type the transaction code CEDF and press Enter. Then enter your application's transaction code. CEDF will intercept every EXEC CICS command and display the command, its options, and the result:
TRANSACTION CUSTINQ - EIB INFORMATION
EIBTIME = 143025 EIBDATE = 0125015
EIBTRNID = INQC EIBTASKN = 00358
EIBTRMID = S027 EIBCPOSN = 00399
EIBCALEN = 00012 EIBAID = ENTER
ABOUT TO EXECUTE COMMAND:
EXEC CICS READ
FILE('CUSTFILE')
INTO(........)
RIDFLD(X'0000004D3B')
LENGTH(51)
RESP(........)
RESP2(........)
PRESS ENTER TO CONTINUE
After the command executes, CEDF shows the results:
COMMAND EXECUTION COMPLETE:
EXEC CICS READ
FILE('CUSTFILE')
INTO(........)
RIDFLD(X'0000004D3B')
LENGTH(51)
RESP(NORMAL)
RESP2(+0000000000)
PRESS ENTER TO CONTINUE
CEDF allows you to modify data values before a command executes, which is useful for testing error conditions without having to create test data.
Temporary Storage Queue Debugging
A useful debugging technique in CICS is to write diagnostic information to a Temporary Storage Queue (TSQ) that can be browsed after the transaction completes:
9000-DEBUG-LOG.
STRING EIBTRNID EIBTRMID
' ' WS-DEBUG-MESSAGE
DELIMITED BY SIZE
INTO WS-DEBUG-RECORD
END-STRING
EXEC CICS WRITEQ TS
QUEUE(WS-DEBUG-QUEUE)
FROM(WS-DEBUG-RECORD)
LENGTH(LENGTH OF WS-DEBUG-RECORD)
RESP(WS-CICS-RESP)
END-EXEC
.
The TSQ can then be browsed using the CEBR transaction (CICS Extended Browse), which displays the contents of any TSQ.
CICS Transaction Dump
When a CICS application abends, CICS produces a transaction dump. Unlike a batch dump, a CICS dump includes the COMMAREA contents, the EIB, and the terminal buffer in addition to WORKING-STORAGE. The dump is written to the CICS dump dataset and can be viewed using the CICS-supplied transaction CDMP or through the IPCS utility.
20.11 Prevention Through Defensive Programming
The best debugging technique is to prevent bugs from occurring in the first place. This section summarizes defensive programming practices that eliminate the most common categories of COBOL bugs.
Always Initialize Variables
* GOOD: Explicit initialization
01 WS-TOTAL PIC S9(7)V99 COMP-3 VALUE 0.
01 WS-COUNT PIC 9(5) COMP VALUE 0.
01 WS-FLAG PIC 9 VALUE 0.
* ALSO GOOD: Initialize at start of processing
1000-INITIALIZE.
INITIALIZE WS-WORK-FIELDS
INITIALIZE WS-COUNTERS
INITIALIZE WS-ACCUMULATORS
.
Always Check File Status
* After EVERY I/O operation:
OPEN INPUT MASTER-FILE
IF WS-MASTER-STATUS NOT = '00'
PERFORM 9100-FILE-ERROR
END-IF
READ MASTER-FILE INTO WS-MASTER-REC
EVALUATE WS-MASTER-STATUS
WHEN '00' CONTINUE
WHEN '10' SET WS-EOF TO TRUE
WHEN OTHER PERFORM 9100-FILE-ERROR
END-EVALUATE
Always Validate Before Using Numeric Data
IF WS-INPUT-AMOUNT IS NUMERIC
MOVE WS-INPUT-AMOUNT TO WS-WORK-AMOUNT
ADD WS-WORK-AMOUNT TO WS-TOTAL
ELSE
DISPLAY 'NON-NUMERIC: ' WS-INPUT-AMOUNT
ADD 1 TO WS-ERROR-COUNT
END-IF
Always Use Explicit Scope Terminators
* GOOD: Explicit scope terminators
READ INPUT-FILE
AT END
SET WS-EOF TO TRUE
NOT AT END
ADD 1 TO WS-COUNT
END-READ
* BAD: Period-terminated, ambiguous
READ INPUT-FILE AT END SET WS-EOF TO TRUE.
Always Protect Table Access
* GOOD: Bounds checking
IF WS-INDEX >= 1 AND WS-INDEX <= WS-TABLE-MAX
MOVE WS-DATA TO WS-TABLE-ENTRY(WS-INDEX)
ELSE
DISPLAY 'INDEX OUT OF RANGE: ' WS-INDEX
END-IF
Always Use ON SIZE ERROR for Arithmetic
COMPUTE WS-RESULT = WS-A * WS-B / WS-C
ON SIZE ERROR
DISPLAY 'ARITHMETIC ERROR'
MOVE 0 TO WS-RESULT
END-COMPUTE
Use EVALUATE Instead of Nested IF
* GOOD: Clear, maintainable
EVALUATE WS-TRANS-CODE
WHEN 'A' PERFORM 3100-ADD
WHEN 'C' PERFORM 3200-CHANGE
WHEN 'D' PERFORM 3300-DELETE
WHEN OTHER PERFORM 3900-INVALID-CODE
END-EVALUATE
* BAD: Nested IF, hard to follow, easy to misalign
IF WS-TRANS-CODE = 'A'
PERFORM 3100-ADD
ELSE
IF WS-TRANS-CODE = 'C'
PERFORM 3200-CHANGE
ELSE
IF WS-TRANS-CODE = 'D'
PERFORM 3300-DELETE
END-IF
END-IF
END-IF
20.12 Memory Analysis and Storage Dumps
When more advanced debugging is required, understanding how COBOL data is laid out in memory is essential. This section covers techniques for analyzing storage content in both batch and CICS environments.
Understanding COBOL Storage Layout
COBOL programs allocate storage in several areas, each with different characteristics:
| Area | Contents | Scope |
|---|---|---|
| WORKING-STORAGE | All WS variables, initialized at program load | Static, persists between CALLs (unless CANCELed) |
| LOCAL-STORAGE | All LS variables, initialized at each invocation | Dynamic, fresh copy each time |
| LINKAGE SECTION | Parameters passed from caller | Mapped to caller's storage |
| FILE SECTION | File buffers | Managed by runtime |
In a dump, WORKING-STORAGE is a contiguous block of memory. The DATA MAP from the compiler listing gives you the offset of each variable within this block. By adding the offset to the starting address of WORKING-STORAGE (shown in the dump or traceback), you can locate any variable.
Interpreting EBCDIC and Packed Decimal in Dumps
On IBM mainframes, character data is stored in EBCDIC, not ASCII. Common character values in EBCDIC:
| Character | EBCDIC Hex |
|---|---|
| Space | X'40' |
| 0-9 | X'F0' - X'F9' |
| A-I | X'C1' - X'C9' |
| J-R | X'D1' - X'D9' |
| S-Z | X'E2' - X'E9' |
| LOW-VALUES | X'00' |
| HIGH-VALUES | X'FF' |
Packed decimal (COMP-3) stores two digits per byte, with the sign in the low nibble of the last byte:
PIC S9(5)V99 COMP-3 VALUE +12345.67
Storage: 01 23 45 67 0C (C = positive)
PIC S9(5)V99 COMP-3 VALUE -12345.67
Storage: 01 23 45 67 0D (D = negative)
PIC S9(5)V99 COMP-3 VALUE 0
Storage: 00 00 00 00 0C
Invalid packed decimal data (which causes S0C7) looks like character data in the packed field:
Expected: 01 23 45 67 0C
Actual: C1 C2 C3 C4 C5 (This is 'ABCDE' in EBCDIC!)
The sign nibble 5 is not valid (must be C, D, or F).
Using IPCS for Dump Analysis
IPCS (Interactive Problem Control System) is the z/OS tool for analyzing dumps. Key IPCS commands for COBOL debugging:
VERBEXIT LEDATA - Display LE runtime information
VERBEXIT CEDATA - Display COBOL-specific data
VERBX LEDATA 'ALL' - Complete LE diagnostic output
VERBX LEDATA 'CEEDUMP' - Formatted COBOL variable display
LIST address LENGTH(n) - Display n bytes at address
The LEDATA verb exit is particularly useful because it can display COBOL variables by name, showing both the formatted value and the hexadecimal content.
20.13 Debugging Checklist
When a COBOL program fails, follow this systematic checklist:
- Read the abend code: What type of error occurred? (S0C7, S0C4, S0C1, S322, etc.)
- Read the LE traceback: Which paragraph and line number did it fail at?
- Check the compiler listing: What statement is at that line? What variables are involved?
- Check the DATA MAP: What are the offsets and types of those variables?
- Check the dump data: What are the hexadecimal contents of those variables? Are they valid for the declared type?
- Trace the data flow: Where did those variables get their values? Follow the XREF backwards.
- Check the input data: Is the input file or parameter data valid?
- Check the JCL: Are all DD statements present? Are file attributes correct?
- Check recent changes: What was changed since the program last worked?
- Reproduce locally: Can you reproduce the error with a subset of the data?
Summary
Debugging COBOL programs requires a combination of tools, techniques, and systematic thinking:
- DISPLAY-based debugging is universally available and effective for tracing program flow and data values. Strategic placement at decision points, I/O operations, and loop boundaries makes it productive.
- D in column 7 provides compile-time toggling of debug statements without editing them out of the source code.
- Compiler listings (DATA MAP, XREF, OFFSET) are essential for dump analysis, providing the mapping between hexadecimal addresses and COBOL source statements and data items.
- Common abend codes (S0C7, S0C4, S0C1, S0CB, S322, S806) each have specific causes, diagnostic procedures, and prevention techniques. Knowing these codes from memory is a basic professional requirement.
- Dump analysis follows a systematic procedure: identify the abend code, extract the PSW address, calculate the offset, look up the COBOL statement, and examine the data values in hex.
- IBM Debug Tool provides interactive source-level debugging on z/OS with breakpoints, stepping, variable inspection, and variable modification.
- GnuCOBOL debugging uses compile options (-debug, -g), environment variables (COB_SET_TRACE, COB_SET_DEBUG), and GDB for interactive debugging on open platforms.
- Unit testing with frameworks like cobol-check and zUnit enables proactive defect prevention through automated, repeatable tests.
- Defensive programming -- initializing variables, checking file status, validating data, protecting array bounds, using scope terminators, and handling arithmetic overflow -- prevents the majority of production bugs.
The most effective debugging strategy is prevention. Every defensive programming technique you apply during development is a production abend you will never have to debug at 3:00 AM.
20.14 Debugging Batch vs. Online Programs: Key Differences
The debugging approach differs significantly between batch and online (CICS) programs. Understanding these differences is essential for programmers who work in both environments.
Batch Program Debugging
Batch programs run in their own address space, have exclusive access to their files (in most cases), and produce output to SYSOUT that you can review after the job completes. Debugging batch programs follows a straightforward cycle:
- Review the job output (SYSOUT listings, DISPLAY output)
- If the program abended, analyze the dump using the compiler listing
- Fix the source code
- Recompile, relink
- Resubmit the job
- Repeat until the bug is resolved
The turnaround time for each iteration is typically minutes (time to compile, link, and run).
Online Program Debugging
CICS programs share an address space with hundreds of other programs and thousands of concurrent users. You cannot simply add DISPLAY statements (DISPLAY is not available in CICS). Instead, you must use CEDF (the CICS Execution Diagnostic Facility), IBM Debug Tool in CICS mode, or write diagnostic information to TSQs or TDQs.
The pseudo-conversational model adds another challenge: each user interaction creates a new task, so you must trace the state across multiple task instances through the COMMAREA.
Key Differences Summary
| Aspect | Batch | CICS |
|---|---|---|
| Output | SYSOUT (DISPLAY) | TSQ/TDQ, CEDF, Debug Tool |
| Dump | SYSUDUMP/SYSABEND | Transaction dump |
| Compile cycle | Minutes | Minutes + CICS NEWCOPY |
| State | Continuous execution | Pseudo-conversational (state in COMMAREA) |
| Concurrency | Usually single-threaded | Multi-user, shared resources |
| Error handling | File status, RETURN-CODE | RESP/RESP2, EIB |
CICS NEWCOPY
After recompiling a CICS program, you must tell CICS to load the new version. This is done with the CEMT transaction:
CEMT SET PROGRAM(CUSTINQ) NEWCOPY
Alternatively, in CICS TS 5.x and later, PHASEIN can be used to gradually transition to the new copy without disrupting active tasks:
CEMT SET PROGRAM(CUSTINQ) PHASEIN
20.15 Systematic Debugging Methodology
Effective debugging is not random exploration -- it is a systematic process. The following methodology applies to all types of COBOL debugging, whether batch, CICS, or DB2.
Step 1: Reproduce
Before attempting to fix a bug, reproduce it. If you cannot reproduce the problem, you cannot verify that your fix works. For batch programs, this means running the program with the same input data that caused the failure. For CICS programs, this means entering the same sequence of inputs on the same screen.
Step 2: Isolate
Narrow down the location of the bug. Use binary search strategy: if the program has 5,000 lines, determine whether the bug is in the first half or the second half. Then narrow further. For batch programs, add DISPLAY statements at strategic points. For CICS programs, use CEDF or TSQ logging.
Step 3: Understand
Once you know where the bug is, understand why it occurs. Do not just look at the symptom -- understand the root cause. An S0C7 is a symptom; the root cause might be a missing validation three paragraphs earlier.
Step 4: Fix
Fix the root cause, not the symptom. If the root cause is missing input validation, add the validation. Do not simply initialize the variable to zero and hope for the best.
Step 5: Verify
Verify that the fix works by reproducing the original problem and confirming it no longer occurs. Also verify that the fix does not break anything else by running regression tests.
Step 6: Prevent
After fixing the bug, consider how to prevent similar bugs in the future. Should you add a coding standard? Should you add a validation routine? Should you create a unit test? The best debugging is the debugging you never have to do because you prevented the bug from being written in the first place.
Exercises
-
DISPLAY Debugging: Take a program you have written in a previous chapter and add strategic DISPLAY statements to trace the processing of the first five records. Run the program and verify that the output matches your expectations.
-
Compiler Listing Analysis: Compile one of your programs with the MAP and XREF options. Find the offset and length of every variable in WORKING-STORAGE. Identify any variables that are defined but never referenced.
-
Abend Diagnosis: Given the following dump information, identify the failing COBOL statement and the likely cause: - Abend code: S0C7 - PSW address: X'0001A4B6' - Program entry point: X'0001A000' - OFFSET listing shows line 185 at offset X'4B0' (COMPUTE statement) - DATA MAP shows WS-QUANTITY at offset X'0048', PIC S9(5) COMP-3 - Dump shows offset X'0048' contains: X'C1C2C3'
-
Test Driver: Write a test driver program for the file status checking routine from Chapter 16. The test driver should test at least five different scenarios (successful operation, end of file, record not found, duplicate key, and file not open).
-
GnuCOBOL Debugging: Compile a program with
-gand use GDB to set a breakpoint at a paragraph, step through five statements, and display the values of three variables at each step. Document the GDB commands you used and the output you observed.