CICS Programming Best Practices

ASRA Abend Code

In discussing aspects of CICS Command level Programming, have you ever wondered why a Program Interrupt (S0C4, S0C7) results in an ASRA Abend code? Firstly, it is important to remember that the middle 2 characters of every Abend code tells us who issued the Abend. An Abend code of AICA, tells us that the Abend was issued by Interval Control. When a User Program executing in CICS Program Checks, the first to get notification of this is z/OS and its Functional Recovery Routines (FFRs). Once z/OS takes control it would normally get rid of the TCB associated with this Address Space. However, this is CICS, so when it originally initialized it informed z/OS by creating an ESTAE error handler exit, that in the event of an error in its Address Space NOT to abend it, and give control to CICS to deal with the abend. z/OS recognizing this, passes control to the error handler in the ESTAE which is the Kernel Domain in CICS. This Domain sees all and knows all in CICS; it looks at its current Domain list and sees that the Application Domain is involved in this Program Check, and passes control to the Domains error handler routine which is the System Recovery Program – DFHSRP.  So, System Recovery issues the ASRA abend code.

Looping/runaway Task

Let’s discuss a looping/runaway Task. The option ICVR (Interval Control Value Runaway) is the amount of Task time that a task can consume between Calls to CICS. You see CICS is a very friendly system; it just says that you can freely use my services, you just have to talk to me once in a while. However, if you don’t talk to me, then I’m going to assume that you are looping and abend you. How does it do this? When you issue an EXEC CICS command and CICS processes your request, then control returns to your program. CICS then starts a Clock and decrements it while you have the CPU processing your COBOL/Assembler statements. If the Clock falls to zero, CICS, by Interval Control, will abend your task AICA.  Actually, it’s quite clever; it abends the Task with an S0C1 (Operation Exception). ICVR controls this Clock and it’s specified in the Overrides and changeable online by a CEMT SET SYSTEM command. 2000 milliseconds (2 seconds) is the default. This is a system-wide value but can be specified differently for each transaction. 

CEDA DEFINE TRANSACTION RUNAWAY=SYSTEM|number. So how to deal with this problem? In the next topic, I’m going to share with you the best way to debug a looping Transaction and it’s NOT using DISPLAY.

COBOL DISPLAY

Be very careful when using COBOL DISPLAY in CICS, as every DISPLAY causes a write to CEEMSG, and as this dataset is used by LE to write abending information, it can get quite cluttered.

Also, there is an MVS GETMAIN in the DISPLAY that is obtained from the sub-pools in the High Private area. The FREEMAIN of this storage is only done at the End of Step – when CICS comes down.  This could be days away.  If the Getmain in the DISPLAY fails, CICS is abended with an S40D.  It is so much better to use the following command: 

EXEC CICS ENTER TRACENUM(00-199) FROM(nnnnn) RESOURCE('8bytes char'). 

This command doesn’t cause the runaway clock to end, so the loop continues on until the AICA – but now you can use CEDF to trace the loop, as this will stop on the Enter Tracenum. The FROM option can show up to 4000 bytes of data, and the RESOURCE option can contain 8 bytes of character data that can show you where you are in the program. As an example: 

EXEC CICS ENTER TRACENUM(100) FROM(ws-field) RESOURCE('BFCOMPUTE'). 

The result is that CEDF will show you Tracenum 100, the contents of ws-field, and tell you that you are just before the COMPUTE statement in the program. If you place this command with different Tracenum values throughout your code, you will quickly find the loop.

ENTER TRACENUM

Here is another good use of ENTER TRACENUM: If your Mainline program Calls a subroutine, CICS has no knowledge of when you leave the Mainline and use CEDF (neither do you). In this situation, code the ENTER TRACENUM command just before the CALL and specify the CALL USING Parameters in the FROM option. If there are more than 8 Bytes, you will need the FROMLENGTH option for the number of bytes you want to trace.  

If you also place this command after you return from the subroutine, you will see in CEDF what values in the Call have been changed. One other area of concern with a CALL USING is if you receive a Protection Exception S0C4. In this case, the first thing to check is your Call parameters to ensure you are passing the correct number. Also, make sure that the receiving Subroutine’s Linkage Section is defined with the same number of Linkage Section 01 Levels coded. 

COBOL creates BLL Cells (BASE LOCATOR for LINKAGE SECTION) and each Cell is defined as a Fullword Address. 

If an expected parameter is not passed, COBOL will assign an address of Low-values to the missing BLL, and as soon as you move data to it – S0C4. 

More on BLLs

When a CICS COBOL program is translated-compiled, and Linked into a Load Library, CICS always establishes the same Linkage Section addresses. That is, BLL 1 will be the address of the EXEC INTERFACE BLOCK (EIB), and BLL 2 will be the address of the first 4096 bytes of the Commarea (DFHCOMMAREA). If the Commarea is not explicitly defined, CICS will create a 1-byte DFHCOMMAREA in order to maintain the correct addressability. If it is defined and is more than 4096 bytes, BLL 3 will contain the next 4096 bytes and so on up to 32K.  The reason that BLLs are broken into chunks of 4096 bytes is that a General Purpose register can only address a maximum of 4096 bytes or x’FFF’ – which of course is 4095. That is because the assembler always counts from zero. Remember, when you are moving data into DFHCOMMAREA, you are moving the data directly into the Working Storage of the program that is Linked to you. Always make sure that your DFHCOMMAREA is NEVER defined as larger than the COMMAREA passed to you, or you run the risk of overlaying the Data Elements in the Working Storage of the program that is Linked to you, causing unpredictable results.

Channels and Containers

As accessing CICS has evolved from the 3270 to Web Services, many of the definitions and values that we have grown with are no longer viable. The Commarea is one of them. With the increasing size of data from the Web in the form of SOAP, XML, JSON, and REST, the Commarea has become too restrictive. Its maximum size is 32K, it only links two programs, and if there is a need to pass the Commarea on to a third program, the DFHCOMMAREA must be copied into Working Storage and used in the Link command. So with this in mind, the CICS Developers introduced Channels and Containers. 

A Channel is a set of Containers that can be passed to another program or programs. A Container is a named block of data that can be passed to another program or programs. There is no restriction on the physical size of a single Container. Containers are stored above the BAR. Channels and Containers are only visible to the programs that created them and to the Programs they are passed to in the EXEC CICS LINK/XCTL commands. CICS will maintain the Channels and Containers and keep them totally separate from the underlying programs, but make them available when requested.

More on Channels and Containers

When a program receives control via a LINK or XCTL, it can discover the current Channel by issuing the command:

EXEC CICS ASSIGN CHANNEL(channelname).  

If there is a current Channel, the 16-byte name of the channel is returned, otherwise it will be blanks. This process can be thought of as the same as checking the EIBCALEN to see if there is any Commarea that has been passed. The Channel can be created in a number of ways.  An EXEC CICS LINK, EXEC CICS XCTL, EXEC CICS START, that names the Channel, EXEC CICS RETURN CHANNEL. It is possible to have a Channel with no Containers, but the real benefit of a Channel is that it can hold one or more Containers.  The command:

EXEC CICS PUT CONTAINER(name) CHANNEL(channelname) FROM(dataarea) 

writes data to the Container. Once the data has been PUT into the Container, it can be retrieved by issuing:

EXEC CICS GET CONTAINER(name) CHANNEL(channelname) INTO(dataarea).

If the channel is not specified, then the current Channel will be used. The Channel goes out of scope when no program can access it. Therefore, on the last EXEC CICS RETURN, CICS will delete the Channel.  CICS itself is beginning to make a lot of use of Channel/Containers; for example, in Web Services during Pipeline processing, all the Message Handlers communicate via Containers.

z/OS Systems Programmer and CICS/TS Systems Programmer of 30 years. Mainframe Training and Software support.