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