| Bellacosa e o perigo do perform recursivo |
☕ Um Café no Bellacosa Mainframe
PERFORM Recursivo em COBOL: O Warning que Todo Padawan Ignora (Até o Job Estourar o TIME e o REGION)
"Recursão é uma ferramenta fantástica... exceto quando você tenta usá-la como estrutura de repetição dentro de um programa COBOL Batch."
Quem vem de Java, C#, Python ou C costuma achar natural escrever funções recursivas.
Quem cresceu no COBOL aprende rapidamente uma regra quase sagrada:
Nunca faça um PERFORM recursivo em um parágrafo ou seção.
Mas por quê?
Vamos abrir o capô do compilador.
Primeiro: o que é um PERFORM recursivo?
Imagine algo assim:
0000-PRINCIPAL.
PERFORM 1000-PROCESSA
STOP RUN.
1000-PROCESSA.
DISPLAY "PROCESSANDO"
PERFORM 1000-PROCESSA.
O programa chama...
...que chama...
...que chama...
...que chama novamente...
Nunca termina.
O warning da compilação
O Enterprise COBOL consegue detectar algumas formas óbvias de recursão.
Durante a compilação pode surgir mensagens semelhantes a:
IGYPSxxxx-W
Recursive PERFORM detected.
ou
Possible recursive PERFORM.
O compilador está dizendo:
"Existe um caminho onde este PERFORM pode executar novamente antes do anterior terminar."
Nem sempre é erro.
Mas quase sempre indica problema de projeto.
Por que isso é perigoso?
Porque PERFORM não foi criado para funcionar como chamada infinita de procedimentos.
Cada PERFORM precisa guardar informações como:
endereço de retorno
contexto de execução
pilha de controle
informações internas do runtime
A cada nova chamada tudo isso cresce.
PERFORM A
↓
PERFORM A
↓
PERFORM A
↓
PERFORM A
↓
PERFORM A
A pilha nunca é liberada.
O que acontece durante a execução?
Enquanto houver memória:
Stack
+----------------+
| retorno |
+----------------+
| retorno |
+----------------+
| retorno |
+----------------+
| retorno |
+----------------+
| retorno |
+----------------+
Cada PERFORM adiciona um novo frame.
Quando acaba a pilha...
Boom.
O programa pode terminar com
Dependendo do ambiente:
S0C1
S0C4
S0CB
S878
S80A
Ou simplesmente:
ABEND
Tudo depende de onde ocorreu a falha.
O erro de TIME no JCL
Muito antes da memória acabar...
o Job pode morrer por tempo.
Exemplo:
//STEP1 EXEC PGM=MEUPROG,TIME=1
ou
TIME=1440
Mesmo com TIME=1440...
o programa nunca termina.
O JES percebe que o tempo máximo foi atingido.
Resultado:
S322
Ou mensagens semelhantes indicando limite de CPU excedido.
Não foi o COBOL.
Foi o JCL protegendo o sistema.
O erro de REGION
Outro clássico.
Cada PERFORM recursivo consome mais memória.
Em algum momento:
REGION=0M
não resolve.
Porque memória infinita não existe.
O resultado costuma ser:
S878
ou
S80A
Falta de armazenamento.
"Mas REGION=0M não é infinito?"
Não.
É apenas o máximo permitido pela instalação.
Existe limite de:
memória virtual
stack
storage abaixo da linha
storage acima da linha
política do sistema
Nada disso é infinito.
O maior problema: lógica
Suponha:
1000-ROTINA.
IF WS-FIM = 'N'
PERFORM 1000-ROTINA
END-IF.
Quem altera:
WS-FIM
Se ninguém alterar...
Nunca haverá saída.
É um loop infinito disfarçado.
Por que não usar recursão em parágrafos e seções?
Porque COBOL foi projetado para outro paradigma.
A linguagem nasceu para processamento sequencial.
Ela possui comandos próprios para repetição.
Como:
PERFORM UNTIL
PERFORM VARYING
SEARCH
SEARCH ALL
Essas estruturas:
são previsíveis
ocupam pouca memória
facilitam depuração
têm melhor desempenho
"Mas COBOL suporta recursão."
Sim.
Desde o Enterprise COBOL moderno existe:
RECURSIVE PROGRAM-ID.
ou
PROGRAM-ID. MEUPROG RECURSIVE.
Isso significa que o programa pode chamar a si próprio.
Exemplo clássico:
árvore binária
parsing
algoritmos matemáticos
estruturas hierárquicas
Mesmo assim...
Não significa que seja recomendado para processamento batch tradicional.
A diferença importante
Errado
Parágrafo
↓
PERFORM
↓
Mesmo parágrafo
Recursão interna.
Difícil de manter.
Correto
Programa A
↓
CALL Programa A
↓
Novo contexto
↓
Retorna
Quando realmente houver necessidade de recursão.
Curiosidade
Os compiladores antigos praticamente desencorajavam qualquer tipo de recursão.
O foco sempre foi:
velocidade
previsibilidade
baixo consumo de memória
A maioria dos sistemas bancários jamais precisou de recursão.
Como um sênior resolveria?
Em vez disso:
PERFORM UNTIL WS-FIM = 'S'
...
END-PERFORM
ou
PERFORM VARYING IDX FROM 1 BY 1
UNTIL IDX > TOTAL
Muito mais claro.
Muito mais rápido.
Muito mais seguro.
Boas práticas
✅ Prefira PERFORM UNTIL para laços controlados.
✅ Use PERFORM VARYING para contadores.
✅ Evite PERFORM chamando o próprio parágrafo.
✅ Revise IFs que nunca alteram a condição de saída.
✅ Analise os warnings do compilador; eles frequentemente apontam defeitos reais de lógica.
✅ Monitore consumo de CPU e storage no SDSF durante testes.
✅ Se precisar de recursão, utilize programas declarados RECURSIVE e valide cuidadosamente profundidade máxima e condição de parada.
✅ Sempre tenha uma condição de saída claramente identificável.
Dicas de depuração
Se um Job "não termina":
Verifique se a CPU continua aumentando no SDSF.
Procure PERFORMs que retornam ao mesmo parágrafo.
Confirme se a variável de controle realmente muda.
Ative
SSRANGEem ambiente de teste para detectar erros relacionados a índices e referências inválidas.Gere um compile listing (
LIST,MAP,XREF) para acompanhar o fluxo de chamadas.Revise mensagens do compilador; um warning ignorado hoje pode virar um ABEND amanhã.
Caminho para o Padawan COBOL
Antes de pensar em recursão, domine completamente:
PERFORMPERFORM THRUPERFORM UNTILPERFORM VARYINGEstrutura de parágrafos e seções
Escopo explícito (
END-IF,END-PERFORM)Fluxo estruturado sem
GO TOSubprogramas com
CALLProgramas
RECURSIVEapenas quando o problema realmente exigir
Quando você entender por que o COBOL prefere estruturas iterativas, começará a enxergar o sistema como os arquitetos do IBM Z enxergam: programas previsíveis, eficientes e fáceis de manter. Em ambientes que processam milhões de transações por dia, previsibilidade vale muito mais do que elegância acadêmica.
Sem comentários:
Enviar um comentário