nova árvore de trabalho
This commit is contained in:
parent
d2833df747
commit
4c2b8d663b
3 changed files with 433 additions and 0 deletions
30
mods/01/.gdb_history
Normal file
30
mods/01/.gdb_history
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
help help
|
||||||
|
h
|
||||||
|
help apropos
|
||||||
|
q
|
||||||
|
q
|
||||||
|
q
|
||||||
|
b main
|
||||||
|
r
|
||||||
|
c
|
||||||
|
r
|
||||||
|
help watch
|
||||||
|
watch resultado
|
||||||
|
l
|
||||||
|
watch z
|
||||||
|
c
|
||||||
|
help watch
|
||||||
|
b soma
|
||||||
|
watch resultado
|
||||||
|
c
|
||||||
|
c
|
||||||
|
i breakpoints
|
||||||
|
r
|
||||||
|
c
|
||||||
|
w resultado
|
||||||
|
watch resultado
|
||||||
|
r
|
||||||
|
c
|
||||||
|
c
|
||||||
|
i breakpoints
|
||||||
|
q
|
389
mods/01/README.org
Normal file
389
mods/01/README.org
Normal file
|
@ -0,0 +1,389 @@
|
||||||
|
#+title: Curso prático de introdução ao GDB
|
||||||
|
#+author: Blau Araujo
|
||||||
|
#+email: blau@debxp.org
|
||||||
|
|
||||||
|
* 1. Primeiro contato guiado
|
||||||
|
|
||||||
|
** Objetivos
|
||||||
|
|
||||||
|
- Entender o que é o GDB;
|
||||||
|
- Conhecer o conceito de /depuração/;
|
||||||
|
- Demonstrar o GDB em ação.
|
||||||
|
|
||||||
|
** 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.
|
||||||
|
|
||||||
|
** O que é depurar (/debugar/)
|
||||||
|
|
||||||
|
Depuração (ou _/debugging/_) é o processo de identificar, analisar e corrigir
|
||||||
|
erros (/bugs/) em um programa ou sistema. Durante a depuração, o programador
|
||||||
|
examina o comportamento do código para entender onde e por que ele não funciona
|
||||||
|
como esperado. O objetivo é encontrar a origem dos problemas e corrigi-los,
|
||||||
|
até que o software opere corretamente.
|
||||||
|
|
||||||
|
Sem o auxílio de ferramentas especializadas, a depuração pode ser feita, por
|
||||||
|
exemplo:
|
||||||
|
|
||||||
|
- Com uma revisão cuidadosa do que está escrito no código-fonte;
|
||||||
|
- Inserindo a exibição de mensagens em pontos suspeitos do código para avaliar
|
||||||
|
variáveis (/"o valor está correto neste ponto?"/) e o próprio fluxo de execução
|
||||||
|
(/"o programa chegou a este ponto?"/);
|
||||||
|
- Com a análise manual de algoritmos, seguindo a execução do programa no código
|
||||||
|
tendo em mente alguns exemplos de entrada.
|
||||||
|
|
||||||
|
Mas existem ferramentas (como o GDB) que auxiliam o processo de depuração que,
|
||||||
|
sem alterar diretamente o código, oferecem formas de:
|
||||||
|
|
||||||
|
- Definir pontos de parada (/breakpoints/);
|
||||||
|
- Monitorar a alteração de valores em variáveis (/whatchpoints/);
|
||||||
|
- Executar o programa passo a passo;
|
||||||
|
- Examinar dados em endereços específicos da memória através de /símbolos/;
|
||||||
|
- Examinar o conteúdo da pilha de chamadas de sistema;
|
||||||
|
- Explorar informações sobre o processo do programa na memória...
|
||||||
|
|
||||||
|
Entre várias outras possibilidades.
|
||||||
|
|
||||||
|
Também existem ferramentas especializadas em aspectos e métodos específicos da
|
||||||
|
depuração, como...
|
||||||
|
|
||||||
|
- *Valgrind:* para examinar a memória e detectar vazamentos e acessos inválidos.
|
||||||
|
- *Linters e analisadores:* para analisar como o código-fonte foi escrito e apontar
|
||||||
|
erros de sintaxe, de estilo e violações de especificações e convenções da linguagem
|
||||||
|
em uso sem, sequer, compilar (se for o caso) e executar o código.
|
||||||
|
|
||||||
|
*** Uma nota sobre depuração preventiva
|
||||||
|
|
||||||
|
Alguns /ambientes integrados de desenvolvimento/ (IDE) e editores de código
|
||||||
|
oferecem meios para sinalizar erros de sintaxe (a falta de um =;= em C, por
|
||||||
|
exemplo) ou destaques coloridos para diferentes tipos de elementos da
|
||||||
|
linguagem. Muitos deles integram-se com /protocolos de servidores de linguagem/
|
||||||
|
(LSP) para funcionarem como /linters/ e analisadores de código em tempo
|
||||||
|
real (ou seja, durante a digitação do código), o que antecipa e ajuda a
|
||||||
|
evitar potenciais causas de erros.
|
||||||
|
|
||||||
|
** Uma pequena demonstração
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
#+begin_quote
|
||||||
|
Sem os símbolos de depuração, nós teríamos que descobrir quais seriam os
|
||||||
|
endereços de dados em variáveis e de outros elementos do código para inspecionar
|
||||||
|
seus valores e comportamentos.
|
||||||
|
#+end_quote
|
||||||
|
|
||||||
|
*** Iniciando o GDB para depuração
|
||||||
|
|
||||||
|
O GDB pode ser iniciado de várias formas para depurar programas. A mais comum,
|
||||||
|
porém, é através da passagem do caminho de um binário executável como argumento
|
||||||
|
na sua invocação na linha de comandos.
|
||||||
|
|
||||||
|
No nosso exemplo:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
gdb ./demo
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Sem a opção =-q= (/quiet/) ou sem configurações de início, o GDB exibe informações
|
||||||
|
de versão e /copyright/ antes das mensagens relativas à depuração, propriamente
|
||||||
|
dita, e do prompt de seu shell de comandos...
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
:~$ gdb ./demo
|
||||||
|
GNU gdb (Debian 16.3-1) 16.3
|
||||||
|
Copyright (C) 2024 Free Software Foundation, Inc.
|
||||||
|
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
|
||||||
|
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:
|
||||||
|
<https://www.gnu.org/software/gdb/bugs/>.
|
||||||
|
Find the GDB manual and other documentation resources online at:
|
||||||
|
<http://www.gnu.org/software/gdb/documentation/>.
|
||||||
|
|
||||||
|
For help, type "help".
|
||||||
|
Type "apropos word" to search for commands related to "word"...
|
||||||
|
Reading symbols from ./demo...
|
||||||
|
(gdb)
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Apesar da poluição visual, essa mensagem padrão pode ser útil para iniciantes,
|
||||||
|
especialmente este trecho sobre ajuda:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
For help, type "help".
|
||||||
|
Type "apropos word" to search for commands related to "word"...
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Ou seja...
|
||||||
|
|
||||||
|
- Com o comando =help= (ou apenas =h=), nós teremos uma lista dos tópicos de
|
||||||
|
ajuda (classes de comandos) e as instruções de uso do próprio comando
|
||||||
|
=help=;
|
||||||
|
- Com o comando =apropos PALAVRA=, nós podemos buscar comandos que casem com
|
||||||
|
=PALAVRA=, que é interpretada como uma expressão regular (REGEX).
|
||||||
|
|
||||||
|
*** Terminando o GDB
|
||||||
|
|
||||||
|
Para sair do GDB, nós podemos teclar =Ctrl+D= ou executar =quit= (ou apenas =q=) no
|
||||||
|
prompt do shell de comandos:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
(gdb) quit
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
*** Omitindo a mensagem de início
|
||||||
|
|
||||||
|
A forma mais simples de omitir a mensagem de início é utilizando a opção =-q=
|
||||||
|
na invocação do GDB:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
:~$ gdb ./demo
|
||||||
|
Reading symbols from ./demo...
|
||||||
|
(gdb)
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Desta forma, a primeira mensagem relevante para a depuração é a única a ser
|
||||||
|
exibida antes do prompt:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
Reading symbols from ./demo...
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Ela diz que o nosso binário foi compilado com símbolos para depuração e que
|
||||||
|
esses símbolos foram lidos e registrados. Mas, vamos sair e compilar novamente
|
||||||
|
o programa para ver o que aconteceria...
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
(gdb) q
|
||||||
|
:~$ gcc -o demo demo.c
|
||||||
|
:~$ gdb ./demo
|
||||||
|
Reading symbols from ./demo...
|
||||||
|
(No debugging symbols found in ./demo)
|
||||||
|
(gdb)
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Desta vez, sem os símbolos de depuração, nós tivemos a mensagem:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
Reading symbols from ./demo...
|
||||||
|
(No debugging symbols found in ./demo)
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
*** Uma nota sobre compilação com símbolos de depuração
|
||||||
|
|
||||||
|
Normalmente, os projetos são compilados para duas finalidades: desenvolvimento
|
||||||
|
e publicação (/release/). É na compilação em desenvolvimento que os símbolos de
|
||||||
|
depuração podem ser úteis. Embora isso não cause prejuízos de desempenho,
|
||||||
|
binários compilados e publicados com símbolos de depuração resultam em arquivos
|
||||||
|
maiores e, dependendo do caso, podem expor informações internas sensíveis.
|
||||||
|
|
||||||
|
Isso não costuma ser relevante no contexto do Software Livre, já que o acesso
|
||||||
|
aos fontes deve ser garantido, mas existem casos de programas escritos para
|
||||||
|
uso interno em alguma cadeia de produção que vão requerer mais cuidados.
|
||||||
|
|
||||||
|
#+begin_quote
|
||||||
|
É possível compilar o código com os símbolos de depuração e, depois, removê-los
|
||||||
|
do binário que será distribuído(=strip -g=), ou ainda, gerar e armazenar
|
||||||
|
cópias dos símbolos em arquivos separados (=objcopy --only-keep-debug=), mas isso
|
||||||
|
foge do nosso escopo de interesses.
|
||||||
|
#+end_quote
|
||||||
|
|
||||||
|
Sendo assim, vamos sair e compilar novamente o nosso programa com os
|
||||||
|
símbolos de depuração.
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
(gdb) q
|
||||||
|
:~$ gcc -g -o demo demo.c
|
||||||
|
:~$ gdb ./demo
|
||||||
|
Reading symbols from ./demo...
|
||||||
|
(gdb)
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
*** Listando o código-fonte
|
||||||
|
|
||||||
|
No GDB, podemos listar o código-fonte com o 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
|
14
mods/01/demo.c
Normal file
14
mods/01/demo.c
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#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;
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue