#+title: Curso prático de introdução ao GDB #+author: Blau Araujo #+email: blau@debxp.org * 5. Casos de uso ** Objetivo Demonstrar o uso do GDB para localizar e solucionar bugs comuns, como: - Falha de segmentação; - Variável não inicializada; - Loop infinito; - Índice de vetor inválido; - Parâmetro incorreto na chamada de uma função; - Registradores incorretos em uma chamada de sistema; - Desalinhamento da pilha; - Erro de empilhamento e desempilhamento de registros; - Estouro de buffer; - Estouro de pilha; - Vazamento de memória. ** Falha de segmentação Acesso inválido à memória causado por um ponteiro não inicializado. Código (=segfault.c=): #+begin_src c #include int main() { int *p = NULL; *p = 42; // ERRO: tentativa de escrever em um ponteiro nulo printf("%d\n", *p); return 0; } #+end_src Compilação: #+begin_example :~$ gcc -g -o segfault segfault.c $ gdb ./segfault Reading symbols from ./segfault... #+end_example *** Processo de depuração Verificar o que acontece ao executar: #+begin_example (gdb) r Starting program: /home/blau/git/gdb-pratico/mods/05/exemplos/segfault [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Program received signal SIGSEGV, Segmentation fault. 0x000055555555514d in main () at segfault.c:5 5 *p = 42; // ERRO: tentativa de escrever em um ponteiro nulo #+end_example Matando o processo: #+begin_example (gdb) k [Inferior 1 (process 1034793) killed] #+end_example Definindo =main= como ponto de parada e executando: #+begin_example (gdb) b main Breakpoint 5 at 0x555555555141: file segfault.c, line 4. (gdb) r Starting program: /home/blau/git/gdb-pratico/mods/05/exemplos/segfault [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Breakpoint 5, main () at segfault.c:4 4 int *p = NULL; #+end_example Listando variáveis locais antes e após a inicialização do ponteiro =p=: #+begin_example (gdb) i locals p = 0x7fffffffdf40 (gdb) n 5 *p = 42; // ERRO: tentativa de escrever em um ponteiro nulo (gdb) i locals p = 0x0 #+end_example O endereço de memória no ponteiro é claramente nulo (=0x0=)! ** Variável não inicializada Uso de uma variável antes da atribuição de um valor (seu valor é lixo de memória). Código do exemplo (=notinit.c=): #+begin_src c #include int main(void) { int a; // PROBLEMA: Variável não inicializada! printf("Digite um número: "); scanf("%d", &a); // Se scanf falhar, o valor de a será lixo! printf("Depois de %d vem %d\n", a, a + 1); return 0; } #+end_src Compilação e teste: #+begin_example :~$ gcc -g -o notinit notinit.c :~$ ./notinit Digite um número: (teclar CTRL+D) Depois de 32765 vem 32766 #+end_example *** Processo de depuração Basta executar o programa passo a passo examinando o valor da variável =a=: #+begin_example :~$ gdb ./notinit Reading symbols from ./notinit... (gdb) b main Breakpoint 1 at 0x1151: file notinit.c, line 7. (gdb) r Starting program: /home/blau/git/gdb-pratico/mods/05/exemplos/notinit [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Breakpoint 1, main () at notinit.c:7 7 printf("Digite um número: "); (gdb) p a $1 = 32767 (gdb) n 8 scanf("%d", &a); // Se scanf falhar, o valor de a será lixo! (gdb) n Digite um número: 10 printf("Depois de %d vem %d\n", a, a + 1); (gdb) p a $2 = 32767 (gdb) n Depois de 32767 vem 32768 12 return 0; (gdb) c Continuing. [Inferior 1 (process 1039355) exited normally] #+end_example Com o erro na execução de =scanf= (simulado com o atalho =CTRL+D=), o valor da variável não inicializada continuou sendo lixo de memória (32767). ** Loop infinito Uma condição de parada incorreta faz com que as repetições não tenham fim. Código de exemplo (=iloop.c=): #+begin_src c #include int main() { int i = 0; // A condição de parada nunca será atendida! while (i < 100) { printf("i = %d\n", i); i *= 2; // Sempre zero! } return 0; } #+end_src Compilação e teste: #+begin_example :~$ gcc -g -o iloop iloop.c :~$ ./iloop i = 0 i = 0 i = 0 i = 0 i = 0 i = 0 i = 0 i = 0 ^C #+end_example *** Processo de depuração #+begin_example $ gdb ./iloop Reading symbols from ./iloop... (gdb) b main Breakpoint 1 at 0x1141: file iloop.c, line 4. (gdb) r Starting program: /home/blau/git/gdb-pratico/mods/05/exemplos/iloop [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Breakpoint 1, main () at iloop.c:4 4 int i = 0; (gdb) n 7 while (i < 100) { (gdb) p i $1 = 0 (gdb) n 8 printf("i = %d\n", i); (gdb) n i = 0 9 i *= 2; // Sempre zero! (gdb) p i $2 = 0 (gdb) n 7 while (i < 100) { (gdb) p i $3 = 0 #+end_example Fica claro que o valor de nunca foi alterado, porque ~2 × 0 = 0~!