Detectar aperturas y cierres de archivos redundantes en COBOL

Cómo detectar aperturas y cierres de archivos redundantes en COBOL: un enfoque de análisis estático

El manejo de archivos en COBOL es engañosamente simple en apariencia. Se declara un archivo en la DIVISIÓN DE ENTORNO, se describe su estructura de registros en la DIVISIÓN DE DATOS y se utilizan OPEN, READ o WRITE, y CLOSE en la DIVISIÓN DE PROCEDIMIENTOS. Cuatro verbos, un ciclo de vida. El problema es que los programas COBOL rara vez se mantienen simples; acumulan lógica, rutas de excepciones, bifurcaciones condicionales y llamadas a PERFORM a lo largo de décadas de mantenimiento. Lo que comienza como un patrón limpio de una sola apertura y un solo cierre se transforma silenciosamente en algo más complejo: archivos abiertos dos veces en una misma ruta de ejecución, sentencias CLOSE que se ejecutan cuando no hay ningún archivo abierto, salidas de error que se desvían completamente más allá de CLOSE.

Ninguno de estos problemas es inmediatamente visible. El programa compila. A menudo se ejecuta sin errores con las entradas que se prueban. Falla silenciosamente con combinaciones de entrada inesperadas o degrada el rendimiento de la ventana de procesamiento por lotes de forma tan gradual que nadie relaciona la causa con el efecto. El análisis estático los detecta antes de que esto ocurra.

Detección de fugas en todos los idiomas

SMART TS XL Mapea cadenas de referencias de larga duración y estructuras de datos ilimitadas en todo tu código fuente.

MÁS INFORMACIÓN

Los cuatro defectos de estado de archivo que vale la pena encontrar

Antes de entrar en los mecanismos de detección, aquí tienes una taxonomía clara de lo que debes buscar. No todos los problemas con archivos COBOL son iguales.

Defecto¿Qué sucede en tiempo de ejecución?Gravedad
Doble APERTURAABRIR en un archivo ya abierto → ESTADO DEL ARCHIVO 41 o terminación anormalAlto
CERRAR sin apertura previaCERRAR un archivo que nunca se abrió → ESTADO DEL ARCHIVO 42 o terminación anormalAlto
Falta el botón CLOSE en la salida normal.El archivo que queda abierto al detener la ejecución → el sistema operativo lo cierra, pero los búferes no vaciados conllevan el riesgo de pérdida de datos.Media
Falta CLOSE en la salida de errorEl programa finaliza de forma anormal con el archivo abierto → posible corrupción de registros en archivos VSAMAlto
APERTURA/CIERRE redundante en un bucleEl archivo se abre y se cierra en cada iteración → sobrecarga de E/S severaMedio-alto

Conclusión clave: Los defectos más graves, como la doble apertura y cierre sin apertura, provocan fallos inmediatos en tiempo de ejecución o riesgos para la integridad de los datos. Los defectos de rendimiento (apertura/cierre a nivel de bucle) no generan ningún error, solo ralentizan los procesos por lotes cuya causa nadie ha podido determinar.

¿Qué hace que esto sea difícil de encontrar manualmente?

El desafío no radica en identificar el patrón. Cualquier desarrollador sabe que no se debe abrir un archivo dos veces. El desafío reside en que el flujo de control de COBOL es no lineal, lo que hace que el rastreo manual no sea fiable.

Consideremos un programa con esta estructura:

cobol

       PROCEDURE DIVISION.
       MAIN-LOGIC.
           PERFORM INITIALIZATION
           PERFORM PROCESS-RECORDS
           PERFORM TERMINATION
           STOP RUN.

       INITIALIZATION.
           OPEN INPUT CUSTOMER-FILE
           OPEN OUTPUT REPORT-FILE
           MOVE 0 TO WS-ERROR-FLAG.

       PROCESS-RECORDS.
           READ CUSTOMER-FILE INTO WS-CUSTOMER-REC
               AT END MOVE 1 TO WS-EOF-FLAG
           END-READ
           IF WS-ERROR-FLAG = 1
               PERFORM ERROR-EXIT
           END-IF
           PERFORM UNTIL WS-EOF-FLAG = 1
               PERFORM PROCESS-ONE-RECORD
               READ CUSTOMER-FILE INTO WS-CUSTOMER-REC
                   AT END MOVE 1 TO WS-EOF-FLAG
               END-EXEC
           END-PERFORM.

       ERROR-EXIT.
           DISPLAY 'Error in processing'
           CLOSE CUSTOMER-FILE    *> CLOSE here...
           STOP RUN.

       TERMINATION.
           CLOSE CUSTOMER-FILE    *> ...and CLOSE here too
           CLOSE REPORT-FILE
           STOP RUN.

¿Puedes detectar el defecto sin rastrear cada ruta de ejecución? Si WS-ERROR-FLAG está configurado en 1 dentro PROCESS-ONE-RECORD (quizás mediante un subprograma llamado), el control fluye hacia ERROR-EXIT, lo que cierra el archivo y se detiene. Eso es correcto. Pero si WS-ERROR-FLAG is No establecido, el bucle se completa, PROCESS-RECORDS devuelve, y TERMINATION corre, que también cierra CUSTOMER-FILEDos cierres, uno apertura. Estado del archivo en tiempo de ejecución: 42.

Este es un programa sencillo de tres secciones. Los programas reales tienen veinte secciones, llamadas condicionales a PERFORM, instrucciones GO TO en código heredado y rutinas de manejo de excepciones que, a su vez, llaman a otras rutinas. El rastreo manual no solo es propenso a errores, sino que además es inviable.


Cómo modelan el análisis estático el estado de los archivos

El análisis estático detecta estos defectos mediante la construcción de un grafo de flujo de control del programa y la propagación del estado del archivo a lo largo de cada ruta.

El análisis asigna a cada archivo una variable de estado con tres valores posibles:

  • CLOSEDEl archivo no se ha abierto o se ha cerrado correctamente.
  • OPENEl archivo se ha abierto y aún no se ha cerrado.
  • UNKNOWN, el estado no se puede determinar estáticamente (por ejemplo, apertura condicional en una rama no analizada completamente)

En cada instrucción OPEN, el análisis comprueba: ¿este archivo ya está en estado? OPEN¿Si es así, doble defecto ABIERTO?

En cada instrucción CLOSE: ¿este archivo está en estado? CLOSED or UNKNOWN? Si CERRADO → cerrar sin defecto abierto.

En cada ruta para DETENER EJECUCIÓN o SALIR DEL PROGRAMA: ¿hay algún archivo todavía en estado? OPEN¿Si es así, falta un defecto de cierre?

En el caso de los bucles, el análisis comprueba si una instrucción OPEN o CLOSE está dominada por un encabezado de bucle, lo que significa que se ejecuta en cada iteración.

Cuidado: Los casos más difíciles para cualquier analizador son las llamadas PERFORM a rutinas compartidas. Si ERROR-HANDLER se llama desde doce lugares diferentes en un programa, el estado del archivo en el punto de la llamada PERFORM determina si se cierra dentro ERROR-HANDLER es correcto o erróneo. Un analizador que no propaga el estado a través de las cadenas PERFORM producirá falsos negativos (defectos reales no detectados) o falsos positivos (código correcto marcado).


El bucle OPEN/CLOSE: un defecto de rendimiento sin código de error.

Este patrón no produce ningún error de ejecución ni anomalía en el estado del archivo. Simplemente se ejecuta lentamente, posiblemente mucho más lento de lo necesario.

cobol

      *> PROBLEMATIC: file opened and closed on every iteration
       PROCESS-ALL-REGIONS.
           PERFORM VARYING WS-REGION-ID FROM 1 BY 1
               UNTIL WS-REGION-ID > 10
               OPEN INPUT CUSTOMER-FILE
               PERFORM PROCESS-REGION-RECORDS
               CLOSE CUSTOMER-FILE
           END-PERFORM.

A menos que el archivo esté físicamente particionado por regiones, este enfoque genera una sobrecarga innecesaria. En la práctica, sería mejor abrir el archivo una sola vez, leer todos los registros y aplicar el filtrado en memoria o mediante lógica.

Cada operación OPEN en un archivo VSAM implica una búsqueda en el catálogo, la inicialización de ACB y la asignación de memoria intermedia. Cada operación CLOSE vacía los búferes, actualiza el catálogo y libera la memoria intermedia. Para un archivo con diez regiones, esto sucede diez veces en lugar de una. Para un archivo con 10 000 registros y mil regiones, los cálculos se vuelven muy complejos.

cobol

      *> CORRECT: open once, filter inside the loop
       PROCESS-ALL-REGIONS.
           OPEN INPUT CUSTOMER-FILE
           PERFORM VARYING WS-REGION-ID FROM 1 BY 1
               UNTIL WS-REGION-ID > 10
               PERFORM PROCESS-REGION-RECORDS
           END-PERFORM
           CLOSE CUSTOMER-FILE.

El análisis estático detecta esto al identificar las instrucciones OPEN y CLOSE que están dominadas por el bucle; es decir, cada ejecución del cuerpo del bucle ejecuta la instrucción OPEN o CLOSE. La detección requiere que el grafo de flujo de control represente la estructura del bucle, no solo la secuencia de instrucciones.


ESTADO DEL ARCHIVO: Señal de estado del archivo de su programa, si lo comprueba.

Cada operación de archivo COBOL establece el campo ESTADO DEL ARCHIVO definido en la entrada FILE-CONTROL. Un programa que comprueba ESTADO DEL ARCHIVO después de cada APERTURA, LECTURA, ESCRITURA y CIERRE puede detectar y responder a cualquier anomalía en el estado del archivo durante la ejecución. Un programa que ignora ESTADO DEL ARCHIVO está trabajando a ciegas.

Lista de verificación: Códigos de ESTADO DE ARCHIVO que todo desarrollador de COBOL debe conocer

  • 00Finalización exitosa
  • 10Fin del archivo (LECTURA: no hay más registros)
  • 35, Archivo no encontrado (ABRIR ENTRADA: el archivo no existe)
  • 41, Archivo ya abierto (OPEN en un archivo en estado OPEN)
  • 42, Archivo no abierto (CERRAR o LEER en un archivo en estado CERRADO)
  • 47Se intentó leer un archivo no abierto (entrada o E/S).
  • 48, Se intentó escribir en un archivo no abierto SALIDA, E/S o EXTENSIÓN
  • 97, Archivo abierto correctamente (específico de IBM, algunos entornos)

cobol

       FILE-CONTROL.
           SELECT CUSTOMER-FILE
               ASSIGN TO CUSTFILE
               FILE STATUS IS WS-CUST-FILE-STATUS.

       WORKING-STORAGE SECTION.
       01 WS-CUST-FILE-STATUS   PIC XX.

       PROCEDURE DIVISION.
           OPEN INPUT CUSTOMER-FILE
           IF WS-CUST-FILE-STATUS NOT = '00'
               DISPLAY 'OPEN failed: ' WS-CUST-FILE-STATUS
               PERFORM ABEND-ROUTINE
           END-IF.

Cuidado: Un error común de mantenimiento es declarar FILE STATUS en la entrada FILE-CONTROL pero nunca hacer referencia a WS-CUST-FILE-STATUS En la DIVISIÓN DE PROCEDIMIENTOS, el campo se rellena después de cada operación de archivo, pero si ningún código lo comprueba, el programa ignora los errores sin generar errores. El análisis estático puede detectar este patrón: FILE STATUS declarado pero nunca referenciado en la lógica condicional.


Tres patrones de COBOL del mundo real que provocan defectos

Patrón 1: El problema de salida de error compartida

Un programa tiene varias secciones de procesamiento, cada una con su propio manejador de errores. Varios manejadores de errores cierran el archivo antes de finalizar. La rutina de terminación normal también cierra el archivo. Si ocurre un error y la recuperación continúa más allá del manejador de errores, el archivo se cierra dos veces.

cobol

       VALIDATE-RECORDS.
           READ CUSTOMER-FILE INTO WS-REC
               AT END MOVE 1 TO WS-EOF
           END-READ
           IF WS-CUST-FILE-STATUS NOT = '00' AND '10'
               CLOSE CUSTOMER-FILE    *> closes here on read error
               PERFORM WRITE-ERROR-LOG
               GO TO TERMINATION      *> skips to close in TERMINATION?
           END-IF.

       TERMINATION.
           CLOSE CUSTOMER-FILE        *> double close if GO TO reached here
           CLOSE REPORT-FILE
           STOP RUN.

El GO TO TERMINATION transfiere el control directamente a TERMINATION, que ejecuta su propia CLOSE CUSTOMER-FILEESTADO DEL ARCHIVO 42.

Solución: Utilizar una única rutina de cierre centralizada. Cada ruta de salida llama a la misma rutina, que comprueba si el archivo está abierto antes de cerrarlo.

Patrón 2: La APERTURA condicional

Un archivo se abre de forma condicional, solo cuando un modo de procesamiento específico está activo. Sin embargo, el cierre es incondicional y se produce en todas las rutas de ejecución, incluso en aquellas donde el archivo nunca se abrió.

cobol

       INITIALIZATION.
           IF WS-PROCESSING-MODE = 'FULL'
               OPEN OUTPUT AUDIT-FILE   *> only opened in FULL mode
           END-IF.

       TERMINATION.
           CLOSE AUDIT-FILE    *> ALWAYS closes -- FILE STATUS 42 in PARTIAL mode
           STOP RUN.

Este defecto es invisible durante las pruebas si estas siempre se ejecutan en modo COMPLETO. Se manifiesta en producción en la primera ejecución en modo PARCIAL.

Patrón 3: EJECUTAR en todas las unidades de compilación

En los programas que utilizan la instrucción CALL para invocar subprogramas, un archivo abierto en el programa principal puede pasarse a un subprograma que lo cierra, y luego el programa principal lo cierra de nuevo.

cobol

      *> Main program
       CALL 'SUBPROG1' USING CUSTOMER-FILE-STATUS
       *> SUBPROG1 closes CUSTOMER-FILE internally
       CLOSE CUSTOMER-FILE    *> double close

Este patrón requiere un análisis interprocedimental , que rastrea el estado del archivo a través del límite de la llamada a la función dentro del comportamiento del subprograma, algo que un simple análisis intraprograma no puede detectar.


Lo que necesita un analizador estático para hacer esto bien.

No todas las herramientas de análisis estático manejan el análisis del estado de los archivos COBOL con la misma profundidad. Aquí hay una lista de verificación para evaluar la capacidad:

¿La herramienta admite cadenas PERFORM? Los programas COBOL utilizan PERFORM para llamar a párrafos y secciones con nombre. Las operaciones de archivo dentro de un destino PERFORM deben ser visibles para el seguimiento de estado del programa que realiza la llamada.

¿La herramienta admite operaciones PERFORM VARYING en línea (bucles)? Se requiere la detección de bucles para identificar operaciones OPEN/CLOSE dominadas por bucles.

¿Realiza un seguimiento del estado a través de GO TO? Los programas COBOL heredados utilizan GO TO de forma extensiva. Un analizador que no pueda seguir las aristas de GO TO en el grafo de flujo de control pasará por alto clases enteras de defectos.

¿Admite las operaciones COPIAR y REEMPLAZAR? El código para el manejo de archivos suele estar en miembros COPIAR. Un analizador que no expanda los miembros COPIAR antes del análisis no podrá realizar las operaciones definidas en el código incluido.

¿Verifica el uso de FILE STATUS? Un análisis completo comprueba no solo si FILE STATUS está declarado, sino también si se verifica realmente después de cada operación.

¿Realiza análisis interprocedimental? Las subprogramas llamadas que realizan operaciones de archivo crean dependencias de estado entre programas. La cobertura real requiere seguir las cadenas de llamadas.

Conclusión clave: Una herramienta que solo revisa un párrafo o sección pasará por alto la mayoría de los defectos reales. Los defectos que importan en producción, aquellos que tardaron años en aparecer y horas en diagnosticarse, son los que abarcan varias secciones, bifurcaciones condicionales y rutinas llamadas.


Qué hacer con los resultados: un marco de priorización

Cuando el análisis estático revela defectos en los archivos, no todos los hallazgos son iguales. A continuación, se explica cómo priorizarlos:

Solucionar inmediatamente (antes de la siguiente ejecución por lotes):

  • Doble OPEN en cualquier ruta de ejecución donde FILE STATUS 41 provocaría un error.
  • CERRAR sin ABRIR en rutas que se ejecutan en producción
  • Falta la instrucción CLOSE en las rutas de salida de error para los archivos VSAM (riesgo de integridad de escritura).

Calendario para el próximo sprint:

  • Operación OPEN/CLOSE dominada por bucles con impacto medible en la ventana de lotes
  • Faltan comprobaciones del ESTADO DE LOS ARCHIVOS en archivos críticos (auditoría, transacciones, informes).
  • APERTURA condicional con CIERRE incondicional en programas multimodo

Seguimiento y solución durante la próxima fase de modernización:

  • Faltan declaraciones de ESTADO DE ARCHIVO
  • CERRAR solo en rutas de salida normales (el sistema operativo lo gestiona, pero es una mala práctica).
  • Defectos interprocedimentales cuya solución requiere coordinar cambios en múltiples programas.

Cómo SMART TS XL Analiza el estado del archivo COBOL

SMART TS XL, análisis de código estático Construye un modelo de flujo de control de cada programa COBOL en el entorno, expandiendo los miembros COPY, siguiendo las cadenas PERFORM, rastreando las aristas GO TO y propagando el estado del archivo a través de cada rama de cada condicional. No se trata de un escaneo de coincidencia de patrones; es un modelo estructural de lo que el programa realmente hace en cada punto alcanzable de su ejecución.

La capacidad de mapeo de dependencias de la aplicación extiende esto a la dimensión interprocedimental: cuando un programa COBOL llama a un subprograma que realiza operaciones de archivo, el mapa de dependencias representa esa relación, lo que permite un análisis que trasciende los límites de las unidades de compilación. Los defectos de doble apertura que abarcan un programa principal y un subprograma llamado son visibles en el modelo estructural de una manera que no lo son en ningún análisis intraprograma.

La capacidad de búsqueda empresarial permite obtener resultados prácticos a gran escala: encuentra en segundos todos los programas del portafolio que abren un conjunto de datos específico, todos los miembros de COPY que contienen una instrucción CLOSE, todos los programas que tienen un objetivo PERFORM que contiene una instrucción OPEN, en millones de líneas de COBOL. Para un equipo de modernización que se prepara para migrar una carga de trabajo por lotes a la nube, esta capacidad de búsqueda transforma una auditoría manual de semanas en una consulta específica.

La capacidad de expansión de JCL agrega el contexto operativo: qué pasos de trabajo de JCL invocan cada programa, a qué conjuntos de datos hace referencia cada instrucción DD y cómo el manejo de archivos en el código COBOL se conecta con los conjuntos de datos físicos definidos en el JCL. Un defecto CLOSE en un archivo que JCL define como un conjunto de datos de salida crítico tiene mayor prioridad que el mismo defecto en un archivo de trabajo temporal, y esa priorización requiere conocer el contexto JCL de cada programa COBOL.


La lección más amplia: el estado del archivo es solo una dimensión.

Las operaciones de archivos redundantes son un caso específico de un problema más general: los programas COBOL que han evolucionado durante décadas contienen comportamientos dependientes del estado (estado de archivo, estados de interruptor, estados de contador) que solo se comprenden correctamente rastreando cada posible ruta de ejecución a través del gráfico de flujo de control completo.

La revisión humana de este tipo de código es lenta, incompleta e inconsistente. Diferentes desarrolladores encuentran diferentes defectos. Un mismo desarrollador, al revisar el mismo programa dos veces, encuentra defectos distintos. El análisis estático es sistemático: aplica las mismas reglas a cada ruta de cada programa, siempre, sin fatiga ni suposiciones.

Para los equipos que gestionan grandes carteras de COBOL, ya sea para mantenimiento continuo, programas de modernización de sistemas heredados o auditorías de cumplimiento, el beneficio del análisis sistemático del estado de los archivos no reside únicamente en los defectos encontrados. Se trata de la certeza de que la cartera se ha analizado por completo y de que los defectos restantes son conocidos, en lugar de estar ocultos.