====== 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. #include int main() { int a = 10; int b = 10; 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 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: . Find the GDB manual and other documentation resources online at: . 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 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
, argc=1, argv=0x7fffffffdcd8, init=, fini=, rtld_fini=, 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/ 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 : 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"**. {{ :dev:debug:gdbinterface01.png?direct&400 |}} {{ :dev:debug:gdbinterface02.png?direct&400 |}} Para utilizar o software DDD é necessário executar o comando **ddd** seguido do programa. {{ :dev:debug:gdbinterface03.png?direct&400 |}}