{"id":181,"date":"2017-04-04T15:38:33","date_gmt":"2017-04-04T15:38:33","guid":{"rendered":"http:\/\/learningcenter.paratools.com\/?p=181"},"modified":"2022-04-14T14:04:48","modified_gmt":"2022-04-14T14:04:48","slug":"les-bases-de-gdb","status":"publish","type":"post","link":"https:\/\/learningcenter.paratools.com\/?p=181","title":{"rendered":"Les Bases de GDB"},"content":{"rendered":"<p>Dans cet article nous allons pr\u00e9senter l&rsquo;utilisation basique du d\u00e9bogueur GDB. Nous commencerons par illustrer les diff\u00e9rents modes de lancement avant de pr\u00e9senter les diff\u00e9rentes commandes de base.<\/p>\n<p>\u00c0 la fin de ce tutoriel vous saurez:<\/p>\n<ul>\n<li>Lancer GDB sur votre programme<\/li>\n<li>Vous attacher \u00e0 un programme en cours d&rsquo;\u00e9x\u00e9cution<\/li>\n<li>Explorer l&rsquo;\u00e9tat d&rsquo;un programme via l&rsquo;invite de commande GDB<\/li>\n<\/ul>\n<p><!--more--><\/p>\n<h1>Installer GDB<\/h1>\n<p>GDB est\u00a0une commande de base pour tout d\u00e9veloppment en C\/C++\/Fortran. Il devrait donc \u00eatre sur votre syst\u00e8me. Si c&rsquo;est le cas, passez cette section.<\/p>\n<p>Pour l&rsquo;installer sous Centos 7:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nyum install gdb\r\n<\/pre>\n<p>Pour l&rsquo;installer sous Ubuntu:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\napt-get install gdb\r\n<\/pre>\n<p>Une fois install\u00e9 vous devriez pouvoir le lancer:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\ngdb\r\n<\/pre>\n<p>Et obtenir une sortie analogue \u00e0 celle-ci:<\/p>\n<blockquote><p>\nGNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-94.el7<br \/>\nCopyright (C) 2013 Free Software Foundation, Inc.<br \/>\nLicense GPLv3+: GNU GPL version 3 or later <http:\/\/gnu.org\/licenses\/gpl.html><br \/>\nThis is free software: you are free to change and redistribute it.<br \/>\nThere is NO WARRANTY, to the extent permitted by law.  Type \u00ab\u00a0show copying\u00a0\u00bb<br \/>\nand \u00ab\u00a0show warranty\u00a0\u00bb for details.<br \/>\nThis GDB was configured as \u00ab\u00a0x86_64-redhat-linux-gnu\u00a0\u00bb.<br \/>\nFor bug reporting instructions, please see:<br \/>\nhttp:\/\/www.gnu.org\/software\/gdb\/bugs.<br \/>\n(gdb)\n<\/p><\/blockquote>\n<h1>Lancer GDB<\/h1>\n<p>GDB se lance avec le programme cible en argument. Pour illutrer cela, consid\u00e9rons le programme erron\u00e9 suivant:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\nint main(int argc, char *argv[])\r\n{\r\n\tint *a = NULL;\r\n\r\n\t*a = 9;\r\n\r\n\treturn 0;\r\n}\r\n<\/pre>\n<p>Compilons le avec les symboles de d\u00e9bug (consid\u00e9rant qu&rsquo;il s&rsquo;appelle t.c) :<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\ngcc -g t.c -o test\r\n<\/pre>\n<p>L&rsquo;option \u00ab\u00a0-g\u00a0\u00bb , demande \u00e0 GCC d&rsquo;ajouter des informations suppl\u00e9mentaires au binaire, en particulier,le binaire contiendra les informations permettant de faire correspondre une fonction \u00e0 ses sources. Il est indispensable d&rsquo;ajouter cette option pour d\u00e9boguer dans de bonnes conditions. Dans le cas contraire, certaines informatiosn pourraient \u00eatre seulement partiellement visibles.<\/p>\n<p>Si nous lan\u00e7ons le programme:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n.\/test\r\n<\/pre>\n<p>Nous obtenons la sortie suivante:<\/p>\n<blockquote><p>\n[1]    10446 segmentation fault  .\/test\n<\/p><\/blockquote>\n<p>Cela s&rsquo;iginifie que le programme a \u00e9t\u00e9 interrompu car il a fait un acc\u00e8s m\u00e9moire incorrect, provoquant une erreur se segmentation. La pluspart du temps, cela s&rsquo;ignifie qu&rsquo;il \u00e0 lu ou \u00e9crit \u00e0 une adresse incorrecte. Dans la majorit\u00e9 des cas impliquant ce type d&rsquo;erreur, il faut utiliser un d\u00e9bogueur pour extraire l&rsquo;\u00e9tat du programme menant \u00e0 cette erreur et donc pour la corriger. Pour langer avec GDB, il faut utiliser simplement la commande suivante:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\ngdb .\/test\r\n<\/pre>\n<p>Notez que si <b>.\/test<\/b> prennait des arguments il faudrait utiliser :<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\ngdb --args .\/test ARG1 ARG2\r\n<\/pre>\n<p>Dans le cas contraire les arguments ne seraient pas transmis au programme.<\/p>\n<p>Pour quitter GDB on peut soit utiliser la commande <b>quit<\/b> dans l&rsquo;invite GDB:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) quit\r\n<\/pre>\n<p>Ou bien de mani\u00e8re plus succinte faire CTRL+D (pour \u00e9mettre un EOF, end-of-file).<\/p>\n<h1>Attacher GDB<\/h1>\n<p>Il est \u00e9galement possible de s&rsquo;attacher \u00e0 un programme en cours d&rsquo;\u00e9x\u00e9cution. Cela peut \u00eatre utile par exemple pour voir o\u00f9 le programme en est et pour diagnostiquer des inter-blocages. Dans cet exemple nous allons diagnostiquer un inter-blocage:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\n\r\nint bar()\r\n{\r\n    while(1){}\r\n}\r\n\r\nint main(int argc, char *argv[])\r\n{\r\n\tbar();\r\n\treturn 0;\r\n}\r\n<\/pre>\n<p>Il est possible de voir que ce programme ne se terminera jamais. Compilons le:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\ngcc -g t.c -o testddlk\r\n<\/pre>\n<p>Et lan\u00e7ons le:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n.\/testddlk\r\n<\/pre>\n<p>Le programme est comme pr\u00e9vu bloqu\u00e9. Maintenant comment le suivre dans GDB ? en particulier car nous ne l&rsquo;avons pas lanc\u00e9 pr\u00e9fix\u00e9 par celui-ci.<\/p>\n<p>La premi\u00e8re \u00e9tape est de r\u00e9cup\u00e9rer le PID du programme cible. Le plus simple est de reposer sur la commande :<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nps ux\r\n<\/pre>\n<p>On a par exemple la sortie suivante:<\/p>\n<blockquote><p>\nUSER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND<br \/>\njbbesna+ 13506  0.0  0.0 145020  2296 ?        S    09:32   0:00 sshd: jbbesnard@pts\/0<br \/>\njbbesna+ 13507  0.0  0.0 145828  6940 pts\/0    Ss   09:32   0:00 -zsh<br \/>\njbbesna+ 13591  0.0  0.0 145020  2300 ?        S    09:32   0:00 sshd: jbbesnard@pts\/1<br \/>\njbbesna+ 13592  0.0  0.0 142792  5796 pts\/1    Ss   09:32   0:00 -zsh<br \/>\n<b>jbbesna+ 13753 89.0  0.0   4156   344 pts\/0    R+   09:34   0:03 .\/testddlk<\/b><br \/>\njbbesna+ 13758  0.0  0.0 151056  1816 pts\/1    R+   09:34   0:00 ps ux\n<\/p><\/blockquote>\n<p>On trouve alors facilement la commande .\/testddlk et son PID : 13753. On peut donc <b>attacher<\/b> GDB \u00e0 ce programme:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n# gdb  PROG      PID\r\n  gdb .\/testddlk 13753\r\n<\/pre>\n<p>Une approche alternative est de lancer GDB:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\ngdb\r\n<\/pre>\n<p>Et d&rsquo;utiliser la commande \u00ab\u00a0attach PID\u00a0\u00bb dans l&rsquo;invite de commande :<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) attach 13753\r\n<\/pre>\n<p>Dans les deux cas vous obtiendrez la sortie suivante :<\/p>\n<blockquote><p>\nReading symbols from \/home\/jbbesnard\/gdb\/testddlk&#8230;done.<br \/>\nAttaching to program: \/home\/jbbesnard\/gdb\/.\/testddlk, process 13753<br \/>\nReading symbols from \/lib64\/libc.so.6&#8230;(no debugging symbols found)&#8230;done.<br \/>\nLoaded symbols for \/lib64\/libc.so.6<br \/>\nReading symbols from \/lib64\/ld-linux-x86-64.so.2&#8230;(no debugging symbols found)&#8230;done.<br \/>\nLoaded symbols for \/lib64\/ld-linux-x86-64.so.2<br \/>\nbar () at .\/t.c:3<br \/>\n3\t    while(1){}<br \/>\nMissing separate debuginfos, use: debuginfo-install glibc-2.17-157.el7_3.1.x86_64\n<\/p><\/blockquote>\n<p>Notez que le programme cible est interrompu, et qu&rsquo;il se troube bien \u00e0 la ligne du deadlock. Si vous quitez GDB (CTRL+D) le programme va reprendre son \u00e9x\u00e9cution normale. Une fois attach\u00e9 dans GDB il est possible de se d\u00e9tacher sans relancer GDB (si par exemple vous suivez de multiples processus).<\/p>\n<p>Pour ce faire on utilise la commande suivante dans l&rsquo;invite de commande de GDB:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) detach\r\n<\/pre>\n<h1>Explorer l&rsquo;\u00e9tat d&rsquo;un programme<\/h1>\n<p>Dans les exemples suivants nous allons consid\u00e9rer le code suivant:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\n#include &lt;omp.h&gt;\r\n#include &lt;stdio.h&gt;\r\n\r\nint add( int a ,  int b )\r\n{\r\n\treturn a + b;\r\n}\r\n\r\nint mul(int a, int b)\r\n{\r\n\treturn a*b;\r\n}\r\n\r\nint foo( int a , int b )\r\n{\r\n\treturn add( a, b ) + mul( a , b );\r\n}\r\n\r\nint main(int argc, char *argv[])\r\n{\r\n\tint a, b;\r\n\ta = 8;\r\n\tb = 9;\r\n\t#pragma omp parallel\r\n\t{\r\n\t\tprintf(&quot;foo : %d\\n&quot;, foo( a , b ) );\r\n\t}\r\n\r\n\treturn 0;\r\n}\r\n<\/pre>\n<p>Ce code est un code OpenMP, si vous n&rsquo;\u00eates pas trop familier avec ce mod\u00e8le de programmation, sachez juste que dans la section avec des acolades de multiples threads seront cr\u00e9\u00e9s. Cela s&rsquo;ignifie que plusieurs fils d&rsquo;\u00e9x\u00e9cution vont traiter le code du prinf et donc \u00e9crire sur la sortie standard.<\/p>\n<p>Pour compiler ce code, nous mettons les symboles de d\u00e9bug <b>-g<\/b> et ajoutons le support OpenMP <b>-fopenmp<\/b>:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\ngcc -g -fopenmp p.c -o par\r\n<\/pre>\n<p>Pour v\u00e9rifier nos dires lan\u00e7ons ce code:<\/p>\n<blockquote><p>\nfoo : 89<br \/>\nfoo : 89<br \/>\nfoo : 89<br \/>\nfoo : 89<br \/>\nfoo : 89<br \/>\nfoo : 89<br \/>\nfoo : 89<br \/>\nfoo : 89\n<\/p><\/blockquote>\n<p>On retrouve huit fois la sortie, cela correspond aux huit coeurs de notre machine de test.<\/p>\n<h2>Les points d&rsquo;arr\u00eat<\/h2>\n<p>Notez avant toute chose que GDB s&rsquo;arr\u00eate si le programme rencontre un signal (par exemple une erreur de segmentation). Il est donc possible de l&rsquo;\u00e9x\u00e9cuter directement pour se rendre au lieu de l&rsquo;erreur sans avoir recours aux breakpoints.<\/p>\n<p>Maintenant, consid\u00e9rons que nous voulions explorer l&rsquo;\u00e9tat du programme dans la fonction add. Pour ce faire, il faut placer un <b>breakpoint<\/b> ou point d&rsquo;arr\u00eat. Il forcera le programme a s&rsquo;interrompre quant il entrera dans la fonction add.<\/p>\n<p>On lance le programme avec GDB:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\ngdb .\/par\r\n<\/pre>\n<p>On place alors un point d&rsquo;arr\u00eat sur add :<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) break add\r\nBreakpoint 1 at 0x4006c7: file p.c, line 6.\r\n<\/pre>\n<p>On remarque alors que GDB confirme la position de ce point d&rsquo;arr\u00eat, indiquant m\u00eame la ligne source.<\/p>\n<p>Il est maintenant temps de lancer le programme avec la commande <b>run<\/b> aussi abr\u00e9geable en <b>r<\/b><\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) run\r\n<\/pre>\n<p>GDB comme attendu va alors arr\u00eater le programme dans <b>add<\/b>:<\/p>\n<blockquote><p>\n[Switching to Thread 0x7ffff45d3700 (LWP 14196)]<br \/>\nBreakpoint 1, add (a=8, b=9) at p.c:6<br \/>\n6\t\treturn a + b;<br \/>\n(gdb)\n<\/p><\/blockquote>\n<p>Le programme cible est alors interrompu au point indiqu\u00e9, p.c \u00e0 la ligne 6 (not\u00e9 p.c:6) dans la fonction add. GDB affiche \u00e9galement la ligne source.<\/p>\n<p>Il est possible de lister les breakpoint actif avec la commande suivante:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) info breakpoint\r\nNum     Type           Disp Enb Address            What\r\n1       breakpoint     keep y   0x00000000004006c7 in add at p.c:6\r\n\tbreakpoint already hit 1 time\r\n<\/pre>\n<p>Noter que cette commande peut \u00eatre racourcie comme de nombreuse commandes GDB:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) i b\r\n<\/pre>\n<p>On peut supprimer un breakpoint avec <b>delete<\/b>:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) delete 1\r\n(gdb) i b\r\nNo breakpoints or watchpoints.\r\n<\/pre>\n<p>On peut temporairement d\u00e9sactiver un breakpoint (remarquer le changement de Enb) :<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) info b\r\nNum     Type           Disp Enb Address            What\r\n3       breakpoint     keep y   0x00000000004006c7 in add at p.c:6\r\n(gdb) disable 3\r\n(gdb) info b\r\nNum     Type           Disp Enb Address            What\r\n3       breakpoint     keep n   0x00000000004006c7 in add at p.c:6\r\n<\/pre>\n<p>Et par sym\u00e9trie, le r\u00e9activer:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) enable 3\r\n(gdb) i b\r\nNum     Type           Disp Enb Address            What\r\n3       breakpoint     keep y   0x00000000004006c7 in add at p.c:6\r\n<\/pre>\n<p>Enfin, <b>disable<\/b> et <b>enable<\/b> sans param\u00e8tre d\u00e9sactivent\/activent tout les points d&rsquo;arr\u00eat.<\/p>\n<p>Enfin on peut continuer l&rsquo;\u00e9x\u00e9cution normale du programme avec :<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) continue\r\nContinuing.\r\n<\/pre>\n<p>\u00c0 partir de maintenant, utilisez:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) c\r\nContinuing.\r\n<\/pre>\n<h2>La pile d&rsquo;appel<\/h2>\n<p>Une fois arr\u00eat\u00e9s dans add, il est possible d&rsquo;afficher la pile d&rsquo;appel (les fonction appel\u00e9es) qui nous ont men\u00e9es \u00e0 cet appel \u00e0 add. C&rsquo;est l&rsquo;une des command les plus importante pour le d\u00e9bogage.<\/p>\n<p>G\u00e9n\u00e9rons alors la pile d&rsquo;appel:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) backtrace\r\n#0  add (a=8, b=9) at p.c:6\r\n#1  0x0000000000400702 in foo (a=8, b=9) at p.c:18\r\n#2  0x00000000004007a1 in main._omp_fn.0 () at p.c:30\r\n#3  0x00007ffff7bcd435 in ?? () from \/lib64\/libgomp.so.1\r\n#4  0x00007ffff79a2dc5 in start_thread () from \/lib64\/libpthread.so.0\r\n#5  0x00007ffff76d173d in clone () from \/lib64\/libc.so.6\r\n<\/pre>\n<p>\u00c0 l&rsquo;avenir utilisez la version courte de cet appel:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) bt\r\n<\/pre>\n<p>On observe que add a \u00e9t\u00e9 appel\u00e9 de la fonction foo qui a elle m\u00eame \u00e9t\u00e9 appel\u00e9e depuis le main (avec la complexit\u00e9 suppl\u00e9mentaire d&rsquo;un contexte OpenMP).<\/p>\n<p>On retrouve pour chaque fonction de la pile d&rsquo;appel les informations suivantes:<\/p>\n<ul>\n<li>#1 : Le num\u00e9ro de frame<\/li>\n<li> 0x0000000000400702  : Addresse du pointeur programme<\/li>\n<li> foo : le nom de la fonction<\/li>\n<li> (a=8, b=9) : les param\u00e8tres de la fonction<\/li>\n<li> at p.c:18 : la position de la fonction dans le code<\/li>\n<li> from \/lib64\/libpthread.so.0 : la biblioth\u00e8que contenant le symbole (quand applicable)<\/li>\n<\/ul>\n<h2>La frame<\/h2>\n<p>Une frame est une instance d&rsquo;appel de fonction dans la pile d&rsquo;appel, est est d\u00e9crite par un identifiant dans le backtrace. Par exmple foo dans l&rsquo;exemple pr\u00e9c\u00e9dent est \u00e0 la frame #1. Il est possible de monter dans la pile d&rsquo;appel avec la commande suivante:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) up\r\n#1  0x0000000000400702 in foo (a=8, b=9) at p.c:18\r\n18\t\treturn add( a, b ) + mul( a , b );\r\n<\/pre>\n<p>Et bien s\u00fbr de descendre:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) down\r\n#0  add (a=8, b=9) at p.c:6\r\n6\t\treturn a + b;\r\n<\/pre>\n<p>Ces commandes \u00e9tant racourcissable en <b>up<\/b> et <b>do<\/b>.<\/p>\n<p>Il est \u00e9galement possible de se rendre directement \u00e0 une frame avec son num\u00e9ro:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) frame 2\r\n#2  0x00000000004007a1 in main._omp_fn.0 () at p.c:30\r\n30\t\t\tprintf(&quot;foo : %d\\n&quot;, foo( a , b ) );\r\n<\/pre>\n<p>\u00c0 partir de maintenant vous utiliserez <b>fr<\/b> en lieu et place de <b>frame<\/b><\/p>\n<p>Il est possible de r\u00e9cup\u00e9rer les informations de la frame courante avec :<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) info frame\r\nStack level 0, frame at 0x7ffff45d2e20:\r\n rip = 0x4006cf in add (p.c:7); saved rip 0x400702\r\n called by frame at 0x7ffff45d2e40\r\n source language c.\r\n Arglist at 0x7ffff45d2e10, args: a=8, b=9\r\n Locals at 0x7ffff45d2e10, Previous frame's sp is 0x7ffff45d2e20\r\n Saved registers:\r\n  rbp at 0x7ffff45d2e10, rip at 0x7ffff45d2e18\r\n<\/pre>\n<p>On trouve dans cette sortie de nombreuses informations, avec par exemple les arguments de la fonction. Notez que l&rsquo;on peut lister les arguments avec :<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) info args\r\na = 8\r\nb = 9\r\n<\/pre>\n<p>Et l&rsquo;ensemble des variables locales avec:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) fr 2\r\n#2  0x00000000004007a1 in main._omp_fn.0 () at p.c:30\r\n30\t\t\tprintf(&quot;foo : %d\\n&quot;, foo( a , b ) );\r\n(gdb) info locals\r\na = 8\r\nb = 9\r\n<\/pre>\n<h2>Faire avancer le programme<\/h2>\n<p>Quand vous lancez GDB, il faut faire \u00ab\u00a0run\u00a0\u00bb (ou \u00ab\u00a0r\u00a0\u00bb) pour que le programme commence. Si vous ne pla\u00e7ez aucun breakpoint, le programme va se terminer. Faire \u00ab\u00a0run\u00a0\u00bb \u00e0 nouveau le lancera encore une fois. Ensuite, quand vous souhaitez quitter un breakpoint, faites \u00ab\u00a0continues\u00a0\u00bb ou \u00ab\u00a0c\u00a0\u00bb. Le programme continue alors son \u00e9x\u00e9cution.<\/p>\n<p>Il est \u00e9galement possible d&rsquo;avancer ligne de code par ligne de code avec \u00ab\u00a0step\u00a0\u00bb ou \u00ab\u00a0s\u00a0\u00bb, et enfin d&rsquo;une seule instruction avec \u00ab\u00a0si\u00a0\u00bb.<\/p>\n<h2>Afficher les sources<\/h2>\n<p>Dans GDB il est possible d&rsquo;afficher les lignes sources proche du point d&rsquo;arr\u00eat. Pour cela on utilise:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) list\r\n1\t#include &lt;omp.h&gt;\r\n2\t#include &lt;stdio.h&gt;\r\n3\r\n4\tint add( int a ,  int b )\r\n5\t{\r\n6\t\treturn a + b;\r\n7\t}\r\n8\r\n9\r\n10\tint mul(int a, int b)\r\n<\/pre>\n<p>Chaque nouvel appel \u00e0 <b>list<\/b> affichera la suite du code.<\/p>\n<p>Pour \u00ab\u00a0remonter\u00a0\u00bb utilisez:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) list -\r\n<\/pre>\n<h2>Inspecter les threads<\/h2>\n<p>Notre petit exemple cr\u00e9e de multiple threads, il y a donc plusieurs instance de pile dans le programme que nous avons d\u00e9bogu\u00e9 jusqu&rsquo;\u00e0 maintenant. Les commandes pr\u00e9c\u00e9dentes se sont donc appliqu\u00e9es \u00e0 un seul thread (flot d&rsquo;\u00e9x\u00e9cution), le choix du thread \u00e9tant celui du premier entrant dans le breakpoint.<\/p>\n<p>Il est possible de lister les threads avec:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) info thread\r\n  Id   Target Id         Frame\r\n  8    Thread 0x7ffff45d3700 (LWP 16038) &quot;par&quot; add (a=8, b=9) at p.c:6\r\n* 7    Thread 0x7ffff4dd4700 (LWP 16037) &quot;par&quot; add (a=8, b=9) at p.c:6\r\n  6    Thread 0x7ffff55d5700 (LWP 16036) &quot;par&quot; add (a=8, b=9) at p.c:6\r\n  5    Thread 0x7ffff5dd6700 (LWP 16035) &quot;par&quot; add (a=8, b=9) at p.c:6\r\n  4    Thread 0x7ffff65d7700 (LWP 16034) &quot;par&quot; add (a=8, b=9) at p.c:6\r\n  3    Thread 0x7ffff6dd8700 (LWP 16033) &quot;par&quot; add (a=8, b=9) at p.c:6\r\n  2    Thread 0x7ffff75d9700 (LWP 16032) &quot;par&quot; add (a=8, b=9) at p.c:6\r\n  1    Thread 0x7ffff7fe27c0 (LWP 16031) &quot;par&quot; add (a=8, b=9) at p.c:6\r\n<\/pre>\n<p>Notez comment le thread actuellement s\u00e9lectionn\u00e9 est indiqu\u00e9 par une ast\u00e9risque (*). De plus il est important d&rsquo;observer que la fonction courante, ses arguments et sa position sont indiqu\u00e9e dans cet appel. Cela le rend tr\u00e8s pratique car il permet de voir en une commande la situation de touts les threads. Ici, ils sont tous dans la fonction add.<\/p>\n<p>Tout comme les frames, il est possible de changer de thread :<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) thread 1\r\n[Switching to thread 1 (Thread 0x7ffff7fe27c0 (LWP 16031))]\r\n#0  add (a=8, b=9) at p.c:6\r\n6\t\treturn a + b;\r\n<\/pre>\n<p>Vous pouvez \u00e9galement utiliser la syntaxe courte <b>thr<\/b>. Une fois ce changement effectu\u00e9, vous pouvez utilisez l&rsquo;ensemble des commandes pour inspecter son contexte.<\/p>\n<p>Il est possible de nommer les threads en utilisant :<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) thread name BUG\r\n(gdb) info thread\r\n  Id   Target Id         Frame\r\n  8    Thread 0x7ffff45d3700 (LWP 30206) &quot;par&quot; add (a=8, b=9) at p.c:6\r\n  7    Thread 0x7ffff4dd4700 (LWP 30205) &quot;par&quot; add (a=8, b=9) at p.c:6\r\n  6    Thread 0x7ffff55d5700 (LWP 30204) &quot;par&quot; add (a=8, b=9) at p.c:6\r\n* 5    Thread 0x7ffff5dd6700 (LWP 30203) &quot;BUG&quot; add (a=8, b=9) at p.c:6\r\n  4    Thread 0x7ffff65d7700 (LWP 30202) &quot;par&quot; add (a=8, b=9) at p.c:6\r\n  3    Thread 0x7ffff6dd8700 (LWP 30201) &quot;par&quot; add (a=8, b=9) at p.c:6\r\n  2    Thread 0x7ffff75d9700 (LWP 30200) &quot;par&quot; add (a=8, b=9) at p.c:6\r\n  1    Thread 0x7ffff7fe27c0 (LWP 30196) &quot;par&quot; add (a=8, b=9) at p.c:6\r\n<\/pre>\n<p>Ce qui est tr\u00e8s pratique pour s&rsquo;y retrouver parfois. Enfin il est possible d&rsquo;appliquer une commande \u00e0 plusieurs threads \u00e0 la fois :<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) thread apply 1-4 print a\r\n\r\nThread 1 (Thread 0x7ffff7fe27c0 (LWP 30196)):\r\n$3 = 8\r\n\r\nThread 2 (Thread 0x7ffff75d9700 (LWP 30200)):\r\n$4 = 8\r\n\r\nThread 3 (Thread 0x7ffff6dd8700 (LWP 30201)):\r\n$5 = 8\r\n\r\nThread 4 (Thread 0x7ffff65d7700 (LWP 30202)):\r\n$6 = 8\r\n<\/pre>\n<p>De plus passer \u00ab\u00a0all\u00a0\u00bb permet d&rsquo;invoquer une commande sur tous les threads, par exemple :<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) thread apply all bt\r\n<\/pre>\n<h2>Inspecter les variables<\/h2>\n<p>A tout moment dans GDB il est possible d&rsquo;explorer le contenu de la m\u00e9moire. Pour cela, on utilise g\u00e9n\u00e9ralement la fonction print. Cette fonction peut s&rsquo;utilisez tr\u00e8s simplement:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) print a\r\n$1 = 8\r\n<\/pre>\n<p>\u00c0 partir de maintenant utilisez la version courte <b>p<\/b>. Print peut effectuer de nombreuses manipulation sur la sortie, de plus les variables pass\u00e9es en param\u00e8tre peuvent \u00eatre alt\u00e9r\u00e9es tout comme en C. Pour illustrer cela trouvons l&rsquo;addresse de a:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) print &amp;a\r\n$2 = (int *) 0x7fffffffe11c\r\n<\/pre>\n<p>Et maintenant affichons le contenu \u00e0 cette addresse comme un int:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) p *((int*)0x7fffffffe11c)\r\n$3 = 8\r\n<\/pre>\n<p>Il est de plus tout \u00e0 fait possible de faire de l&rsquo;arithm\u00e9tique de pointeur. Par exemple en consid\u00e9rant la mani\u00e8re dont les variables sont rang\u00e9es sur la pile, b est stock\u00e9 juste avant a, v\u00e9rifions le:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\np ((int*)0x7fffffffe11c)[-1]\r\n$10 = 9\r\n<\/pre>\n<p>On a bien &#038;b = &#038;a &#8211; 4 , avec 4 la taille en octed d&rsquo;un entier:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) p &amp;b\r\n$11 = (int *) 0x7fffffffe118\r\n<\/pre>\n<p>Si nous consid\u00e9rons un code trivial avec un tableau:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\n#include &lt;stdio.h&gt;\r\n\r\nint main(int argc, char *argv[])\r\n{\r\n\tint *a = NULL;\r\n\tint array[4] = {1,2,3,4};\r\n\t*a = 9;\r\n\treturn 0;\r\n}\r\n<\/pre>\n<p>On peut afficher le contenu de ce tableau dans GDB:<\/p>\n<p>Directement:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) p array\r\n$1 = {1, 2, 3, 4}\r\n<\/pre>\n<p>En utilisant l&rsquo;op\u00e9rateur de longueur:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) p array[0]@4\r\n$2 = {1, 2, 3, 4}\r\n<\/pre>\n<p>Ou bien via un cast:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) p *((int[4]*) &amp;array[0])\r\n$9 = {1, 2, 3, 4}\r\n<\/pre>\n<p>Il y a \u00e9galement des modifieurs de format, par exemple \u00ab\u00a0x\u00a0\u00bb pour hexad\u00e9cimal (voir <a href=\"http:\/\/ftp.gnu.org\/old-gnu\/Manuals\/gdb\/html_chapter\/gdb_9.html\">ici<\/a> pour la liste):<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) p\/x array[0]@4\r\n$7 = {0x1, 0x2, 0x3, 0x4}\r\n<\/pre>\n<h2>Inspecter la m\u00e9moire<\/h2>\n<p>En reprenant notre exemple pr\u00e9c\u00e9dent, il est possible d&rsquo;explorer la m\u00e9moire, par exemple pour afficher la m\u00e9moire \u00e0 l&rsquo;endroit du tableau sur 4 entiers :<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) x\/4x &amp;array[0]\r\n0x7fffffffe170:\t0x00000001\t0x00000002\t0x00000003\t0x00000004\r\n<\/pre>\n<p>Les arguments de format \u00e9tant pris en compte:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) x\/4d &amp;array[0]\r\n0x7fffffffe170:\t1\t2\t3\t4\r\n<\/pre>\n<p>Par d\u00e9faut, l&rsquo;offset est de 4 octets, un entier, si vous voullez changer pour un byte tout en affichant maintenant 16 bytes (ou 4 * 4 entiers de 4 bytes) :<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n(gdb) x\/16db &amp;array[0]\r\n0x7fffffffe170:\t1\t0\t0\t0\t2\t0\t0\t0\r\n0x7fffffffe178:\t3\t0\t0\t0\t4\t0\t0\t0\r\n<\/pre>\n<p>Voir <a href=\"http:\/\/ftp.gnu.org\/old-gnu\/Manuals\/gdb\/html_chapter\/gdb_9.html\">ici<\/a> pour les diff\u00e9rentes options.<\/p>\n<h1>Conclusion<\/h1>\n<p>Dans ce tutorial vous avez apris \u00e0 utliser GDB, \u00e0 le lancer ou bien \u00e0 vous attacher au processus cible. Ensuite, nous avons couvert les diff\u00e9rentes commandes de base. Dans un tuturial plus avanc\u00e9 nous couvrirons plus en d\u00e9tails les watchpoints et la possibilit\u00e9 de placer des commandes sur les breakpoints.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Dans cet article nous allons pr\u00e9senter l&rsquo;utilisation basique du d\u00e9bogueur GDB. Nous commencerons par illustrer les diff\u00e9rents modes de lancement avant de pr\u00e9senter les diff\u00e9rentes commandes de base. \u00c0 la fin de ce tutoriel vous saurez: Lancer GDB sur votre programme Vous attacher \u00e0 un programme en cours d&rsquo;\u00e9x\u00e9cution Explorer l&rsquo;\u00e9tat d&rsquo;un programme via l&rsquo;invite [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[27],"tags":[30,29,31],"_links":{"self":[{"href":"https:\/\/learningcenter.paratools.com\/index.php?rest_route=\/wp\/v2\/posts\/181"}],"collection":[{"href":"https:\/\/learningcenter.paratools.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/learningcenter.paratools.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/learningcenter.paratools.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/learningcenter.paratools.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=181"}],"version-history":[{"count":38,"href":"https:\/\/learningcenter.paratools.com\/index.php?rest_route=\/wp\/v2\/posts\/181\/revisions"}],"predecessor-version":[{"id":251,"href":"https:\/\/learningcenter.paratools.com\/index.php?rest_route=\/wp\/v2\/posts\/181\/revisions\/251"}],"wp:attachment":[{"href":"https:\/\/learningcenter.paratools.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=181"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/learningcenter.paratools.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=181"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/learningcenter.paratools.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=181"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}