Bugs difíceis de achar

Saiu um artigo na Wired News sobre os piores bugs da história. Entre eles estão a explosão de um oleoduto soviético em plena guerra-fria (como se não bastasse chernobyl), o primeiro worm da Internet (que se aproveita de um buffer overflow da função gets) e o famoso erro de divisão em ponto flutuante do Pentium: um erro de cálculo de cerca de 0,006% que causou um prejuízo de 457 milhões de dólares para a Intel.

Mas o que achei mais legal, apesar de não estar na lista, estava relacionado com o Mariner 1, primeira espaçonave de um programa da NASA para pesquisar Marte, Vênus e Mercúrio em võos automatizados. O Mariner 1 não chegou a sair de órbita, pois houve uma falha na antena de comunicação entre módulos e um bug no programa do computador de bordo.

Falava-se que o bug havia sido gerado ao trocar uma vírgula por um ponto em um loop escrito em FORTRAN. Apesar de não ter sido esse o causador da falha do computador da nave do projeto Mariner, ele existiu de fato em outro projeto da NASA, o Mercury. A linha fatal no caso era essa:
DO 17 I = 1. 10
É óbvio que a intenção do programador foi fazer um loop até o label 17 dez vezes, pois a instrução para isso é:
DO 17 I = 1, 10
Mas pela troca da vírgula pelo ponto, e como em FORTRAN os caracteres de espaço não são significativos, a linha com o bug não representa mais um loop, mas uma atribuição à uma variável chamada "DO17I":
DO17I = 1.10
Esse detalhe esdrúxulo de uma das linguagens mais famosas da época nos leva a crer que antigamente os programadores deveriam estar muito mais atentos durante a digitação de código do que os programadores de hoje em dia, com seus
ambientes com verificação sintática embutida. Existe inclusive um texto humorístico de longa data comparando programadores de verdade e programadores de linguagens estruturadas como PASCAL recém-saídos da faculdade, carinhosamente citados no texto como "Quiche Eaters" (comedores de pastelão).

O tipo de erro de falta de atenção do programa da NASA lembra uma das mais duras críticas às linguagem C e C++: é fácil escrever um código errado do ponto de vista lógico mas sintaticamente correto (compilável). Alguns exemplos famosos:
/************************************************/
// batata entre os iniciantes
if( isActived && isTimeToLaunch );
doTheStuff();

// dizem que até Brian Kernighan
// (criador do C) criticava

switch( value )
{
case 10:
evaluateSentence();
case 11:
elevenException();
}

// mais um "top beginner"
if( newValue = 20 )
doSpecificStuff();

// infelizmente isso é muito comum
int calcPayment()
{
if( testing == true ) return 1000;
else if( newValue > 500 ) return 1500;
}

// o perigo dos brackets opcionais
if( value == 12 )
//func();
doSpecificStuff();

/**************************************************/
Dessa coleção de problemas, o compilador nos brinda com dois warnings:
warning C4390: ';' : empty controlled statement found; is this the intent?
warning C4715: 'calcPayment' : not all control paths return a value
Em nível 4 para aviso de erros (o padrão de um projeto é 3) há um warning adicional:
warning C4706: assignment within conditional expression
Agora imagine o número de horas noturnas em frente ao micro que você não poderia ter economizado em sua vida se aumentasse o nível de warning e lêsse-os de vez em quando? Nada é infalível e você deve ter plena consciência disso.

É por isso que aprender bem a base é essencial para compreender o assunto de forma mais ampla e evitar erros no futuro. Uma das coisas que aprendi ao longo do doutorado é que não adianta pular etapas no aprendizado. As falhas e dificuldades vão aparecer uma hora ou outra e será necessário voltar para a base.

Mas e aí? Tente ver onde estão as falhas nos códigos acima. Você consegue? Comente! Em outra ocasião eu escrevo sobre isto.

Comments

Popular posts from this blog

Portal CFD Brasil

Avaliação da qualidade da malha

Hipótese do Contínuo