Comando gdb no Linux (debug de programas) [Guia Básico]
O Comando gdb no Linux é uma ferramenta muito útil para se depurar softwares compilados com o GCC.
Utilizando o depurador gdb pela primeira vez
Para que o depurador possa funcionar, é necessário que o software seja compilado com a opção "-g" do gcc, de forma que o compilador irá providenciar informações de depuração no código objeto.
Exemplo:
$ gcc –wall –g principal.c rede.c interface.c –o pingnovo
A ideia principal para depurar um programa é inserir um breakpoint no ponto onde você deseja iniciar a depuração em uma determinada linha N do código.
O breakpoint faz uma parada na execução do programa em algum determinado ponto que se deseje examinar com mais cuidado.
Depois de inserir o breakpoint, deve-se executar o programa dentro do gdb e avançar na execução, conferindo os parâmetros passados às funções, o tipo e conteúdo das variáveis, até que o erro seja encontrado. Então, corrige-se o erro no código-fonte e compila-se novamente o programa.
Para utilizar o gdb é simples:
# gdb programa-executávelGNU gdb 5.3.92Copyright 2003 Free Software Foundation, Inc.GDB is free software, covered by the GNU General Public License, and you arewelcome to change it and/or distribute copies of it under certain conditions.Type "show copying" to see the conditions.There is absolutely no warranty for GDB. Type "show warranty" for details.This GDB was configured as "i586-suse-linux"...(gdb)
Sua interface é baseada em comandos do Linux com sintaxes relativamente simples.
Os comandos podem ser escritos por extenso ou de forma simplificada, onde geralmente utilizam-se somente suas primeiras letras. Os parâmetros como números de linha e nomes de variáveis são passados imediatamente após o nome do comando.
Vejamos os comandos mais importantes:
Para inserir um breakpoint em uma determinada linha do código:
break N
ou
b N
Onde N é a linha de código que se deseja parar a execução do programa. Para isto, pode ser necessário visualizar o código-fonte do programa.
Para remover todos os breakpoints:
delete
ou
d
Para rodar o programa até encontrar o primeiro breakpoint:
run
ou
r
Para listar o código fonte do programa:
list
ou
l
Para executar as próximas instruções do programa. Uma instrução pode ser entendida como uma função:
next N
ou
n N
Onde N é o número de instruções.
Para executar as próximas N linhas depois do breakpoint:
step N
ou
s N
Para encerrar o programa:
kill
ou
k
Para exibir informações sobre o tipo de uma variável:
what variável
ou
w variável
Para exibir o conteúdo de uma variável:
print variável
ou
p variável
Para visualizar o estado de uma variável a cada passo da execução:
display variável
Para visualizar o conteúdo de todas as variáveis instanciadas:
info locals
Para ver os registradores do programa:
info registers
Para visualizar o código assembly do programa:
disassemble
Para visualizar pilha de recursão:
backtrace
ou
bt
Para obter ajuda em relação a algum comando:
help comando
ou
h comando
Para sair do gdb:
quit
ou
q
Exemplo de depuração de um programa
Depurar programas sem utilizar ferramentas apropriadas pode ser uma tarefa exaustiva e muitas vezes impossível. Para auxiliar esta tarefa, segue um exemplo do procedimento para fazer descobertas dos problemas utilizando o GDB.
O seguinte programa exemplo foi feito para provocar um erro de cálculo de ponto flutuante:
#include <stdio.h>float calculo(int numero1, int numero2){ int diferenca; float resultado; diferenca = numero1 - numero2; resultado = numero1 / diferenca; return resultado;}int main(int argc, char *argv[]){ int valor, divisor, i; float resultado, total; valor = 10.1; divisor = 6; total = 0; for(i = 0; i < 10; i++) { resultado = calculo(valor, divisor); total += resultado; printf("%d dividido por %d é %f. A soma dos resultados é %f.\n", valor, valor-divisor, resultado,total); divisor++; valor--; }return 0;}
Para compilar este programa salvo como “erro.c”:
$ gcc –g erro.c –o erro
A opção “-g” compila o programa com as informações necessárias para depuração.
Este programa deveria executar um loop dez vezes e calcular o valor acumulado de sucessivas divisões. No entanto ao executá-lo, um erro será abortará sua execução:
$ ./erro10 dividido por 4 é 2.000000. A soma dos resultados é 2.000000.9 dividido por 2 é 4.000000. A soma dos resultados é 6.000000.Floating point exception
Para fazer a depuração é necessário executar o programa através do GDB:
$ gdb erroGNU gdb 5.3.92Copyright 2003 Free Software Foundation, Inc.GDB is free software, covered by the GNU General Public License, and you arewelcome to change it and/or distribute copies of it under certain conditions.Type "show copying" to see the conditions.There is absolutely no warranty for GDB. Type "show warranty" for details.This GDB was configured as "i586-suse-linux"...(gdb)
O programa poderá ser executado dentro do GDB com o comando “run”:
(gdb) runStarting program: /root/sistemas/erro10 dividido por 4 é 2.000000. A soma dos resultados é 2.000000.9 dividido por 2 é 4.000000. A soma dos resultados é 6.000000.Program received signal SIGFPE, Arithmetic exception.0x0804835e in calculo (numero1=8, numero2=8) at gdb.c:77 resultado = numero1 / diferenca;
O GDB indicou que o programa fez uma operação matemática ilegal na linha 7 precisamente na função calculo:
Os valores das variáveis numero1 e numero2 são 8. Podemos listar o código fonte do programa para ver o que ele está fazendo com o comando “list”:
(gdb) list2 float calculo(int numero1, int numero2)3 {4 int diferenca;5 float resultado;6 diferenca = numero1 - numero2;7 resultado = numero1 / diferenca;8 return resultado;9 }10 int main(int argc, char *argv[])11 {
O comando list irá listar o código de 10 em 10 linhas. Ele poderá ser repetido até que todo o código seja exibido.
Como primeiro passo é interessante examinar o conteúdo das variáveis com o comando “print”:
(gdb) print numero1$1 = 8(gdb) print numero2$2 = 8(gdb) print diferenca$3 = 0
Na linha 7 do programa onde ocorreu o erro, pode-se verificar que a operação “resultado = numero1 / diferença”. Analisando o valor das variáveis e a instrução pode-se deduzir que o programa está tentando fazer uma divisão por zero.
Podemos continuar o processamento do programa com o comando “continue”:
(gdb) continueContinuing.Program terminated with signal SIGFPE, Arithmetic exception.The program no longer exists.
Como o erro divisão por zero aborta a execução, o programa não continuará. Será necessário usar os breakpoints e executar o programa novamente. É interessante instalar o ponto de parada na função main antes da chamada da função calculo.
(gdb) list10 int main(int argc, char *argv[])11 {12 int valor, divisor, i;13 float resultado, total;14 valor = 10.1;15 divisor = 6;16 total = 0;17 for(i = 0; i < 10; i++)18 {19 resultado = calculo(valor, divisor);20 total += resultado;21 printf("%d dividido por %d é %f. A soma dos resultados é %f.\n", = valor, valor-divisor, resultado,total);22 divisor++;23 valor--;24 }25 return 0;
Podemos instalar o ponto de parada na linha 12 no início da função main e executar novamente o programa:
(gdb) break 12Breakpoint 1 at 0x8048384: file gdb.c, line 12.(gdb) runStarting program: /root/sistemas/erroBreakpoint 1, main (argc=1, argv=0xbfffd874) at gdb.c:1414 valor = 10.1;
O processamento irá parar na linha 14 que é contém instrução significativa depois da linha 12. Podemos avançar o processamento linha a linha com o comando “next”:
(gdb) next15 divisor = 6;(gdb) next16 total = 0;(gdb) next17 for(i = 0; i < 10; i++)
Os conteúdos das variáveis poderão ser exibidos com o comando “print” seguido do nome da variável:
(gdb) print valor$5 = 10(gdb) print divisor$6 = 6
O valor de todas as variáveis instanciadas poderá ser visto com o comando “info locals”:
(gdb) info localsvalor = 10divisor = 6i = 0resultado = -1.99878407total = 0
O próximo passo é executar o programa linha a linha até que o conteúdo das variáveis valor e divisor seja 8. O comando “display” permite que o conteúdo de uma variável seja exibido a cada instrução processada:
(gdb) display valor1: valor = 10(gdb) display divisor2: divisor = 6
O avanço das instruções poderá ser feito com o comando “next”:
(gdb) next20 total += resultado;2: divisor = 61: valor = 10(gdb) next21 printf("%d dividido por %d é %f. A soma dos resultados é %f.\n", valor, valor-divisor, resultado,total);2: divisor = 61: valor = 10(gdb) next10 dividido por 4 é 2.000000. A soma dos resultados é 2.000000.22 divisor++;2: divisor = 61: valor = 10(gdb) next23 valor--;2: divisor = 71: valor = 10(gdb) next17 for(i = 0; i < 10; i++)2: divisor = 71: valor = 9(gdb) next19 resultado = calculo(valor, divisor);2: divisor = 71: valor = 9(gdb) next20 total += resultado;2: divisor = 71: valor = 9(gdb) next21 printf("%d dividido por %d é %f. A soma dos resultados é %f.\n", valor, valor-divisor, resultado,total);2: divisor = 71: valor = 9(gdb) next9 dividido por 2 é 4.000000. A soma dos resultados é 6.000000.22 divisor++;2: divisor = 71: valor = 9(gdb) next23 valor--;2: divisor = 81: valor = 9(gdb) next17 for(i = 0; i < 10; i++)2: divisor = 81: valor = 8
Neste ponto, a função calculo irá retornar a divisão por zero. O valor da variável poderá ser trocado em execução para driblar o erro com o comando “set”:
(gdb) info localsvalor = 8divisor = 8i = 1resultado = 4total = 6(gdb) set valor = 10(gdb) info localsvalor = 10divisor = 8i = 1resultado = 4total = 6
O programa irá continuar o processamento até encontrar outra divisão por zero.
(gdb) next19 resultado = calculo(valor, divisor);2: divisor = 81: valor = 10(gdb) next20 total += resultado;2: divisor = 81: valor = 10(gdb) next21 printf("%d dividido por %d é %f. A soma dos resultados é %f.\n", valor, valor-divisor, resultado,total);2: divisor = 81: valor = 10(gdb) next10 dividido por 2 é 5.000000. A soma dos resultados é 11.000000.22 divisor++;2: divisor = 81: valor = 10(gdb) next23 valor--;2: divisor = 91: valor = 10(gdb) next17 for(i = 0; i < 10; i++)2: divisor = 91: valor = 9(gdb) next19 resultado = calculo(valor, divisor);2: divisor = 91: valor = 9(gdb) nextProgram received signal SIGFPE, Arithmetic exception.0x0804835e in calculo (numero1=9, numero2=9) at gdb.c:77 resultado = numero1 / diferenca;
No caso deste exemplo, o programa está na declaração das variáveis como inteiros não permitindo o resto de uma divisão.
O GDB é um poderoso depurador de programas. Encorajamos você a instalar uma interface gráfica para ele para facilitar o seu uso. Há outros depuradores também disponíveis.
Aprenda muito mais sobre Linux em nosso curso online. Você pode efetuar a matrícula aqui. Se você já tem uma conta, ou quer criar uma, basta entrar ou criar seu usuário aqui.