Debugar um programa é a maneira mais efetiva de se encontrar erros e avaliar o fluxo de execução do programa. Em MC202, os laboratórios são feitos em C. Em C, temos diversas rotinas de manipulação de memória, ponteiros e chamadas de funções. Em todos esses casos realizar a depuração do programa irá permitir identificar erros com facilidade.
Para debugar um programa iremos utilizar a ferramenta GDB. Com ela é possível:
- executar instrução por instrução de um programa
- manipular valores de variáveis
- acompanhar chamadas de funções;
- inspecionar a memória do programa
- e várias outras funcionalidades.
Nesse tutorial, você irá aprender a instalar o GDB e debugar um programa bem simples.
Instalação
GNU/Linux
NO Ubuntu ou Debian, utilize os comandos para instalar o GDB:
user@desktop:~$ sudo apt-get update && sudo apt-get install gdb -y
Para verificar a instalação utilize:
user@desktop:~$ gdb --version
Caso a instalação foi concluída com sucesso, uma mensagem irá aparecer. A mensagem é semelhante a:
GNU gdb (Ubuntu 9.1-0ubuntu1) 9.1
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Mac e Windows
Você pode instalar o GDB com passos parecidos aos utilizados para instalar o compilador GCC.
Debugando um programa
Para facilitar o processo de depuração, é importante compilar o programa passando a flag -g
para o GCC. Essa flag instrui o compilador a adicionar dados no binário executável contendo informações úteis e outros metadados. Por exemplo, ele insere uma tabela mapeando cada linha do código-fonte às instruções de máquina binárias correspondentes no arquivo executável. Assim, compilamos um arquivo main.c
como:
user@desktop:~$ gcc -std=c99 -Wall -Werror -g main.c -o main -lm
Carregando um programa
Uma vez compilado o programa, é necessário carregá-lo no GDB utilizando o comando:
user@desktop:~$ gdb ./main
Isso abrir uma sessão interativa do GDB e já irá carregar todos os metadados do programa na memória, mas seu programa ainda não terá começado a executar. Uma mensagem como a seguinte irá aparecer:
user@desktop:~$ gdb ./main
GNU gdb (Ubuntu 9.1-0ubuntu1) 9.1
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./main...
(gdb)
Breakpoints
Os breakpoints são utilizados para parar a execução do programa imediatamente antes da execução de um determinado ponto. O comando utilizado é o break <argumento>
ou b <argumento>
. Um argumento pode ser o número da linha como argumento (b 10
) ou o nome de uma função (b main
).
Outros comandos
Além do comando de breakpoint outros comandos importantes que são comumente utilizados são:
run
our
: inicia a execução do programa.continue
ouc
: continua a execução do programa até a próxima parada.step
ous
: executa a próxima linha do programa.next
oun
: igual aos
, porém, não entra em funções.print
oup
: imprime na tela o valor de uma variável.display
: imprime na tela o conteúdo cada vez que o programa para.delete
oud
: remove todos os breakpoints.
Debugando um primeiro programa
Vamos utilizar o seguinte programa main.c
nesse tutorial:
#include <stdio.h>
void escreva(int x) {
printf("Escrevendo: %d\n", x);
}
int main(void) {
int i = 10;
while (i >= 0) {
escreva(i);
i--;
}
return 0;
}
Compile o programa main.c com a flag de depuração(-g
):
user@desktop:~$ gcc -std=c99 -Wall -Werror -g main.c -o main -lm
Carregue o programa com o gdb:
user@desktop:~$ gdb ./main
Crie um breakpoint na função main:
(gdb) b main
Inicie a execução do programa:
(gdb) r
A execução irá parar com a seguinte mensagem:
Breakpoint 1, main () at main.c:8
8 int i = 10;
Utilize o comando n
para executar uma linha. A seguinte mensagem irá aparecer:
(gdb) n
10 while(i >= 0) {
(gdb)
Visualize o valor da variável i
:
(gdb) p i
$1 = 10
(gdb)
Imprima o valor de i
em hexadecimal cada vez que o programa parar:
(gdb) display/x i
Coloque um breakpoint na linha 11:
(gdb) b 11
Continue a execução até o próximo breakpoint c
:
(gdb) c
Continuando.
Breakpoint 2, main () at main.c:11
11 escreva(i);
1: /x i = 0xa
Repita o comando anterior:
(gdb) c
Continuando.
Breakpoint 2, main () at main.c:11
11 escreva(i);
1: /x i = 0x9
Remova todos os breakpoints e continue até o programa terminar:
(gdb) d
Delete all breakpoints? (y or n) y
(gdb) c
Continuing.
Escrevendo: 9
Escrevendo: 8
Escrevendo: 7
Escrevendo: 6
Escrevendo: 5
Escrevendo: 4
Escrevendo: 3
Escrevendo: 2
Escrevendo: 1
Escrevendo: 0
[Inferior 1 (process 7492) exited normally]
Finalize o gdb com o comando quit
(gdb) quit