Como você aborda erros intermitentes?

votos
31

Cenário

Você tem vários relatórios de bugs todos mostrando o mesmo problema. Eles são todos enigmático com contos semelhantes de como o problema ocorreu. Você segue os passos, mas não reproduzir de forma confiável o problema. Depois de algumas investigações e buscas web, você suspeitar que pode estar acontecendo e você é certeza que você pode corrigi-lo.

Problema

Infelizmente, sem uma maneira confiável para reproduzir o problema original, você não pode verificar se ele realmente resolve o problema ao invés de ter qualquer efeito ou exacerbando e mascarando o problema real. Você simplesmente não pode corrigi-lo até que se torne reprodutível de cada vez, mas é um grande erro e não corrigi-lo causaria a seus usuários uma série de outros problemas.

Questão

Como é que você vai verificar sua mudança?

Acho que este é um cenário muito familiar para quem projetou software, então eu tenho certeza que há uma infinidade de abordagens e as melhores práticas para lidar com erros como este. No momento, estamos olhando para um desses problemas em nosso projeto onde eu ter passado algum tempo determinar o problema, mas foram incapazes de confirmar as minhas suspeitas. Um colega é tomar banhos de testar a minha correção na esperança de que um dia de execução sem um crash equivale a é fixo. No entanto, eu prefiro uma abordagem mais confiável e eu percebi que há uma riqueza de experiência aqui no SO.

Publicado 09/12/2008 em 15:31
fonte usuário
Em outras línguas...                            


18 respostas

votos
12

Erros que são difíceis de reproduzir é o único mais difícil de resolver. O que você precisa ter certeza de que você encontrou a raiz do problema, mesmo que o problema não pode ser reproduzido com sucesso.

Os erros intermitentes mais comuns são causados ​​por raça-condições - ao eliminar a raça, ou garantir que um lado ganha sempre de ter eliminado a raiz do problema, mesmo se você não pode confirmá-la com sucesso, testando os resultados. A única coisa que você pode testar é que a causa não precisa se repetir.

fixação, por vezes, o que é visto como a raiz de fato resolve um problema, mas não o caminho certo - não há como evitar isso. A melhor maneira de evitar erros intermitentes é ser cuidadoso e metódico com o projeto do sistema e arquitetura.

Respondeu 09/12/2008 em 15:40
fonte usuário

votos
7

Você nunca vai ser capaz de verificar a correção sem identificar a causa raiz e chegando com uma maneira confiável para reproduzir o erro.

Para identificar a causa raiz: Se sua plataforma permite, ligar alguma depuração post-mortem para o problema.

Por exemplo, no Windows, obter o seu código para criar um arquivo de minidespejo (descarga do core no Unix) quando encontra este problema. Você pode então começar o cliente (ou Winqual, no Windows) para lhe enviar esse arquivo. Isso deve lhe dar mais informações sobre como seu código está errado no sistema de produção.

Mas, sem isso, você ainda vai precisar para chegar a uma maneira confiável para reproduzir o erro. Caso contrário, você nunca vai ser capaz de verificar se ele é fixo.

Mesmo com todas essas informações, você pode acabar a fixação de um bug que parece, mas não é, o que o cliente está vendo.

Respondeu 09/12/2008 em 15:40
fonte usuário

votos
5

Eu uso o que eu chamo "estilo pesado programação defensiva" : adicionar afirma em todos os módulos que parece ligado pelo problema. O que eu quero dizer é, adicionar um monte de afirma , afirma evidências, afirmam estado de objetos em todos os seus memebers, afirmam estado "environnement", etc.

Afirma ajudá-lo a identificar o código que não está ligado ao problema.

Na maioria das vezes eu encontrar a origem do problema apenas por escrever as afirmações, uma vez que obriga a reler todo o código e plundge sob as entranhas do aplicativo para compreendê-lo.

Respondeu 09/12/2008 em 15:47
fonte usuário

votos
5

Instrumento da compilação com mais extensa (possivelmente opcional) o registo e os dados de poupança que permite a reprodução exata da UI variável passos os usuários levou antes do acidente ocorrido.

Se esses dados não permite de forma confiável você para reproduzir o problema, então você estreitou a classe de erro. Tempo para olhar para as fontes de comportamento aleatória, tais como variações na configuração do sistema, as comparações ponteiro, dados não inicializados, etc.

Às vezes você "sabe" (ou melhor, sentir) que você pode corrigir o problema sem extensos testes ou testes de unidade andaimes, porque você realmente entender a questão. No entanto, se você não fizer isso, ele muitas vezes se resume a algo como "nós correu 100 vezes e o erro não ocorreu, então vamos considerá-lo fixo até a próxima vez que for relatado.".

Respondeu 09/12/2008 em 15:39
fonte usuário

votos
4

Não há uma resposta a este problema. Às vezes, a solução que você encontrou ajuda você a descobrir o cenário para reproduzir o problema, caso em que você pode testar esse cenário antes e depois da correção. Às vezes, porém, que a solução que você encontrou corrige apenas um dos problemas, mas não todos eles, ou como você diz mascara um problema mais profundo. Eu gostaria de poder dizer "fazer isso, ele trabalha o tempo todo", mas não há um "presente" que se encaixa nesse cenário.

Respondeu 09/12/2008 em 15:39
fonte usuário

votos
2

Você diz em um comentário que você acha que é uma condição de corrida. Se você acha que sabe o "recurso" do código está gerando a condição, você pode escrever um teste para tentar forçá-lo.

Aqui está um código de risco em c:

const int NITER = 1000;
int thread_unsafe_count = 0;
int thread_unsafe_tracker = 0;

void* thread_unsafe_plus(void *a){
  int i, local;
  thread_unsafe_tracker++;
  for (i=0; i<NITER; i++){
    local = thread_unsafe_count;
    local++;
    thread_unsafe_count+=local;
  };
}
void* thread_unsafe_minus(void *a){
  int i, local;
  thread_unsafe_tracker--;
  for (i=0; i<NITER; i++){
    local = thread_unsafe_count;
    local--;
    thread_unsafe_count+=local;
  };
}

que I podem testar (em um enironment pthreads) com:

pthread_t th1, th2;
pthread_create(&th1,NULL,&thread_unsafe_plus,NULL);
pthread_create(&th2,NULL,&thread_unsafe_minus,NULL);
pthread_join(th1,NULL);
pthread_join(th2,NULL);
if (thread_unsafe_count != 0) {
  printf("Ah ha!\n");
}

Na vida real, você provavelmente vai ter que quebrar seu código suspeito de alguma forma para ajudar a raça bater mais ofter.

Se funcionar, ajustar o número de linhas e outros parâmetros para torná-lo bater a maior parte do tempo, e agora você tem uma chance.

Respondeu 09/12/2008 em 18:24
fonte usuário

votos
2

Primeiro você precisa para obter rastreamentos de pilha de seus clientes, de que maneira você pode realmente fazer algumas forense.

Em seguida fazer testes fuzz com entrada aleatória, e manter esses testes de corrida por longos períodos, eles são ótimos em encontrar os casos de fronteira irracionais, que os programadores humanos e testadores podem encontrar através casos de uso e compreensão do código.

Respondeu 09/12/2008 em 15:48
fonte usuário

votos
1

Eu correr em erros em sistemas que parecem causar erros de forma consistente, mas quando percorrendo o código em um depurador o problema desaparece misteriosamente. Em todos esses casos, o problema era uma questão de tempo.

Quando o sistema estava funcionando normalmente, houve algum tipo de conflito de recursos ou dar o próximo passo antes da última terminou. Quando eu pisei através dele no depurador, as coisas estavam se movendo lentamente o suficiente para que o problema desapareceu.

Depois que eu descobri que era um problema de tempo, foi fácil encontrar uma solução. Eu não tenho certeza se este é o caso em sua situação, mas sempre que os erros desaparecem no depurador problemas de tempo são os meus primeiros suspeitos.

Respondeu 09/12/2008 em 18:32
fonte usuário

votos
1

Por um erro difícil de reproduzir, o primeiro passo é geralmente documentação. Na área do código que está falhando, modificar o código para ser hiper-explícita: um comando por linha; pesado, manuseamento excepção diferenciadas; detalhado, saída de depuração mesmo prolixo. Dessa forma, mesmo se você não pode reproduzir ou corrigir o erro, você pode ganhar muito mais informações sobre a causa da próxima vez que o fracasso é visto.

O segundo passo é geralmente afirmação de pressupostos e verificação de limites. Tudo o que você pensa que sabe sobre o código em questão, escrever .Asserts e cheques. Especificamente, verifique objetos de nulidade e de existência (se o seu idioma é dinâmico).

Em terceiro lugar, verificar a sua cobertura de teste de unidade. Será que os seus testes de unidade na verdade cobrir cada bifurcação na execução? Se você não tem testes de unidade, este é provavelmente um bom lugar para começar.

O problema com erros unreproducible é que eles são apenas unreproducible para o desenvolvedor. Se seus usuários finais insistem em reproduzi-los, é uma valiosa ferramenta para alavancar o acidente no campo.

Respondeu 09/12/2008 em 18:19
fonte usuário

votos
1

cenário específico

Enquanto eu não quero concentrar-se em apenas o problema que estou tendo, aqui estão alguns detalhes da edição atual que enfrentamos e como eu abordado até agora.

O problema ocorre quando o utilizador interage com a interface do utilizador (um TabControl para ser exacto), a uma fase particular de um processo. Ele nem sempre ocorre e eu acredito que este é porque a janela de tempo para o problema a ser exibido é pequena. Minha suspeita é que a inicialização de um UserControl (estamos em .NET, utilizando C #) coincide com um evento de mudança de estado de uma outra área da aplicação, o que leva a um tipo de letra que está sendo descartado. Enquanto isso, um outro controlo (uma etiqueta) tenta chamar a sua cadeia com essa fonte, e, portanto, do acidente.

No entanto, na verdade, confirmando o que leva à fonte que está sendo eliminados tem sido difícil. A correção atual tem sido para clonar a fonte para que o rótulo de desenho ainda tem uma fonte válida, mas isso realmente máscaras a raiz do problema que é a fonte a ser eliminados em primeiro lugar. Obviamente, eu gostaria de rastrear a sequência completa, mas isso está provando muito difícil eo tempo é curto.

Abordagem

Minha abordagem foi o primeiro a olhar para o rastreamento de pilha de nossos relatórios de falhas e examinar o código Microsoft com refletor. Infelizmente, isso levou a uma GDI + chamar com pouca documentação, que retorna apenas um número para o erro - .NET transforma isso em uma mensagem bastante inútil indicando que algo é inválido. Ótimo.

De lá, fui para olhar para o que chamada em nosso código leva a este problema. A pilha começa com um loop de mensagem, não no nosso código, mas eu achei uma chamada para Update () na área geral sob suspeita e, usando instrumentação (traços, etc), fomos capazes de confirmar a cerca de 75% de certeza que este era a fonte da mensagem de pintura. No entanto, não foi a fonte do bug - pedindo o rótulo para pintar não é crime.

De lá, eu olhei para cada aspecto da chamada tinta que foi deixando de funcionar (DrawString) para ver o que poderia ser inválido e começou a governar cada um para fora até que ele caiu sobre os itens descartáveis. Eu, então, determinado quais tivemos controle e a fonte era o único. Então, dei uma olhada em como lidamos com a fonte e sob quais circunstâncias nós disposto para identificar quaisquer causas potenciais. Eu era capaz de chegar a uma sequência plausível de eventos que se encaixam os relatórios dos usuários e, portanto, capaz de codificar uma solução de baixo risco.

Claro, ele passou pela minha cabeça que o bug foi no quadro, mas eu gostaria de assumir que asneira antes de passar a culpa para a Microsoft.

Conclusão

Então, é assim que me aproximei um exemplo particular deste tipo de problema. Como você pode ver, é inferior a ideal, mas se encaixa com o que muitos já disseram.

Respondeu 09/12/2008 em 16:21
fonte usuário

votos
1

Estes são horríveis e quase sempre resistentes aos 'fixa' o engenheiro pensa que ele está colocando em, como eles têm o hábito de voltar a morder meses depois. Desconfie de quaisquer correções feitas a erros intermitentes. Esteja preparado para um pouco de trabalho duro e exploração madeireira intensiva como isso soa mais um problema de testes do que um problema de desenvolvimento.

Meu próprio problema quando superação de erros como estes era que eu era muitas vezes muito perto do problema, e não em pé de volta e olhando para a foto maior. Tentar conseguir alguém para olhar como você abordar o problema.

Especificamente meu erro foi a ver com a definição de tempos de espera e vários outros números mágicos que, em retrospecto, onde fronteira e assim trabalhou quase todo o tempo. O truque no meu caso foi o de fazer um monte de experimentação com as configurações que eu poderia descobrir o que valoriza seria 'quebrar' o software.

Será que as falhas acontecem durante períodos de tempo específicos? Em caso afirmativo, onde e quando? É apenas algumas pessoas que parecem reproduzir o bug? O conjunto de entradas parecem convidar o problema? Que parte do aplicativo que ele falha em? O bug parece mais ou menos intermitente no campo?

Quando eu era um testador de software minhas principais ferramentas onde uma caneta e papel para gravar notas de minhas ações anteriores - lembrar um monte de detalhes aparentemente insignificantes é vital. Ao observar e recolher pequenos pedaços de dados o tempo todo o bug irá aparecer para tornar-se menos intermitente.

Respondeu 09/12/2008 em 16:17
fonte usuário

votos
1

Algumas perguntas que você poderia perguntar-se:

  • Quando é que este pedaço de código último trabalho sem problema.
  • O que foi feito desde que parou de funcionar.

Se o código nunca trabalhou a abordagem seria diferente naturalmente.

Pelo menos quando muitos usuários mudar um monte de código todo o tempo este é um cenário muito comum.

Respondeu 09/12/2008 em 15:53
fonte usuário

votos
1

Nesta situação, onde nada mais funciona, eu introduzir log adicional.

Eu também adicionar em notificações de e-mail que me mostram o estado do aplicativo quando ele quebra.

Às vezes eu adicionar contadores de desempenho ... Eu coloquei que os dados em uma tabela e olhar para as tendências.

Mesmo que nada mostra-se, você está estreitando as coisas. De uma forma ou de outra, você vai acabar com teorias úteis.

Respondeu 09/12/2008 em 15:47
fonte usuário

votos
1

Esses tipos de erros são muito frustrante. Extrapolar-lo para diferentes máquinas com diferentes tipos de hardware personalizado que podem ser neles (como na minha empresa), e boy oh boy é que se torna um pesadelo. Atualmente tenho vários erros como este no momento no meu trabalho.

Minha regra de ouro: Eu não corrigi-lo, a menos que eu possa reproduzi-lo sozinho ou eu sou apresentado com um log que mostra claramente algo de errado. Caso contrário, eu não posso verificar a minha mudança, nem posso verificar se a minha mudança não quebrou nada. Claro, é apenas uma regra de ouro - eu faço exceções.

Eu acho que você tem razão para se preocupar com a abordagem do seu colleuge.

Respondeu 09/12/2008 em 15:44
fonte usuário

votos
0

Simplesmente: pedir ao usuário que relatou.

Eu só uso um dos repórteres como um sistema de verificação. Geralmente a pessoa que estava disposto a relatar um bug é mais do que feliz em ajudá-lo a resolver seu problema [1]. Basta dar-lhe a sua versão com uma possível correção e perguntar se o problema está desaparecido. Nos casos em que o erro é uma regressão, o mesmo método pode ser usado para bissectar onde ocorreu o problema dando ao utilizador com o problema múltiplas versões para testar. Em outros casos, o usuário também pode ajudá-lo a depurar o problema, dando-lhe uma versão com mais recursos de depuração.

Isso irá limitar os efeitos negativos de uma possível correção para essa pessoa em vez de adivinhar que algo vai corrigir o erro e, em seguida, mais tarde, percebendo que você acabou de lançar uma "correção de bug" que não tem nenhum efeito ou no pior caso um efeito negativo para a estabilidade do sistema.

Você também pode limitar os possíveis efeitos negativos da "correção de bug", dando a nova versão para um número limitado de usuários (por exemplo para todos os que relataram o problema) e liberar a correção só depois disso.

Também aqueles que ela pode confirmar que a correção que você fez obras, é fácil adicionar testes que garante que a sua correção vai ficar no código (pelo menos no nível de teste de unidade, se o bug é difícil de reproduzir em nível de sistema mais elevado ).

Claro que isso exige que o que você está trabalhando em suporta este tipo de abordagem. Mas se isso não acontecer eu realmente fazer o que puder para habilitá-lo - os usuários finais estão mais satisfeitos e muitos dos problemas mais difíceis tecnologia apenas ir embora e prioridades vêm claro quando desenvolvimento podem interagir diretamente com os usuários finais do sistema.

[1] Se você já relatou um erro, você provavelmente sabe que muitas vezes a resposta da equipe de desenvolvimento / manutenção de alguma forma é negativo do ponto de vista dos usuários finais ou não haverá nenhuma resposta em tudo - especialmente em situações em que o bug não pode ser reproduzido pela equipe de desenvolvimento.

Respondeu 05/09/2014 em 11:50
fonte usuário

votos
0

Uma vez que você entender completamente o bug (e isso é um grande "uma vez"), você deve ser capaz de reproduzi-lo à vontade. Quando o código de reprodução (teste automatizado) está escrito, você corrigir o erro.

Como chegar ao ponto onde você entender o bug?

Instrumento o código (log como um louco). Trabalhar com o seu QA - eles são bons em recriar o problema, e você precisa mandar kit de ferramentas dev completo disponível para você em suas máquinas. Use ferramentas automatizadas para não inicializadas de memória / recursos. Simplesmente olhar para o código. Sem solução fácil que existe.

Respondeu 09/12/2008 em 19:56
fonte usuário

votos
0

A menos que haja grandes limitações de tempo, eu não começar a testar alterações até que eu possa reproduzir de forma confiável o problema.

Se você realmente teve que, eu suponho que você poderia escrever um caso de teste que parece, por vezes, provocar o problema, e adicioná-lo à sua suite de testes automatizados (você tem uma suite de testes automatizados, certo?), E, ​​em seguida, fazer a sua mudança e esperança Nesse caso teste nunca falhar novamente, sabendo que, se você realmente não corrigir alguma coisa, pelo menos agora você tem mais chance de captura-lo. Mas pelo tempo que você pode escrever um caso de teste, você quase sempre têm coisas reduzidos até o ponto em que você não está lidando com uma (aparentemente) situação tão não-determinista.

Respondeu 09/12/2008 em 19:06
fonte usuário

votos
0

Estes problemas têm sido sempre causados ​​por:

  1. Problemas de memória
  2. Problemas de threading

Para resolver o problema, você deve:

  • Instrumento seu código (Adicione instruções de log)
  • Revisão do código enfiar
  • alocação de memória Code Review / dereferencing

As revisões de código provavelmente só acontecerá se for uma prioridade, ou se você tem uma forte suspeita sobre qual código é compartilhada pelos vários relatórios de bugs. Se é um problema de segmentação, em seguida, verificar a sua segurança do thread - Certifique-se variáveis ​​acessível por ambos os segmentos estão protegidos. Se é um problema de memória, em seguida, verifique suas alocações e dereferences e, especialmente, ser suspeito de código que aloca e retorna a memória, ou o código que usa alocação de memória por outra pessoa, que pode ser de liberá-lo.

Respondeu 09/12/2008 em 15:47
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more