Diagnosing Non-Terminating PERFORM VARYING Loops in Enterprise COBOL for z/OS
A step-by-step diagnostic guide for COBOL developers when a PERFORM VARYING loop consumes CPU without terminating, covering syntax checks, data definitions, and escalation criteria for IBM Enterprise COBOL V6.3.
27 Sept 2026, 11:44 UTC

Recognizable condition
A COBOL batch job that locks a CPU, writes no further output, and eventually exceeds its time limit is often stuck in a PERFORM VARYING loop whose termination condition never becomes true. The useful takeaway: before you escalate to IBM support, verify that the loop index actually changes, that its data definition cannot overflow, and that no flow-control statement bypasses the implicit increment.
In IBM Enterprise COBOL for z/OS V6.3, PERFORM VARYING initializes an index, increments or decrements it by a specified amount each iteration, and tests a condition before or after the loop body. When the index stops moving toward the boundary, the generated branch always falls through to the loop start and the address space spins.
Cause and diagnostic table
| Suspected cause | What to look for |
|---|---|
| Index variable not modified | The VARYING or AFTER clause is missing, or the identifier is redefined so the compiler updates a different storage location. |
| Index overflow | The index is defined with a small binary picture such as PIC S9(4) COMP-5; when the value exceeds the field capacity it wraps, so the UNTIL comparison never succeeds. |
| AFTER condition never true | The UNTIL test references a constant, a literal, or a variable that is never changed inside the loop. |
| Flow-control statements bypass the update | A GO TO, EXIT PERFORM, or conditional branch inside the loop jumps past the implicit increment. |
| Compiler optimization defect | Syntax and data definitions are correct, Debug Tool shows the index changing, yet the generated code does not exit. |
Ordered checks
- Examine the
PERFORM VARYINGstatement syntax. Verify that every loop index is named in aVARYINGorAFTERclause and that theBYphrase is present and non-zero. In nested loops, confirm eachAFTERidentifier is distinct and updated by its ownBYvalue. - Check the data definition of the index variable. Look at the
PICclause andUSAGE. A signed binary field such asPIC S9(4) COMP-5occupies two bytes and can hold only up to 9999; if the loop limit is larger, the index may wrap to a negative value and theUNTILcondition will never be met. - Evaluate the
AFTERandUNTILcondition values. Ensure the relational test compares the index against a variable or expression that can actually change. A test such asUNTIL WS-COUNT > 0whereWS-COUNTis initialized to zero and never modified will loop forever. - Inspect the loop body for flow-control statements. Search for
GO TO,EXIT PERFORM,STOP RUN, or conditional branches that might skip the implicit increment. AnEXIT PERFORMinside a conditionalIFcan terminate early, but aGO TOthat targets a label before the loop update can create an infinite cycle. - Compile with the
LISToption and review the generated assembly. Run the compile step withLISTand examine the generated code around the loop branch. Look for a conditional branch that always falls through to the loop start. Where to run: the compile step on z/OS, typically in a batch job or through TSO/ISPF. Required permissions: access to the compiler data sets and the source library. Risk: listing output can be large; direct it to a temporary data set to avoid filling spool space.
Fixes tied to findings
- If the index is not updated: Correct the
VARYINGorAFTERclause so the identifier is incremented or decremented by the intended amount. For example, add a missingBY 1or ensure theAFTERvariable is not accidentally aliased to the same storage as the outer index. - If the picture clause allows overflow: Enlarge the index field or change its usage. Moving from
PIC S9(4) COMP-5toPIC S9(9) COMPorPIC S9(7) COMP-3prevents wrap-around for larger limits. Caution: verify that the new size does not break record layouts, copybook mappings, or database column definitions. - If the
AFTERcondition is constant: Replace the literal or unmodified variable with an expression that reflects loop progress. For example, changeUNTIL WS-IDX > 1000whereWS-IDXis never incremented to a properly varying index. - If unintended
GO TOstatements exist: Remove theGO TOand restructure the logic with structured verbs such asIF/ELSEorEVALUATE. If an early exit is required, useEXIT PERFORMrather than jumping backward. - If a compiler defect is suspected: Apply the latest IBM Enterprise COBOL PTF for V6.3, recompile, and retest. Document the compiler version, PTF level, and the failing source for the support ticket.
Concrete example
The following fragment illustrates a nested loop where the inner AFTER condition tests against a constant boundary that can be bypassed by flow control:
01 WS-OUTER PIC S9(4) COMP-5 VALUE 0.
01 WS-INNER PIC S9(4) COMP-5 VALUE 0.
01 WS-LIMIT PIC S9(4) VALUE 100.
PERFORM VARYING WS-OUTER FROM 1 BY 1
UNTIL WS-OUTER > WS-LIMIT
AFTER WS-INNER FROM 1 BY 1
UNTIL WS-INNER > 0
DISPLAY 'Outer=' WS-OUTER ' Inner=' WS-INNER
END-PERFORM.
If a GO TO inside the body resets WS-INNER or if the UNTIL test is changed to a constant that is never reached, the outer loop can spin indefinitely. The corrected version ensures each AFTER index moves toward a changing boundary:
PERFORM VARYING WS-OUTER FROM 1 BY 1
UNTIL WS-OUTER > WS-LIMIT
AFTER WS-INNER FROM 1 BY 1
UNTIL WS-INNER > WS-LIMIT
DISPLAY 'Outer=' WS-OUTER ' Inner=' WS-INNER
END-PERFORM.
Note: This example is illustrative and has not been executed; validate against your own compiler and data definitions.
Practical verification
To confirm the diagnosis without waiting for a production timeout, build a small test case. Compile a standalone program with Enterprise COBOL V6.3 using default options, run it, and observe the hang. Then apply the fix—correct the UNTIL condition or enlarge the index—and rerun. The loop should terminate and produce the expected number of iterations. Alternatively, use IBM Debug Tool to set a breakpoint at the PERFORM statement, step through the first few iterations, and watch the index variable change. If the index does not change, the defect is in the PERFORM clause or the data definition.
Limitations and version assumptions
This guidance targets IBM Enterprise COBOL for z/OS V6.3. Older compilers, such as IBM COBOL for MVS, may handle COMP-5 indexes and optimization differently. Always confirm the behavior on your specific compiler level and PTF. Changes to numeric data definitions must be checked against existing file layouts, copybooks, and database mappings to avoid silent data corruption.
Escalation criteria
Engage IBM support when:
- The
PERFORM VARYINGsyntax is correct and the index variable demonstrably changes in a Debug Tool session, yet the loop still does not terminate. - The issue persists after applying the latest PTF and appears in multiple programs, suggesting an environment or compiler defect rather than a source-code error.
- The generated assembly from the
LISToutput shows a branch structure that contradicts the source logic.
Before escalating, capture the source member, the LIST output, the compiler version and PTF level, the job log, and a Debug Tool trace of the first several iterations.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.