GNU/Linux >> Tutoriels Linux >  >> Linux

5 conseils pour le débogueur GNU

Le débogueur GNU (gdb) est un outil inestimable pour inspecter les processus en cours d'exécution et résoudre les problèmes pendant que vous développez des programmes.

Vous pouvez définir des points d'arrêt à des emplacements spécifiques (par nom de fonction, numéro de ligne, etc.), activer et désactiver ces points d'arrêt, afficher et modifier les valeurs des variables et faire toutes les choses standard que vous attendez d'un débogueur. Mais il possède de nombreuses autres fonctionnalités que vous n'avez peut-être pas expérimentées. En voici cinq à essayer.

Points d'arrêt conditionnels

Définir un point d'arrêt est l'une des premières choses que vous apprendrez à faire avec le débogueur GNU. Le programme s'arrête lorsqu'il atteint un point d'arrêt, et vous pouvez exécuter des commandes gdb pour l'inspecter ou modifier des variables avant de permettre au programme de continuer.

Par exemple, vous savez peut-être qu'une fonction souvent appelée se bloque parfois, mais uniquement lorsqu'elle obtient une certaine valeur de paramètre. Vous pouvez définir un point d'arrêt au début de cette fonction et exécuter le programme. Les paramètres de la fonction sont affichés chaque fois qu'elle atteint le point d'arrêt, et si la valeur du paramètre qui déclenche le blocage n'est pas fournie, vous pouvez continuer jusqu'à ce que la fonction soit appelée à nouveau. Lorsque le paramètre gênant déclenche un plantage, vous pouvez parcourir le code pour voir ce qui ne va pas.

(gdb) break some_crashes
Breakpoint 1 à 0x40110e :fichier prog.c, ligne 5.
(gdb) run
[...]
Breakpoint 1, some_crashes (f=0x7fffffffd1bc) à prog.c:5
5      fprintf(stderr,
(gdb) continue
Breakpoint 1, some_crashes (f=0x7fffffffd1bc) à prog.c:5
5      fprintf(stderr,
(gdb) continuer

Pour rendre cela plus reproductible, vous pouvez compter le nombre de fois que la fonction est appelée avant l'appel spécifique qui vous intéresse et définir un compteur sur ce point d'arrêt (par exemple, "continuer 30" pour lui faire ignorer les 29 prochaines fois qu'il atteint le point d'arrêt).

Mais là où les points d'arrêt deviennent vraiment puissants, c'est dans leur capacité à évaluer les expressions au moment de l'exécution, ce qui vous permet d'automatiser ce type de test. Entrez :points d'arrêt conditionnels.

break [LOCATION] if CONDITION

(gdb) break some_crashes if !f
Breakpoint 1 at 0x401132 :file prog.c, line 5.
(gdb) run
[...]
Point d'arrêt 1, parfois_crashes (f=0x0) à prog.c:5
5      fprintf(stderr,
(gdb)

Au lieu que gdb demande quoi faire chaque fois que la fonction est appelée, un point d'arrêt conditionnel vous permet de faire en sorte que gdb s'arrête à cet emplacement uniquement lorsqu'une expression particulière est évaluée comme vraie. Si l'exécution atteint l'emplacement du point d'arrêt conditionnel, mais que l'expression est évaluée comme fausse, le

Plus de ressources Linux

  • Aide-mémoire des commandes Linux
  • Aide-mémoire des commandes Linux avancées
  • Cours en ligne gratuit :Présentation technique de RHEL
  • Aide-mémoire sur le réseau Linux
  • Aide-mémoire SELinux
  • Aide-mémoire sur les commandes courantes de Linux
  • Que sont les conteneurs Linux ?
  • Nos derniers articles Linux

le débogueur laisse automatiquement le programme continuer sans demander à l'utilisateur quoi faire.

Commandes de points d'arrêt

Une fonctionnalité encore plus sophistiquée des points d'arrêt dans le débogueur GNU est la possibilité de scripter une réponse à l'atteinte d'un point d'arrêt. Les commandes de point d'arrêt vous permettent d'écrire une liste de commandes du débogueur GNU à exécuter chaque fois qu'il atteint un point d'arrêt.

Nous pouvons l'utiliser pour contourner le bogue que nous connaissons déjà dans les sometimes_crashes fonction et la faire revenir de cette fonction sans danger lorsqu'elle fournit un pointeur nul.

Nous pouvons utiliser silencieux comme première ligne pour avoir plus de contrôle sur la sortie. Sans cela, le cadre de la pile sera affiché chaque fois que le point d'arrêt est atteint, même avant l'exécution de nos commandes de point d'arrêt.

(gdb) break some_crashes
Point d'arrêt 1 à 0x401132 :fichier prog.c, ligne 5.
(gdb) commandes 1
Tapez les commandes pour le(s) point(s) d'arrêt 1, une par ligne .
Terminez par une ligne disant juste "end".
>silent
>if !f
 >frame
 >printf "Skipping call\n"
 >return 0
 >continue
 >end
>printf "Continuing\n"
>continue
>end
(gdb) run
Programme de démarrage :/home/twaugh/Documents/GDB/prog
avertissement :section chargeable ".note.gnu.property" en dehors des segments ELF
Continue
Continue
Continue
#0  sometimes_crashes (f=0x0) at prog.c:5
5      fprintf(stderr,
Appel ignoré
[Inférieur 1 (processus 9373) terminé normalement]
(gdb)

Vider la mémoire binaire

GNU Debugger a un support intégré pour examiner la mémoire en utilisant le x commande dans divers formats, y compris octal, hexadécimal, etc. Mais j'aime voir deux formats côte à côte :les octets hexadécimaux à gauche et les caractères ASCII représentés par ces mêmes octets à droite.

Quand je veux voir le contenu d'un fichier octet par octet, j'utilise souvent hexdump -C (hexdump vient du paquet util-linux). Voici le x de gdb commande affichant les octets hexadécimaux :

 (gdb) x / 33xb myData 
0x404040 :0x02 0x001 0x00 0x02 0x001
0x404048 :0x01 0x47 0x00 0x12 0x61 0x74 0x74 0x72
0x404050 :0x69 0x62 0x65 0x73 0x2d 0x63 0x63
0x404058 :0x68 0x61 0x65 0x75 0x00 0x05
0x404060 :0x00

Et si vous pouviez apprendre à gdb à afficher la mémoire comme le fait hexdump ? Vous pouvez, et en fait, vous pouvez utiliser cette méthode pour n'importe quel format que vous préférez.

En combinant le vidage commande pour stocker les octets dans un fichier, le shell commande pour exécuter hexdump sur le fichier, et la define commande, nous pouvons créer notre propre nouveau hexdump commande pour utiliser hexdump pour afficher le contenu de la mémoire.

(gdb) define hexdump
Tapez les commandes pour la définition de "hexdump".
Terminez par une ligne indiquant simplement "end".
>dump binary memory /tmp/dump.bin $ arg0 $arg0+$arg1
>shell hexdump -C /tmp/dump.bin
>end

Ces commandes peuvent même aller dans le ~/.gdbinit fichier pour définir la commande hexdump de façon permanente. Le voici en action :

(gdb) hexdump mydata sizeof(mydata)
00000000  02 01 00 02 00 00 00 01  01 47 00 12 61 74 74 72  |.........G..attr|
00000010 69 62 75 74 65 73 2D 63 68 61 72 73 65 75 00 05 | Ibutes-CharSeu .. |
00000020 00 | |
00000021

Démontage en ligne

Parfois, vous voulez en savoir plus sur ce qui s'est passé avant un plantage, et le code source ne suffit pas. Vous voulez voir ce qui se passe au niveau des instructions du CPU.

Le démontage La commande vous permet de voir les instructions CPU qui implémentent une fonction. Mais parfois, la sortie peut être difficile à suivre. Habituellement, je veux voir quelles instructions correspondent à une certaine section de code source dans la fonction. Pour ce faire, utilisez le /s modificateur pour inclure les lignes de code source avec le désassemblage.

(gdb) disassemble/s main
Dump du code assembleur pour la fonction main :
prog.c :
11    {
   0x0000000000401158 <+0> :   push   %rbp
   0x0000000000401159 <+1> :  mov      %rsp,%rbp
   0x000000000040115c <+4> :   sub      $0x10,%rsp

12      int n =0 ;
 0 000 000x06 0 000 000x06 <+8> :   movl   $0x0,-0x4(%rbp)

13      parfois_crashes(&n);
   0x0000000000401167 <+15> :   lea     -0x4(%rbp),%rax
   0x000000000040116b <+19> :   mov     %rax,%rdi
   0x000000000040116e <+22> :   callq  0x401126
[...snipped...]

Ceci, ainsi que les registres d'informations pour voir les valeurs actuelles de tous les registres et commandes du CPU comme stepi pas à pas une instruction à la fois, vous permet d'avoir une compréhension beaucoup plus détaillée du programme.

Débogage inverse

Parfois, vous aimeriez pouvoir remonter le temps. Imaginez que vous avez atteint un point de surveillance sur une variable. Un point de surveillance est comme un point d'arrêt, mais au lieu d'être défini à un emplacement dans le programme, il est défini sur une expression (à l'aide de la balise watch commande). Chaque fois que la valeur de l'expression change, l'exécution s'arrête et le débogueur prend le contrôle.

Alors imaginez que vous avez atteint ce point de surveillance et que la mémoire utilisée par une variable a changé de valeur. Cela peut s'avérer être causé par quelque chose qui s'est produit beaucoup plus tôt; par exemple, la mémoire a été libérée et est maintenant réutilisée. Mais quand et pourquoi a-t-il été libéré ?

Le débogueur GNU peut même résoudre ce problème car vous pouvez exécuter votre programme à l'envers !

Il y parvient en enregistrant soigneusement l'état du programme à chaque étape afin de pouvoir restaurer les états précédemment enregistrés, donnant l'illusion que le temps s'écoule en arrière.

Pour activer cet enregistrement d'état, utilisez le target record-full commande. Ensuite, vous pouvez utiliser des commandes qui semblent impossibles, telles que :

  • étape inverse , qui revient à la ligne source précédente
  • inverser-suivant , qui revient à la ligne source précédente, en reculant sur les appels de fonction
  • finition inversée , qui rembobine jusqu'au moment où la fonction actuelle était sur le point d'être appelée
  • inverser-continuer , qui revient à l'état précédent du programme qui déclencherait (maintenant) un point d'arrêt (ou tout autre élément provoquant son arrêt)

Voici un exemple de débogage inversé en action :

(gdb) b main
Point d'arrêt 1 à 0x401160 :fichier prog.c, ligne 12.
(gdb) r
Programme de démarrage :/home/twaugh/Documents/GDB/prog
[...]

Breakpoint 1, main () at prog.c:12
12      int n =0 ;
(gdb) target record-full
(gdb) c
Suite.

Le programme a reçu le signal SIGSEGV, erreur de segmentation.
0x0000000000401154 dans some_crashes (f=0x0) à prog.c:7
7      return *f;
(gdb) reverse-finish
Retour à l'appel de #0  0x0000000000401154 dans some_crashes (f=0x0)
        à prog.c:7
0x0000000000401190 dans main() at prog.c:16
16      some_crashes(0);

Ce ne sont là que quelques-unes des choses utiles que le débogueur GNU peut faire. Il y en a bien d'autres à découvrir. Quelle fonctionnalité cachée, peu connue ou tout simplement incroyable de gdb est votre préférée ? Merci de le partager dans les commentaires.


Linux
  1. 3 conseils pour imprimer avec Linux

  2. Conseils rapides pour le client OpenShift oc

  3. Unix Less Command :10 conseils pour une navigation efficace

  4. Depends.exe pour GNU/Linux

  5. Définir un point d'arrêt dans le code C ou C++ par programmation pour gdb sous Linux

Un tutoriel pratique pour utiliser le débogueur de projet GNU

Conseils pour utiliser la commande top sous Linux

Conseils/astuces Meld utiles pour les utilisateurs intermédiaires

Conseils d'utilisation de tmux

Conseils d'utilisation de l'écran

Convertir les millisecondes en timespec pour le port GNU