Detectar aberturas e fechamentos de arquivos redundantes em COBOL

Como detectar aberturas e fechamentos de arquivos redundantes em COBOL: uma abordagem de análise estática

O gerenciamento de arquivos em COBOL é enganosamente simples à primeira vista. Você declara um arquivo na DIVISÃO DE AMBIENTE, descreve seu layout de registro na DIVISÃO DE DADOS e usa OPEN, READ ou WRITE e CLOSE na DIVISÃO DE PROCEDIMENTOS. Quatro verbos, um ciclo de vida. O problema é que os programas COBOL raramente permanecem simples; eles acumulam lógica, caminhos de exceção, ramificações condicionais e chamadas PERFORM ao longo de décadas de manutenção. O que começa como um padrão limpo de abertura e fechamento únicos se transforma silenciosamente em algo mais complexo: arquivos abertos duas vezes em um único caminho de execução, instruções CLOSE que são executadas quando nenhum arquivo está aberto, saídas de erro que se ramificam além do CLOSE.

Nenhum desses problemas é imediatamente visível. O programa compila. Muitas vezes, ele é executado sem erros, com as entradas testadas. Ele falha silenciosamente em combinações de entrada inesperadas ou degrada o desempenho da janela de lote tão gradualmente que ninguém associa a causa ao efeito. A análise estática os encontra antes que qualquer um desses problemas aconteça.

Detecção de vazamentos em todos os idiomas

SMART TS XL Mapeia cadeias de referência de longa duração e estruturas de dados ilimitadas em toda a sua base de código.

Mais informações

Os quatro defeitos de estado de arquivo que vale a pena encontrar

Antes de abordarmos os mecanismos de detecção, aqui está uma taxonomia clara do que você deve procurar. Nem todos os problemas em arquivos COBOL são iguais.

DefeitoO que acontece durante a execução?Gravidade
ABERTO DUPLOABRIR em um arquivo já aberto → STATUS DO ARQUIVO 41 ou encerramento anormalAlto
FECHAR sem abertura préviaFECHAR um arquivo que nunca foi aberto → STATUS DO ARQUIVO 42 ou encerramento anormalAlto
Ausência de FECHAR na saída normalArquivo deixado aberto ao PARAR A EXECUÇÃO → O SO o fecha, mas buffers não liberados correm o risco de perda de dados.Suporte:
Falta o comando CLOSE em caso de erro.O programa termina anormalmente com o arquivo aberto → possível corrupção de registros em arquivos VSAMAlto
ABERTURA/FECHAR redundante em um loopArquivo aberto e fechado a cada iteração → sobrecarga severa de E/SMédio-Alto

Ponto principal: Os defeitos de maior gravidade, como abrir e fechar duas vezes sem abrir a primeira, causam falhas imediatas em tempo de execução ou riscos à integridade dos dados. Os defeitos de desempenho (abrir/fechar em nível de loop) não geram nenhum erro, apenas lentidão na execução de processos em lote, cuja causa ainda não foi identificada.

O que torna isso difícil de encontrar manualmente?

O desafio não é identificar o padrão. Qualquer desenvolvedor sabe que não se deve abrir um arquivo duas vezes. O desafio é que o fluxo de controle do COBOL não é linear de maneiras que tornam o rastreamento manual pouco confiável.

Considere um programa com esta estrutura:

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.

Você consegue identificar o defeito sem rastrear cada caminho de execução? Se sim WS-ERROR-FLAG está definido como 1 dentro PROCESS-ONE-RECORD (talvez por meio de um subprograma chamado), os fluxos de controle para ERROR-EXIT, que fecha o arquivo e para. Isso está correto. Mas se WS-ERROR-FLAG is não definido, o loop se completa, PROCESS-RECORDS retorna, e TERMINATION corridas, que também fecha CUSTOMER-FILEDois fechamentos, um abertura. STATUS DO ARQUIVO EM TEMPO DE EXECUÇÃO 42.

Este é um programa simples de três seções. Programas reais têm vinte seções, chamadas condicionais PERFORM, instruções GO TO em código legado e rotinas de tratamento de exceções que, por sua vez, chamam outras rotinas. O rastreamento manual não é apenas propenso a erros, como também é inviável.


Como os modelos de análise estática processam o estado dos arquivos

A análise estática detecta esses defeitos construindo um grafo de fluxo de controle do programa e propagando o estado do arquivo ao longo de cada caminho.

A análise atribui a cada arquivo uma variável de estado com três valores possíveis:

  • CLOSEDO arquivo não foi aberto ou foi fechado com êxito.
  • OPENO arquivo foi aberto e ainda não foi fechado.
  • UNKNOWN, o estado não pode ser determinado estaticamente (por exemplo, abertura condicional em um ramo não totalmente analisado)

A cada instrução OPEN, a análise verifica: este arquivo já está no estado OPEN? Se sim → defeito duplo ABERTO.

A cada instrução CLOSE: este arquivo está no estado CLOSED or UNKNOWN? Se FECHADO → fechar sem defeito aberto.

Em cada caminho para PARAR A EXECUÇÃO ou SAIR DO PROGRAMA: existe algum arquivo ainda em estado de execução? OPEN? Se sim → defeito de fechamento ausente.

Para laços de repetição, a análise verifica se uma instrução OPEN ou CLOSE é dominada por um cabeçalho de laço, o que significa que ela é executada em todas as iterações.

Cuidado: Os casos mais difíceis para qualquer analisador são as chamadas PERFORM para rotinas compartilhadas. Se ERROR-HANDLER é chamada de doze locais diferentes em um programa, o estado do arquivo no ponto da chamada PERFORM determina se o fechamento interno ocorrerá. ERROR-HANDLER está correto ou incorreto. Um analisador que não propaga o estado através das cadeias PERFORM produzirá falsos negativos (deixando passar defeitos reais) ou falsos positivos (sinalizando código correto).


O circuito de abertura/fechamento em nível de loop: um defeito de desempenho sem código de erro.

Esse padrão não produz erros de tempo de execução nem anomalias no status do arquivo. Ele simplesmente é executado lentamente, potencialmente várias ordens de magnitude mais lentamente do que o necessário.

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 o arquivo esteja fisicamente particionado por região, essa abordagem causa sobrecarga desnecessária. Na prática, seria melhor abrir o arquivo uma única vez, ler todos os registros e aplicar a filtragem na memória ou por meio de lógica.

Cada operação OPEN em um arquivo VSAM envolve uma consulta ao catálogo, inicialização do ACB e alocação do pool de buffers. Cada operação CLOSE libera os buffers, atualiza o catálogo e libera o ACB. Para um arquivo com dez regiões, isso acontece dez vezes em vez de uma. Para um arquivo com 10,000 registros e mil regiões, os cálculos se tornam complexos.

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.

A análise estática detecta isso identificando instruções OPEN e CLOSE que são dominadas pelo laço, ou seja, toda execução do corpo do laço executa o OPEN ou o CLOSE. A detecção requer que o grafo de fluxo de controle represente a estrutura do laço, e não apenas a sequência de instruções.


STATUS DO ARQUIVO: Sinalizador do estado do arquivo do seu programa, caso você o verifique.

Cada operação de arquivo COBOL define o campo FILE STATUS definido na entrada FILE-CONTROL. Um programa que verifica o FILE STATUS após cada operação OPEN, READ, WRITE e CLOSE pode detectar e responder a qualquer anomalia no estado do arquivo em tempo de execução. Um programa que ignora o FILE STATUS está operando às cegas.

Lista de verificação: Códigos de STATUS DE ARQUIVO que todo desenvolvedor COBOL deve conhecer

  • 00Conclusão bem-sucedida
  • 10Fim do arquivo (LEITURA: não há mais registros)
  • 35Arquivo não encontrado (ABRIR ENTRADA: o arquivo não existe)
  • 41Arquivo já aberto (ABRIR em um arquivo que já está aberto)
  • 42Arquivo não aberto (CLOSE ou READ em um arquivo no estado FECHADO)
  • 47, Tentativa de LEITURA em arquivo não aberto (ENTRADA ou E/S)
  • 48, Tentativa de ESCRITA em arquivo não aberto OUTPUT, IO ou EXTEND
  • 97Arquivo aberto com sucesso (específico da IBM, em alguns ambientes)

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: Um erro comum de manutenção é declarar o STATUS DO ARQUIVO na entrada FILE-CONTROL, mas nunca fazer referência a ele. WS-CUST-FILE-STATUS na DIVISÃO DE PROCEDIMENTOS. O campo é preenchido após cada operação de arquivo, mas se nenhum código o verificar, o programa continua executando mesmo com erros. A análise estática pode identificar esse padrão: STATUS DO ARQUIVO declarado, mas nunca referenciado na lógica condicional.


Três padrões reais de COBOL que causam defeitos

Padrão 1: O Problema de Saída de Erro Compartilhado

Um programa possui múltiplas seções de processamento, cada uma com seu próprio manipulador de erros. Vários manipuladores de erros fecham o arquivo antes de retornar. A rotina de encerramento normal também fecha o arquivo. Se ocorrer um erro e a recuperação prosseguir além do manipulador de erros, o arquivo será fechado duas vezes.

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.

O GO TO TERMINATION transfere o controle diretamente para TERMINATION, que executa a sua própria CLOSE CUSTOMER-FILE. STATUS DO ARQUIVO 42.

Correção: Utilize uma única rotina de fechamento centralizada. Cada caminho de saída chama a mesma rotina, que verifica se o arquivo está aberto antes de fechá-lo.

Padrão 2: A abertura condicional

Um arquivo é aberto condicionalmente, apenas quando um determinado modo de processamento está ativo. Mas o comando CLOSE é incondicional, ocorrendo em todos os caminhos de execução, inclusive naqueles em que o arquivo nunca foi aberto.

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.

Esse defeito é invisível nos testes se estes forem sempre executados no modo COMPLETO. Ele aparece em produção na primeira execução no modo PARCIAL.

Padrão 3: EXECUTAR em todas as unidades de compilação

Em programas que usam CALL para invocar subprogramas, um arquivo aberto no programa principal pode ser passado para um subprograma que o fecha, e então o programa principal o fecha novamente.

cobol

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

Esse padrão exige análise interprocedural , rastreando o estado do arquivo através do limite da chamada (CALL) até o comportamento do subprograma, algo que uma simples análise intraprograma não consegue detectar.


O que um analisador estático precisa para fazer isso bem

Nem todas as ferramentas de análise estática lidam com a análise do estado de arquivos COBOL com a mesma profundidade. Aqui está uma lista de verificação para avaliar a capacidade de análise:

A ferramenta lida com cadeias PERFORM? Os programas COBOL usam PERFORM para chamar parágrafos e seções nomeados. As operações de arquivo dentro de um destino PERFORM devem ser visíveis para o rastreamento de estado do chamador.

A ferramenta lida com PERFORM VARYING inline (loops)? A detecção de loops é necessária para identificar operações OPEN/CLOSE dominadas por loops.

Será que ele rastreia o estado ao longo do GO TO? Programas COBOL legados usam GO TO extensivamente. Um analisador que não consegue acompanhar as arestas do GO TO no grafo de fluxo de controle deixará de detectar classes inteiras de defeitos.

Ele lida com COPY e REPLACE? O código de manipulação de arquivos geralmente está em membros COPY. Um analisador que não expande os membros COPY antes da análise não detectará as operações definidas no código incluído.

Verifica o uso de FILE STATUS? Uma análise completa verifica não apenas se FILE STATUS está declarado, mas também se ele é realmente verificado após cada operação.

O programa realiza análise interprocedural? Subprogramas chamados (CALL) que executam operações em arquivos criam dependências de estado entre programas. A cobertura completa exige o acompanhamento das cadeias de chamadas (CALL).

Ponto principal: Uma ferramenta que verifica apenas um único parágrafo ou seção deixará passar a maioria dos defeitos reais. Os defeitos que importam em produção, aqueles que levaram anos para aparecer e horas para serem diagnosticados, são os que abrangem várias seções, ramificações condicionais e rotinas chamadas.


O que fazer com os resultados: uma estrutura de priorização

Quando a análise estática revela defeitos nos arquivos, nem todas as descobertas são iguais. Veja como priorizá-las:

Corrija imediatamente (antes da próxima execução em lote):

  • Abertura dupla em qualquer caminho de execução onde o STATUS DO ARQUIVO 41 causaria uma falha anormal.
  • FECHAR sem ABRIR em caminhos que são executados em produção
  • Ausência de fechamento (CLOSE) nos caminhos de saída de erro para arquivos VSAM (risco de integridade de gravação).

Cronograma para o próximo sprint:

  • Operação OPEN/CLOSE dominada por loops com impacto mensurável na janela de lote
  • Ausência de verificações de STATUS DE ARQUIVO em arquivos críticos (auditoria, transação, relatório)
  • Abertura condicional com fechamento incondicional em programas multimodo

Acompanhar e abordar as questões durante a próxima fase de modernização:

  • Declarações de STATUS DE ARQUIVO ausentes
  • FECHAR apenas nos caminhos de saída normais (o sistema operacional lida com isso, mas é uma má prática)
  • Defeitos interprocedimentais cuja correção exige a coordenação de alterações em vários programas.

Como SMART TS XL Analisa o estado do arquivo COBOL

SMART TS XL'S análise de código estático Constrói um modelo de fluxo de controle de cada programa COBOL no ambiente, expandindo os membros COPY, seguindo as cadeias PERFORM, rastreando as arestas GO TO e propagando o estado do arquivo por cada ramificação de cada condicional. Não se trata de uma busca por padrões; é um modelo estrutural do que o programa realmente faz em cada ponto alcançável de sua execução.

A capacidade de mapeamento de dependências de aplicativos estende isso à dimensão interprocedural: quando um programa COBOL chama um subprograma que executa operações de arquivo, o mapa de dependências representa essa relação, permitindo análises que ultrapassam os limites das unidades de compilação. Defeitos de abertura dupla (Double OPEN) que abrangem um programa principal e um subprograma chamado são visíveis no modelo estrutural de uma forma que não são em nenhuma análise intraprograma.

A funcionalidade de busca corporativa torna os resultados acionáveis ​​em escala: encontre todos os programas no portfólio que abrem um conjunto de dados específico, todos os membros COPY que contêm uma instrução CLOSE, todos os programas que possuem um alvo PERFORM contendo um OPEN, em milhões de linhas de COBOL em segundos. Para uma equipe de modernização que se prepara para migrar uma carga de trabalho em lote para a nuvem, essa funcionalidade de busca transforma uma auditoria manual de semanas em uma consulta direcionada.

A capacidade de expansão do JCL adiciona o contexto operacional: quais etapas do job JCL invocam cada programa, quais conjuntos de dados cada instrução DD referencia e como o gerenciamento de arquivos no código COBOL se conecta aos conjuntos de dados físicos definidos no JCL. Um defeito de FECHAR em um arquivo que o JCL define como um conjunto de dados de saída crítico tem prioridade maior do que o mesmo defeito em um arquivo de trabalho temporário, e essa priorização requer o conhecimento do contexto JCL de cada programa COBOL.


A lição mais ampla: o estado do arquivo é apenas uma dimensão.

Operações redundantes em arquivos são um exemplo específico de um problema mais geral: programas COBOL que evoluíram ao longo de décadas contêm comportamentos dependentes do estado — estado do arquivo, estados de switch, estados de contador — que só são compreendidos corretamente rastreando todos os caminhos de execução possíveis através do grafo de fluxo de controle completo.

A revisão humana desse tipo de código é lenta, incompleta e inconsistente. Desenvolvedores diferentes encontram defeitos diferentes. O mesmo desenvolvedor, ao revisar o mesmo programa duas vezes, encontra defeitos diferentes. A análise estática é sistemática: aplica as mesmas regras a cada caminho em cada programa, sempre, sem fadiga ou suposições.

Para equipes que gerenciam grandes portfólios COBOL, seja para manutenção contínua, programas de modernização de sistemas legados ou auditorias de conformidade, o benefício da análise sistemática do estado dos arquivos não se resume apenas aos defeitos encontrados. Trata-se da confiança de que o portfólio foi analisado completamente e que os defeitos remanescentes são conhecidos, e não ocultos.