Guia basico GDB
Traduzido de Tutorial of gcc and gdb.
Introdução
'gdb é um depurador (debugger) feito pelo projeto GNU. gdb pode executar passo a passo o seu programa linha por linha e mesmo instrução por instrução. Com ele se consegue também observar o valor de qualquer variável em tempo de execução. Por fim, ele ajuda a identificar o lugar e o motivo que causou um erro de execução do programa.
Uso básico
Todo programa a ser depurado com gdb precisa ser compilado pelo gcc com a opção -g. Isso é demonstrado abaixo com um pequeno programa de exemplo.
- Copie este programa para o arquivo exemplo.c:
#include <stdio.h> #include <string.h> int main() { int n; char nome[32]; printf("Digite seu nome: "); fgets(nome, 32, stdin); nome[strlen(nome)] = 0; for (n=strlen(nome); n >= 0; n--) { printf("%c", nome[n]); } return 0; }
- Compile-o da seguinte forma:
gcc -g -o exemplo exemplo.c
- Use o gdb para depurá-lo:
gdb ./exemplo
- ... o que vai mostrar uma apresentação do gdb na sua tela:
GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04 Copyright (C) 2012 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". For bug reporting instructions, please see: <http://bugs.launchpad.net/gdb-linaro/>... Lendo símbolos de /home/aluno/exemplo...concluído. (gdb)
- ... o que vai mostrar uma apresentação do gdb na sua tela:
- Antes de iniciar a execução do programa, defina ao menos um ponto de parada (breakpoint). O gdb para a execução nesse ponto, a partir do qual se pode executar passo-a-passo. Um ponto de parada pode ser uma função ou uma linha do programa. No exemplo, o ponto de parada foi definido como sendo a função main:
(gdb) break main Ponto de parada 1 at 0x40063c: file exemplo.c, line 4.
- Execute o programa, notando que a execução para no ponto de parada que foi definido:
(gdb) run Starting program: /home/aluno/exemplo Breakpoint 1, main () at exemplo.c:4 4 int main() {
- Para avançar uma linha do programa, use o comando next. Após esse comando é mostrada a próxima linha do programa a ser executada:
(gdb) next 8 printf("Digite seu nome: ");
- Para terminar a depuração, use o comando quit:
(gdb) quit A debugging session is active. Inferior 1 [process 15861] will be killed. Quit anyway? (y or n) Y
Execução de um programa que pede argumentos de linha de comando
Devem-se informar os argumentos do programa em seguida ao comando run. Ex: se o programa exemplo pedisse um argumento:
(gdb) run arg1
... e se pedisse dois argumentos:
(gdb) run arg1 arg2
Execução para descobrir em que linha está um erro
Quando um programa termina com um erro fatal, como Falha de segmentação, torna-se necessário saber exatamente o que o programa tentou fazer e desencadeou o erro. O gdb possibilita descobrir isso facilmente, bastando executar o programa dentro do depurador. Por exemplo, seja o programa erro.c:
#include <stdio.h>
#include <string.h>
int main() {
char * nome;
printf("Digite seu nome: ");
fgets(nome, 32, stdin);
nome[strlen(nome)-1] = 0;
printf("seu nome é: %s\n", nome);
return 0;
}
Ao compilá-lo e executá-lo, obtém-se um erro fatal:
$ gcc -g -o erro erro.c
$ ./erro
Digite seu nome: maneca
Falha de segmentação (imagem do núcleo gravada)
Executando-se esse programa com gdb descobre-se onde ocorreu o erro. Após o erro, usa-se o comando backtrace para mostrar em que linha do programa ele aconteceu:
$ gdb erro
(gdb) run
Starting program: /home/aluno/erro
Digite seu nome: maneca
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7a8a270 in _IO_getline_info () from /lib/x86_64-linux-gnu/libc.so.6
(gdb) backtrace
#0 0x00007ffff7a8a270 in _IO_getline_info ()
from /lib/x86_64-linux-gnu/libc.so.6
#1 0x00007ffff7a8913b in fgets () from /lib/x86_64-linux-gnu/libc.so.6
#2 0x00000000004005b9 in main () at erro.c:8
(gdb)
Nesse exemplo, o erro se deu na linha 8 de erro.c:
fgets(nome, 32, stdin);
... e o motivo foi ter tentado escrever dados na variável nome, porém essa variável não aponta uma área de memória previamente alocada.
Passo a passo
A execução passo-a-passo pode ser feita com os comandos next ou step. Com next executa-se uma linha de código, sem entrar em funções que porventura sejam chamadas. Com step executa-se uma linha, porém entrando em funções chamadas. Por exemplo, seja o programa exemplo1.c abaixo:
#include <stdio.h>
#include <string.h>
void mostraInvertido(char * texto) {
int n;
for (n=strlen(texto); n > 0; n--) {
printf("%c", texto[n-1]);
}
puts("");
}
int main() {
char nome[32];
printf("Digite seu nome: ");
fgets(nome, 32, stdin);
nome[strlen(nome)-1] = 0;
mostraInvertido(nome);
return 0;
}
Se a depuração passo-a-passo for realizada com next:
(gdb) break main
Ponto de parada 1 at 0x4006f4: file exemplo1.c, line 13.
(gdb) run
Starting program: /home/aluno/exemplo1
Breakpoint 1, main () at ex1.c:13
13 int main() {
(gdb) next
16 printf("Digite seu nome: ");
(gdb) next
17 fgets(nome, 32, stdin);
(gdb) next
Digite seu nome: maneca
18 nome[strlen(nome)-1] = 0;
(gdb) next
19 mostraInvertido(nome);
(gdb) next
acenam
20 return 0;
(gdb) next
21 }
(gdb) next
0x00007ffff7a3b78d in __libc_start_main () from /lib/x86_64-linux-gnu/libc.so.6
(gdb)
Note o que acontece se for usado step ao executar a linha 19 (mostraInvertido(nome)). O depurador começa a executar dentro da funjção mostraInvertido:
(gdb) run
Starting program: /home/msobral/tmp/ex1
Breakpoint 1, main () at ex1.c:13
13 int main() {
(gdb) next
16 printf("Digite seu nome: ");
(gdb) next
17 fgets(nome, 32, stdin);
(gdb) next
Digite seu nome: maneca
18 nome[strlen(nome)-1] = 0;
(gdb) next
19 mostraInvertido(nome);
(gdb) step
mostraInvertido (texto=0x7fffffffe030 "maneca") at ex1.c:7
7 for (n=strlen(texto); n > 0; n--) {
(gdb) next
8 printf("%c", texto[n-1]);
(gdb) next
7 for (n=strlen(texto); n > 0; n--) {
(gdb) next
8 printf("%c", texto[n-1]);
(gdb) next
7 for (n=strlen(texto); n > 0; n--) {
(gdb) next
8 printf("%c", texto[n-1]);
(gdb) next
7 for (n=strlen(texto); n > 0; n--) {
(gdb) next
8 printf("%c", texto[n-1]);
(gdb) next
7 for (n=strlen(texto); n > 0; n--) {
(gdb) next
8 printf("%c", texto[n-1]);
(gdb) next
7 for (n=strlen(texto); n > 0; n--) {
(gdb) next
8 printf("%c", texto[n-1]);
(gdb) next
7 for (n=strlen(texto); n > 0; n--) {
(gdb) next
10 puts("");
(gdb) next
acenam
11 }
(gdb) next
main () at exemplo1.c:20
20 return 0;
(gdb) next
21 }
(gdb) next
0x00007ffff7a3b78d in __libc_start_main () from /lib/x86_64-linux-gnu/libc.so.6
(gdb)
Executando até o o final de uma função
Estando no passo-a-passo, pode-se executar até o final da função corrente com o comando finish. Assim o programa executa direto até chegar o final da função, e logo após retornar volta-se ao passo-a-passo:
(gdb) run
Starting program: /home/aluno/exemplo1
Breakpoint 1, main () at exemplo1.c:13
13 int main() {
(gdb) next
16 printf("Digite seu nome: ");
(gdb) next
17 fgets(nome, 32, stdin);
(gdb) next
Digite seu nome: maneca
18 nome[strlen(nome)-1] = 0;
(gdb) next
19 mostraInvertido(nome);
(gdb) step
mostraInvertido (texto=0x7fffffffe030 "maneca") at ex1.c:7
7 for (n=strlen(texto); n > 0; n--) {
(gdb) next
8 printf("%c", texto[n-1]);
(gdb) next
7 for (n=strlen(texto); n > 0; n--) {
(gdb) next
8 printf("%c", texto[n-1]);
(gdb) finish
Run till exit from #0 mostraInvertido (texto=0x7fffffffe030 "maneca")
at ex1.c:8
acenam
main () at exemplo1.c:20
20 return 0;
(gdb) next
21 }
(gdb) next
0x00007ffff7a3b78d in __libc_start_main () from /lib/x86_64-linux-gnu/libc.so.6
(gdb)
Saindo do passo-a-passo
Pode-se sair do passo-a-passo com o comando continue. Com ele o programa volta a executar continuamente, até chegar a um ponto de parada ou terminar.
(gdb) run
Starting program: /home/aluno/exemplo1
Breakpoint 1, main () at exemplo1.c:13
13 int main() {
(gdb) next
16 printf("Digite seu nome: ");
(gdb) next
17 fgets(nome, 32, stdin);
(gdb) continue
Continuando.
Digite seu nome: maneca
acenam
[Inferior 1 (process 17002) exited normally]
(gdb)
Executando até um ponto específico
Uma vez no passo-a-passo, pode-se usar o comando advance para avançar até uma certa linha do código, ou até uma determinada função. Quer dizer, a execução é feita cotinuamente (sem passo-a-passo) até chegar ao ponto do programa desejado.
(gdb) run
Starting program: /home/aluno/exemplo1
Breakpoint 1, main () at exemplo1.c:13
13 int main() {
(gdb) advance 17
main () at exemplo1.c:17
17 fgets(nome, 32, stdin);
(gdb) next
Digite seu nome: maneca
18 nome[strlen(nome)-1] = 0;
(gdb) continue
Continuando.
acenam
[Inferior 1 (process 17077) exited normally]
(gdb)
Avançando até uma certa linha do programa
(gdb) run
Starting program: /home/aluno/exemplo1
Breakpoint 1, main () at exemplo1.c:13
13 int main() {
(gdb) advance mostraInvertido
Digite seu nome: maneca
mostraInvertido (texto=0x7fffffffe030 "maneca") at exemplo1.c:7
7 for (n=strlen(texto); n > 0; n--) {
(gdb) next
8 printf("%c", texto[n-1]);
(gdb) continue
Continuando.
acenam
[Inferior 1 (process 17095) exited normally]
(gdb)
Avançando até uma certa função