123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413 |
- <!--
- Introdução às técnicas básicas de depuração de código
- -->
- <meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
- <meta name='keywords' content='mac0122, material, professores, leonidas de oliveira brandao'>
- <link rel='stylesheet' type='text/css' href='css_img_js_conf/all.css'>
- <link rel='stylesheet' type='text/css' href='css_img_js_conf/line_introducao_programacao.css'>
- <div class="pagina">
- <p class="secao">Por que devemos saber depurar o código?</p>
- <p>
- Se você já está envolvido com o aprendizado de <i>programação</i> há algum tempo, então muito provavelmente já recebeu
- <font color="#aa0000">mensagens de erro</font>, algumas <b>sintáticas</b> (quando existe algum erro na <i>sintaxe</i> de seu código)
- mas outras, geralmente mais difíceis de resolver, na <i>lógica da execução</i>, neste caso um erro <b>semântico</b> (o programa não faz
- o que era esperado que ele fizesse).
- </p>
- <p>
- Outra situação desagradável quanto é utilizado um ambiente para teste automático do código (caso é o caso do uso do
- <i>Moodle</i> com <i>VPL</i> ou com o <i>iTarefa/iVProg</i>), é o aprendiz conseguir (aparentemente) rodar com sucesso em seu ambiente,
- mas ao passar para o ambiente de seu curso, recebe alguma mensagem de erro...
- </p>
- <p>
- Então o que você faz? Olha todas as linhas do código e nada, tudo parece estar como deveria.
- O código parece correto, "não tem motivo algum para a saída ser diferente da esperada, certamente o
- <font color="#aa0000">sistema do curso tem erro</font>" é o que você pensa.
- <!--O avaliador automático deve estar com problema.-->
- </p>
- <p>
- Esta é uma reação natural, porém
- <font color="#000088">nem sempre "lemos" o que está codificado, mas o que pensamos ter codificado</font>,
- talvez você já tenha passado por isso ao escrever um redação...
- Nestes casos, o problema é nao darmos a devida atenção para todos os detalhes, mas em códigos mais complexos, eventualmente não
- examinamos algumas situações que os <i>casos-de-teste</i> estão preparados para testar.
- </p>
- <p>
- Portanto, seja por falta de atenção, paciência ou complexidade do código, erros são comuns.
- Então o que devemos fazer para achar e corrigir estes erros?
- </p>
- <p>
- Primeiro devemos entender a natureza do erro, existem duas grandes categorias,
- <font color="#008800"<i>erro sintático</i></font> ou <font color="#008800"<i>erro semântico</i></font>.
- </p>
- <ul>
- <li><b>Erro Sintático</b>:
- É um erro na sintaxe da linguagem, em sua gramática, como por exemplo, usar letra maiúscula inicial para o <i>comando de seleção</i>
- (o sistema não reconhecerá como "comando de seleção", tentará como uma "atribuição", mas como "atribuição" também não estará correta).
- Neste caso, seu programa <font color="#aa0000">não</font> poderá ser "rodado".
- <!--de escrita, em geral o próprio compilador avisa, seja como <i>erro</i> (seu programa não irá compilar) ou <i>avisos</i> (um problema "corrigivel" pelo compilador)-->
- </li>
- <li><b>Erro semântico</b>:
- É um erro na "lógica de seu código", em sua <i>semântica</i>, o código está sintaticamente correto, porém não faz o que se esperava dele.
- Por isso, este tipo de erro é geralmente mais difícil de ser identificado e corrigido.
- Para isso algumas técnicas básicas, como as apresentadas a seguir, podem ser de grande valia.
- <!-- Como o código está escrito seguindo a sintaxe correta, o compilador não acusa erro. -->
- </li>
- </ul>
- <p class="secao">Como tratar erros sintáticos?</p>
- <p>
- Como estes erros são acusados pelo próprio compilador são mais fáceis de identificar. Em geral, a mensagem de erro gerada indica a linha
- onde ocorreu o erro. Então é preciso calma e uma leitura cuidadosa para entender o significado da mensagem.
- </p>
- <p class="subsecao">1. Leia as mensagens de erro geradas pelo compilador/interpretador</p>
- <p>
- As mensagens de erro indicam qual o problema encontrado e porque a compilação/execução falhou. Por exemplo, considere os
- códigos <i>C</i> e <i>Python</i> a seguir:
- </p>
- <center>
- <i>Tab. 1. Exemplos de códigos com erros sintáticos (nome errado para o comando de impressão).</i>
- <table ><tr style="border-bottom: 1px solid #000;"><td style="border-right: 1px solid #000;"><i>C</i></td><td> <i>Python</i></td></tr>
- <tr><td style="border-right: 1px solid #000;">
- <pre>#include <stdio.h>
- int main (void) {
- <font color="#aa0000">prit</font>("Hello World\n");
- }</pre>
- <p>
- A mensagem de erro (ou advertência) recebida ao tentar compilar:
- </p>
- <pre style="font-size:0.8em">main.c:2:1: warning: implicit declaration of function ‘<font color="#aa0000">prit</font>’ [-Wimplicit-function-declaration]
- main.c: undefined reference to `prit'
- </pre></td><td><pre> def main () :
- <font color="#aa0000">prit</font>("Hello World");
- main();</pre>
- <p>
- A mensagem de erro recebida:
- <pre style="font-size:0.8em"> Traceback (most recent call last):
- File "teste2.py", line 3, in <module>
- main();
- File "teste2.py", line 2, in main
- <font color="#aa0000">prit</font>("teste");
- NameError: global name 'prit' is not defined</pre></td></tr></table>
- </center>
- <p>
- Examinando as mensagens recebidas, podemos perceber que a linha com a "tentativa" de comando <tt><font color="#aa0000">prit</font></tt>
- está com problema.
- Se prestarmos atenção, nesta linha está escrito <tt><font color="#aa0000">prit</font></tt> ao invés de
- <tt><font color="#008800">printf</font></tt> para a linguagem <i>C</i>
- e <tt><font color="#008800">print</font></tt> para a linguagem <i>Python</i>.
- </p>
- <p>
- Infelizmente nem sempre os erros são tão claros e simples como este. Como programação também é uma tarefa de exploração, as vezes
- utilizamos ferramentas que não estamos habituados, portanto apenas a leitura do erro não é suficiente.
- </p>
- <p class="subsecao">2. Procurar na <i>Internet</i></p>
- <p>
- Provavelmente você não é a primeira pessoa a receber esta mensagem de erro, nem será a última, então, se fizer uma busca pela
- <i>Web</i>, provavelmente encontrará uma mensagem relatando erro semelhante e poderá estudar as proposta de correção.
- <!-- com uma busca rápida em fóruns, você conseguirá achar outra pessoa que passou por esta mesma situação e como ela solucionou. -->
- </p>
- <p>
- Em geral basta copiar o <i>mensagem de erro</i> e colar na barra de pesquisa do seu "buscador" preferido
- (experimente o <a href="https://duckduckgo.org/?q=%22debugging+program%22+%22find+out%22+error&t=h_&ia=web"
- title="Um buscador que tem por princípio NAO persegui-lo! Não vender seus dados...">duckduckgo.org</a>).
- </p>
- <!--
- teste_c/usar_pacote_assert.c
- http://www.cs.yale.edu/homes/aspnes/pinewiki/C(2f)Debugging.html
- -->
- <p class="secao">Como tratar erros semânticos?</p>
- <p>
- Como são erros de "lógica de programação", <!-- ou seja, seu programa executa, --> então existe ao menos um <i>conjunto de entradas</i>
- para as quais a saída não é a esperada.
- Isto nos deixa em uma situação que requer mais atenção e nem sempre será uma solução fácil.
- Muitas vezes requerem reescrita parcial ou total do código.
- </p>
- <p class="subsecao">1. Procure <i>simular</i> seu código</p>
- <p>
- Se o seu algoritmo não é muito grande, pode-se fazer uma simulação dele para entender exatamente o que está fazendo e com isso identificar
- o momento que o primeiro erro aparece.
- </p>
- <p>
- Para simular, construa uma tabela com todas variáveis de seu código (ou apenas aquelas que deseja rastrear),
- sendo que cada variável terá sua coluna.
- A cada instrução que altere o valor de determinada variáveis, deve-se registrar o novo valor na coluna correspondente,
- na linha seguinte à última linha que teve um valor registrado, ou seja, as linhas da tabela indicam a ordem de execução
- (uma entrada mais acima indica que a instrução que alterou a variável correspondente ocorreu "mais cedo").
- </p>
- <p>
- <b>ATENÇÃO</b>, é essencial seguir precisamente a ordem de execução dos comandos e deve-se simular/executar
- <b>exatamente</b> o que está redigido (e não como "acha que deveria estar")!
- </p>
- <center>
- <p>
- <i>Tab. 2. Exemplo de simulação de um código (sem erro): imprimir a somas dos naturais</i>. Usando "pseudo-código".</i>
- <table><tr><td><pre style="font-size:0.8em">
- # Codigo | N | soma | i | Impressoes | Explicacoes (por linha)
- --------------------------------------------+-----------------------+------------+--------------------------
- 1 variaveis N, soma=0, i=0; | ? | 0 | 0 | | 1 : valores iniciais (N desconhecido!)
- 2 N = leia(); // ler valor e guardar em N | 3 | | | | 2 : ler valor e guardar em N (supor 3)
- 3 enquanto (i < N) { | | | | | 3 : 0 < 3 verdadeiro => entra no laco
- 4 soma = soma + i; | | 0 | | | 4 : acumular i em soma
- 5 i = i + 1; | | | 1 | | 5 : acumular 1 em i
- 6 } | | | | | 6 : final laco, voltar 'a linha 3
- 7 escreva("fim"); | | | | | 3 : 1 < 3 verdadeiro => entra no laco
- | | 1 | | | 4 : acumular i em soma
- | | | 2 | | 5 : acumular 1 em i
- | | | | | 6 : final laco, voltar 'a linha 3
- | | | | | 3 : 2 < 3 verdadeiro => entra no laco
- | | 3 | | | 4 : acumular i em soma
- | | | 3 | | 5 : acumular 1 em i
- | | | | | 6 : final laco, voltar 'a linha 3
- | | | | | 3 : 3 < 3 falso => vai para final do laco
- | | | | 3 | 7 : escrever valor em soma</pre>
- </td></tr></table></p>
- </center>
- <p class="subsecao">2. Releia o código</p>
- <p>
- Eventualmente existe um erro de lógica que "salte aos olhos", podendo deste modo encontrá-lo rapidamente.
- Vejamos dois casos clássicos de erros semânticos (e que portanto podem rapidamente ser identificados), um
- na linguagem <i>C</i> e outro na linguagem <i>Python</i>.
- </p>
- <center>
- <i>Tab. 3. Um exemplo de erro semânticos clássico em <i>C</i> e um em <i>Python</i>.</i>
- <br/>
- <table ><tr style="border-bottom: 1px solid #000;"><td style="border-right: 1px solid #000;">Erro semântico "clássico" em <i>C</i></td>
- <td> </td>
- <td>Erro semântico "clássico" em <i>Python</i></td></tr><tr><td style="border-right: 1px solid #000;"></td></tr>
- <tr><td style="border-right: 1px solid #000;">
- <pre>if (x = 0) // supondo que x chegue aqui com valor 0
- printf("O valor e' nulo\n");</pre>
- <p>
- Entretanto, a mensagem <b>não</b> é impressa!
- A razão é que em <i>C</i> uma <i>expressão lógica</i> é uma <i>expressão aritmética</i> que é comparada com o valor nulo,
- se for zero, então o resultado é <b>falso</b>, senão é <b>verdadeiro</b>.
- Como a <i>expressão lógica</i> que aparece é <tt>x = 0</tt>, na verdade é um <i>comando de atribuição</i>,
- significando que a variável <tt>x</tt> receberá (novamente) o valor nulo
- e o resultado da <i>expressão aritmética</i> é precisamente o zero!
- Ou seja, o resultado lógico será <i>falso</i> e o comando subordinado <b>não</b> será impresso!<br/>
- Assim, correção é simples:
- </p>
- <p>
- <pre>if (x == 0) // correcao: usar "=="
- printf("O valor e' nulo\n");</pre></td>
- <td> </td>
- <td>
- <pre>if (x==0) : # supondo que x chegue aqui com valor 1
- y = 2;
- print("O valor e' nulo");</pre>
- </p>
- </center>
- <p>
- Entretanto, a mensagem é <b>incorretamente</b> impressa!
- A razão é que em <i>Python</i> a <i>subordinação</i> de comandos é definida por <i>indentações</i>, ou seja,
- para que o <tt>print("O valor e' nulo")</tt> estivesse corretamente subordinado ao comando <tt>if</tt> ele precisaria estar
- alinhado ao comando <tt>y = 2;</tt>.<br/>
- Assim, correção é simples:
- <pre> if (x == 0) :
- y = 2;
- print("O valor e' nulo"); # correcao, alinhar com a linha acima</pre></td></tr></table>
- </center>
- <p>
- Porém, nem sempre é realista reler o código todo, então foque em partes críticas,
- <!--
- Partes críticas são limites entre
- trechos de código, portanto devemos ter bastante atenção para <b>seleção</b> (if), <b>condicional de laços</b>
- (while, for, do while), <b>atribuições</b> e <b>chamadas de função</b>.
- -->
- aquelas partes em que o erro acontece. Geralmente o erro encontra-se em algum comando de <b>seleção</b> (<tt>if</tt>),
- em <b>condicional de laço</b> (<tt>while</tt>, <tt>for</tt> ou outros), em <b>atribuições</b> ou em <b>chamadas de função</b>.
- </p>
- <p>
- Este método requer maior conhecimento do programador para saber qual a parte crítica do código, elas podem ser diferentes para cada problema.
- Além disto é necessário um entendimento maior do problema.
- <!-- , conseguindo identificar a quais comandos são necessários e em qual ordem devem ser executados. -->
- Então a técnica seguinte pode ajudar.
- </p>
- <a name="bandeiras">
- <p class="subsecao">3. Utilizar "bandeiras" (flags) </p>
- </a>
- <p>
- Nem sempre é possível identificar tudo apenas relendo o código, principalmente em códigos mais complexos. Portanto precisamos de
- mais ferramentas. Esta técnica ajuda a encontrar as <i>partes críticas</i> de seu código e consiste em imprimir algumas variáveis
- ao longo do código.
- </p>
- <p>
- Se você não tem a mínima ideia de onde o erro esteja, pode colocar uma impressão de uma lista de variáveis como primeira instrução
- dentro de cada comando de <i>seleção</i> ou em cada comando de <i>repetição</i>.
- No caso de laço infinito (um erro muito comum!), o uso de uma "bandeira" como primeira instrução do <i>comando de repetição</i>
- lhe indicará claramente o problema, <i>laço infinito</i>!
- <!--
- Esta técnica consiste em imprimir os valores de variáveis em um momento importante, em geral, logo antes dela ser utilizada.
- Através dessa impressão podemos comparar nossa expectativa, qual o valor que a variável deveria
- ter, com a realidade, qual valor ela tem.
- -->
- </p>
- <!--
- <p>
- Desta forma devemos criar casos de teste nos quais saibamos os valores que as variáveis devem assumir durante a execução do código.
- </p>
- -->
- <p>
- Considere o seguinte problema: <i>Escreva um programa que imprima a frase "hello world" 10 vezes</i>.
- Suponha que um colega tenha apresentado a seguinte solução (em <i>C</i> à esquerda e em <i>Python</i> à direita).
- </p>
- <center>
- <i>Tab. 4. Um exemplo de erro semântico clássico: laço infinito.</i>
- <table ><tr style="border-bottom: 1px solid #000;"><td style="border-right: 1px solid #000;"><i>C</i></td><td> <i>Python</i></td></tr>
- <tr><td style="border-right: 1px solid #000;">
- <pre>#include <stdio.h>
- int main (void) {
- int i=0;
- while (i < 10) {
- printf("hello world\n");
- }
- }</pre>
- </td><td><pre> def main () :
- i = 0;
- while (i < 10) :
- print("Hello World");</pre>
- </td></tr></table>
- </center>
- <p>
- Ao executar o código, a frase fica sendo impressa indefinidamente (portanto <i>laço infinito</i>), neste caso, qual o melhor local para inserirmos uma bandeira?
- </p>
- <p>
- Como o problema está relacionado à frase ser impressa muitas vezes, fica natural colocar a <i>bandeira</i> como primeira instrução do <i>comando de repetição</i>.
- </p>
- <p style="color:#00aa00">
- Mas qual variável imprimir?
- </p>
- <p>
- No exemplo acima (tab. 4), não é difícil deduzir, pois o laço usa a variável <tt>i</tt> como controle (e não tem outra...).
- </p>
- <p>
- Ao fazer isto e executarmos o código percebemos que além da frase, o valor de <tt>i</tt> está sempre com o valor nulo.
- Portanto o que falta é o incremento na variável de controlo, antes de testar a <i>condição de entrada</i>.
- <br/>
- Ou seja, identificamos que não existe uma atribuição para incrementar a variável de controle.
- </p>
- <p style="color:#00aa00">
- O que fazer em códigos maiores?
- </p>
- <p>
- Quando seu código for grande, será necessário identificar cada "bandeira", por exemplo, usando a linguagem "Portugol",
- use algo parecido com:
- <tt>imprima("1: alguma informacao daqui");... imprima("2: algo daqui");...imprima("10: algo daqui");...</tt>.
- </p>
-
- <p>
- Mas vale a pena fazer uma análise geral de seu código e da resposta obtida, isso pode indicar um provável local de erro,
- neste caso, concentre-se neste trecho, colocando uma "bandeira" em cada comando de <i>seleção</i> e em todos os inícios
- de <i>laços</i>.
- Neste caso vale a pena diferenciar as mensagens, por exemplo, com:
- <tt>imprima("se 1: i=%d", i); ... imprima("laco 1: j=%d", j); ... </tt>.
- </p>
- <p>
- Esta técnica é muito utilizada por ser rápida e extremamente efetiva.
- Mas lembre-se: sempre apague/comente as "bandeiras" depois de utilizá-las,
- você não quer que a execução do seu código final fique poluída com a impressão de várias "bandeiras".
- </p>
- <p class="subsecao">4. Explique o código para alguém:</p>
- <p>
- Esta técnica é conhecida como
- "<a href="https://rubberduckdebugging.com/" title="mais detalhes sobre RDD">Rubber duck debugging" (RDD - "depuração pato de borracha")</a>,
- que está associada a ideias bastante antigas
- (como "tente ensinar para aprender") e outras nem tanto, como a técnica "pensamento em voz alta" (<i>think aloud</i>).
- A RDD consiste em explicar, linha por linha, o código para uma outra pessoa, ou na falta de uma pessoa explique para um objeto
- (como o "pato de borracha").
- </p>
- <p>
- Pode-se adotar uma abordagem hierárquica, procurando explicar de modo mais geral o que seu programa deveria fazer e depois ir detalhando.
- Primeiro explique os objetivos do problema, o que você deseja fazer e quais ideias tem para resolvê-lo. Eventualmente pode haver um
- problema de entendimento de enunciado.
- <br/>
- Se o problema estiver nos detalhes, tente explicar cada trecho de seu código e, em cada um, o que que cada linha dele faz.
- </p>
- <p>
- Ao explicar o trabalho para outra pessoa que não tem a mesma familiaridade com o problema ou com seu código,
- eventualmente você poderá compreender melhor o fez ou o que deveria fazer.
- <!-- acaba entrando em detalhes que pareciam óbvios ou irrelevantes, sendo por vezes a causa da dor de cabeça.-->
- </p>
- <p class="autoria">
- <a href="https://www.ime.usp.br/~leo" target="_blank" title="seguir para a pagina do prof. Leônidas">Leônidas de Oliveira Brandão</a><br/>
- <a href="http://www.ime.usp.br/~leo" target="_blank" title="seguir para a página do LInE">http://line.ime.usp.br</a>
- </p>
- <p class="rodape">
- <b>Alterações</b>:<br/>
- 2020/08/15: novo formato, pequenas revisões<br/>
- 2020/08/07: Sexta, 07 Agosto 2020, 20:15<br/>
- 2020/03/30: Segunda, 30 Março 2020, 21:15
- </p>
- <!--
- Para entender melhor esta história clique aqui</a>.
- https://rubberduckdebugging.com/
- https://www.techopedia.com/definition/31880/rubber-duck-debugging
- http://lists.ethernal.org/oldarchives/cantlug-0211/msg00174.html
- -->
- </div>
|