Skip to content

Bug search and fix patterns

Jerson Seling edited this page Oct 5, 2020 · 4 revisions

Bug search and fix patterns

Padrões usados para descobrir defeitos em código.

Fluxo para encontrar um bug

Quando o bug ocorre?

  • Reprodução do cenário em que o erro ocorre.

Onde o erro ocorre no código?

  • Depuração via debugger.
  • Log
  • Código sonda

Como o erro ocorre no código?

  • Isolamento de trechos suspeitos.
  • Regressão para versões antigas, onde o erro não ocorria.

Técnicas

Código sonda

Um código fazia uma requisição HTTP e parou de funcionar. Alguma coisa de alguma forma afetou o componente de fazer requisições HTTP. Crio o menor código possível com uma requisição HTTP e o coloco no ponto mais inicial da execução da aplicação. Se a requisição funciona, vou colocando ela em pontos mais tardios da execução da aplicação, no momento que ele não funciona, significa que o defeito está em um ponto anterior. Sempre colocando o código no meio dos métodos, então na metade da metade, e assim por diante, se aprofundando nos submétodos até chegar no defeito. Descobri que era um código que descarregava a DLL de socket.

  procedure TesteIdHTTP;
  var
    lRequest :TStringStream;
    lResponse : TStringStream;
    AIdHTTP: TIdHTTP;
  begin
    AIdHTTP := TIdHTTP.Create(nil);
    lRequest := TStringStream.Create('');
    lResponse := TStringStream.Create('');
    try
      AIdHTTP.Request.CustomHeaders.Clear;
      AIdHTTP.Request.CustomHeaders.Add('Authorization:Basic UEc6Q2JCYkYybjRGbzlD');
      AIdHTTP.Post('http://172.21.4.13:8080/uas/oauth/token?grant_type=password&username=FULANO&password=abc123', lRequest, lResponse);
    finally
      lRequest.Free();
      lResponse.Free();
      AIdHTTP.Free;
    end;
  end;

Bug por corrupção de memória

As vezes pode ocorrer de um objeto "A" ser destruído, mas ainda ser mantido em algum outro ponto do sistema, um ponteiro "pA" para esse objeto que não existe mais. Pode ocorrer do endereço de memória do valor desse ponteiro ser usado por algum outro objeto "B", e se for executada alguma operação no ponteiro "pA", esse valor desse endereço pode acabar sendo alterado, corrompendo alguma propriedade do objeto "B", o que pode gerar um erro instantaneamente ou após algumas operações serem feitas com "B" e tentarem acessar essas propriedades corrompidas.

Ao se deparar com esse cenário o programador focará instintivamente no objeto "B" e nas operações feitas com ele, pois é nele que ocorre o erro, mas não encontrará nenhuma evidência que justifique o erro, pois não há nada de errado com "B". No máximo ele encontrará que até determinado ponto alguma propriedade de "B" tinha um valor, mas misteriosamente após algum evento, ela possui outro valor, na maioria das vezes inválido.

Para descobrir o que gera essa alteração das propriedades pode-se usar data breakpoints, que são breakpoints que monitoram um endereço de memória específico, que quando alterado, são disparados, mostrando a callstack do que está gerando essa alteração. Esses breakpoints só podem ser criados em tempo de execução, quando sabe-se qual o endereço de memória que a propriedade ocupará. Usa-se um breakpoint normal para parar a execução em algum ponto que aloque o endereço de memória, vê-se o endereço da propriedade (no Delphi Ctrl+F7 e por exemplo: Pointer(aObjeto.Nome)) e cria-se o data breakpoint apontando para esse entereço.

Isolamento

Exclusão de código suspeito

Um procedimento apresenta lentidão e não se quer usar um profiler. Códigos suspeitos de causar a lentidão são comentados e se verifica se o procedimento ficou sem a lentidão. Se ficou rápido, um a um os códigos vão sendo descomentados, até ficar lento novamente. O último código descomentado é o causador do problema. Se há submétodos, reinicia-se o processo dentro do método.

Interrupção de código

É uma espécie de código sonda util em memory leaks. É uma espécie de exclusão de código suspeito também. Um memory leak acontece após um código. Adiciono um comando que interrompe o método no meio, se ocorreu o memory leak, ele está antes da interrupção, se não está depois. Vai se refinando a posição da interrupção metade a metade, até encontrar o método problemático. Se ele tem submétodos, aprofunda-se neste, repetindo-se a operação.

Tipos de bugs

  • Acidentais - Fáceis de resolver. Resolvidos por manutenção.
  • Problema de modelagem - Difíceis, mas são os mais recompensadores de se resolver. Suas soluções amadurecem o código. Resolvidos por evolução.

https://twitter.com/acdlite/status/1058092028460916736