Depuradores
Depurar código é um problema tão comum que existem programas feitos especialmente para ajudar outros programadores a depurar com mais eficiência. Eles são chamados de depuradores, e existem muitos depuradores que funcionam com a linguagem C. Vamos dar uma olhada no gdb, um depurador comum usado no terminal.
Para nossos exemplos, vamos usar o algoritmo Quicksort.
Quicksort é um algoritmo que ordena um array selecionando primeiro um elemento do array como pivô.
Em seguida, os elementos são organizados de acordo com uma das condições abaixo:
- Elementos menores que o pivô.
- Elementos maiores que o pivô.
Depois que a ordenação é feita, o mesmo processo é chamado recursivamente nas partes superior e inferior do array, usando o pivô como ponto de referência.
Nossa versão do quicksort assume que o elemento mais à esquerda é o “maior” elemento e o mais à direita é o pivô dentro da partição.
| Figura 1: Quicksort usando o elemento mais à direita como pivô e assumindo que o elemento mais à esquerda é o “maior” elemento. |
GDB (GNU Project Debugger) é um depurador poderoso que permite depurar programas a partir da linha de comando, o que é útil em casos onde você não tem acesso a uma interface gráfica (GUI).
É importante entender como o programa funciona para depurar de forma eficaz. Nossa implementação de ‘quicksort’ executa uma implementação recursiva de quicksort e realiza a ordenação se o elemento atual for menor que o pivô, assumindo que o primeiro elemento é o “maior” elemento. A ordenação em si ocorre na função partition.
Passos para depurar com GDB
Compile o programa
- Abra a aba Shell e compile o programa:
make Quicksort
Quando o programa é compilado, as flags: -g e -Og são usadas. A primeira indica ao compilador que adicione informações de depuração, o que significa que sem essa flag, o gdb não seria capaz de depurar o programa. A segunda diz ao compilador para otimizar o programa de uma forma que não afete a estrutura de execução. Sem -Og, o compilador poderia otimizar parte do seu código, tornando o depurador menos eficaz.
É importante lembrar da segunda flag. Para depuração, você deve SEMPRE garantir que o compilador faça otimizações mínimas no seu código, pois otimizações podem alterar drasticamente como o código é executado!
Execute o programa
Digite o comando
gdb examples/Quicksort. Isso abrirá a interface de linha de comando do GDB. Para depurar um programa comgdbvocê pode usargdb <nome do programa>.Certifique-se de que o GDB exibe
Reading symbols from ./examples/Quicksort..., caso contrário você não anexou o programa ao GDB.Você pode sair do GDB digitando o comando
quit(ou qualquer prefixo:qfunciona) como se fosse um comando normal do shell.
Você deve ver algo como isto:
![]() |
|---|
| Figura 2: Executando GDB no arquivo ‘Quicksort’. |
Depurando o programa
Para depurar o programa, precisamos executá-lo a partir do GDB.
- Digite o comando
run(our). Isso executará o programa como se você o tivesse rodado pelo terminal normal.
(gdb) run
O programa primeiro imprime o conteúdo do array a ser ordenado: um array de números desordenados. Em seguida, ele executa o algoritmo de ordenação e, por fim, imprime o array ordenado. Você pode ver como os elementos mudam de posição durante a ordenação!
No entanto, parece que a ordenação não está acontecendo como esperado.
Vamos usar uma das ferramentas mais importantes que os depuradores oferecem: breakpoints. Um breakpoint informa ao depurador para pausar o programa sempre que atingir aquela linha de código. Isso permite que você veja o que está acontecendo no programa em tempo real. Note que o depurador não executa a linha onde o breakpoint está até você continuar a execução.
Primeiro vamos identificar onde a ordenação dos elementos acontece. Você sabe onde isso está acontecendo?
- Coloque um breakpoint onde a ordenação acontece usando a sintaxe
break <arquivo:linha>.
Rode o programa com
rune observe como ele para ao chegar no breakpoint.Enquanto o programa está pausado, você pode ver os valores das variáveis. Teste alguns comandos
printpara avaliar expressões. Aqui está um exemplo de como usarprint:
# você pode imprimir o valor de uma variável
print minhaVariavel
# você pode criar expressões mais elaboradas
print minhaVariavel + 2
Você pode avançar manualmente no código usando
next(oun), que fará o depurador avançar para a próxima linha de código sem entrar em uma função.O comando
stepmove o depurador para a primeira linha de código dentro de uma chamada de função.
Você precisa encontrar as variáveis com problema que estão fazendo o programa não funcionar como esperado. Tente ver se você consegue identificar o problema!
Note que todos os depuradores compartilham os mesmos conceitos básicos: eles permitem que você avance pelo seu código em tempo real. Você deve quase sempre usar depuradores em vez de apenas print. Você não vai se arrepender!
Comandos do GDB
Existem muitos comandos que o GDB oferece. Esta tabela resume alguns úteis, mas também recomendamos usar esta GDB Reference Sheet para ver mais comandos.
| Comando | O que faz |
|---|---|
gdb <nome do programa> | Executa/carrega o programa com o GDB |
run ou r | Executa o programa carregado |
quit ou q | Sai do GDB |
break <arquivo:linha> | Cria um breakpoint na linha especificada do arquivo |
print <nome da variável> ou print <expressão> | Exibe o valor de uma expressão que pode conter variáveis e constantes |
next ou n | Avança para a próxima linha de código sem entrar em uma função |
step | Avança para a primeira linha de código dentro de uma função |
continue ou c | Continua executando o código até o próximo breakpoint |
delete <nº do breakpoint> ou d <nº do breakpoint> | Deleta o breakpoint na linha especificada |
backtrace ou bt | Exibe a pilha de chamadas (stack trace) mostrando quais funções foram chamadas até a linha atual |
