Como efetivamente fazer depuração manual? [fechadas]

8

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)?

Anto
fonte
20
"A ferramenta de depuração mais eficaz ainda é o pensamento cuidadoso, juntamente com declarações de impressão colocadas criteriosamente." - Brian Kernigham
3
Concordo plenamente. A ferramenta de depuração mais poderosa ainda é a impressão de instruções. Outro conselho é depurar o código real e não os comentários.
Romeroqj
4
@ jromero: eu não diria que as declarações impressas são as "mais poderosas". Mais difundido e fácil de usar, com certeza.
Whatsisname
1
Essa citação de Kernighan me faz desejar poder rebaixar os comentários. A depuração de instruções de impressão é uma ferramenta de último recurso.
Mason Wheeler
2
@Mason: A pergunta pressupõe que depuradores não estão disponíveis (então a maneira "real" de rastrear a execução se foi), então o que mais você usaria para rastrear a execução?

Respostas:

7

Há várias técnicas:

  1. 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'.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

SK-logic
fonte
1
-1: Não posso concordar 4). Você parece propor isso como uma solução para o bug? Se sim, isso é mau e você tem um problema. Tudo que faz é tornar os sintomas vão away- ele ainda está lá, você tem apenas escondido .... por agora ....
mattnz
@mattnz, mente explicando? Proponho essa abordagem como uma alternativa a um depurador interativo. O que, por sua vez, apenas destaca os sintomas, não as causas reais. 4) é uma maneira de identificar um problema, não uma solução. De fato, minha abordagem é muito melhor do que a depuração na maioria dos casos, pois oferece uma cobertura melhor. Portanto, é provável que você não tenha entendido o que eu proponho. Tente lê-lo novamente.
SK-logic
Vi os desenvolvedores usarem as ações apresentadas na Etapa 4 para "consertar" um defeito usando o "Eu poderia fazer isso acontecer, agora não posso, eu o consertei - enviei". Sua postagem sugere que esse método fornece correções de erros válidas.
mattnz
@mattnz, não, não sugere nada parecido com isto. Estou descrevendo uma maneira de investigar um bug, não para corrigi-lo. Depuradores não corrigem bugs, e a pergunta era sobre uma alternativa a um depurador.
SK-logic
18

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.


fonte
12
+1 E se você não encontrar um colega, use um ursinho de pelúcia .
Péter Török 22/02
4
@ Péter Török: Eu não sei ... ursos de pelúcia tendem a olhar para trás com seus frios olhos mortos ... ignorando tudo o que você diz, fazendo você se sentir inútil, minúsculo, insignificante ... Faz depuração com um ursinho de pelúcia .. . difícil.
FrustratedWithFormsDesigner
1
@FrustratedWithFormsDesigner, consulte o link que adicionei.
Péter Török 22/02
2
@ Peter, temo que ter uma prateleira cheia de ursinhos de pelúcia pronta para os colegas buscarem depuração, possa causar uma impressão errada aos clientes.
1
@FrustratedWithFormsDesigner: E como é que diferem de muitos colegas
mattnz
3

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.

FrustratedWithFormsDesigner
fonte
Você pode usar qualquer coisa, mas um depurador do tipo de gdb ou o que você encontraria em seu IDE
Anto
@ Anto: Isso soa como uma linha de uma tarefa de casa, mas nesse caso, o registro em um arquivo ou o console não está usando um depurador como "gdb ou o que você encontra no seu IDE". O gdb e outros depuradores permitem percorrer seu código linha por linha e inspecionar os valores das variáveis ​​à medida que o programa é executado . Depurar por log significa que você precisa usar um rastreamento (no arquivo ou no console) da execução do programa após a conclusão e descobrir o que aconteceu.
FrustratedWithFormsDesigner
Eu sei, portanto, o que você recomendou é permitido. Não, isso não é tarefa de casa; Estou no ensino médio e não temos programação / cs.
Anto 22/02
2
@Anto: Ok. A única desvantagem desse método é se você estiver tentando depurar um programa com problemas de sincronização. Por exemplo, se for um problema do IPC, as instruções de impressão / log podem afetar a rapidez com que os processos se comunicam e ter o log ativado ou desativado pode afetar a reprodução do problema (nesse caso, você realmente precisa usar @ Conselho de Thorbjørn Ravn Andersen). Em alguns casos, o log pode prejudicar severamente o desempenho, mas geralmente apenas quando muitos acessam um sistema grande ao processar quantidades muito grandes de dados ... mas algo para estar ciente.
FrustratedWithFormsDesigner
3

Depende. Funcionou antes? Se o código que costumava funcionar quebrar de repente, você deve examinar cuidadosamente as alterações mais recentes.

Dima
fonte
2
Essa abordagem não deve ser subestimada: o histórico de revisões é uma ótima maneira de identificar erros no código que funcionava anteriormente - mesmo ao adicionar novos recursos.
EdA-qa mort-ora-y
2
Ouvi dizer que 'git bisect' automatiza um pouco essa tarefa. Ainda tenho que tentar.
Clayton Stanley
2

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.

ggambett
fonte
2

Depois que as outras respostas falham , sempre há a depuração de pesquisa binária :

  1. Eliminar uma certa porção (de preferência metade) das possíveis causas (linhas de código, revisões , entrada, etc.)
  2. Tente reproduzir o problema novamente.
  3. Se o problema persistir: volte para a etapa 1.
  4. Se você tiver apenas uma causa (linha de código, revisão, parte da entrada, etc) restante: viva! Procedimento de saída.
  5. Caso contrário: reverta a etapa 1 e agora elimine a outra metade.
  6. Volte ao passo 2.

Nota: obviamente, esta técnica só funciona se você puder reproduzir o problema de maneira confiável.

Jeroen
fonte
1. Elimine metade das causas possíveis. O problema desaparece. 2. Restaure essa metade e elimine a outra. O problema desaparece. 3. Elimine apenas algumas causas possíveis. O problema desaparece se você eliminar 20% arbitrários deles. 4. Comece a examinar o desempenho, o mecanismo subjacente e a rodar em círculos. 5. Pânico.
SF.
Com letras grandes e amigáveis.
Jeroen
0

"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.


fonte
Não tenho nenhum teste de unidade, pois não tenho um projeto ou código específico; Estou apenas pedindo métodos de depuração
Anto
Por que diabos você votaria nesta resposta? Pare de mijar no escuro e faça o teste de unidade então.
Não fui eu quem downvoted então vá chorar para alguém
Anto
2
O teste de unidade não substitui a depuração, ele simplesmente ajuda a compartimentar e restringir os bugs. O que torna a depuração mais simples, quando um bug aparece em um teste de unidade codificado. No IME, a maioria dos bugs complicados ocorre em interações de componentes (teste difícil de unidade) e são detectados com muito mais frequência em conjuntos de testes básicos no estilo de regressão.
Clayton Stanley
-1) Como você corrige o código identificado por um teste de unidade quebrado - você o depura ...... Os testes de unidade detectam bugs, depuradores e depuração são usados ​​para corrigir o defeito.
mattnz
0

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.

Clayton Stanley
fonte
0

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.

SF.
fonte