Folclore de programadores e engenheiros (parte 1)

Folclore de programadores e engenheiros (parte 1)

Esta é uma seleção de histórias da Internet sobre como os bugs às vezes têm manifestações completamente incríveis. Talvez você também tenha algo para contar.

Alergia de carro a sorvete de baunilha

Uma história para engenheiros que entendem que o óbvio nem sempre é a resposta e que, por mais rebuscados que os fatos possam parecer, eles ainda são fatos. A Divisão Pontiac da General Motors Corporation recebeu uma reclamação:

Esta é a segunda vez que escrevo para você e não culpo você por não responder, porque parece loucura. Nossa família tem a tradição de tomar sorvete todas as noites depois do jantar. Os tipos de sorvete mudam a cada vez e depois do jantar toda a família escolhe qual sorvete comprar, depois vou à loja. Recentemente comprei um Pontiac novo e desde então minhas idas para comprar sorvete se tornaram um problema. Veja bem, toda vez que compro sorvete de baunilha e volto da loja, o carro não pega. Se eu levar qualquer outro sorvete, o carro dá partida sem problemas. Quero fazer uma pergunta séria, por mais estúpida que pareça: “O que há no Pontiac que faz com que ele não ligue quando trago sorvete de baunilha, mas ligue facilmente quando trago outro sabor de sorvete?” "

Como você pode imaginar, o presidente da divisão estava cético em relação à carta. No entanto, por precaução, enviei um engenheiro para verificar. Ele ficou surpreso ao ser recebido por um homem rico e instruído que morava em uma região bonita. Eles concordaram em se encontrar imediatamente após o jantar para que os dois pudessem ir à loja comprar sorvete. Naquela noite era baunilha e, quando voltaram para o carro, ele não pegava.

O engenheiro veio mais três noites. Na primeira vez o sorvete foi de chocolate. O carro deu partida. Na segunda vez houve sorvete de morango. O carro deu partida. Na terceira noite ele pediu baunilha. O carro não pegou.

Raciocinando racionalmente, o engenheiro recusou-se a acreditar que o carro fosse alérgico a sorvete de baunilha. Portanto, combinei com o dono do carro que ele continuaria suas visitas até encontrar uma solução para o problema. E ao longo do caminho começou a fazer anotações: anotou todas as informações, horário do dia, tipo de gasolina, horário de chegada e retorno da loja, etc.

O engenheiro logo percebeu que o dono do carro gastava menos tempo comprando sorvete de baunilha. O motivo foi a disposição das mercadorias na loja. O sorvete de baunilha era o mais popular e ficava em um freezer separado na frente da loja para facilitar sua localização. E todas as outras variedades estavam no fundo da loja, e demorava muito mais tempo para encontrar a variedade certa e pagar.

Agora a pergunta era para o engenheiro: por que o carro não deu partida se havia passado menos tempo desde o momento em que o motor foi desligado? Como o problema era o tempo e não o sorvete de baunilha, o engenheiro rapidamente encontrou a resposta: era uma eclusa de gás. Acontecia todas as noites, mas quando o dono do carro passava mais tempo procurando sorvete, o motor conseguia esfriar o suficiente e dava partida com facilidade. E quando o homem comprou sorvete de baunilha, o motor ainda estava muito quente e a trava do acelerador não teve tempo de se dissolver.

Moral: Mesmo problemas completamente malucos às vezes são reais.

Bater Bandicoot

É doloroso vivenciar isso. Como programador, você se acostuma a culpar seu código primeiro, segundo, terceiro... e em algum lugar no décimo milésimo lugar você culpa o compilador. E mais abaixo na lista você já culpa o equipamento.

Aqui está minha história sobre o bug de hardware.

Para o jogo Crash Bandicoot, escrevi um código para carregar e salvar em um cartão de memória. Para um desenvolvedor de jogos tão presunçoso, foi como um passeio no parque: pensei que o trabalho levaria vários dias. No entanto, acabei depurando o código por seis semanas. Ao longo do caminho, resolvi outros problemas, mas a cada poucos dias voltava a esse código por algumas horas. Foi uma agonia.

O sintoma era o seguinte: quando você salva o jogo atual e acessa o cartão de memória, quase sempre tudo corre bem... Mas às vezes a operação de leitura ou gravação atinge o tempo limite sem motivo óbvio. Uma gravação curta geralmente danifica o cartão de memória. Quando um jogador tenta salvar, ele não apenas falha, mas também destrói o mapa. Besteira.

Depois de um tempo, nossa produtora na Sony, Connie Bus, começou a entrar em pânico. Não foi possível lançar o jogo com esse bug e seis semanas depois eu não entendia o que estava causando o problema. Através de Connie, entramos em contato com outros desenvolvedores do PS1: alguém encontrou algo semelhante? Não. Ninguém teve problemas com o cartão de memória.

Quando você não tem ideias para depuração, a única abordagem que resta é “dividir para conquistar”: remover cada vez mais código do programa defeituoso até que reste um fragmento relativamente pequeno que ainda cause o problema. Ou seja, você corta o programa pedaço por pedaço até que a parte que contém o bug permaneça.

Mas a questão é que é muito difícil cortar pedaços de um videogame. Como executá-lo se você removeu o código que emula a gravidade? Ou desenhando personagens?

Portanto, temos que substituir módulos inteiros por stubs que fingem fazer algo útil, mas na verdade fazem algo muito simples que não pode conter erros. Temos que escrever essas muletas para que o jogo pelo menos funcione. Este é um processo lento e doloroso.

Resumindo, eu consegui. Removi mais e mais trechos de código até ficar com o código inicial que configura o sistema para rodar o jogo, inicializa o hardware de renderização, etc. Claro que nesta fase não consegui criar um menu salvar e carregar, pois teria que criar um stub para todo o código gráfico. Mas eu poderia fingir ser um usuário usando a tela (invisível) de salvar e carregar e pedir para salvar e depois gravar no cartão de memória.

Isso me deixou com um pequeno pedaço de código que ainda apresentava o problema acima - mas ainda acontecia aleatoriamente! Na maioria das vezes tudo funcionava bem, mas ocasionalmente ocorriam falhas. Removi quase todo o código do jogo, mas o bug ainda estava ativo. Isso foi intrigante: o código restante não fez nada.

Em algum momento, provavelmente por volta das três da manhã, um pensamento me ocorreu. As operações de leitura e gravação (entrada/saída) envolvem tempos de execução precisos. Quando você trabalha com um disco rígido, cartão de memória ou módulo Bluetooth, o código de baixo nível responsável pela leitura e gravação o faz de acordo com os pulsos do clock.

Com a ajuda de um relógio, um dispositivo que não está diretamente conectado ao processador é sincronizado com o código em execução no processador. O relógio determina a taxa de transmissão – a velocidade com que os dados são transmitidos. Se houver confusão com os tempos, então o hardware ou o software, ou ambos, também ficarão confusos. E isso é muito ruim, porque os dados podem ser danificados.

E se algo em nosso código confundir os tempos? Verifiquei tudo relacionado a isso no código do programa de teste e percebi que definimos o temporizador programável no PS1 para 1 kHz (1000 ticks por segundo). Isso é bastante; por padrão, quando o console é iniciado, ele funciona a 100 Hz. E a maioria dos jogos usa essa frequência.

Andy, o desenvolvedor do jogo, ajustou o cronômetro para 1 kHz para que os movimentos fossem calculados com mais precisão. Andy tende a exagerar e, se emularmos a gravidade, faremos isso com a maior precisão possível!

Mas e se acelerar o cronômetro de alguma forma afetasse o tempo geral do programa e, portanto, o relógio que regula a taxa de transmissão do cartão de memória?

Comentei o código do temporizador. O erro não ocorreu novamente. Mas isso não significa que consertamos, pois a falha ocorreu aleatoriamente. E se eu tivesse sorte?

Alguns dias depois, experimentei novamente o programa de teste. O bug não ocorreu novamente. Voltei para a base de código completa do jogo e modifiquei o código de salvamento e carregamento para que o cronômetro programável fosse redefinido para seu valor original (100 Hz) antes de acessar o cartão de memória e, em seguida, redefinido para 1kHz. Não houve mais acidentes.

Mas por que isso aconteceu?

Voltei ao programa de teste novamente. Tentei encontrar algum padrão na ocorrência de um erro com um temporizador de 1 kHz. Eventualmente percebi que o erro ocorre quando alguém joga com um controle PS1. Já que eu raramente faria isso sozinho - por que precisaria de um controlador ao testar salvar e carregar código? - Eu nem percebi essa dependência. Mas um dia um de nossos artistas estava esperando que eu terminasse os testes - provavelmente eu estava xingando naquele momento - e girou nervosamente o controle nas mãos. Ocorreu um erro. "Espere o que?!" Bem, faça de novo!

Quando percebi que esses dois eventos estavam interligados, consegui reproduzir facilmente o erro: comecei a gravar no cartão de memória, movi o controlador e estraguei o cartão de memória. Para mim, parecia um bug de hardware.

Procurei Connie e contei-lhe minha descoberta. Ela transmitiu a informação a um dos engenheiros que projetou o PS1. “Impossível”, ele respondeu, “não pode ser um problema de hardware”. Pedi a Connie que marcasse uma conversa para nós.

O engenheiro me ligou e discutimos em seu inglês ruim e em meu japonês (extremamente) ruim. Finalmente eu disse: “Deixe-me enviar meu programa de teste de 30 linhas onde mover o controlador causa um bug”. Ele concordou. Disse que era uma perda de tempo e que estava muito ocupado trabalhando em um novo projeto, mas que cederia porque éramos um desenvolvedor muito importante para a Sony. Limpei meu programa de teste e enviei para ele.

Na noite seguinte (estávamos em Los Angeles e ele em Tóquio), ele me ligou e pediu desculpas timidamente. Foi um problema de hardware.

Não sei exatamente qual era o bug, mas pelo que ouvi na sede da Sony, se você definir o cronômetro para um valor alto o suficiente, ele interferirá nos componentes da placa-mãe próximos ao cristal do cronômetro. Um deles era um controlador de taxa de transmissão para o cartão de memória, que também definia a taxa de transmissão dos controladores. Não sou engenheiro, então posso ter estragado alguma coisa.

Mas o resultado final é que houve interferência entre os componentes da placa-mãe. E ao transmitir dados simultaneamente através da porta do controlador e da porta do cartão de memória com um temporizador funcionando a 1 kHz, bits foram perdidos, dados foram perdidos e o cartão foi danificado.

Vacas ruins

Na década de 1980, meu mentor Sergei escreveu software para o SM-1800, um clone soviético do PDP-11. Este microcomputador acaba de ser instalado numa estação ferroviária perto de Sverdlovsk, um importante centro de transportes da URSS. O novo sistema foi projetado para rotear vagões e tráfego de carga. Mas continha um bug irritante que causava travamentos e travamentos aleatórios. As quedas sempre ocorriam quando alguém voltava para casa à noite. Mas apesar de uma investigação minuciosa no dia seguinte, o computador funcionou corretamente em todos os testes manuais e automáticos. Isso geralmente indica uma condição de corrida ou algum outro bug competitivo que ocorre sob certas condições. Cansado de ligações tarde da noite, Sergei decidiu ir ao fundo da questão e, antes de tudo, entender quais condições no pátio de triagem levaram ao colapso do computador.

Primeiro, ele coletou estatísticas de todas as quedas inexplicáveis ​​e criou um gráfico por data e hora. O padrão era óbvio. Depois de observar por mais alguns dias, Sergei percebeu que poderia facilmente prever o tempo de futuras falhas do sistema.

Ele logo descobriu que as interrupções só ocorriam quando a estação separava cargas de gado do norte da Ucrânia e do oeste da Rússia com destino a um matadouro próximo. Isto por si só era estranho, porque o matadouro era abastecido por fazendas localizadas muito mais perto, no Cazaquistão.

A central nuclear de Chernobyl explodiu em 1986 e a precipitação radioactiva tornou as áreas circundantes inabitáveis. Vastas áreas no norte da Ucrânia, na Bielorrússia e no oeste da Rússia foram contaminadas. Suspeitando de altos níveis de radiação nas carruagens que chegavam, Sergei desenvolveu um método para testar esta teoria. A população foi proibida de ter dosímetros, então Sergei se registrou junto a vários militares na estação ferroviária. Depois de vários goles de vodca, ele conseguiu convencer um soldado a medir o nível de radiação em uma das carruagens suspeitas. Descobriu-se que o nível era várias vezes superior aos valores normais.

Além de o gado emitir muita radiação, seu nível era tão alto que levava à perda aleatória de bits na memória do SM-1800, que ficava em um prédio próximo à estação.

Houve escassez de alimentos na URSS e as autoridades decidiram misturar a carne de Chernobyl com carne de outras regiões do país. Isto permitiu reduzir o nível geral de radioatividade sem perder recursos valiosos. Ao saber disso, Sergei preencheu imediatamente os documentos de emigração. E as falhas do computador pararam por conta própria quando o nível de radiação diminuiu com o tempo.

Através dos canos

Era uma vez, a Movietech Solutions criava softwares para cinemas, voltados para contabilidade, venda de ingressos e gestão geral. A versão DOS do aplicativo principal era bastante popular entre redes de cinemas de pequeno e médio porte na América do Norte. Portanto, não é surpreendente que, quando foi anunciada uma versão do Windows 95, integrada com as mais recentes telas sensíveis ao toque e quiosques de autoatendimento, e equipada com todos os tipos de ferramentas de relatórios, ela também rapidamente se tornou popular. Na maioria das vezes, a atualização ocorreu sem problemas. A equipe de TI local instalou novos equipamentos, migrou dados e os negócios continuaram. Exceto quando não durou. Quando isso acontecesse, a empresa enviaria James, apelidado de “O Limpador”.

Embora o apelido sugira um tipo nefasto, o limpador é apenas uma combinação de instrutor, instalador e pau para toda obra. James passava alguns dias no local do cliente montando todos os componentes e depois passava mais alguns dias ensinando à equipe como usar o novo sistema, resolvendo quaisquer problemas de hardware que surgissem e essencialmente ajudando o software em sua infância.

Portanto, não é de surpreender que, durante esses tempos agitados, James chegasse ao escritório pela manhã e, antes que pudesse chegar à sua mesa, fosse recebido pelo gerente, cheio de cafeína além do habitual.

“Receio que você precise ir para Annapolis, Nova Escócia, o mais rápido possível.” Todo o sistema deles caiu e, depois de uma noite trabalhando com seus engenheiros, não conseguimos descobrir o que aconteceu. Parece que a rede falhou no servidor. Mas somente depois que o sistema estiver funcionando por vários minutos.

— Eles não voltaram ao sistema antigo? - James respondeu completamente sério, embora mentalmente tenha arregalado os olhos de surpresa.

— Exatamente: o especialista em TI “mudou as prioridades” e decidiu sair com o antigo servidor. James, eles instalaram o sistema em seis locais e pagaram apenas pelo suporte premium, e seu negócio agora é administrado como na década de 1950.

James se endireitou ligeiramente.

- Isso é outro assunto. Ok, vamos começar.

Ao chegar em Anápolis, a primeira coisa que fez foi encontrar o primeiro cinema do cliente que apresentava problema. No mapa tirado no aeroporto, tudo parecia decente, mas a área ao redor do endereço desejado parecia suspeita. Não é um gueto, mas lembra o filme noir. Enquanto James estacionava na calçada do centro da cidade, uma prostituta se aproximou dele. Dado o tamanho de Annapolis, provavelmente era o único em toda a cidade. Sua aparição imediatamente trouxe à mente o famoso personagem que oferecia sexo por dinheiro na tela grande. Não, não sobre Julia Roberts, mas sobre Jon Voight [alusão ao filme "Midnight Cowboy" - aprox. faixa].

Depois de mandar a prostituta embora, James foi ao cinema. A área ao redor havia melhorado, mas ainda dava a impressão de estar degradada. Não que James estivesse muito preocupado. Ele já esteve em lugares miseráveis ​​antes. E este foi o Canadá, onde até os assaltantes são educados o suficiente para dizer “obrigado” depois de roubarem a sua carteira.

A entrada lateral do cinema ficava num beco úmido. James foi até a porta e bateu. Logo ele rangeu e abriu ligeiramente.

-Você é faxineiro? - uma voz rouca veio de dentro.

- Sim, sou eu... vim consertar tudo.

James entrou no saguão do cinema. Aparentemente não tendo outra escolha, os funcionários começaram a distribuir ingressos em papel aos visitantes. Isto dificultou os relatórios financeiros e muito menos detalhes mais interessantes. Mas a equipe cumprimentou James com alívio e imediatamente o levou para a sala do servidor.

À primeira vista, estava tudo bem. James se conectou ao servidor e verificou os locais suspeitos de costume. Sem problemas. No entanto, por precaução, James desligou o servidor, substituiu a placa de rede e reverteu o sistema. Ela imediatamente começou a trabalhar integralmente. A equipe começou a vender ingressos novamente.

James ligou para Mark e o informou da situação. Não é difícil imaginar que James queira ficar por aqui e ver se algo inesperado acontece. Ele desceu as escadas e começou a perguntar aos funcionários o que aconteceu. Obviamente o sistema parou de funcionar. Eles desligaram e ligaram, tudo funcionou. Mas depois de 10 minutos o sistema caiu.

Justamente neste momento algo semelhante aconteceu. De repente, o sistema de bilhetagem começou a gerar erros. A equipe suspirou e pegou os tíquetes de papel, e James correu para a sala do servidor. Tudo parecia bem com o servidor.

Então um dos funcionários entrou.

— O sistema está funcionando novamente.

James ficou intrigado porque não tinha feito nada. Mais precisamente, nada que fizesse o sistema funcionar. Ele desconectou-se, pegou o telefone e ligou para a linha de suporte da empresa. Logo o mesmo funcionário entrou na sala do servidor.

- O sistema está fora do ar.

James olhou para o servidor. Um padrão interessante e familiar de formas multicoloridas dançava na tela - tubos caoticamente contorcidos e entrelaçados. Todos nós já vimos esse protetor de tela em algum momento. Foi lindamente renderizado e literalmente hipnotizante.


James apertou um botão e o padrão desapareceu. Ele correu para a bilheteria e no caminho encontrou um funcionário que voltava para ele.

— O sistema está funcionando novamente.

Se você consegue fazer uma palma facial mental, foi exatamente isso que James fez. Protetor de tela. Ele usa OpenGL. E portanto, durante a operação, consome todos os recursos do processador do servidor. Como resultado, cada chamada ao servidor termina com um tempo limite.

James voltou para a sala do servidor, fez login e substituiu o protetor de tela pelos lindos canos por uma tela em branco. Ou seja, em vez de um protetor de tela que consome 100% dos recursos do processador, instalei outro que não consome recursos. Então esperei 10 minutos para verificar meu palpite.

Quando James chegou ao próximo cinema, ele estava se perguntando como explicar ao seu empresário que tinha acabado de voar 800 km para desligar o protetor de tela.

Crash durante uma determinada fase da lua

História verdadeira. Um dia surgiu um bug de software que dependia da fase da lua. Havia uma pequena rotina comumente usada em vários programas do MIT para calcular a aproximação da verdadeira fase da Lua. O GLS incorporou essa rotina em um programa LISP que, ao escrever um arquivo, geraria uma linha com um carimbo de data/hora de quase 80 caracteres. Era muito raro que a primeira linha de uma mensagem acabasse sendo muito longa e levasse à próxima linha. E quando o programa leu esse arquivo mais tarde, ele amaldiçoou. O comprimento da primeira linha dependia da data e hora exatas, bem como do comprimento da especificação da fase no momento em que o carimbo de data/hora foi impresso. Ou seja, o bicho dependia literalmente da fase da lua!

Primeira edição em papel Arquivo de jargão (Steele-1983) continha um exemplo de tal linha que levou ao bug descrito, mas o compositor o “consertou”. Desde então, isso foi descrito como um "bug da fase da lua".

No entanto, tenha cuidado com suposições. Há alguns anos, engenheiros do CERN (Centro Europeu de Pesquisa Nuclear) encontraram erros em experimentos realizados no Grande Colisor de Elétrons-Positrons. Como os computadores processam ativamente a enorme quantidade de dados gerados por este dispositivo antes de mostrar o resultado aos cientistas, muitos especularam que o software era de alguma forma sensível à fase da lua. Vários engenheiros desesperados descobriram a verdade. O erro surgiu devido a uma ligeira mudança na geometria do anel de 27 km devido à deformação da Terra durante a passagem da Lua! Esta história entrou para o folclore da física como “A Vingança de Newton sobre a Física de Partículas” e um exemplo da conexão entre as leis mais simples e antigas da física e os conceitos científicos mais avançados.

Dar descarga no banheiro para o trem

O melhor bug de hardware de que já ouvi falar foi em um trem de alta velocidade na França. O bug levou à frenagem de emergência do trem, mas apenas se houvesse passageiros a bordo. Em cada um desses casos, o trem foi retirado de serviço, verificado, mas nada foi encontrado. Então ele foi mandado de volta para a linha e imediatamente parou.

Durante uma das verificações, um maquinista que viajava no trem foi ao banheiro. Ele logo foi embora, BOOM! Parada de emergência.

O engenheiro contatou o motorista e perguntou:

— O que você estava fazendo antes de frear?

- Bom, diminuí a velocidade na descida...

Isso foi estranho, porque durante a operação normal o trem desacelera dezenas de vezes nas descidas. O trem seguiu em frente e na descida seguinte o maquinista avisou:

- Vou desacelerar.

Nada aconteceu.

— O que você fez durante a última frenagem? - perguntou o motorista.

- Bem... eu estava no banheiro...

- Bem, então vá ao banheiro e faça o que você fez quando descermos de novo!

O engenheiro foi ao banheiro e quando o motorista avisou: “Estou diminuindo a velocidade”, ele deu descarga. Claro, o trem parou imediatamente.

Agora eles poderiam reproduzir o problema e precisavam encontrar a causa.

Após dois minutos, notaram que o cabo do controle remoto do freio motor (o trem tinha um motor em cada extremidade) estava desconectado da parede do quadro elétrico e repousava sobre o relé que controlava o solenóide do plugue do vaso sanitário... Quando o relé foi ligado, criou interferência no cabo do freio e a proteção do sistema contra falhas incluiu simplesmente a frenagem de emergência.

O gateway que odiava FORTRAN

Há alguns meses, notamos que as conexões de rede no continente [isso foi no Havaí] estavam ficando muito, muito lentas. Isso pode durar de 10 a 15 minutos e ocorrer novamente de repente. Depois de algum tempo, meu colega reclamou comigo que as conexões de rede no continente em geral não funciona. Ele tinha alguns códigos FORTRAN que precisavam ser copiados para uma máquina no continente, mas não foi possível porque "a rede não aguentou o tempo suficiente para que o upload do FTP fosse concluído".

Sim, descobriu-se que ocorreram falhas de rede quando um colega tentou enviar por FTP um arquivo com código-fonte em FORTRAN para uma máquina no continente. Tentamos arquivar o arquivo: então ele foi copiado sem problemas (mas a máquina de destino não tinha descompactador, então o problema não foi resolvido). Finalmente “dividimos” o código FORTRAN em pedaços muito pequenos e os enviamos um de cada vez. A maioria dos fragmentos foi copiada sem problemas, mas algumas peças não passaram, ou passaram depois numerosos tentativas.

Quando examinamos as passagens problemáticas, descobrimos que elas tinham algo em comum: todas continham blocos de comentários que começavam e terminavam com linhas com C maiúsculo (como um colega preferia comentar em FORTRAN). Enviamos um e-mail para especialistas de rede no continente e pedimos ajuda. Claro, eles queriam ver amostras dos nossos arquivos que não podiam ser transferidos via FTP... mas nossas cartas não chegaram até eles. Finalmente chegamos a um simples descrevercomo são os arquivos intransferíveis. Funcionou :) [Ouso adicionar um exemplo de um dos comentários problemáticos do FORTRAN aqui? Provavelmente não vale a pena!]

No final conseguimos descobrir. Um novo gateway foi instalado recentemente entre a nossa parte do campus e a rede continental. Tinha ENORME dificuldade em transmitir pacotes que continham bits repetidos de C maiúsculo! Apenas alguns desses pacotes poderiam ocupar todos os recursos do gateway e impedir a passagem da maioria dos outros pacotes. Reclamamos com o fabricante do gateway... e eles responderam: “Ah, sim, você está diante de um bug de C repetido! Já sabemos sobre ele. Acabamos resolvendo o problema comprando um novo gateway de outro fabricante (em defesa do primeiro, a incapacidade de transferir programas FORTRAN pode ser uma vantagem para alguns!).

Tempos difíceis

Há alguns anos, enquanto trabalhava na criação de um sistema ETL em Perl para reduzir os custos dos ensaios clínicos de fase 40, precisei processar cerca de 000 datas. Dois deles não passaram no teste. Isso não me incomodou muito porque essas datas foram retiradas de dados fornecidos pelo cliente que muitas vezes eram, digamos, surpreendentes. Mas quando verifiquei os dados originais, descobri que essas datas eram 1º de janeiro de 2011 e 1º de janeiro de 2007. Achei que o bug estava contido no programa que acabei de escrever, mas descobri que já eram 30 anos velho. Isso pode parecer misterioso para quem não está familiarizado com o ecossistema de software. Devido à decisão de longa data de outra empresa de ganhar dinheiro, meu cliente me pagou para consertar um bug que uma empresa introduziu por acidente e a outra de propósito. Para você entender do que estou falando, preciso falar sobre a empresa que adicionou o recurso que acabou virando um bug, além de alguns outros eventos interessantes que contribuíram para o bug misterioso que corrigi.

Nos velhos tempos, os computadores Apple às vezes redefiniam espontaneamente a data para 1º de janeiro de 1904. O motivo era simples: ele usava um “relógio do sistema” alimentado por bateria para controlar a data e a hora. O que aconteceu quando a bateria acabou? Os computadores começaram a rastrear a data pelo número de segundos desde o início de uma época. Por época queríamos dizer a data original de referência, e para Macintoshes era 1º de janeiro de 1904. E depois que a bateria acabou, a data atual foi redefinida para a data especificada. Mas por que isso aconteceu?

Anteriormente, a Apple usava 32 bits para armazenar o número de segundos desde a data original. Um bit pode armazenar um de dois valores - 1 ou 0. Dois bits podem armazenar um de quatro valores: 00, 01, 10, 11. Três bits - um valor em oito: 000, 001, 010, 011, 100 , 101, 110, 111, etc. E 32 poderia armazenar um dos 232 valores, ou seja, 4 segundos. Para datas da Apple, isso equivale a cerca de 294 anos, portanto, Macs mais antigos não conseguem lidar com datas posteriores a 967. E se a bateria do sistema acabar, a data será redefinida para 296 segundos desde o início da época, e você terá que definir a data manualmente toda vez que ligar o computador (ou até comprar uma bateria nova).

No entanto, a decisão da Apple de armazenar datas como segundos desde a época significou que não poderíamos lidar com datas anteriores à época, o que teve consequências de longo alcance, como veremos. A Apple introduziu um recurso, não um bug. Entre outras coisas, isso significava que o sistema operacional Macintosh era imune ao “bug do milênio” (o que não poderia ser dito sobre muitos aplicativos Mac que tinham seus próprios sistemas de data para contornar as restrições).

Vá em frente. Usávamos o Lotus 1-2-3, o “aplicativo matador” da IBM que ajudou a lançar a revolução do PC, embora os computadores Apple tivessem o VisiCalc, que tornou o computador pessoal um sucesso. Para ser justo, se o 1-2-3 não tivesse aparecido, os PCs dificilmente teriam decolado e a história dos computadores pessoais poderia ter se desenvolvido de forma muito diferente. O Lotus 1-2-3 tratou incorretamente 1900 como um ano bissexto. Quando a Microsoft lançou sua primeira planilha, a Multiplan, conquistou uma pequena fatia do mercado. E quando lançaram o projeto Excel, decidiram não apenas copiar o esquema de nomenclatura de linhas e colunas do Lotus 1-2-3, mas também garantir a compatibilidade de bugs tratando deliberadamente 1900 como um ano bissexto. Este problema ainda existe hoje. Ou seja, no 1-2-3 isso era um bug, mas no Excel foi uma decisão consciente que garantiu que todos os usuários do 1-2-3 pudessem importar suas tabelas para o Excel sem alterar os dados, mesmo que estivessem incorretos.

Mas havia outro problema. Primeiro, a Microsoft lançou o Excel para Macintosh, que não reconhecia datas anteriores a 1º de janeiro de 1904. E no Excel, 1º de janeiro de 1900 foi considerado o início da era. Portanto, os desenvolvedores fizeram uma mudança para que seu programa reconhecesse o tipo de época e armazenasse os dados dentro de si de acordo com a época desejada. A Microsoft até escreveu um artigo explicativo sobre isso. E essa decisão levou ao meu bug.

Meu sistema ETL recebia planilhas Excel de clientes que foram criadas no Windows, mas também podiam ser criadas em um Mac. Portanto, o início da era na tabela poderia ser 1º de janeiro de 1900 ou 1º de janeiro de 1904. Como descobrir? O formato de arquivo Excel mostra as informações necessárias, mas o analisador que usei não as mostrou (agora mostra) e presumiu que você conhece a época de uma tabela específica. Provavelmente eu poderia ter passado mais tempo entendendo o formato binário do Excel e enviando um patch ao autor do analisador, mas tinha muito mais a fazer pelo cliente, então escrevi rapidamente uma heurística para determinar a época. Ela era simples.

No Excel, a data 5 de julho de 1998 pode ser representada no formato "07-05-98" (sistema americano inútil), "5 de julho de 98", "5 de julho de 1998", "5 de julho de 98" ou algum outro formato, outro formato inútil (ironicamente, um dos formatos que minha versão do Excel não oferecia era o ISO 8601). No entanto, na tabela, a data não formatada foi armazenada como "35981" para a época 1900 ou "34519" para a época 1904 (os números representam o número de dias desde a época). Simplesmente usei um analisador simples para extrair o ano da data formatada e, em seguida, usei o analisador do Excel para extrair o ano da data não formatada. Se ambos os valores diferiam em 4 anos, então eu sabia que estava usando um sistema com a época de 1904.

Por que não usei datas formatadas? Porque 5 de julho de 1998 pode ser formatado como "Julho de 98" com o dia do mês perdido. Recebemos tabelas de tantas empresas que as criaram de tantas maneiras diferentes que coube a nós (neste caso, a mim) acertar as datas. Além disso, se o Excel acertar, nós também deveríamos!

Ao mesmo tempo, encontrei 39082. Deixe-me lembrá-lo de que o Lotus 1-2-3 considerou 1900 um ano bissexto e isso foi repetido fielmente no Excel. E como isso adicionou um dia ao ano de 1900, muitas funções de cálculo de data podem estar erradas para esse mesmo dia. Ou seja, 39082 poderia ter sido 1º de janeiro de 2011 (em Macs) ou 31 de dezembro de 2006 (em Windows). Se meu “analisador de ano” extraiu o ano de 2011 do valor formatado, então está tudo bem. Mas como o analisador do Excel não sabe qual época está sendo usada, o padrão é a época 1900, retornando o ano 2006. Minha aplicação viu que a diferença era de 5 anos, considerou um erro, registrou e retornou um valor não formatado.

Para contornar isso, escrevi isto (pseudocódigo):

diff = formatted_year - parsed_year
if 0 == diff
    assume 1900 date system
if 4 == diff
    assume 1904 date system
if 5 == diff and month is December and day is 31
    assume 1904 date system

E então todas as 40 datas foram analisadas corretamente.

No meio de grandes trabalhos de impressão

No início da década de 1980, meu pai trabalhou na Storage Technology, uma divisão extinta que criava unidades de fita e sistemas pneumáticos para alimentação de fita em alta velocidade.

Eles redesenharam as unidades para que pudessem ter uma unidade central “A” conectada a sete unidades “B”, e o pequeno sistema operacional na RAM que controlava a unidade “A” pudesse delegar operações de leitura e gravação a todas as unidades “B”.

Cada vez que a unidade “A” era iniciada, era necessário inserir um disquete na unidade periférica conectada a “A” para carregar o sistema operacional em sua memória. Era extremamente primitivo: o poder computacional era fornecido por um microcontrolador de 8 bits.

O público-alvo desses equipamentos eram empresas com data warehouses muito grandes – bancos, redes de varejo, etc. – que precisavam imprimir muitas etiquetas de endereço ou extratos bancários.

Um cliente teve um problema. No meio de um trabalho de impressão, uma unidade “A” específica pode parar de funcionar, fazendo com que todo o trabalho pare. Para restaurar o funcionamento da unidade, a equipe teve que reiniciar tudo. E se isso acontecesse no meio de uma tarefa de seis horas, uma enorme quantidade de tempo dispendioso do computador seria perdida e o cronograma de toda a operação seria interrompido.

Técnicos foram enviados da Storage Technologies. Mas, apesar dos seus melhores esforços, não conseguiram reproduzir o erro em condições de teste: parecia ocorrer no meio de grandes trabalhos de impressão. O problema não era o hardware, eles substituíram tudo o que podiam: RAM, microcontrolador, unidade de disquete, todas as partes concebíveis da unidade de fita – o problema persistiu.

Aí os técnicos ligaram para a sede e chamaram o Perito.

O especialista pegou uma cadeira e uma xícara de café, sentou-se na sala de informática – naquela época havia salas dedicadas a computadores – e observou a equipe colocar na fila um grande trabalho de impressão. O especialista estava esperando que ocorresse uma falha – e aconteceu. Todos olharam para o Especialista, mas ele não tinha ideia do porquê isso aconteceu. Então ele ordenou que o trabalho fosse colocado novamente na fila e todos os funcionários e técnicos voltassem ao trabalho.

O especialista sentou-se novamente na cadeira e começou a esperar uma falha. Cerca de seis horas se passaram e a falha ocorreu. O Especialista novamente não teve ideia, exceto que tudo aconteceu em uma sala cheia de gente. Ele ordenou que a missão fosse reiniciada, sentou-se e esperou.

Na terceira falha, o Especialista percebeu algo. A falha ocorreu quando o pessoal trocou as fitas em uma unidade externa. Além disso, a falha ocorreu assim que um dos funcionários passou por um determinado ladrilho do chão.

O piso elevado era feito de telhas de alumínio colocadas a uma altura de 6 a 8 centímetros. Vários fios de computadores passavam sob o piso elevado para evitar que alguém pisasse acidentalmente em um cabo importante. Os ladrilhos foram colocados com muita firmeza para evitar que detritos entrassem no piso elevado.

O perito percebeu que um dos ladrilhos estava deformado. Quando um funcionário pisava em seu canto, as bordas do ladrilho roçavam nos ladrilhos adjacentes. As peças plásticas que conectavam as telhas também esfregavam nelas, o que causava microdescargas estáticas que criavam interferência de radiofrequência.

Hoje, a RAM está muito melhor protegida contra interferências de radiofrequência. Mas naqueles anos não era esse o caso. O especialista percebeu que essa interferência atrapalhava a memória e, com ela, o funcionamento do sistema operacional. Ele ligou para o serviço de suporte, encomendou novos ladrilhos, instalou-os ele mesmo e o problema desapareceu.

A maré está alta!

A história se passava em uma sala de servidores, no quarto ou quinto andar de um escritório em Portsmouth (eu acho), na área das docas.

Um dia o servidor Unix com o banco de dados principal travou. Eles o reiniciaram, mas ele felizmente continuou a cair continuamente. Decidimos ligar para alguém do serviço de suporte.

O cara do suporte... Acho que o nome dele era Mark, mas isso não importa... Acho que não o conheço. Não importa, realmente. Vamos ficar com Mark, ok? Ótimo.

Então, algumas horas depois o Mark chegou (não é muito longe de Leeds até Portsmouth, você sabe), ligou o servidor e tudo funcionou sem problemas. Típico maldito suporte, o cliente fica muito chateado com isso. Mark examina os arquivos de log e não encontra nada de desagradável. Então Mark volta para o trem (ou qualquer meio de transporte em que ele chegou, poderia ter sido uma vaca manca, pelo que sei... de qualquer forma, não importa, ok?) e volta para Leeds, tendo perdido o dia.

Naquela mesma noite, o servidor travou novamente. A história é a mesma... o servidor não sobe. Mark tenta ajudar remotamente, mas o cliente não consegue iniciar o servidor.

Outro trem, ônibus, merengue de limão ou alguma outra porcaria, e Mark está de volta a Portsmouth. Olha, o servidor inicializa sem problemas! Milagre. Mark passa várias horas verificando se tudo está em ordem com o sistema operacional ou software e parte para Leeds.

Por volta do meio do dia o servidor trava (calma!). Desta vez parece razoável trazer o pessoal de suporte de hardware para substituir o servidor. Mas não, depois de umas 10 horas também cai.

A situação se repetiu durante vários dias. O servidor funciona, trava após cerca de 10 horas e não inicia nas próximas 2 horas. Verificaram refrigeração, vazamentos de memória, verificaram tudo, mas não encontraram nada. Então as falhas pararam.

A semana passou despreocupada... todos estavam felizes. Feliz até que tudo comece de novo. A imagem é a mesma. 10 horas de trabalho, 2-3 horas de inatividade...

E então alguém (acho que me disseram que essa pessoa não tinha nada a ver com TI) disse:

"É a maré!"

A exclamação foi recebida com olhares vazios, e a mão de alguém provavelmente hesitou no botão de chamada de segurança.

“Ele para de funcionar com a maré.”

Este pareceria ser um conceito completamente estranho para os trabalhadores de suporte de TI, que provavelmente não lerão o Tide Yearbook enquanto se sentam para tomar um café. Explicaram que isso não poderia ter relação alguma com a maré, pois o servidor estava funcionando há uma semana sem falhas.

“Na semana passada a maré estava baixa, mas esta semana está alta.”

Um pouco de terminologia para quem não tem licença de iate. As marés dependem do ciclo lunar. E à medida que a Terra gira, a cada 12,5 horas a atração gravitacional do Sol e da Lua cria um maremoto. No início do ciclo de 12,5 horas há maré alta, no meio do ciclo há vazante e no final há maré alta novamente. Mas à medida que a órbita da Lua muda, também muda a diferença entre a maré baixa e a maré alta. Quando a Lua está entre o Sol e a Terra ou no lado oposto da Terra (lua cheia ou sem lua), temos marés Syzygyn - as marés altas mais altas e as marés baixas mais baixas. Na meia-lua temos marés em quadratura – as marés mais baixas. A diferença entre os dois extremos diminui bastante. O ciclo lunar dura 28 dias: syzygian - quadratura - syzygian - quadratura.

Quando foi explicada aos técnicos a essência das forças das marés, eles imediatamente pensaram que precisavam chamar a polícia. E bastante lógico. Mas acontece que o cara estava certo. Duas semanas antes, um contratorpedeiro atracou não muito longe do escritório. Cada vez que a maré subia a uma certa altura, o posto de radar do navio acabava no nível do chão da sala dos servidores. E o radar (ou equipamento de guerra electrónica, ou algum outro brinquedo militar) criou o caos nos computadores.

Missão de vôo para o foguete

Recebi a tarefa de portar um grande sistema de controle e monitoramento de lançamento de foguetes (cerca de 400 mil linhas) para novas versões do sistema operacional, compilador e linguagem. Mais precisamente, do Solaris 2.5.1 ao Solaris 7, e do Verdix Ada Development System (VADS), escrito em Ada 83, ao sistema Rational Apex Ada, escrito em Ada 95. O VADS foi adquirido pela Rational, e seu produto foi obsoleto, embora o Rational tenha tentado implementar versões compatíveis de pacotes específicos do VADS para facilitar a transição para o compilador Apex.

Três pessoas me ajudaram a compilar o código de forma limpa. Demorou duas semanas. E então trabalhei sozinho para fazer o sistema funcionar. Resumindo, foi a pior arquitetura e implementação de sistema de software que encontrei, então demorou mais dois meses para concluir a portabilidade. O sistema foi então submetido a testes, o que levou mais alguns meses. Corrigi imediatamente os bugs encontrados durante os testes, mas seu número diminuiu rapidamente (o código-fonte era um sistema de produção, então sua funcionalidade funcionava de forma bastante confiável, só tive que remover os bugs que surgiram durante a adaptação ao novo compilador). Eventualmente, quando tudo estava funcionando como deveria, fui transferido para outro projeto.

E na sexta-feira antes do Dia de Ação de Graças, o telefone tocou.

O lançamento do foguete deveria ser testado em cerca de três semanas e, durante os testes de laboratório da contagem regressiva, a sequência de comandos foi bloqueada. Na vida real, isso abortaria o teste, e se o bloqueio ocorresse poucos segundos após a partida do motor, diversas ações irreversíveis ocorreriam nos sistemas auxiliares, o que exigiria uma longa – e cara – prontidão do foguete. Não teria começado, mas muita gente teria ficado muito chateada com a perda de tempo e de muito, muito dinheiro. Não deixe ninguém lhe dizer que o Departamento de Defesa gasta dinheiro de forma imprudente – nunca conheci um gerente de contratação que não colocasse o orçamento em primeiro ou segundo lugar, seguido pelo cronograma.

Nos meses anteriores, esse desafio de contagem regressiva foi executado centenas de vezes em muitas variações, com apenas alguns pequenos soluços. Portanto, a probabilidade de isso acontecer era muito baixa, mas as suas consequências eram muito significativas. Multiplique esses dois fatores e você entenderá que as notícias previam uma semana de férias arruinada para mim e para dezenas de engenheiros e gerentes.

E foi dada atenção a mim como a pessoa que portou o sistema.

Tal como acontece com a maioria dos sistemas críticos de segurança, muitos parâmetros foram registrados, por isso foi bastante fácil identificar as poucas linhas de código que foram executadas antes da falha do sistema. E, claro, não havia absolutamente nada de incomum neles; as mesmas expressões foram executadas com sucesso, literalmente, milhares de vezes durante a mesma execução.

Chamamos o pessoal do Apex para o Rational porque foram eles que desenvolveram o compilador e algumas das rotinas que desenvolveram foram chamadas no código suspeito. Eles (e todos os outros) ficaram impressionados com a necessidade de chegar à raiz de um problema de importância literalmente nacional.

Como não havia nada de interessante nos diários, decidimos tentar reproduzir o problema num laboratório local. Esta não foi uma tarefa fácil, pois o evento ocorreu aproximadamente uma vez a cada 1000 execuções. Um motivo suspeito foi que uma chamada para uma função mutex desenvolvida pelo fornecedor (parte do pacote de migração VADS) Unlock não levou ao desbloqueio. O thread de processamento que chamou a função processou mensagens de pulsação, que chegavam nominalmente a cada segundo. Aumentamos a frequência para 10 Hz, ou seja, 10 vezes por segundo, e começamos a correr. Cerca de uma hora depois, o sistema se bloqueou. No log, vimos que a sequência de mensagens gravadas era a mesma do teste com falha. Fizemos várias outras execuções, o sistema foi bloqueado de forma consistente 45-90 minutos após o início e todas as vezes o log continha a mesma rota. Embora tecnicamente estivéssemos executando um código diferente - a frequência da mensagem era diferente - o comportamento do sistema era o mesmo, então estávamos confiantes de que esse cenário de carregamento estava causando o mesmo problema.

Agora precisávamos descobrir onde exatamente ocorreu o bloqueio na sequência de expressões.

Esta implementação do sistema usou o sistema de tarefas Ada e o usou de forma incrivelmente pobre. Tarefas são uma construção de alto nível executável simultaneamente em Ada, algo como threads de execução, apenas incorporados à própria linguagem. Quando duas tarefas precisam se comunicar, elas “marcam um encontro”, trocam os dados necessários e então interrompem o encontro e retornam às suas execuções independentes. No entanto, o sistema foi implementado de forma diferente. Após o encontro de uma tarefa de destino, essa tarefa de destino se reunia com outra tarefa, que então se reunia com uma terceira tarefa, e assim por diante, até que algum processamento fosse concluído. Depois disso, todos esses encontros foram concluídos e cada tarefa teve que retornar à sua execução. Ou seja, estávamos lidando com o sistema de chamada de função mais caro do mundo, que parava todo o processo de “multitarefa” enquanto processava parte dos dados de entrada. E antes disso não causava problemas apenas porque o rendimento era muito baixo.

Descrevi esse mecanismo de tarefa porque quando um encontro era solicitado ou esperado para ser concluído, uma "troca de tarefa" poderia ocorrer. Ou seja, o processador poderia começar a processar outra tarefa que está pronta para ser executada. Acontece que quando uma tarefa está pronta para se encontrar com outra tarefa, uma tarefa completamente diferente pode começar a ser executada e, eventualmente, o controle retorna ao primeiro encontro. E outros eventos podem ocorrer que fazem com que a tarefa mude; um desses eventos é uma chamada para uma função do sistema, como imprimir ou executar um mutex.

Para entender qual linha de código estava causando o problema, eu precisava encontrar uma maneira de registrar o progresso por meio de uma sequência de instruções sem acionar uma alternância de tarefas, o que evitaria a ocorrência de uma falha. Então eu não pude aproveitar Put_Line()para evitar a execução de operações de E/S. Eu poderia definir uma variável de contador ou algo semelhante, mas como posso ver seu valor se não consigo exibi-lo na tela?

Além disso, ao examinar o log, descobriu-se que, apesar do congelamento no processamento das mensagens de heartbeat, que bloqueou todas as operações de I/O do processo e impediu que outros processamentos fossem realizados, outras tarefas independentes continuaram a ser executadas. Ou seja, o trabalho não foi totalmente bloqueado, apenas uma cadeia (crítica) de tarefas.

Esta foi a pista necessária para avaliar a expressão de bloqueio.

Criei um pacote Ada que continha uma tarefa, um tipo enumerado e uma variável global desse tipo. Literais enumeráveis ​​foram vinculados a expressões específicas da sequência problemática (por exemplo Incrementing_Buffer_Index, Locking_Mutex, Mutex_Unlocked) e, em seguida, inseriu nele expressões de atribuição que atribuíram a enumeração correspondente a uma variável global. Como o código-objeto de tudo isso simplesmente armazenava uma constante na memória, a troca de tarefas como resultado de sua execução era extremamente improvável. Suspeitávamos principalmente de expressões que poderiam alternar a tarefa, uma vez que o bloqueio ocorria na execução, em vez de retornar ao retornar a tarefa (por vários motivos).

A tarefa de rastreamento simplesmente era executada em loop e verificada periodicamente para ver se o valor da variável global havia mudado. A cada alteração, o valor era salvo em um arquivo. Depois, uma breve espera e um novo cheque. Escrevi a variável no arquivo porque a tarefa foi executada somente quando o sistema a selecionou para execução ao alternar a tarefa na área do problema. O que quer que tenha acontecido nesta tarefa não afetaria outras tarefas bloqueadas não relacionadas.

Esperava-se que quando o sistema atingisse o ponto de execução do código problemático, a variável global fosse redefinida ao passar para cada expressão seguinte. Então acontecerá algo que fará com que a tarefa mude, e como sua frequência de execução (10 Hz) é menor que a da tarefa de monitoramento, o monitor poderá capturar o valor da variável global e gravá-la. Em uma situação normal, eu poderia obter uma sequência repetida de um subconjunto de enumerações: os últimos valores da variável no momento da troca de tarefas. Ao travar, a variável global não deverá mais mudar, e o último valor escrito indicará qual expressão não foi concluída.

Executei o código com rastreamento. Ele congelou. E o monitoramento funcionou como um relógio.

O log continha a sequência esperada, que foi interrompida por um valor indicando que um mutex havia sido chamado Unlock, e a tarefa não é concluída - como é o caso de milhares de chamadas anteriores.

Os engenheiros da Apex estavam analisando febrilmente seu código neste momento e encontraram um lugar no mutex onde, teoricamente, um bloqueio poderia ocorrer. Mas sua probabilidade era muito baixa, pois apenas uma determinada sequência de eventos ocorridos em um determinado momento poderia levar ao bloqueio. Lei de Murphy, pessoal, é a Lei de Murphy.

Para proteger o trecho de código que eu precisava, substituí as chamadas de função mutex (construídas sobre a funcionalidade mutex do sistema operacional) por um pequeno pacote nativo de mutex Ada para controlar o acesso mutex a esse trecho.

Eu o inseri no código e executei o teste. Sete horas depois, o código ainda estava funcionando.

Meu código foi enviado ao Rational, onde eles o compilaram, desmontaram e verificaram se ele não usava a mesma abordagem usada nas funções mutex problemáticas.

Esta foi a revisão de código mais concorrida da minha carreira 🙂 Havia cerca de dez engenheiros e gerentes na sala comigo, outras dez pessoas estavam em uma teleconferência - e todos examinaram cerca de 20 linhas de código.

O código foi revisado, novos arquivos executáveis ​​foram montados e submetidos a testes formais de regressão. Algumas semanas depois, o teste de contagem regressiva foi bem-sucedido e o foguete decolou.

Ok, está tudo muito bem, mas qual é o objetivo da história?

Foi um problema absolutamente nojento. Centenas de milhares de linhas de código, execução paralela, mais de uma dúzia de processos interativos, arquitetura e implementação deficientes, interfaces para sistemas embarcados e milhões de dólares gastos. Sem pressão, certo.

Eu não fui o único trabalhando nesse problema, embora estivesse sob os holofotes enquanto fazia a portabilidade. Mas mesmo que eu tenha feito isso, isso não significa que eu entendi todas as centenas de milhares de linhas de código, ou mesmo as dei uma olhada rápida. O código e os logs foram analisados ​​por engenheiros de todo o país, mas quando eles me contaram suas hipóteses sobre as causas da falha, levei apenas meio minuto para refutá-las. E quando me pediam para analisar teorias, eu repassava para outra pessoa, porque era óbvio para mim que esses engenheiros estavam indo na direção errada. Parece presunçoso? Sim, é verdade, mas rejeitei as hipóteses e os pedidos por outro motivo.

Eu entendi a natureza do problema. Eu não sabia exatamente onde estava acontecendo ou por quê, mas sabia o que estava acontecendo.

Ao longo dos anos acumulei muito conhecimento e experiência. Fui um dos pioneiros no uso do Ada e entendi suas vantagens e desvantagens. Eu sei como as bibliotecas de tempo de execução Ada lidam com tarefas e com execução paralela. E eu entendo programação de baixo nível em nível de memória, registradores e assembler. Ou seja, tenho profundo conhecimento na minha área. E eu os usei para encontrar a causa do problema. Eu não apenas resolvi o bug, mas entendi como encontrá-lo em um ambiente de execução muito sensível.

Essas histórias de luta com o código não são muito interessantes para aqueles que não estão familiarizados com as características e condições de tal luta. Mas estas histórias ajudam-nos a compreender o que é necessário para resolver problemas realmente difíceis.

Para resolver problemas realmente difíceis, você precisa ser mais do que apenas um programador. Você precisa entender o “destino” do código, como ele interage com seu ambiente e como o próprio ambiente funciona.

E então você terá sua própria semana de férias arruinada.

Para ser continuado.

Fonte: habr.com

Adicionar um comentário