# Descanso
Caloni, 2009-03-01 <quotes> <self> <now> [up] [copy]Nossa energia física também está sujeita a ciclos.
Tolle, Eckhart (O Poder do Agora, 1997)
# Resolvendo bugs quase impossíveis
Caloni, 2009-03-05 <essays> <blog> [up] [copy]Quase todos os problemas do Universo são resolvidos depois de um belo dia de depuração, código comentado, descomentado, recomentado e umas muitas e boas doses de café. Alguns outros problemas mais cabeludos precisam de uma boa noitada na frente do computador, e mais café. E, finalmente, existem aqueles que nem tomando o estoque inteiro de café a coisa anda.
Um exemplo: um hook global do Windows que quando ativado em determinados eventos envia mensagens para uma única janela que cataloga informações sobre diversas janelas e processos no sistema. Esse procedimento é uma subfunção do programa principal, que já possui seus próprios problemas e idiossincrasias. Em momentos aparentemente aleatórios algumas funcionalidades não parecem estar de acordo com o que se espera.
Para esse tipo de situação que envolve 1. o sistema como um todo, 2. processos de terceiros e 3. comportamento obscuro por parte do resto do código, vale a pena seguir um checklist mais rigoroso, colocar seu bonezinho de CSI e partir para desmembrar o funcionamento do código problemático:
1. Como o programa deveria funcionar?
2. O que exatamente não funciona?
3. O que pode ser? O que NÃO pode ser?
4. Existe uma maneira de provar?
Cada uma dessas perguntas deve ser respondida com a maior sinceridade e disciplina, custe o que custar.
Esse deve ser o primeiro e mais importante indício do que pode estar acontecendo. Sem entender o funcionamento do programa, dificilmente conseguiremos passar para os passos seguintes. Na maioria das vezes, sem saber onde a coisa começa e termina, o problema vai ficar rindo da nossa cara até entendermos de fato que aquele if não merece estar naquela linha.
Para facilitar esse entendimento, nada como elaborar uma pequena explicação para si mesmo no estilo How Stuff Works. Não precisa exagerar e fazer uma tese a respeito e criar vídeos explicativos. Só precisa descrever o fluxo com os detalhes aparentemente importantes para a resolução do problema.
Continuando nosso exemplo:
1. O programa inicia e cria uma thread específica.
2. Essa thread específica cria uma janela que monitora e carrega uma DLL.
3. Essa DLL é chamada pela thread e instala um hook global no sistema.
4. O hook recebe eventos de todos os processos que possuem janelas.
5. Quando eventos específicos são disparados, o processo atual envia uma mensagem para a janela que monitora.
6. A janela que monitora monta uma tabela estatística dos eventos.
7. De tempos em tempos, essa tabela é escrita em disco em um arquivo encriptado.
A lista acima é longa o suficiente para podermos elaborar perguntas interessantes e pequena o suficiente para podermos ter em mente o seu funcionamento como um todo, o que é vital para o sucesso das observações durante a depuração.
Note que a pergunta nos direciona para o sintoma do problema, não o problema em si, que provavelmente ainda não é conhecido. E nunca é demais lembrar que podemos estar lidando com uma série de problemas trabalhando em conjunto para nos deixar acordados por dias a fio.
Exemplos de respostas possíveis: a tabela estatística perde a lógica em determinado momento, o hook algumas vezes não funciona, aleatoriamente um dos processos "hookados" capota.
Essa pergunta deve ser respondida com uma análise das respostas das duas primeiras perguntas. Batendo os sintomas do problema com o seu funcionamento macro, uma ou mais cabeças aos poucos irão elaborando teorias a respeito de onde pode estar falhando.
Ex: talvez por algum motivo a DLL esteja sendo descarregada (que lugares podem ser estes?), alguém está desinstalando o hook (quais as partes do código que fazem isso?), alguma ferramenta de análise está atrapalhando nossos resultados (o que acontece se rodarmos sem o DebugView?).
Ao mesmo tempo que os sintomas do problema acusam que algo está errado, existem os sintomas de que alguma coisa, afinal de contas, está funcionando nessa porcaria de código. Através dos sintomas positivos é possível chegar a algumas conclusões sobre o que está funcionando bem.
Ex: o arquivo de log está sendo atualizado, a thread da janela que monitora recebe mensagens continuamente, algumas informações da tabela não estão corrompidas.
Esse é o pulo do gato, a parte que diferencia meninos e meninas de homens e mulheres. Se conseguirmos, através de código de teste e/ou observação, aos poucos provar nossas conclusões a respeito do problema e conseguir elaborar, passo a passo, uma "maquete mental" de todo o código funcional, será possível aos poucos ir descartando teorias e reforçando nossa confiança sobre o caminho que estamos trilhando.
Às vezes uma pequena mudança no código pode provar inúmeras coisas, como inocentar algumas partes e proteger-se de acusações infundadas feitas anteriormente. É uma briga contra o próprio ego, especialmente se o código foi feito por você mesmo.
Ex: Desabilitei o tratamento dos eventos e o hook continua funcionando.
O importante é nunca parar de pensar sobre o problema, evitando ao máximo agir mecanicamente e por impulso, a não ser que exista um bom motivo para isso. Às vezes apenas pensando de novo sobre o mesmo assunto comprova-se algo. É uma fase muito rica e próspera na resolução de problemas e deve ser aproveitada.
Ex: Quando estava habilitado o tratamento de eventos, o hook parava de funcionar em menos de cinco minutos. Agora, rodando os testes por três horas, o hook continua ativo.
Ex: Desabilitei um dos eventos que possui comunicação remota com o servidor. O hook continuou funcionando, apesar do resto dos eventos.
Por fim, com uma pequena dose de sorte e muitas doses de força de vontade (e café), o problema cansa de se esconder e mostra a cara.
Ex: Quando há falha na comunicação com o servidor com o erro 666 uma exceção é lançada, e quando capturada tenta gerar um logue, só que esse logue está mal formatado e causa com que a thread inteira vá para o espaço.
Essa é a hora em que todos se esquecem do esforço que custou chegar até ali e não documentam nada do que foi feito. Desse jeito perde-se todo esse tempo não apenas uma vez, mas todas as vezes que alguém diferente do time mexer com a mesma situação. Por isso deve-se, com a cuca fresca, escrever algumas dicas de como reproduzir o problema e elaborar um pequeno relatório ou algo que o valha do que foi feito, como foi feito e por que funcionou. Mais uma vez, não exagere. Deixe as apresentações sofisticadas de PowerPoint para os outros departamentos da empresa.
Como deve ter parecido, esse tipo de abordagem leva tempo e não é fácil de ser levado adiante sem disciplina e muita persistência. Por esse motivo é que só deve ser usado naqueles problemas em que já se perdeu uma imensidade de tempo e esperança, uma situação irremediável e que ainda não conseguiu vislumbrar o dia em que finalmente poderemos dedicar nossas vidas profissionais para uma outra tarefa mais interessante.
2009-03-05 Alex S. Torres:
olá, parabens muito interressante o assunto abordado, bem mais tenho uma duvida, talvez alguem por aqui já teve esse problema e pode esclarecer, eu fiz um programa para hook compilei ele no ming, usando o codeblock, e tambem o compilei no visual c++, bem ambos funcionam perfeitamente no windows xp 32 bits, porem no vista 64, eles nao funcionam, de fato nao conseguem capiturar mensagem alguma, li em algum lugar que precisa compilar a dll com um compilador 64 bits, mais aí teria problemas no xp, alguem sabe como faço um hook que funciona no xp e no vista?
2009-03-05 Daniel Quadros:
Para mim a coisa fica cabeluda quando surge o "em momentos aparentemente aleatórios". A minha teoria de depuração é que o primeiro passo é achar uma sequência, por mais longa e doida que seja, que provoque o erro. A partir daí, você vai simplificando a sequência para cercar a causa e vai "instrumentando" o código para achar o culpado. Eu acredito que uma vez que você consegue reproduzir à vontade um problema está bem perto de resolvê-lo.
# Construindo provas de conceito (PoC)
Caloni, 2009-03-19 <essays> <blog> [up] [copy]Uma prova de conceito bem feita segue todos os passos em uma forma micro para entender e provar como as coisas irão funcionar no código de produção: a forma macro.
A consequência interessante disso é que, uma vez que a prova de conceito deva ser um miniprojeto das principais partes de um software, desenvolvê-la significa programar todas as partes que realmente importam, ou seja, centrais para o funcionamento.
Portanto, conclui-se que desenvolver provas de conceito é a coisa mais divertida do Universo.
Além de serem extremamente divertidas e disputadas entre os programadores, desenvolver provas de conceito gera uma gama de vantagens para o desenvolvimento "sério" do software como um todo, "rodável" e "vendável":
Apenas essas vantagens já praticamente obrigam o profissional do software a pensar em produtos novos em termos de como pode-se testar tudo o que se está dizendo antes de realmente começar a trabalhar pra valer.
Mas antes que se pense que fazer provas de conceito não requer nenhuma responsabilidade e que o que você quer ser quando crescer é desenvolvedor de prova de conceito, é necessário colocar alguns pingos nos is antes de continuar. Para criar provas de conceito realmente agregadoras para o projeto, deve-se sempre:
Por último, deve-se pensar sempre em todos os programadores da equipe desenvolvendo provas de conceito. Um doce tão gostoso não pode ser privilégio apenas dos veteranos ou dos acadêmicos chatos, pois torna a vida dos "corregedores" de bugs chata e enfadonha. E error prone.
Por isso, desde a estagiária até o mocinho bicentenário merecem mexer em código fresco pelo menos uma vez a cada ciclo de desenvolvimento, que terminará com uma versão nova cheia de melhorias que foram testadas em suas respectivas provas de conceito. Provas de conceito que todos tiveram a honra de brincar um pouquinho.
# Depurando até o último segundo
Caloni, 2009-03-31 <computer> <blog> [up] [copy]Como depurar um programa que dá pau logo no final do desligamento de uma máquina?
No cenário em que isso se passa não existem usuários logados no momento, o que significa a impossibilidade de rodar qualquer programa em uma sessão prévia e mantê-lo no ar após o logoff. A não ser que se trate de um serviço.
O nosso programa é justamente um serviço, e por isso ele continua rodando até o final, ou bem perto dele. A primeira ideia que vem à mente é instalar o Msvcmon - depurador remoto do Visual Studio - como um serviço, como aliás já foi demonstrado neste blogue.
Essa é uma boa ideia, de fato. Contudo, não podemos esquecer que a ordem de descarregamento dos serviços pode não favorecer o nosso depurador remoto e ele ir embora antes que consigamos "atachar" nosso VC no programa faltoso. Além do mais, a própria rede, que é disponibilizada com a ajuda de serviços, pode não estar no ar, mesmo que o Msvcmon esteja.
Tudo bem, vamos dizer que você é um expert em configuração de dependências de serviços e conseguiu fazer com que a rede, o Msvcmon e o programa faltoso sejam os últimos serviços - com exceção dos drivers - a serem descarregados. Bravo!
Contudo, isso não vai adiantar de muita coisa se for necessário parar a execução por um breve momento e analisar a pilha por, digamos, cinco segundos. Esse é o tempo que o sistema - que continua rodando - precisa para desligar a máquina.
Agora o problema é outro: não há tempo para análise durante a depuração, pois o sistema continua rodando. Nesse caso, teremos que ser mais radicais e parar o próprio sistema para que possamos depurar calmamente o problema. Isso implica em termos que utilizar um depurador de kernel (WinDbg), pois só ele tem poderes de congelar o sistema inteiro.
Mas, ainda assim, precisamos de um depurador de user para fazer análises mais profundas ou, pelo menos, mais simples, com a ajuda de símbolos e tudo mais. Nesse caso é necessário usar um depurador de user que redireciona o controle para o depurador de kernel. A transição user mode >> kernel mode pode ser feita com apenas algumas configurações antes do reboot.
E, após toda essa bagunça, podemos depurar, no conforto de uma VM, o bendito programa matador.
2009-03-31 Werner:
Caloni, me perdoe, mas às vezes lendo seus posts, acho que você é doido...
Não consigo entender, como alguém consegue entender o que vc entende. Mas ao mesmo tempo invejo isso.
Essa capacidade de Debug que você consegue executar está além do meu intelecto. Confesso que depois de começar a ler o seu blog, andei fazendo umas brincadeiras aqui, mas estou longe de conseguir meu primeiro "sucesso"...
Enfim, vou lendo e quem sabe uma hora entra alguma coisa na minha cabeça...
2009-04-01 Werner:
Caloni,
Você não deve considerar meu comentário como crítica. Foi apenas um pequeno desabafo, ante a minha dificuldade em entender o seu nível de debugging. Você já vive mergulhado nesse universo, que para mim é novo, de depuração de aplicativos em tempo real, coisa que eu, apesar de já ter "ouvido falar" nunca havia imaginado ser uma coisa de rotina para certos profissionais.
Acredito que esse conhecimento abre muitas portas, e torna o programador muito mais do que um simples "teclador de F9/F11", como a maioria (eu incluso), a quem o conceito de "depuração" limita-se a "prender" o código-fonte dentro do ambiente de desenvolvimento, utilizando os recursos embutidos (F9/F11/etc)...
O problema não acredito ser você, mas sim o iniciante (eu, novamente), que se depara com o "caminhão" de informações que você já vem aqui colocando, e precisa saber onde começar com o bê-a-bá.
Eu não sei mais ler Assembler, desde que me formei no curso de eletrônica (saudosos tempos em que eu programava em Assembly Z-80), há 15 anos atrás, e me admira ver que há pessoas como você, que provavelmente sabem ler o conteúdo das janelas do WinDBG como se fossem um livro aberto. E saber isso deve ser muito gratificante, eu imagino.
Para te citar dois exemplos práticos (que me fizeram lembrar de você):
Depois de começar a ler o seu blogue, me atentei para o fato de que poderia talvez buscar formas de "capturar" chamadas feitas às APIs Win32 (pois só posso imaginar que esses programas de "Setup" dos aplicativos buscam informações da versão do Sistema Operacional dentro de algum método específico que é fornecido pela API), e tentar alterar o seu valor de retorno, antes do mesmo ser lido pelo aplicativo.
Nessa brincadeira, depois de quebrar a cabeça com o SoftICE (sem entender muito o que eu estava fazendo), acabei me deparando com a ferramenta SpyStudio, que se parece bastante com um "debugger for dummies"... no momento, estou brincando com ele. Já consegui, graças a ele, localizar quais os métodos que estão nas DLLs kernel32 e user32, e que têm por função fornecer informações sobre o Sistema. Para mim, o maior problema até agora, está em localizar quais as chamadas que são feitas, para interceptar as corretas (já interceptei um só aplicativo fazendo várias chamadas para métodos diferentes, porém ao interceptar o retorno das mesmas e alterar os parâmetros corretos, ainda não consegui alcançar o meu objetivo).
Citei esses dois casos para que veja como, apesar de minha expressa dificuldade em entender seus textos avançados, eu estou dando pequenos passos.
O que você precisa compreender, se me permite dizer isso, é que não há forma de você simplificar um assunto tão complexo quanto debug em tempo real, nem a estrutura interna dos processadores atuais, a linguagem assembler, etc... aos interessados, cabe buscar fontes de informação que os coloquem na trilha certa, e o resto o tempo se encarrega.
Para mim, acho que é necessário começar com uma boa leitura. Sei que deve haver livros (não um, mas vários), que forneçam uma introdução ao assunto, e permitam dominar, com tempo, as técnicas nas quais você já é mestre. Já sei que o debugging é uma técnica multidisciplinar; não há um livro que fale sobre tudo, são muitos os assuntos a serem dominados. Mas se você puder, apreciaria muito uma "shopping list" de publicações que você considere válidas, e que me coloquem no caminho certo (assim eu posso contar com algo a mais além do "trial-and-error"...)
Um abraço e continue sempre!
(O meu nível de matemática não permite comentários??? :))
2009-04-01 Fernando Roberto:
Isso me faz lembrar de um trecho que li do Windows Driver Model, onde Walter Oney descreve que certa vez teve que depurar um driver que apresentava problemas com os eventos de gerenciamento de energia durante a transição para o Standby.
No caso dele, o driver de serial, de vídeo e de rede já tinham ido pra caminha. Nessa situaçao WinDbg e SoftIce deixaram de ser uma opção óbvia. Escrever mensagens em disco também não foi uma escolha interessante já que o File System havia desligado. Tudo que ele tinha era uma tela preta e um congelamento da máquina durante o processo de standby ou o retorno dele.
Por sorte, o teclado e a porta paralela ainda permaneciam ligados enquanto o problema era reproduzido, ele então decidiu utilizar o SoftIce e esperar o problema acontecer. Então, quando tudo parece congelado ele torce para que o SoftIce tenha manipulado algum ASSERT quando pressiona a tecla "Print Screeen". O SoftIce então manda a saída da tela para a impressora. Assim, a cada comando que ele lançava no SoftIce, ele fazia um Print Screen da tela. Muitas folhas mais tarde ele descobre o problema.
O cara é um herói. :-)
Windows Driver Model (2nd Edition) - página 434 "Debugging Power Management"
2009-04-01 Caloni:
Olá, Mr. Ferdinando.
Essa história foi de tirar lágrima das olhos!
Você me lembrou de mais um caso extremo de depuração: microcontroladores! Lembro-me do DQ (ou outro "old-timer") comentar em um dos encontros de C++ como ele fazia para "depurar" aquelas caixinhas dos infernos: usando um led. A cada passada da rotina depurada, o led era aceso e apagado. Dessa forma, era possível saber qual a parte do código que fazia com que o dispositivo travasse.
[]s
2009-04-01 Caloni:
Caro Werner,
Então o que eu temia é verdade: nada posso fazer para amenizar a longa e sinuosa estrada que leva programadores de F9/F11 para desbravadores das ciências ocultas da computação, prontos para encontrar o culpado no melhor estilo CSI que nem os experts da Polícia Federal conseguiriam fazer!
Por outro lado, sua história de tentativas e erros me deixou muito animado, pois demonstra que não estou sozinho nesse mundo de depuração hardcore. Dessa forma, espero aos poucos, conforme for acontecendo, narrar algumas aventuras em modo Debug que passo nos dias mais felizes do trabalho.
[]s
PS: Houve algum problema com o meu "Math Tester Enterprise Edition"? Espero que não, do contrário não terei como consertá-lo com meus conhecimentos matemáticos.
E por falar em matemática, saiu um artigo muito interessante (2026-03-21 link quebrado) no Coding Horror sobre a dicotomia programação/matemática.
2009-04-01 Caloni:
Olá, Werner!
Vou considerar seu comentário uma crítica. Claro! Afinal de contas, a ideia do blogue era unicamente compartilhar conhecimento com as pessoas. Se estou me expressando confusamente, então acho que o objetivo foi por água abaixo.
Espero que você não desista de suas tentativas de depuração. Mesmo que muitas coisas não deem certo nas primeiras vezes, com o tempo, experiência e cada vez mais conhecimento tudo vai ficando mais claro e lógico.
Se você puder me dizer quais os principais obstáculos para o aprendizado em técnicas de debugging ficarei muito grato, pois me dá a chance de melhorar meus artigos.
[]s
2009-04-02 Werner:
Caro Caloni,
Infelizmente (ou talvez, felizmente), se fosse assim tão simples e fácil explicar o caminho para as ciências ocultas da computação aos "não-Jedi", facilmente tais artes negras seriam banalizadas, assim como acontece hoje com muitas das tais "linguagens" de programação que estão em voga. Imagine o que aconteceria com as carreiras e os salários...
Mas enfim... de qualquer modo, eu padawan, vou tentando me manter no rumo, mesmo que a passos de formiga (e às vezes, de caranguejo - um passo à frente seguido de 3 passos atrás...). Assim é a vida.
Quanto ao "Math Tester", acredito ter se tratado de alguma questão referente a "timeout", levando em conta a proporção direta entre tamanho do texto X tempo dispendido.
Abraços!!
(Em tempo: Se você é carabão mesmo, quero vê-lo "debugar" este código-fonte...) :-)
2009-04-03 Werner:
Gostei muito do artigo no Coding Horror, que você citou. E devo dizer que concordo com o ponto de vista exposto.
Assim como o autor explica, vejo também a matemática como uma ferramenta dentre as milhares que existem, definida para solução de problemas específicos. Assim como nenhum programador precisa necessariamente ser economista para escrever um programa que auxilie no equilíbrio das contas do mês, ou técnico de futebol profissional, para criar um programa que faça a projeção das tabelas do campeonato estadual.
É claro que existem aplicações onde a matemática é a ferramenta mais indicada, e muito provavelmente lhe garantirá o sucesso na proporção direta do seu nível de conhecimento. Eu tive a oportunidade de sentir isso na carne: ano passado, trabalhei para uma companhia que me enviou ao exterior, para aprender a mexer com uma ferramenta snap-in para sistemas CAD profissionais, a qual eu desenvolveria, futuramente. Para isso, fui estudar com programadores que eram mestres e doutores em matemática e engenharia mecânica (para meu terror, pois tenho verdadeiro trauma de matemática, desde o ginásio, e graças ao fatídico "Caloni's Math Tester Enterprise Edition", o trauma aumenta exponencialmente a cada comentário meu aqui). Era tudo envolvendo séries finitas, cálculo espacial, e por aí vai. Para você ter uma idéia, um dos exercícios que me foram propostos consistia em importar um arquivo criado no aplicativo CAD (em três formatos distintos e tendo nada em comum entre si), uma peça qualquer simples, calcular suas medidas extremas em três dimensões, e finalmente, gravar um novo arquivo contendo a peça e mais a "bounding box" (trata-se de uma representação traçada em torno da peça, exatamente nas dimensões mais extremas, como se fosse uma "caixa envolvente"). Levei aproximadamente 5 dias para completar o exercício, sendo que 1/3 do tempo passei estudando e rabiscando em papel, conceitos de geometria que não havia visto mais desde a oitava série!!
Apesar de meus esforços terem gerado resultado (a meu ver) satisfatório, havia um fator bastante desagradável, a pressão imprimida pelos instrutores, que acharam o tempo dispendido por mim inaceitável (lembro-me que um deles ainda disse-me que ao bolar o exercício, tinha resolvido o mesmo em uma tarde...)
Aprendi duas lições que levarei comigo:
Primeiro, nunca trabalhe para alemães. Eu disse: NUNCA.
Segundo, se você venceu um desafio, não deixe que ninguém (especialmente um alemão metido a besta) lhe diga que você não está à altura.
(E terceira: C++ é amigo, amiiiiigo... amigoooo!!)
Abraços! :-)
2009-04-09 Alan:
O que seria o math tester enterprise edition??? Fiquei curioso agora... :D
2009-04-11 Caloni:
É simplesmente o formulário de comentários, que pede que você faça uma conta besta que nunca dá mais de 20 =)
[]s
2009-04-12 Werner:
Ufa, me causou tremendo alívio essa informação... meu buffer não comporta mais do que um small integer... :-P