231 lines
5 KiB
Org Mode
231 lines
5 KiB
Org Mode
#+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 <stdio.h>
|
||
|
||
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 <stdio.h>
|
||
|
||
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 <stdio.h>
|
||
|
||
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~!
|
||
|