Percorra uma sessão completa de depuração em um programa com bug.
Por que gdb
Depuração com printf funciona, mas o gdb é mais rápido quando você precisa:
Localizar exatamente onde um segfault acontece
Rastrear quando uma variável assumiu um valor inesperado
Parar em uma iteração específica de laço
Capturar o exato momento em que uma variável é escrita (via watchpoint)
Ver a cadeia completa de chamadas (um backtrace)
Configuração (flag -g)
Sempre compile com informações de depuração habilitadas primeiro.
$ gcc -g -O0 main.c -o app # -g embute números de linha & nomes de variáveis$ gdb ./app
(gdb)
Por que -O0? Com otimizações ligadas, variáveis podem desaparecer em registradores e linhas do código-fonte podem sumir, então step e print não se comportam como o esperado. Use sempre -O0 durante a depuração.
Programa de exemplo com bug
// sum.c: deveria calcular 1+2+...+N (tem um bug)#include<stdio.h>intsum_to(int n) {
int s = 0;
for (int i = 1; i < n; i++) { // BUG: deveria ser i <= n
s += i;
}
return s;
}
intmain(void) {
int r = sum_to(10);
printf("sum = %d\n", r); // esperado 55, obtido 45return0;
}
Sessão básica: break / run / step / print
$ gcc -g sum.c -o sum
$ gdb ./sum
(gdb)break sum_to# breakpoint no início da função
Breakpoint 1 at 0x1149: file sum.c, line 5.
(gdb)run# inicia o programa
Breakpoint 1, sum_to (n=10) at sum.c:5
5 int s = 0;
(gdb)next# avança uma linha (sem entrar em chamadas)
6 for (int i = 1; i < n; i++) {
(gdb)print n
$1 = 10
(gdb)print i
No symbol "i" in current context. # ainda não foi declarada(gdb)next
7 s += i;
(gdb)print i
$2 = 1 # agora i=1(gdb)continue
sum = 45 # errado! esperado 55
Breakpoints condicionais
(gdb)break sum.c:7 if i == 5(gdb)run
Breakpoint 2, sum_to at sum.c:7
(gdb)print s
$3 = 10 # 0+1+2+3+4 = 10 ✓
step vs. next:step (s) entra nas funções chamadas; next (n) trata uma chamada como uma única linha.
Impressão automática com display
(gdb)display i# imprime a cada parada(gdb)display s(gdb)next
1: i = 3
2: s = 3
Stack trace (bt / frame)
Quando um bug acontece bem fundo em uma cadeia de chamadas, você vai querer saber quem chamou a função atual.
(gdb)backtrace# ou 'bt'
#0 sum_to (n=10) at sum.c:7
#1 main () at sum.c:13
(gdb)frame 1# pula para o frame do main
#1 main () at sum.c:13
13 int r = sum_to(10);
(gdb)print r
$4 = 0 # ainda 0, a chamada não terminou(gdb)frame 0# volta para o frame interno
Depurando recursão:bt mostra todos os níveis; pule entre eles com frame N e inspecione cada conjunto de argumentos.
Watchpoints (para quando uma variável muda)
Perfeito para bugs do tipo "de algum jeito esta variável ficou com o valor errado".
(gdb)break main(gdb)run(gdb)watch r# observa escritas em r
Hardware watchpoint 2: r
(gdb)continue
Hardware watchpoint 2: r
Old value = 0
New value = 45
main () at sum.c:13
Escopo: Um watchpoint em uma variável local é liberado quando sua função retorna. Para globais ou memória no heap, use watch *ptr ou watch -location.
Depurando um segfault
Quando executado sob o gdb, um crash para exatamente na instrução problemática.
// crash.c#include<stdio.h>#include<string.h>intmain(void) {
char*p = NULL;
strcpy(p, "hello"); // escrita através de NULL → crashreturn0;
}
$ gcc -g crash.c -o crash
$ gdb ./crash
(gdb)run
Program received signal SIGSEGV, Segmentation fault.
__strcpy_avx2 () at .. # dentro da libc(gdb)bt
#0 __strcpy_avx2 () from /lib/libc.so.6
#1 0x... in main () at crash.c:6 # ← nosso código(gdb)frame 1
#1 main () at crash.c:6
6 strcpy(p, "hello");
(gdb)print p
$1 = 0x0 # NULL, confirmado
Post-mortem com core dumps: Habilite com ulimit -c unlimited; após o crash, rode gdb ./app core.
Resumo rápido
Comando
Abrev.
Significado
break fn / file:line
b
Define um breakpoint
info breakpoints
i b
Lista todos
delete N
d
Remove o N-ésimo
run [args]
r
Inicia o programa
continue
c
Continua até o próximo break
next
n
Próxima linha (sem entrar em chamadas)
step
s
Próxima linha (entra em chamadas)
finish
fin
Executa até a função atual retornar
print expr
p
Mostra um valor (p *p, p a[5], p arr@10)
display expr
disp
Imprime automaticamente a cada parada
backtrace
bt
Mostra o stack trace
frame N
f
Muda para o frame N
watch expr
-
Para quando expr muda
list
l
Mostra 10 linhas do código
set variable x=5
set var
Modifica uma variável em execução
quit
q
Sai do gdb
Modo TUI
$ gdb -tui ./app
# painel de código em cima, prompt embaixo# alterne com Ctrl+X depois A
IDEs: VS Code e CLion dirigem o gdb por baixo dos panos. Depois que você ficar confortável no terminal, as GUIs deles aceleram ainda mais.
Desafios
Desafio 1: Corrigir sum_to
Use gdb para descobrir por que sum_to(10) retorna 45 em vez de 55, e corrija.
Desafio 2: Prática de watchpoint
Escreva um programa em que uma global int g = 0; é modificada por várias funções. Use watch g para observar cada mudança em ordem.
Desafio 3: Inspecione a recursão
Calcule fact(5) recursivamente, pare quando n==2 e percorra cada frame com frame N e print n para ver a pilha.
Desafio 4: Localize um segfault
Escreva um pequeno acesso a array fora dos limites, rode sob gdb e use bt e print i para encontrar o índice problemático.
Teste de Revisão
Confira seu entendimento desta aula.
Q1. Qual comando inicia a execução do programa no gdb?
run
go
exec
run (ou r) inicia o programa e o executa até o próximo breakpoint. Você pode passar argumentos como run arg1 arg2.
Q2. Qual comando define um breakpoint no início de main?
break main
stop main
watch main
break (ou b) para em uma localização específica, enquanto watch para quando o valor de uma variável muda.
Q3. Qual comando mostra o valor atual da variável x?
print x ou p x
show x
get x
print avalia uma expressão e exibe o resultado. Arrays e structs são expandidos para você inspecionar o conteúdo.