remoção do antigo conteúdo
This commit is contained in:
parent
4c2b8d663b
commit
ebb4065a8b
1 changed files with 0 additions and 743 deletions
743
gdb-01.org
743
gdb-01.org
|
@ -1,743 +0,0 @@
|
|||
#+title: Curso prático de introdução ao GDB
|
||||
#+author: Blau Araujo
|
||||
#+email: blau@debxp.org
|
||||
|
||||
* Parte 1: Conceitos Básicos
|
||||
|
||||
** O que é o GDB
|
||||
|
||||
O GDB (/GNU Debugger/) é uma ferramenta de depuração de programas escritos em
|
||||
diversas linguagens. Ele possibilita a inspeção da execução de um programa
|
||||
em tempo real ou após uma falha, fornecendo recursos como:
|
||||
|
||||
- Pontos de parada (/breakpoints/);
|
||||
- Execução passo a passo (/step-by-step/);
|
||||
- Visualização e modificação de variáveis;
|
||||
- Análise de memória e registradores;
|
||||
- Inspeção da pilha de chamadas (/backtrace/);
|
||||
- Avaliação de expressões em tempo de execução;
|
||||
- Depuração de múltiplas /threads/;
|
||||
- Depuração remota (via =gdbserver=).
|
||||
|
||||
Por operar em baixo nível, o GDB oferece uma visão detalhada do comportamento
|
||||
interno do programa, sendo essencial tanto para o desenvolvimento quanto para
|
||||
o diagnóstico de erros complexos.
|
||||
|
||||
** Primeiro contato
|
||||
|
||||
Considere o seguinte programa em C (=demo.c=):
|
||||
|
||||
#+begin_src c
|
||||
#include <stdio.h>
|
||||
|
||||
int soma(int a, int b) {
|
||||
int resultado = a + b;
|
||||
return resultado;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int x = 10;
|
||||
int y = 20;
|
||||
int z = soma(x, y);
|
||||
printf("Resultado: %d\n", z);
|
||||
return 0;
|
||||
}
|
||||
#+end_src
|
||||
|
||||
Compilação com símbolos de depuração:
|
||||
|
||||
#+begin_example
|
||||
gcc -g -o demo demo.c
|
||||
#+end_example
|
||||
|
||||
Carregando o binário no GDB:
|
||||
|
||||
#+begin_example
|
||||
gdb ./demo
|
||||
#+end_example
|
||||
|
||||
*** Listando o código-fonte
|
||||
|
||||
No GDB, podemos listar o código-fonte (comando =list=):
|
||||
|
||||
#+begin_example
|
||||
(gdb) list
|
||||
3 int soma(int a, int b) {
|
||||
4 int resultado = a + b;
|
||||
5 return resultado;
|
||||
6 }
|
||||
7
|
||||
8 int main() {
|
||||
9 int x = 10;
|
||||
10 int y = 20;
|
||||
11 int z = soma(x, y);
|
||||
12 printf("Resultado: %d\n", z);
|
||||
#+end_example
|
||||
|
||||
Por padrão, somente 10 linhas são exibidas, mas podemos teclar =Enter= algumas vezes
|
||||
até que todo o código seja listado.
|
||||
|
||||
#+begin_example
|
||||
(gdb)
|
||||
13 return 0;
|
||||
14 }
|
||||
#+end_example
|
||||
|
||||
Chagando ao final, podemos reiniciar a listagem com =list .=:
|
||||
|
||||
#+begin_example
|
||||
(gdb) list .
|
||||
3 int soma(int a, int b) {
|
||||
4 int resultado = a + b;
|
||||
5 return resultado;
|
||||
6 }
|
||||
7
|
||||
8 int main() {
|
||||
9 int x = 10;
|
||||
10 int y = 20;
|
||||
11 int z = soma(x, y);
|
||||
12 printf("Resultado: %d\n", z);
|
||||
#+end_example
|
||||
|
||||
*** Ponto de parada e execução
|
||||
|
||||
Nós podemos definir pontos de parada com o comando =break=:
|
||||
|
||||
#+begin_example
|
||||
(gdb) break main
|
||||
Breakpoint 1 at 0x115b: file demo.c, line 9.
|
||||
#+end_example
|
||||
|
||||
Assim, quando o programa for executado (com o comando =run=), a execução será
|
||||
pausada nos símbolos definidos como pontos de parada:
|
||||
|
||||
#+begin_example
|
||||
(gdb) run
|
||||
Starting program: /home/blau/tmp/gdb/demo
|
||||
[Thread debugging using libthread_db enabled]
|
||||
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
|
||||
|
||||
Breakpoint 1, main () at demo.c:9
|
||||
9 int x = 10;
|
||||
#+end_example
|
||||
|
||||
Neste exemplo, o ponto de parada é a função =main=, e nós vemos a próxima linha
|
||||
a ser executada (linha =9=). Para avançar para as próximas linhas, nós podemos
|
||||
executar o comando =next=:
|
||||
|
||||
#+begin_example
|
||||
(gdb) next
|
||||
10 int y = 20;
|
||||
#+end_example
|
||||
|
||||
A linha =9= foi executada e a próxima será a linha =10=. Se quisermos continuar
|
||||
executando o comando =next=, basta teclar =Enter= imediatamente em seguida:
|
||||
|
||||
#+begin_example
|
||||
(gdb)
|
||||
11 int z = soma(x, y);
|
||||
#+end_example
|
||||
|
||||
Neste ponto, as variáveis =x= e =y= já foram carregadas e nós podemos conferir
|
||||
seus valores com o comando =print=:
|
||||
|
||||
#+begin_example
|
||||
(gdb) print x
|
||||
$1 = 10
|
||||
(gdb) print y
|
||||
$2 = 20
|
||||
#+end_example
|
||||
|
||||
#+begin_quote
|
||||
O resultado de cada avaliação é armazenado no GDB em uma variável especial
|
||||
numerada (=$n=) de acordo com a ordem da avaliação.
|
||||
#+end_quote
|
||||
|
||||
A próxima linha a ser executada será a linha =11=, onde temos a chamada da
|
||||
função =soma=. Se executarmos =next= novamente, a função será executada e nós
|
||||
iremos para a linha seguinte na função =main=. Mas nós também podemos entrar
|
||||
na função =soma= e acompanhar a sua execução passo a passo com o comando
|
||||
=step=:
|
||||
|
||||
#+begin_example
|
||||
(gdb) step
|
||||
soma (a=10, b=20) at demo.c:4
|
||||
4 int resultado = a + b;
|
||||
#+end_example
|
||||
|
||||
Aqui, nós podemos inspecionar os valores de =a=, =b= e do valor inicial de =resultado=,
|
||||
antes da linha =4= ser executada:
|
||||
|
||||
#+begin_example
|
||||
(gdb) print a
|
||||
$3 = 10
|
||||
(gdb) print b
|
||||
$4 = 20
|
||||
(gdb) print resultado
|
||||
$5 = 0
|
||||
#+end_example
|
||||
|
||||
Com o comando =next=, nós avançamos na função e já podemos exibir o novo valor
|
||||
de =resultado=:
|
||||
|
||||
#+begin_example
|
||||
(gdb) next
|
||||
5 return resultado;
|
||||
(gdb) print resultado
|
||||
$6 = 30
|
||||
#+end_example
|
||||
|
||||
Se, a partir daqui, nós quisermos executar todo o restante do programa,
|
||||
basta executar comando =continue=:
|
||||
|
||||
#+begin_example
|
||||
(gdb) continue
|
||||
Continuing.
|
||||
Resultado: 30
|
||||
[Inferior 1 (process 214693) exited normally]
|
||||
#+end_example
|
||||
|
||||
Para sair do GDB...
|
||||
|
||||
#+begin_example
|
||||
(gdb) quit
|
||||
:~$
|
||||
#+end_example
|
||||
|
||||
** Instalação
|
||||
|
||||
*** Debian e derivados
|
||||
|
||||
#+begin_example
|
||||
sudo apt update
|
||||
sudo apt install gdb
|
||||
#+end_example
|
||||
|
||||
O pacote =build-essential= instala outras ferramentas úteis para desenvolvimento,
|
||||
incluindo o =gcc= (/GNU Compiler Collection/), o =make= e as dependências mais comuns.
|
||||
|
||||
#+begin_example
|
||||
sudo apt install build-essential
|
||||
#+end_example
|
||||
|
||||
*** Fedora e derivados
|
||||
|
||||
#+begin_example
|
||||
sudo dnf install gdb
|
||||
#+end_example
|
||||
|
||||
Para um ambiente completo de desenvolvimento:
|
||||
|
||||
#+begin_example
|
||||
sudo dnf groupinstall "Development Tools"
|
||||
#+end_example
|
||||
|
||||
*** Arch Linux e derivados
|
||||
|
||||
#+begin_example
|
||||
sudo pacman -S gdb
|
||||
#+end_example
|
||||
|
||||
O grupo =base-devel= contém ferramentas úteis para compilação e depuração:
|
||||
|
||||
#+begin_example
|
||||
sudo pacman -S base-devel
|
||||
#+end_example
|
||||
|
||||
*** Verificação da instalação
|
||||
|
||||
Versão:
|
||||
|
||||
#+begin_example
|
||||
gdb --version
|
||||
#+end_example
|
||||
|
||||
Ajuda:
|
||||
|
||||
#+begin_example
|
||||
gdb --help
|
||||
#+end_example
|
||||
|
||||
** Personalização e configurações de início
|
||||
|
||||
O GDB pode ser configurado por meio de arquivos de inicialização lidos
|
||||
automaticamente ao iniciar. Esses arquivos permitem predefinir opções úteis,
|
||||
automatizar tarefas e estender o ambiente de depuração.
|
||||
|
||||
*** Configuração por usuário
|
||||
|
||||
O arquivo =~/.gdbinit=:
|
||||
|
||||
O GDB executa esse arquivo sempre que for iniciado, a menos que seja desativado
|
||||
com a opção =-nh=.
|
||||
|
||||
Configuração de exemplo:
|
||||
|
||||
#+begin_src gdb
|
||||
set pagination off # Não pausa a saída do GDB
|
||||
set confirm off # Não pede confirmação para alguns comandos
|
||||
set print pretty on # Formata a apresentação de structs e arrays
|
||||
set history save on # Salva histórico entre seções
|
||||
set history size 1000 # Tamanho máximo do histórico
|
||||
set disassembly-flavor intel # Define a sintaxe Intel na desmontagem de binários
|
||||
#+end_src
|
||||
|
||||
Também pode ser interessante omitir as mensagens de versão no início do GDB,
|
||||
o que é feito no arquivo =~/.config/gdb/gdbearlyinit=:
|
||||
|
||||
#+begin_example
|
||||
set startup-quietly on
|
||||
#+end_example
|
||||
|
||||
#+begin_quote
|
||||
O arquivo =gdbearlyinit= é carregado antes do GDB executar outros arquivos
|
||||
de início e só recebe definições que afetam o seu próprio comportamento.
|
||||
#+end_quote
|
||||
|
||||
*** Configuração por projeto
|
||||
|
||||
Arquivo =.gdbinit= no diretório do projeto (configuração local):
|
||||
|
||||
Se existir um arquivo =.gdbinit= no diretório corrente, o GDB pode executá-lo,
|
||||
mas isso é bloqueado por padrão:
|
||||
|
||||
#+begin_example
|
||||
:~/projeto$ gdb
|
||||
Warning: File ".gdbinit" auto-loading has been declined by your `auto-load safe-path'...
|
||||
#+end_example
|
||||
|
||||
Para permitir o carregamento, temos que adicionar o caminho do projeto ao
|
||||
arquivo do usuário:
|
||||
|
||||
#+begin_src sh
|
||||
echo "add-auto-load-safe-path $(pwd)" >> ~/.gdbinit
|
||||
#+end_src
|
||||
|
||||
#+begin_quote
|
||||
Isso é muito utilizado para definir /breakpoints/ automáticos, carregar símbolos
|
||||
extras, configurar scripts, etc.
|
||||
#+end_quote
|
||||
|
||||
*** Configuração global
|
||||
|
||||
O arquivo de configuração do sistema, =/etc/gdb/gdbinit=, é lido antes de
|
||||
=~/.gdbinit= e pode ser usado para definir opções globais em ambientes
|
||||
compartilhados (ex: laboratórios ou servidores educacionais).
|
||||
|
||||
*** Comandos customizados
|
||||
|
||||
Nos arquivos =.gdbinit=, também é possível definir comandos customizados
|
||||
com a sintaxe:
|
||||
|
||||
#+begin_example
|
||||
define COMANDO
|
||||
LISTA DE COMANDOS
|
||||
end
|
||||
document COMANDO
|
||||
DESCRIÇÃO
|
||||
end
|
||||
#+end_example
|
||||
|
||||
*** Scripts em Python
|
||||
|
||||
É possível estender o GDB usando scripts em Python no =.gdbinit=, mas isso
|
||||
foge do escopo deste curso.
|
||||
|
||||
** Binários ELF, símbolos e códigos-fonte
|
||||
|
||||
Todo arquivo executável pelo sistema é construído em um binário segundo um
|
||||
formato padrão. No GNU/Linux, esse formato é o *ELF* (/Executable and Linkable Format/),
|
||||
que inclui no binário:
|
||||
|
||||
- O código de máquina do programa;
|
||||
- Dados de variáveis globais e estáticas;
|
||||
- Dados constantes;
|
||||
- Tabelas de símbolos;
|
||||
- Informações de depuração (se incluídas).
|
||||
|
||||
Os primeiros bytes de um arquivo ELF contém um cabeçalho com diversas
|
||||
informações sobre o binário, o que nós podemos listar com o utilitário
|
||||
=readelf=:
|
||||
|
||||
#+begin_example
|
||||
:~$ readelf -h demo
|
||||
Cabeçalho ELF:
|
||||
Magia: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
|
||||
Classe: ELF64
|
||||
Dados: complemento 2, little endian
|
||||
Versão: 1 (actual)
|
||||
OS/ABI: UNIX - System V
|
||||
Versão ABI: 0
|
||||
Tipo: DYN (Position-Independent Executable file)
|
||||
Máquina: Advanced Micro Devices X86-64
|
||||
Versão: 0x1
|
||||
Endereço do ponto de entrada: 0x1050
|
||||
Início dos cabeçalhos do programa: 64 (bytes no ficheiro)
|
||||
Start of section headers: 14944 (bytes no ficheiro)
|
||||
Bandeiras: 0x0
|
||||
Tamanho deste cabeçalho: 64 (bytes)
|
||||
Tamanho dos cabeçalhos do programa:56 (bytes)
|
||||
Nº de cabeçalhos do programa: 14
|
||||
Tamanho dos cabeçalhos de secção: 64 (bytes)
|
||||
Nº dos cabeçalhos de secção: 37
|
||||
Índice de tabela de cadeias da secção: 36
|
||||
#+end_example
|
||||
|
||||
*** Símbolos e o código-fonte
|
||||
|
||||
Símbolos são nomes associados a elementos do programa, como funções e
|
||||
variáveis. Quando os símbolos de depuração são incluídos no binário
|
||||
(compilando com =-g=, por exemplo), o GDB é capaz de:
|
||||
|
||||
- Exibir nomes legíveis, em vez de endereços de memória;
|
||||
- Acompanhar o fluxo do programa com o código-fonte;
|
||||
- Receber definições de /breakpoints/ por nomes de funções;
|
||||
- Fazer a associação de códigos binários com as linhas do código-fonte;
|
||||
- Acessar nomes de variáveis, tipos, estruturas, etc.
|
||||
|
||||
Nós podemos verificar se o programa foi compilado com os símbolos de
|
||||
depuração com o utilitário =file=...
|
||||
|
||||
Sem a opção =-g=:
|
||||
|
||||
#+begin_example
|
||||
:~$ gcc -o demo demo.c
|
||||
:~$ file demo
|
||||
demo: ELF 64-bit LSB pie executable [...] not stripped
|
||||
#+end_example
|
||||
|
||||
Com a opção =-g=:
|
||||
|
||||
#+begin_example
|
||||
:~$ gcc -g -o demo demo.c
|
||||
:~$ file demo
|
||||
demo: ELF 64-bit LSB pie executable [...] with debug_info, not stripped
|
||||
#+end_example
|
||||
|
||||
Repare que, desta vez, nós temos a informação =with debug_info= no final
|
||||
da linha.
|
||||
|
||||
Nós também podemos verificar se há símbolos de depuração a partir das
|
||||
informações no formato ELF:
|
||||
|
||||
#+begin_example
|
||||
:~$ readelf -S demo | grep debug
|
||||
[28] .debug_aranges PROGBITS 0000000000000000 00003037
|
||||
[29] .debug_info PROGBITS 0000000000000000 00003067
|
||||
[30] .debug_abbrev PROGBITS 0000000000000000 00003180
|
||||
[31] .debug_line PROGBITS 0000000000000000 0000324a
|
||||
[32] .debug_str PROGBITS 0000000000000000 000032ba
|
||||
[33] .debug_line_str PROGBITS 0000000000000000 00003367
|
||||
#+end_example
|
||||
|
||||
Mas o próprio GDB informa se há símbolos de depuração ao iniciar...
|
||||
|
||||
Sem a opção =-g=:
|
||||
|
||||
#+begin_example
|
||||
:~$ gcc -o demo demo.c
|
||||
:~$ gdb demo
|
||||
Reading symbols from demo...
|
||||
(No debugging symbols found in demo)
|
||||
#+end_example
|
||||
|
||||
Com a opção =-g=:
|
||||
|
||||
#+begin_example
|
||||
:~$ gcc -g -o demo demo.c
|
||||
:~$ gdb demo
|
||||
Reading symbols from demo...
|
||||
#+end_example
|
||||
|
||||
** O GDB e a programação em baixo nível
|
||||
|
||||
O GDB permite observar e controlar a execução de programas no nível mais próximo
|
||||
da máquina, o que inclui:
|
||||
|
||||
- Acompanhamento de registradores da CPU;
|
||||
- Inspeção de endereços de memória brutos;
|
||||
- Execução instrução por instrução em assembly (=stepi=, =nexti=);
|
||||
- Observação do /stack frame/ e chamadas (=backtrace=, =info frame=);
|
||||
- Controle direto de /flags/, pilha, heap e segmentos do processo.
|
||||
|
||||
Isso o torna útil para:
|
||||
|
||||
- Diagnóstico de /segmentation faults/;
|
||||
- Estudo de chamadas de sistema;
|
||||
- Reversão e engenharia de baixo nível;
|
||||
- Entendimento de como o compilador transforma código em instruções.
|
||||
|
||||
*** Exemplo com um programa em assembly
|
||||
|
||||
Código-fonte (=soma.asm=):
|
||||
|
||||
#+begin_src asm
|
||||
section .data
|
||||
a dq 10
|
||||
b dq 20
|
||||
resultado dq 0
|
||||
|
||||
section .text
|
||||
global _start
|
||||
|
||||
_start:
|
||||
mov rax, [a]
|
||||
add rax, [b]
|
||||
mov [resultado], rax
|
||||
|
||||
; saída limpa com código 0
|
||||
mov rax, 60 ; syscall: exit
|
||||
xor rdi, rdi ; status 0
|
||||
syscall
|
||||
#+end_src
|
||||
|
||||
Montagem e link-edição:
|
||||
|
||||
#+begin_example
|
||||
nasm -f elf64 -g soma.asm -o soma.o
|
||||
ld soma.o -o soma
|
||||
#+end_example
|
||||
|
||||
Abertura com o GDB:
|
||||
|
||||
#+begin_example
|
||||
gdb ./soma
|
||||
Reading symbols from soma...
|
||||
#+end_example
|
||||
|
||||
Definindo =_start= como ponto de parada:
|
||||
|
||||
#+begin_example
|
||||
(gdb) b _start
|
||||
Breakpoint 1 at 0x401000: file soma.asm, line 10.
|
||||
#+end_example
|
||||
|
||||
Listando o fonte:
|
||||
|
||||
#+begin_example
|
||||
(gdb) l
|
||||
1 section .data
|
||||
2 a dq 10
|
||||
3 b dq 20
|
||||
4 resultado dq 0
|
||||
5
|
||||
6 section .text
|
||||
7 global _start
|
||||
8
|
||||
9 _start:
|
||||
10 mov rax, [a]
|
||||
(gdb)
|
||||
11 add rax, [b]
|
||||
12 mov [resultado], rax
|
||||
13
|
||||
14 ; saída limpa com código 0
|
||||
15 mov rax, 60 ; syscall: exit
|
||||
16 xor rdi, rdi ; status 0
|
||||
17 syscall
|
||||
#+end_example
|
||||
|
||||
Executando:
|
||||
|
||||
#+begin_example
|
||||
(gdb) r
|
||||
Starting program: /home/blau/tmp/gdb/soma
|
||||
|
||||
Breakpoint 1, _start () at soma.asm:10
|
||||
10 mov rax, [a]
|
||||
#+end_example
|
||||
|
||||
Observando os dados rotulados como =a=, =b= e =resultado= (assembly não tem variáveis):
|
||||
|
||||
#+begin_example
|
||||
(gdb) x/1gx &a
|
||||
0x402000 <a>: 0x000000000000000a
|
||||
(gdb) x/1gx &b
|
||||
0x402008 <b>: 0x0000000000000014
|
||||
(gdb) x/1gx &resultado
|
||||
0x402010 <resultado>: 0x0000000000000000
|
||||
#+end_example
|
||||
|
||||
#+begin_quote
|
||||
O comando =x= exibe um conteúdo na memória e, com as opções =/1gx=, eu estou
|
||||
pedindo para mostrar uma /giant word/ (=1g=), que é uma palavra de 8bytes, em
|
||||
base hexadecimal (=x=).
|
||||
#+end_quote
|
||||
|
||||
Executando as três instruções seguintes:
|
||||
|
||||
#+begin_example
|
||||
(gdb) n
|
||||
11 add rax, [b]
|
||||
(gdb)
|
||||
12 mov [resultado], rax
|
||||
(gdb)
|
||||
15 mov rax, 60 ; syscall: exit
|
||||
#+end_example
|
||||
|
||||
Observando o no valor em =resultado=:
|
||||
|
||||
#+begin_example
|
||||
(gdb) x/1gx &resultado
|
||||
0x402010 <resultado>: 0x000000000000001e
|
||||
#+end_example
|
||||
|
||||
Exibindo o estado atual de todos os registradores da CPU:
|
||||
|
||||
#+begin_example
|
||||
(gdb) i registers
|
||||
rax 0x1e 30
|
||||
rbx 0x0 0
|
||||
rcx 0x0 0
|
||||
rdx 0x0 0
|
||||
rsi 0x0 0
|
||||
rdi 0x0 0
|
||||
rbp 0x0 0x0
|
||||
rsp 0x7fffffffe020 0x7fffffffe020
|
||||
r8 0x0 0
|
||||
r9 0x0 0
|
||||
r10 0x0 0
|
||||
r11 0x0 0
|
||||
r12 0x0 0
|
||||
r13 0x0 0
|
||||
r14 0x0 0
|
||||
r15 0x0 0
|
||||
rip 0x401018 0x401018 <_start+24>
|
||||
eflags 0x206 [ PF IF ]
|
||||
cs 0x33 51
|
||||
ss 0x2b 43
|
||||
ds 0x0 0
|
||||
es 0x0 0
|
||||
fs 0x0 0
|
||||
gs 0x0 0
|
||||
fs_base 0x0 0
|
||||
gs_base 0x0 0
|
||||
#+end_example
|
||||
|
||||
Continuando a execução até o término:
|
||||
|
||||
#+begin_example
|
||||
(gdb) c
|
||||
Continuing.
|
||||
[Inferior 1 (process 222188) exited normally]
|
||||
#+end_example
|
||||
|
||||
*** Notações de tamanhos de palavra
|
||||
|
||||
| Tamanho | Arquitetura x86-64 | NASM / Assembly | GDB (comando =x=) |
|
||||
|---------+--------------------+-----------------+-----------------|
|
||||
| 1 byte | byte | =byte= | =b= |
|
||||
| 2 bytes | word | =word= | =h= (/halfword/) |
|
||||
| 4 bytes | doubleword | =dword= | =w= (/word/) |
|
||||
| 8 bytes | quadword | =qword= | =g= (/giant word/) |
|
||||
|
||||
** O GDB e a programação em C/C++
|
||||
|
||||
O GDB tem suporte avançado para C e C++, oferecendo:
|
||||
|
||||
- Identificação de tipos, nomes, escopos e estruturas;
|
||||
- Acesso a variáveis locais, globais e parâmetros;
|
||||
- Depuração de ponteiros e aritmética de ponteiros;
|
||||
- Interação com /structs/, =typedef=, =enum=, =union=, etc.;
|
||||
- Acompanhamento de chamadas recursivas e /stack frames/;
|
||||
- Em C++: suporte a classes, herança, /namespaces/ e /templates/.
|
||||
|
||||
*** Exemplo com um programa em C
|
||||
|
||||
Código-fonte (=somac.c=):
|
||||
|
||||
#+begin_src c
|
||||
#include <stdio.h>
|
||||
|
||||
int soma(int a, int b) {
|
||||
int resultado = a + b;
|
||||
return resultado;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int x = 10, y = 20;
|
||||
int z = soma(x, y);
|
||||
printf("Resultado: %d\n", z);
|
||||
return 0;
|
||||
}
|
||||
#+end_src
|
||||
|
||||
Compilação:
|
||||
|
||||
#+begin_example
|
||||
gcc -g -o somac somac.c
|
||||
#+end_example
|
||||
|
||||
Abrindo com o GDB e definindo o ponto de parada na função =soma=:
|
||||
|
||||
#+begin_example
|
||||
:~$ gdb ./somac
|
||||
Reading symbols from ./somac...
|
||||
(gdb) b soma
|
||||
Breakpoint 1 at 0x1143: file somac.c, line 4.
|
||||
#+end_example
|
||||
|
||||
Iniciando a execução do programa:
|
||||
|
||||
#+begin_example
|
||||
(gdb) r
|
||||
Starting program: /home/blau/tmp/gdb/somac
|
||||
[Thread debugging using libthread_db enabled]
|
||||
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
|
||||
|
||||
Breakpoint 1, soma (a=10, b=20) at somac.c:4
|
||||
4 int resultado = a + b;
|
||||
#+end_example
|
||||
|
||||
Exibindo chamadas de funções na pilha:
|
||||
|
||||
#+begin_example
|
||||
(gdb) backtrace
|
||||
#0 soma (a=10, b=20) at somac.c:4
|
||||
#1 0x0000555555555178 in main () at somac.c:10
|
||||
#+end_example
|
||||
|
||||
Executando passo a passo até a chamada da função =printf=:
|
||||
|
||||
#+begin_example
|
||||
(gdb) n
|
||||
5 return resultado;
|
||||
(gdb) n
|
||||
6 }
|
||||
(gdb) n
|
||||
main () at somac.c:11
|
||||
11 printf("Resultado: %d\n", z);
|
||||
(gdb) backtrace
|
||||
#0 main () at somac.c:11
|
||||
#+end_example
|
||||
|
||||
Entrando na função =printf= (=glibc=):
|
||||
|
||||
#+begin_example
|
||||
(gdb) s
|
||||
__printf (format=0x555555556004 "Resultado: %d\n") at ./stdio-common/printf.c:28
|
||||
warning: 28 ./stdio-common/printf.c: Arquivo ou diretório inexistente
|
||||
#+end_example
|
||||
|
||||
Observando as chamadas de funções na pilha:
|
||||
|
||||
#+begin_example
|
||||
(gdb) backtrace
|
||||
#0 __printf (format=0x555555556004 "Resultado: %d\n") at ./stdio-common/printf.c:28
|
||||
#1 0x0000555555555194 in main () at somac.c:11
|
||||
#+end_example
|
||||
|
||||
Continuando a execução até o término:
|
||||
|
||||
#+begin_example
|
||||
(gdb) c
|
||||
Continuing.
|
||||
Resultado: 30
|
||||
[Inferior 1 (process 223980) exited normally]
|
||||
#+end_example
|
||||
|
||||
** Interface TUI
|
||||
|
||||
|
||||
|
||||
|
||||
|
Loading…
Add table
Reference in a new issue