Ferramentas do usuário

Ferramentas do site


dev:debug:gdb_debugger

Essa é uma revisão anterior do documento!


GDB - Debugger

Para quem programa em ambiente Linux existe uma ferramenta de debug muito importante o software gdb. Ele permite realizar certos debug em programas C. Vamos a um exemplo de um Ola Mundo e abri-lo no gdb.

olamundo.c
#include <stdio.h>
 
int main()
{
   printf("Ola mundo\n");
   return 0;
}

Para compilar com suporte a simbolos que o gdb precisa para realizar o debug.

gcc -g -o olamundo olamundo.c

Para entrar no gdb e necessário somente passar como parametro o nome do programa no nosso caso de exemplo seria o nome olamundo.

ricardobarbosa@isadora:~/dev/c/pilha$ gdb olamundo 
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
Copyright (C) 2014 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 olamundo...done.
(gdb) 
(gdb) 
(gdb) 

Iremos cair no prompt de comando do gdb. O gdb funciona como um médico analisando um paciente, o paciente no nosso caso e o programa olamundo. Outro conceito importante do gdb é o breakpoint. O que seria um breakpoint seguindo a tradução seria um ponto de parada ou ponto de interrupção, ou seja, criaremos um ponto de parada onde nos executaremos o programa e quando ele chegar naquele ponto de parada irá parar e esperar interação do usuário, para verificar o conteúdo de uma variavel ou visualizar endereços de memória, etc.

Vamos criar um breakpoint na função main.

(gdb) break main
Breakpoint 1 at 0x400531: file olamundo.c, line 5.
(gdb) 

Agora iremos executar nosso programa e ver ele parar no breakpoint na função main.

(gdb) run
Starting program: /home/ricardobarbosa/dev/c/pilha/olamundo 

Breakpoint 1, main () at olamundo.c:5
5          printf("Ola mundo\n");
(gdb) 

podemos também visualizar o código do programa através do comando list.

(gdb) list
1       #include <stdio.h>
2
3       int main()
4       {
5          printf("Ola mundo\n");
6          return 0;
7       }
(gdb) 

Para executar passo-a-passo temos dois comandos

  • step: continua a execução, roda a próxima linha de código e pausa se a próxima instrução for uma função entra na mesma e excutará linha-a-linha.
  • next: continua a execução, roda a próxima linha de código e pausa se a próxima instrução for uma função ele executa a função inteira e retorna apenas o resultado se a função retornar.

Então executaremos a próxima instrução.

(gdb) next
Ola mundo
6          return 0;
(gdb) 

Repare que ele executou e exibiu a mensagem ola mundo e mostrou a próxima instruçao de return 0 o qual indica sucesso. Se continuarmos dando next executaremos todo o programa e o mesmo será finalizado.

(gdb) next
Ola mundo
6          return 0;
(gdb) next
7       }
(gdb) next
__libc_start_main (main=0x40052d <main>, argc=1, argv=0x7fffffffdcd8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdcc8) at libc-start.c:321
321     libc-start.c: No such file or directory.
(gdb) 

Além da execução de instruções passo-a-passo podemos ver o contéudo de variaveis. Vamos utilizar o seguinte código para exemplificar isto.

(gdb) break main
Breakpoint 1 at 0x4004f1: file olamundo2.c, line 5.
(gdb) run
Starting program: /home/ricardobarbosa/dev/c/pilha/olamundo2 

Breakpoint 1, main () at olamundo2.c:5
5               int a=10;
(gdb) n
6               int b=10;
(gdb) print a
$1 = 10
(gdb) print b
$2 = 0
(gdb) n
8               c=a+b;
(gdb) print b
$3 = 10
(gdb) n
9               return 0;
(gdb) print c
$4 = 20
(gdb) 

Existe alguns comandos para verificar variaveis.

O comando “info variables” para listar os nomes de todas as variaveis globais e estatica.

O comando “info locals” para listar as váriaveis locais que estão atualmente na pilha.

(gdb) info locals
a = 10
b = 10
c = 20
(gdb) 

O comando “info args” para listar os argumentos passados para o programa.

O comando “info frame” exibe informações sobre a pilha

(gdb) info frame
Stack level 0, frame at 0x7fffffffdc00:
 rip = 0x40050a in main (olamundo2.c:9); saved rip = 0x7ffff7a36f45
 source language c.
 Arglist at 0x7fffffffdbf0, args: 
 Locals at 0x7fffffffdbf0, Previous frame's sp is 0x7fffffffdc00
 Saved registers:
  rbp at 0x7fffffffdbf0, rip at 0x7fffffffdbf8
(gdb) 

O comando “backtrace” exibe um rastreio da pilha

(gdb) backtrace 
#0  foo (x=255, y=255) at exemplo01.c:14
#1  0x0000000000400778 in main () at exemplo01.c:50
(gdb) 

No comando acima mostra o frame da pilha para função main e função foo

Para navegar pela frame da pilha

gdb> frame 1

É possível também visualizar o conteúdo de uma posição de memória através do comando “x”. O comando “x” segue uma sintaxe bem simples

x/<contagem><formato><unidade tamanho> endereço

  • Contagem: número de elementos que será exibido.
  • Formato: formato que iremos utilizar
    • o - octal
    • x - hexadecimal
    • d - decimal
    • u - decimal sem sinal(unsigned)
    • t - binario
    • f - ponto flutuante
    • a - endereço
    • c - caracter(char)
    • s - string
    • i - instruction
  • Unidade de tamanho:
    • b - byte
    • h - halfword (16-bit)
    • w - word (32-bit)
    • g - giant word (64-bit)

Exemplo queremos visualizar o conteudo do registrador SP em hexadecimal, decimal e decimal sem sinal(unsigned).

(gdb) x/x $rsp 
0x7fffffffdb20: 0x00000001
(gdb) x/d $rsp 
0x7fffffffdb20: 1
(gdb) x/u $rsp 
0x7fffffffdb20: 1
(gdb) 

Para verificar a instrução armazenada no registrador RIP

(gdb) x/i $rip
=> 0x4005a1 <foo+180>:  movabs $0x406fe00000000000,%rax
(gdb) 

Mostrar o pilha

(gdb) x/100x $rsp
0x7fffffffdb20: 0x00000001      0x00007fff      0xf7ffe1c8      0x00007fff
0x7fffffffdb30: 0x00000000      0x00000000      0x00000000      0x00000000
0x7fffffffdb40: 0x00000000      0x00000000      0x00000000      0x00000000
0x7fffffffdb50: 0x00000000      0x00000000      0xf7ffe520      0x00007fff
0x7fffffffdb60: 0xffffdb90      0x00007fff      0xffffdb80      0x00007fff
0x7fffffffdb70: 0xf63d4e2e      0x00000000      0x0040030b      0x00000000
0x7fffffffdb80: 0xffffffff      0x00000000      0xffffdce8      0x00007fff
0x7fffffffdb90: 0xf7a251f8      0x00007fff      0xf7ff74c0      0x00007fff
0x7fffffffdba0: 0xf7ffe1c8      0x00007fff      0x00000000      0x00000000
0x7fffffffdbb0: 0x00000001      0x00000000      0x004007dd      0x00000000
0x7fffffffdbc0: 0xffffdbf0      0x00007fff      0x00000000      0x00000000
0x7fffffffdbd0: 0x00400790      0x00000000      0xffffdbf0      0x00007fff
0x7fffffffdbe0: 0x00400778      0x00000000      0x00000000      0x406fe000
0x7fffffffdbf0: 0x00000000      0x00000000      0xf7a36f45      0x00007fff
0x7fffffffdc00: 0x00000000      0x00000000      0xffffdcd8      0x00007fff
0x7fffffffdc10: 0x00000000      0x00000001      0x00400745      0x00000000
0x7fffffffdc20: 0x00000000      0x00000000      0xd1071e96      0x69ad688b
0x7fffffffdc30: 0x00400400      0x00000000      0xffffdcd0      0x00007fff
0x7fffffffdc40: 0x00000000      0x00000000      0x00000000      0x00000000
0x7fffffffdc50: 0x69071e96      0x96529774      0x0cfd1e96      0x965287cd
0x7fffffffdc60: 0x00000000      0x00000000      0x00000000      0x00000000
0x7fffffffdc70: 0x00000000      0x00000000      0x00400790      0x00000000
0x7fffffffdc80: 0xffffdcd8      0x00007fff      0x00000001      0x00000000
0x7fffffffdc90: 0x00000000      0x00000000      0x00000000      0x00000000
0x7fffffffdca0: 0x00400400      0x00000000      0xffffdcd0      0x00007fff
(gdb) 

Mais um exemplo, exibir 4 palavras da pilha em hexadecimal.

(gdb) x/4xw $sp
0x7fffffffdb20: 0x00000001      0x00007fff      0xf7ffe1c8      0x00007fff
(gdb) 

debugar visualmente

Para o debug com uma interface gráfica temos duas opções: o prórpio gdb tem uma “interface gráfica” utilizando a biblioteca ncurses e o software ddd que mais é um front-end para vários debug como o próprio gdb, dbx, wdb, jdb, perl debugger, etc.

Para utilizar o ncurses do gdb e necessário somente passar o parametro “-tui”.

Para utilizar o software DDD é necessário executar o comando ddd seguido do programa.