Digamos que você não tenha um depurador disponível, qual seria uma abordagem eficaz para o código de depuração que não funcione (conforme o esperado)?
Digamos que você não tenha um depurador disponível, qual seria uma abordagem eficaz para o código de depuração que não funcione (conforme o esperado)?
Respostas:
Há várias técnicas:
Exploração madeireira. Se você não tiver acesso a arquivos, faça login em um console serial ou em qualquer dispositivo de saída disponível. É uma boa idéia escrever sempre seu código com o registro em mente, permitindo uma compilação condicional para diferentes níveis de registro, de 'none' a 'overbloated'.
Cortando. Exclua as partes do seu código em torno de um ponto suspeito de bug, um por um, e analise o que você fez quando o bug desapareceu.
Asserções ou contratos. É uma boa idéia encher seu código com declarações desde o início. Eles não apenas ajudam na depuração, mas também servem como uma documentação adicional para o seu código.
Semelhante ao 2. - altere sua entrada e reorganize o código, a menos que o bug desapareça ou mude seu comportamento. Geralmente, é uma boa ideia jogar com vários níveis de otimização (se você estiver codificando em C ou C ++), pois os erros relacionados ao ponteiro tendem a ser extremamente voláteis em seu comportamento.
Uma versão automatizada do 3. - use o código instrumentado, por exemplo, execute o binário em valgrind.
E, é claro, existem muito mais ferramentas e truques, dependendo da natureza do seu ambiente de execução e do seu código.
Encontre um colega e explique o problema detalhadamente enquanto você percorre o código problemático passo a passo.
Freqüentemente, o ato de explicar deixa claro para seu colega ou para você mesmo.
Existe um sistema de registro para gerenciar a saída do programa? Existe pelo menos um console para imprimir ou arquivos nos quais você pode gravar? O uso de consoles ou arquivos de log é uma maneira de depurar sem um depurador. Dê ao programa uma entrada para que você saiba qual deve ser a saída, verifique se a saída funciona e verifique se o registro fornece muitos detalhes do processo. Em seguida, tente com uma entrada que dê a saída errada. Felizmente, os logs fornecerão um rastreamento detalhado do que deu errado.
Depende. Funcionou antes? Se o código que costumava funcionar quebrar de repente, você deve examinar cuidadosamente as alterações mais recentes.
1) Faça o que for necessário para tornar o bug 100% reprodutível ou o mais próximo possível de 100% possível
2) Rastreie o problema usando printf () ou outro recurso de registro. Esta é uma arte, porém, e depende da natureza do bug.
Se você não tem absolutamente nenhuma idéia sobre a localização do bug, mas, por exemplo, você sabe que uma condição se torna falsa em algum momento (o estado do programa quebrou - vamos chamá-lo de isBroken ()), você pode fazer uma pesquisa detalhada / partição para descobrir a localização do problema. Por exemplo, registre o valor de isBroken () no início no final dos principais métodos:
void doSomething (void)
{
printf("START doSomething() : %d\n", isBroken());
doFoo();
doBar();
printf("END doSomething() : %d\n", isBroken());
}
Se no log você vê
START doSomething() : 0
END doSomething() : 1
você sabe que algo deu errado lá. Então você remove todos os outros códigos de log e tenta esta nova versão:
void doSomething (void)
{
printf("START doSomething() : %d\n", isBroken());
doFoo();
printf("AFTER doFoo() : %d\n", isBroken());
doBar();
printf("END doSomething() : %d\n", isBroken());
}
Agora, no log, você pode ver isso
START doSomething() : 0
AFTER doFoo() : 0
END doSomething() : 1
Então agora você sabe que o doBar () aciona o bug e pode repetir o procedimento acima em doBar (). Idealmente, você reduz o erro a uma única linha.
É claro que isso pode ajudá-lo a revelar os sintomas do bug e não a causa raiz - por exemplo, você encontra um ponteiro NULL que não deve ser NULL, mas por quê? Você pode registrar novamente, mas verificando uma condição "quebrada" diferente.
Se você tiver uma falha em vez de um estado quebrado, é mais ou menos o mesmo - a última linha do log fornece uma dica de onde as coisas acontecem.
Depois que as outras respostas falham , sempre há a depuração de pesquisa binária :
Nota: obviamente, esta técnica só funciona se você puder reproduzir o problema de maneira confiável.
"A ferramenta de depuração mais eficaz ainda é o pensamento cuidadoso, juntamente com declarações de impressão colocadas criteriosamente." - Brian Kernighan 'Hoje é dia, pode ter sido verdade! O método mais eficaz é examinar os testes de unidade, mas acho que você não possui nenhum.
Depende do bug.
Se o erro é do tipo 'por que o código está executando A', então pode ser útil testar seu próprio entendimento do código em torno da localização de 'código executando A'. Introduza o novo código que você espera gerar novos bugs (esse código deve fazer B, e C). Normalmente, encontro rapidamente algum código novo que gera um comportamento que não espero. Então espero pacientemente enquanto minha mente constrói um modelo mental atualizado do comportamento do código para que a última mudança faça sentido e, em seguida, essa mudança de modelo mental geralmente explica por que o código está fazendo A.
O segundo caso foi discutido em detalhes aqui. Onde você herdou o código, não tem um modelo mental sólido de como o código funciona, não tem uma boa idéia sobre a localização específica do bug etc. Nesse caso, faça uma busca detalhada / divida e divida métodos de conquista com instruções de impressão podem funcionar. E se estiver sob controle de origem, verifique a alteração de código mais recente.
Expandindo a "A ferramenta de depuração mais eficaz ainda é um pensamento cuidadoso, juntamente com instruções de impressão colocadas criteriosamente".
Primeiro, tente restringir o momento em que o bug ocorre. Torne os sintomas observáveis pelo usuário observáveis pelo sistema. (digamos, algumas seqüências de caracteres mudam para rabiscos, adicione um loop que controla o conteúdo do script e aciona sua depuração conforme ela muda.) Obviamente, se o erro for um travamento, adicione o tratamento de segfault.
Em seguida, tente restringir o encadeamento se o problema ocorrer no ambiente multithread. Dê a cada thread um identificador e despeje-o quando o erro ocorrer.
Depois de ter a linha, polvilhe o código de uma determinada linha com printfs copiosamente para fixar o ponto em que ela surge.
Em seguida, volte para onde a ação real que a cria ocorre (a ação destrutiva será um pouco antes de onde os dados danificados desencadeiam o problema). Examine quais estruturas / variáveis ocorrem nas proximidades da memória, observe loops que os afetam, verifique pontos onde o valor corrompido é gravado.
Depois de entender o problema que estava causando o problema, antes de corrigi-lo, pense duas vezes qual deveria ser o comportamento correto.