Translate

Mostrar mensagens com a etiqueta Debugging COBOL. Mostrar todas as mensagens
Mostrar mensagens com a etiqueta Debugging COBOL. Mostrar todas as mensagens

quinta-feira, 11 de maio de 2023

Guia Completo de COBOL Recursivo no IBM Mainframe

 

 

Bellacosa Mainframe cobol recursivo


☕ Um Café no Bellacosa Mainframe

Guia Completo de COBOL Recursivo no IBM Mainframe

Da Teoria à Engenharia de Software em Enterprise COBOL

Ao longo desta série exploramos um dos assuntos mais fascinantes — e também menos compreendidos — do Enterprise COBOL: a recursividade.

Embora poucos sistemas corporativos utilizem algoritmos recursivos no dia a dia, compreender esse recurso permite enxergar o funcionamento interno do Enterprise COBOL, do Language Environment (LE) e da pilha de execução (Call Stack), oferecendo uma visão muito mais profunda sobre como programas COBOL realmente funcionam.

Esta série foi escrita pensando no Programador COBOL Padawan que deseja evoluir para Pleno e Sênior, compreendendo não apenas a sintaxe da linguagem, mas também sua arquitetura e seus mecanismos internos. 

📘 Parte 1 — Conceitos Fundamentais

Nesta primeira parte mostramos que recursividade vai muito além do tradicional exemplo do cálculo do fatorial.

Foram apresentados conceitos como:

  • O que realmente significa um programa recursivo.

  • Como funciona o Call Stack.

  • Como o Enterprise COBOL cria novas ativações do programa.

  • Diferenças entre programas RECURSIVE e tradicionais.

  • A importância da Working-Storage e da Local-Storage.

  • Como o Language Environment participa da execução.

➡️ Leia a Parte 1: https://eljefemidnightlunch.blogspot.com/2023/01/cobol-recursivo-muito-alem-do-fatorial.html


📘 Parte 2A — Construindo o Primeiro Programa Recursivo

Na segunda etapa colocamos a teoria em prática.

Construímos um programa recursivo completo em Enterprise COBOL e acompanhamos sua execução passo a passo.

Entre os assuntos abordados:

  • Exemplo completo comentado.

  • Caso Base (Base Case).

  • Crescimento e redução da pilha.

  • Como ocorre o retorno das chamadas (Unwinding).

  • Comparação entre recursividade e PERFORM.

  • Boas práticas para evitar erros comuns.

➡️ Leia a Parte 2A:
https://eljefemidnightlunch.blogspot.com/2023/02/cobol-recursivo-muito-alem-do-fatorial.html


📘 Parte 2B — Aplicações Reais da Recursividade

Depois dos conceitos básicos, mostramos onde a recursividade realmente faz sentido em ambientes corporativos.

Foram apresentados diversos cenários reais, como:

  • Percorrimento de árvores.

  • Estruturas XML.

  • Objetos JSON.

  • Busca em profundidade (DFS).

  • QuickSort.

  • MergeSort.

  • Estruturas hierárquicas.

  • Organogramas.

  • Diretórios do z/OS UNIX (USS).

  • Conceitos utilizados por compiladores e bancos de dados.

Também discutimos quando não utilizar recursividade.

➡️ Leia a Parte 2B:
https://eljefemidnightlunch.blogspot.com/2023/03/cobol-recursivo-muito-alem-do-fatorial.html


📘 Parte 2C — Performance, Debugging e Engenharia

Na última parte entramos nos detalhes que normalmente interessam aos Programadores Mainframe mais experientes.

Entre os temas abordados:

  • Performance da recursividade.

  • Consumo de memória.

  • Stack Overflow.

  • Tail Recursion.

  • Call Stack.

  • Debugging de programas recursivos.

  • Análise de Dumps.

  • Papel do Language Environment (LE).

  • Working-Storage versus Local-Storage.

  • Checklist para utilização segura da recursividade.

  • Dicas, truques e curiosidades pouco conhecidas.

➡️ Leia a Parte 2C:

https://eljefemidnightlunch.blogspot.com/2023/04/cobol-recursivo-muito-alem-do-fatorial.html


O que um Programador COBOL deve levar desta série?

Mesmo que você nunca desenvolva um algoritmo recursivo em produção, compreender esse tema permitirá entender melhor:

  • Como o Enterprise COBOL administra memória.

  • Como funciona a pilha de chamadas.

  • Como parâmetros são preservados.

  • O papel do Language Environment.

  • Por que existe a Local-Storage Section.

  • Como analisar dumps mais complexos.

  • Como modelar problemas hierárquicos de forma elegante.

Em outras palavras, estudar recursividade não serve apenas para aprender uma técnica de programação; serve para compreender a engenharia invisível que sustenta aplicações críticas executadas diariamente no IBM Z.

Conclusão

Recursividade é uma ferramenta poderosa, mas não deve ser utilizada apenas porque produz código elegante. Em processamento linear, o tradicional PERFORM continua sendo, na maioria dos casos, a solução mais eficiente.

Entretanto, quando lidamos com árvores, estruturas aninhadas, documentos XML, objetos JSON, algoritmos de busca, compiladores e diversos outros problemas naturalmente hierárquicos, a recursividade oferece uma forma clara, organizada e expressiva de modelar a solução.

Esperamos que esta série tenha ajudado você a enxergar o Enterprise COBOL sob uma nova perspectiva. Mais do que aprender um recurso da linguagem, você percorreu uma jornada pela arquitetura do IBM Mainframe, compreendendo como memória, pilha de execução, Language Environment e engenharia de software trabalham em conjunto para manter alguns dos sistemas mais críticos do mundo em funcionamento.

☕ Nos encontramos no próximo Café no Bellacosa Mainframe!


quarta-feira, 19 de abril de 2023

COBOL Recursivo — Muito Além do Fatorial (Parte II section C)

 

Bellacosa Mainframe apresenta cobol recursivo parte II sec c

☕ Um Café no Bellacosa Mainframe

COBOL Recursivo — Muito Além do Fatorial (Parte 2C)

Performance, Tail Recursion, Debugging, Language Environment e os Segredos que Todo Programador Mainframe Deveria Conhecer

"A pergunta que todo Programador COBOL Sênior faz não é 'a recursão funciona?', mas sim 'quanto ela custa, quando vale a pena e como o Enterprise COBOL administra tudo isso por baixo dos panos?'" 


Introdução

Chegamos à última parte da nossa jornada.

Na Parte 1 entendemos o conceito.

Na Parte 2A acompanhamos a pilha crescendo e diminuindo.

Na Parte 2B vimos onde a recursividade realmente faz sentido.

Agora vamos responder às perguntas que normalmente aparecem em entrevistas técnicas e discussões entre Programadores Sêniores.

  • A recursão é lenta?

  • Quanto de memória ela consome?

  • O compilador otimiza chamadas recursivas?

  • Existe Tail Recursion no Enterprise COBOL?

  • Como depurar uma rotina recursiva?

  • O que acontece em um dump?

  • Como o Language Environment administra tudo isso?

Prepare mais um café.

Agora vamos olhar por dentro do motor do Enterprise COBOL.


O verdadeiro custo de uma chamada recursiva

Imagine uma rotina extremamente simples.

ROTINA A

↓

ROTINA A

↓

ROTINA A

↓

ROTINA A

Muitos imaginam que apenas uma instrução CALL é executada.

Na realidade ocorre muito mais.

Cada chamada exige que o sistema preserve o contexto da execução atual antes de iniciar a próxima.

Normalmente isso envolve:

  • salvar registradores utilizados;

  • armazenar o endereço de retorno;

  • reservar espaço para variáveis locais;

  • preparar parâmetros;

  • criar um novo frame de execução;

  • transferir o controle para a nova ativação.

Quando essa rotina retorna, todo esse processo acontece novamente, porém na ordem inversa.


Quanto custa isso?

Cada chamada possui um custo fixo.

Imagine uma recursão com profundidade 500.

Teremos aproximadamente:

  • 500 ativações;

  • 500 retornos;

  • centenas de registradores preservados;

  • centenas de frames criados.

Enquanto isso um simples:

PERFORM VARYING

reutiliza praticamente o mesmo ambiente durante toda a execução.

É por isso que loops costumam ser mais rápidos.


O PERFORM continua sendo o campeão

Imagine um processamento de um arquivo VSAM.

100 milhões de registros

Qual solução utilizar?

Recursão?

Jamais.

O correto continua sendo.

PERFORM UNTIL EOF

Por quê?

Porque o problema é linear.

Cada registro é independente.

Não existe árvore.

Não existe hierarquia.

Não existe motivo para criar milhares de novos contextos de execução.


Quando a recursão vence

Agora imagine.

Empresa

↓

Departamento

↓

Subdepartamento

↓

Equipe

↓

Funcionário

Aqui o problema possui profundidade variável.

Não sabemos quantos níveis existirão.

A estrutura muda constantemente.

Nesse cenário a recursividade pode produzir um código muito menor, mais legível e muito mais fácil de manter.


Complexidade não é desempenho

Existe uma confusão bastante comum.

Alguns desenvolvedores acreditam que:

"Se um algoritmo é recursivo então ele é lento."

Não.

O que determina o desempenho é o algoritmo.

QuickSort continua sendo extremamente eficiente.

MergeSort também.

DFS também.

A recursão é apenas uma técnica utilizada para implementar esses algoritmos.


A profundidade da pilha

Imagine.

Nível 1

↓

Nível 2

↓

Nível 3

↓

...

↓

Nível 500

Cada nível ocupa memória.

Quanto maior a profundidade.

Maior o consumo.

Por isso uma pergunta importante é:

Qual a profundidade máxima esperada?


Stack Overflow

Toda pilha possui limite.

Se o algoritmo continuar chamando a si próprio indefinidamente.

Chegará um momento em que não haverá espaço suficiente.

Resultado.

Stack Overflow.

Em ambientes Enterprise COBOL isso normalmente se manifesta como falha de execução provocada pelo esgotamento da pilha ou da região disponível para o processo.


Como evitar?

Existem algumas regras simples.

Sempre possuir:

  • caso base;

  • redução do problema;

  • validação da entrada;

  • profundidade conhecida.

Nunca confiar que os dados "sempre estarão corretos".


Um pequeno erro pode ser catastrófico

Imagine.

PROCESSA(100)

↓

PROCESSA(100)

↓

PROCESSA(100)

Percebe o problema?

O valor nunca muda.

O caso base jamais será alcançado.

O algoritmo continuará chamando a si próprio até consumir toda a pilha.

Esse é um dos bugs mais perigosos em programas recursivos.


Tail Recursion

Agora chegamos a um assunto que raramente aparece em livros de COBOL.

Considere.

ROTINA

↓

chama novamente

↓

retorna imediatamente

Não existe mais nada para fazer após o retorno.

Esse padrão recebe o nome de Tail Recursion.


Por que ela é especial?

Alguns compiladores conseguem transformar automaticamente esse tipo de recursão em um simples loop.

Resultado.

A pilha praticamente deixa de crescer.

Essa otimização é conhecida como Tail Call Optimization (TCO).


E o Enterprise COBOL?

O Enterprise COBOL não é conhecido por realizar uma otimização geral de Tail Call equivalente à encontrada em linguagens como Scheme ou algumas implementações modernas de C/C++. Em outras palavras, não é seguro assumir que uma chamada recursiva em posição de cauda será convertida automaticamente em um laço.

A recomendação prática para aplicações corporativas continua sendo:

  • se a profundidade pode ser grande;

  • e existe uma solução iterativa clara;

prefira PERFORM.

Sempre que escrever uma rotina recursiva pensando em desempenho, consulte a documentação da versão específica do compilador e valide o comportamento com testes e medições.


Debugging

Aqui começa uma das maiores dificuldades.

Imagine um breakpoint.

Você observa.

LS-NUMERO = 4

Continua executando.

Agora.

LS-NUMERO = 3

Depois.

LS-NUMERO = 2

Depois.

LS-NUMERO = 1

O iniciante acredita que a variável está sendo alterada.

Na realidade.

Você está olhando ativações diferentes.

Cada uma possui sua própria Local-Storage.

Esse detalhe costuma confundir quem está depurando um programa recursivo pela primeira vez.


Como depurar corretamente

A primeira dica.

Sempre descubra:

"Em qual nível da pilha estou?"

Depois.

Observe:

  • parâmetros;

  • variáveis locais;

  • valor de retorno.

Nunca apenas o conteúdo de uma variável.


O Dump

Quando ocorre um abend.

O dump costuma revelar algo parecido.

ROTINA

↓

ROTINA

↓

ROTINA

↓

ROTINA

↓

ROTINA

Centenas de vezes.

Isso normalmente indica:

  • ausência de caso base;

  • dados inválidos;

  • profundidade inesperada.

Aprender a reconhecer esse padrão economiza muitas horas de investigação.


Language Environment (LE)

Poucos Programadores Júnior conhecem o LE.

Mas praticamente todo programa Enterprise COBOL moderno depende dele.

O Language Environment é responsável por diversos serviços de tempo de execução, incluindo a organização do ambiente necessário para chamadas de programas, tratamento de exceções, gerenciamento de pilha e integração entre linguagens.

Quando um programa recursivo cria novas ativações, existe uma infraestrutura por trás garantindo que cada contexto seja preservado corretamente.

Sem esse ambiente de execução seria muito mais difícil oferecer suporte consistente a recursos modernos do Enterprise COBOL.


O papel do LOCAL-STORAGE revisitado

Depois de tudo que vimos.

Fica fácil entender.

Cada frame precisa de suas próprias variáveis.

É exatamente isso que Local-Storage oferece.

Visualmente.

+-------------------+

Frame 1

LOCAL-STORAGE

+-------------------+

Frame 2

LOCAL-STORAGE

+-------------------+

Frame 3

LOCAL-STORAGE

+-------------------+

Cada chamada possui sua própria área.

Nenhuma interfere na outra.


E a Working-Storage?

Continua existindo.

Mas pertence ao programa.

Não à chamada.

Visualmente.

WORKING-STORAGE

↓

Frame 1

↓

Frame 2

↓

Frame 3

Todos enxergam a mesma área.

Por isso ela deve armazenar apenas informações realmente compartilhadas.


O impacto em aplicações CICS

Embora o CICS suporte programas escritos em Enterprise COBOL, nem todo programa é um bom candidato à recursão.

Em aplicações OLTP, normalmente buscamos:

  • baixa latência;

  • previsibilidade;

  • consumo controlado de recursos.

Por isso, algoritmos profundamente recursivos raramente aparecem na lógica de transações de alta frequência.

Quando uma solução recursiva for necessária, ela deve ser cuidadosamente analisada quanto à profundidade máxima, uso de memória e comportamento sob carga.


Recursão e paralelismo

Existe outro ponto interessante.

Recursividade não significa paralelismo.

Nem concorrência.

São conceitos completamente diferentes.

É possível possuir:

  • algoritmo recursivo sequencial;

  • algoritmo iterativo paralelo;

  • algoritmo recursivo paralelo.

Não confunda os conceitos.


Quando um Sênior escolhe recursão?

Normalmente quando observa:

✓ estrutura hierárquica

✓ profundidade variável

✓ código muito mais simples

✓ facilidade de manutenção

✓ menor complexidade lógica

Ou seja.

A decisão raramente é baseada apenas em velocidade.


Checklist Bellacosa ☕

Antes de escrever um algoritmo recursivo, faça estas perguntas:

  • Existe um caso base claramente definido?

  • Cada chamada aproxima o problema desse caso base?

  • A profundidade máxima é conhecida ou razoavelmente limitada?

  • Uma solução iterativa seria significativamente mais simples?

  • As variáveis específicas de cada chamada estão em LOCAL-STORAGE?

  • O algoritmo foi testado com entradas extremas?

  • O comportamento em erro e em dumps é compreendido pela equipe?

Se alguma resposta for "não", vale a pena revisar o projeto antes de seguir.


Truques de Programador Mainframe

Algumas boas práticas que ajudam muito.

✔ Nunca misture lógica recursiva com variáveis globais desnecessárias.

✔ Documente claramente qual é o caso base.

✔ Comente qual parâmetro reduz o problema.

✔ Sempre teste entradas:

  • mínimas;

  • máximas;

  • inválidas.

✔ Desenhe a árvore de chamadas antes de codificar.

✔ Não escolha recursão apenas porque o código fica "bonito".

Código elegante que produz um abend continua sendo um programa ruim.


Easter Egg Bellacosa ☕

Existe uma curiosidade interessante.

Muitos Programadores Mainframe trabalham vinte ou trinta anos sem escrever um único algoritmo recursivo.

Mesmo assim.

Os melhores profissionais costumam compreender perfeitamente:

  • Call Stack;

  • Frames;

  • Local-Storage;

  • Language Environment;

  • Endereços de retorno;

  • Passagem de parâmetros.

Por quê?

Porque todos esses conceitos aparecem diariamente em dumps, depuração, integração com C, Assembler, APIs, LE e análise de problemas complexos.

Ou seja.

Você pode nunca escrever um QuickSort recursivo.

Mas provavelmente utilizará o conhecimento adquirido estudando recursão durante toda sua carreira.


Uma reflexão final

Existe um velho ditado entre arquitetos de software.

"Toda abstração tem um custo."

A recursividade é uma abstração poderosa.

Ela reduz dezenas de linhas de código para poucas chamadas elegantes.

Em troca.

Consome pilha.

Cria novos contextos.

Exige planejamento.

O Programador Júnior pergunta:

"Posso usar?"

O Programador Pleno pergunta:

"Vale a pena usar?"

O Programador Sênior pergunta:

"Qual é o impacto dessa decisão daqui a cinco anos?"

É essa mudança de perspectiva que diferencia quem apenas domina a sintaxe de quem realmente compreende engenharia de software.


Conclusão da Série

Se você acompanhou esta série desde a Parte 1, provavelmente percebeu que o objetivo nunca foi ensinar apenas um algoritmo de fatorial.

Nosso verdadeiro objetivo foi mostrar que a recursividade é uma porta de entrada para compreender a arquitetura do Enterprise COBOL.

Ao estudar esse tema, aprendemos sobre:

  • Call Stack;

  • Frames de execução;

  • RECURSIVE;

  • WORKING-STORAGE versus LOCAL-STORAGE;

  • passagem de parâmetros;

  • modelagem de problemas hierárquicos;

  • árvores, XML, JSON e algoritmos clássicos;

  • desempenho, consumo de memória e depuração.

Talvez você passe toda a carreira sem precisar implementar uma rotina recursiva em produção.

Ainda assim, entender como ela funciona tornará muito mais fácil compreender dumps, o Language Environment, integrações com C e Assembler, além do comportamento interno do Enterprise COBOL.

No fim das contas, a maior contribuição da recursividade não é ensinar o computador a chamar uma rotina novamente.

É ensinar o programador a pensar em estruturas, contexto, memória e arquitetura.

E essa forma de pensar acompanha um verdadeiro Engenheiro Mainframe por toda a vida profissional.

☕ Fim da série "COBOL Recursivo — Muito Além do Fatorial". Juntas, as Partes 1, 2A, 2B e 2C formam um material extenso e progressivo, saindo dos fundamentos até aspectos de arquitetura e engenharia de software voltados ao Enterprise COBOL no IBM Z.