MODULE 13 · MAINFRAME INTERNALS AND CAPSTONE · 5/10

Data exceptions and S0C7

18 min40 XPExercise

On z/OS, the arithmetic behind ADD, COMPUTE and IF A > B on packed and zoned fields runs as decimal hardware instructions (AP, SP, MP, ZAP, CP...). Before they calculate, they check every nibble: digits must be 0–9 and the sign must be a valid sign. If not, the CPU raises a data exception, program interruption code 7, and the step ends with the most famous abend on the mainframe:

CEE3207S The system detected a data exception (System Completion Code=0C7).
IEF450I PAYJOB STEP020 - ABEND=S0C7 U0000 REASON=00000007

It is the first abend every mainframe developer learns to fix.

Where bad data comes from

  • Spaces in numeric fields. MOVE SPACES TO CUST-REC fills packed fields with X'40'. An empty input column is spaces too.
  • Uninitialised storage. A WORKING-STORAGE field without a VALUE clause holds whatever was there, often X'00' (low-values), which has no sign nibble.
  • Wrong layout. A copybook out of step with the file, so the program reads text bytes as a packed field.
  • Using the record after end of file, or after a failed READ.
  • Upstream garbage. Another system sent letters in a number column.

GnuCOBOL doesn't abend

GnuCOBOL does not check nibbles by default. It computes something and carries on. Run this:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. SOC7DEMO.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-REC.
           05  WS-NAME      PIC X(10).
           05  WS-BALANCE   PIC S9(5)V99 COMP-3.
       01  WS-TOTAL         PIC S9(7)V99 COMP-3 VALUE 0.
       PROCEDURE DIVISION.
           MOVE SPACES TO WS-REC
           DISPLAY "BALANCE BYTES X'" FUNCTION HEX-OF(WS-BALANCE) "'"
           IF WS-BALANCE IS NUMERIC
               DISPLAY "BALANCE IS NUMERIC"
           ELSE
               DISPLAY "BALANCE IS NOT NUMERIC"
           END-IF
           ADD WS-BALANCE TO WS-TOTAL
           DISPLAY "TOTAL AFTER ADD: " WS-TOTAL
           INITIALIZE WS-REC
           DISPLAY "AFTER INITIALIZE X'" FUNCTION HEX-OF(WS-BALANCE) "'"
           STOP RUN.
BALANCE BYTES X'20202020'
BALANCE IS NOT NUMERIC
TOTAL AFTER ADD: +0202020.20
AFTER INITIALIZE X'0000000C'

On z/OS the ADD would be S0C7. Here you get a nonsense total of 202,020.20, which is arguably worse: a wrong number in a report nobody questions. (Spaces are X'20' in ASCII, X'40' in EBCDIC.) GnuCOBOL can check at run time if you compile with cobc -debug; then the ADD stops with 'WS-BALANCE' (Type: PACKED-DECIMAL) not numeric.

Defending against it

  1. Validate input before arithmetic. The class test IF field IS NUMERIC works on zoned and packed fields and checks every nibble. Don't touch a number from a file until it passes.
  2. Initialise. Give every numeric working-storage field a VALUE, and clear records with INITIALIZE, not MOVE SPACES.
  3. Reject, don't guess. Write bad records to an error file with a reason, count them, and set a return code so the job and the operators know.

On the job

To fix a S0C7: find the failing statement from the dump (see the abends lesson), identify which field in it holds bad data, then look at that field's bytes in the input dataset in HEX. The fix is almost never "make the abend go away". It is either correcting the data, correcting the layout, or adding the validation that should have been there.

Your task

The branch sales feed sometimes contains damaged records. On z/OS the first bad one would abend the job with S0C7. Here, GnuCOBOL just computes garbage: run the starter and it reports a total of 21,407.02. Make the program defend itself.

Input, DD name TXNIN, ORGANIZATION IS SEQUENTIAL, 16-byte records (open it under Datasets and press HEX to see the damage):

Field Picture Bytes
TXN-ID X(6) 1–6
TXN-QTY 9(3) 7–9
TXN-PRICE S9(5)V99 COMP-3 10–13
TXN-BRANCH X(3) 14–16

For each record:

  • If TXN-QTY is not numeric, write a reject line with reason QTY NOT NUMERIC.
  • Otherwise, if TXN-PRICE is not numeric, reject it with PRICE NOT NUMERIC.
  • Otherwise add TXN-QTY * TXN-PRICE to the total.

Rejects, DD name REJECTS, line sequential: the transaction id, a space and the reason (REJ-REC and WRITE-REJECT are in the starter; move the reason to WS-REASON first):

T00003 QTY NOT NUMERIC

Keep the summary the starter displays, and end with return code 4 if any record was rejected, 0 otherwise.

Check your understanding

1. What causes a S0C7 abend?
2. A record is cleared with MOVE SPACES TO CUST-REC, then ADD 1 TO CUST-VISITS (a COMP-3 field) runs. What happens on z/OS?
fixed format
Run your program to see its output here. The first visible test's input and datasets are used.
Submit to grade your program against every test.