224 lines
7.1 KiB
C
224 lines
7.1 KiB
C
|
/*gdb-tutorial.c, Matthieu Moy pour ENSIMAG - Modif by LG pour Polytech Lille*/
|
|||
|
#include <stdio.h>
|
|||
|
|
|||
|
void f(void);
|
|||
|
void calcul_et_imprime_fact(int n);
|
|||
|
int fact(int n);
|
|||
|
|
|||
|
/* Ce fichier est un tutorial pas-<2D>-pas pour utiliser le d<>boggeur gdb.
|
|||
|
Un d<EFBFBD>bogueur est un outil pr<EFBFBD>cieux pour comprendre ce qui ne va pas
|
|||
|
dans vos programmes. Les quelques minutes que vous allez passer <EFBFBD>
|
|||
|
apprendre <EFBFBD> l'utiliser seront largement rentabilis<EFBFBD>es par la suite !
|
|||
|
|
|||
|
Commencez par le compiler. Pour pouvoir utiliser gdb, il faut
|
|||
|
utiliser l'option -g de votre compilateur <EFBFBD> la compilation :
|
|||
|
|
|||
|
$ clang -g gdb-tutorial3.c -o gdb-tutorial
|
|||
|
(clang vous met un warning que vous ignorerez).
|
|||
|
|
|||
|
Vous pouvez lancer l'ex<EFBFBD>cutable gdb-tutorial dans gdb en ligne de
|
|||
|
commande comme ceci :
|
|||
|
|
|||
|
$ gdb ./gdb-tutorial
|
|||
|
|
|||
|
Commen<EFBFBD>ons par quelque chose de simple : d<EFBFBD>marrer le programme. On
|
|||
|
tape "run", ou simplement la lettre "r" dans la ligne de commande
|
|||
|
gdb (dans votre terminal) L'ex<EFBFBD>cution s'arrete sur un "segmentation
|
|||
|
fault <EFBFBD> la ligne 50 de ce fichier, on d'y retrouve !*/
|
|||
|
|
|||
|
int main (int argc, char ** argv) {
|
|||
|
unsigned i;
|
|||
|
int j;
|
|||
|
int * ptr = NULL; /* NULL est une adresse invalide, on n'a pas le
|
|||
|
droit de d<EFBFBD>r<EFBFBD>f<EFBFBD>rencer un pointeur NULL */
|
|||
|
printf ("Bonjour\n");
|
|||
|
printf ("Je vais faire un acces memoire interdit\n");
|
|||
|
/*
|
|||
|
La ligne suivante va faire un "Segmentation fault".
|
|||
|
|
|||
|
Le d<EFBFBD>boggeur va s'arr<EFBFBD>ter sur cette ligne.
|
|||
|
*/
|
|||
|
*ptr = 0;
|
|||
|
/*
|
|||
|
Bon, le bug <EFBFBD>tait facile <EFBFBD> trouver. Mettez la ligne ci-dessus en
|
|||
|
commentaire pour <EFBFBD>liminer le probl<EFBFBD>me, recompilez, et lisez la
|
|||
|
suite.
|
|||
|
On va maintenant faire une execution pas-<EFBFBD>-pas. Il faut commencer
|
|||
|
par donner <EFBFBD> gdb un endroit du code o<EFBFBD> on veut
|
|||
|
s'arr<EFBFBD>ter : un point d'arr<EFBFBD>t.
|
|||
|
|
|||
|
Dans la ligne de commande gdb : "break gdb-tutorial3.c:55"
|
|||
|
|
|||
|
puis "run"
|
|||
|
|
|||
|
*/
|
|||
|
printf ("On avance ...\n"); /* mettre le point d'arr<72>t ici */
|
|||
|
/*
|
|||
|
Maintenant, vous devriez avoir votre programme arr<EFBFBD>t<EFBFBD> sur la ligne
|
|||
|
qui pr<EFBFBD>c<EFBFBD>de. Tapez "next", ou simplement "n" pour avancer dans
|
|||
|
l'execution du programme, plusieurs fois. Petite astuce, apr<EFBFBD>s
|
|||
|
avoir tap<EFBFBD> "n" une fois, il suffit d'appuyer sur Entr<EFBFBD>e pour
|
|||
|
refaire un "next".
|
|||
|
*/
|
|||
|
printf ("On avance encore ...\n");
|
|||
|
printf ("On avance encore un peu ...\n");
|
|||
|
printf ("On avance encore un peu ...\n");
|
|||
|
printf ("On avance une derniere fois ...\n");
|
|||
|
printf ("Et voila !\n");
|
|||
|
/*
|
|||
|
Il y a deux commandes pour executer un programme pas <EFBFBD> pas :
|
|||
|
- next, pour ex<EFBFBD>cuter compl<EFBFBD>tement l'instruction suivante
|
|||
|
- step, "rentre" dans la fonction si l'instruction suivante est un
|
|||
|
appel de fonction.
|
|||
|
Essayez successivement "step" et "next" sur les lignes suivantes.
|
|||
|
Au passage "up" permet de revenir un cran plus haut dans la pile d'exec
|
|||
|
*/
|
|||
|
f(); /*step 1 fois, puis next pour avancer a l'interieur de f*/
|
|||
|
f();
|
|||
|
f();
|
|||
|
/*
|
|||
|
Bon, l'execution pas <EFBFBD> pas, c'est bien, mais c'est p<EFBFBD>nible quand
|
|||
|
il y a beaucoup de code <EFBFBD> ex<EFBFBD>cuter. Par exemple, la boucle
|
|||
|
suivante prendrait trop de temps, on veut la sauter. Pour cela,
|
|||
|
poser un point d'arr<EFBFBD>t sur la ligne qui suit la boucle (98), puis
|
|||
|
faites "continue" (ou juste "c") dans gdb.
|
|||
|
*/
|
|||
|
j = 0;
|
|||
|
for (i = 0; i <= 1000; ++i) {
|
|||
|
j = j + i;
|
|||
|
}
|
|||
|
printf ("Fin de la boucle\n"); /* mettre le point d'arr<72>t ici */
|
|||
|
/*
|
|||
|
On peut bien s<EFBFBD>r visualiser le contenu d'une variable,
|
|||
|
avec la commande "print", ou simplement "p". On peut
|
|||
|
maintenir l'affichage d'une variable avec "display".
|
|||
|
|
|||
|
Par exemple, faites maintenant "display i", puis ex<EFBFBD>cutez la
|
|||
|
boucle suivante pas <EFBFBD> pas. Faites "p j" pour connaitre la valeur
|
|||
|
de j. Ensuite mettez un breakpoint apres la boucle (ligne 130)
|
|||
|
et "c" pour y aller.
|
|||
|
*/
|
|||
|
|
|||
|
for (i = 1; i <= 1000; i = i * 2) {
|
|||
|
j = j + i;
|
|||
|
}
|
|||
|
/*
|
|||
|
On peut aussi afficher des expressions C un peu plus complexes.
|
|||
|
Essayez par exemple
|
|||
|
(gdb) p i+j
|
|||
|
(gdb) p &i
|
|||
|
(gdb) p &j
|
|||
|
(gdb) p dire_bonjour("Monsieur")
|
|||
|
|
|||
|
(dans le dernier cas, gdb va effectivement appeler la fonction
|
|||
|
dire_bonjour, d<EFBFBD>finie plus bas)
|
|||
|
|
|||
|
On peut aussi s'amuser un peu avec les pointeurs. Faites:
|
|||
|
|
|||
|
(gdb) p &i
|
|||
|
$2 = (unsigned int *) 0xbf9bfde8
|
|||
|
|
|||
|
La valeur 0xbf9bfde8, adresse de i, sera a priori diff<EFBFBD>rente chez
|
|||
|
vous. Affichez maintenant l'expression *(unsigned int *)0xbf9bfde8
|
|||
|
(en rempla<EFBFBD>ant 0xbf9bfde8 par la valeur que vous avez eu ci-dessus).
|
|||
|
Vous devriez avoir 1024, qui est bien la valeur de i.
|
|||
|
|
|||
|
Avant de passer <EFBFBD> la suite, on va supprimer cette impression de i
|
|||
|
display, puis undisplay n avec n le num<EFBFBD>ro du display concernant i
|
|||
|
|
|||
|
*/
|
|||
|
printf ("i=%d\n", i);
|
|||
|
|
|||
|
/*
|
|||
|
Tout <EFBFBD>a se complique un peu quand on a des appels de fonctions.
|
|||
|
Entrez dans la fonction fact appel<EFBFBD>e <EFBFBD> la ligne suivante avec la
|
|||
|
commande "s", on se retrouve l<EFBFBD> bas !
|
|||
|
*/
|
|||
|
calcul_et_imprime_fact(10);
|
|||
|
|
|||
|
/*
|
|||
|
Une derni<EFBFBD>re chose : il arrive que le programme parte dans une
|
|||
|
boucle infinie, et on voudrait savoir o<EFBFBD> est cette boucle.
|
|||
|
|
|||
|
Rien de plus simple : lancez le programme ("c" pour "continue"),
|
|||
|
puis tapez Control-c pour arr<EFBFBD>ter son execution et reprendre la
|
|||
|
main dans gdb (dans votre terminal)
|
|||
|
|
|||
|
Utilisez cette technique pour voir la(les)quelle(s) de ces boucles
|
|||
|
est/sont une(des) boucle(s) infinie(s). Commentez les boucles infinies et
|
|||
|
recompilez avant de passer <EFBFBD> la suite.
|
|||
|
*/
|
|||
|
j = 1;
|
|||
|
while (j > 0) {
|
|||
|
++j;
|
|||
|
}
|
|||
|
|
|||
|
i = 10;
|
|||
|
while (i >= 0) {
|
|||
|
--i;
|
|||
|
}
|
|||
|
|
|||
|
i = 0; j = 0;
|
|||
|
while (i == j) {
|
|||
|
++i; ++j;
|
|||
|
}
|
|||
|
/* Voil<69>, vous avez termin<69> et vous savez <20> peu pr<70>s tout ce que vous
|
|||
|
devez savoir pour une utilisation courante. Les plus curieux
|
|||
|
pourront lire le manuel de gdb :
|
|||
|
http://sourceware.org/gdb/download/onlinedocs/ */
|
|||
|
}
|
|||
|
|
|||
|
void f(void) {
|
|||
|
printf ("Appel de f()\n");
|
|||
|
printf ("Fin de f()\n");
|
|||
|
}
|
|||
|
|
|||
|
void calcul_et_imprime_fact(int n) {
|
|||
|
/*
|
|||
|
Re-bonjour,
|
|||
|
|
|||
|
<EFBFBD> ce stade, n doit <EFBFBD>tre <EFBFBD>gal <EFBFBD> 10. V<EFBFBD>rifiez avec un "p n". Entrez
|
|||
|
dans la fonction fact avec un "step", on s'y retrouve.
|
|||
|
*/
|
|||
|
int res = fact(n);
|
|||
|
printf ("fact(%d) = %d\n", n, res);
|
|||
|
}
|
|||
|
|
|||
|
int fact(int n) {
|
|||
|
//printf ("Calcul de fact(%d)\n", n);
|
|||
|
/*
|
|||
|
Si c'est la premi<EFBFBD>re fois que vous passez ici, n doit <EFBFBD>tre <EFBFBD>gal <EFBFBD>
|
|||
|
10. V<EFBFBD>rifiez avec un "p n". Continuez l'ex<EFBFBD>cution pas <EFBFBD> pas avec
|
|||
|
"step" (et non "next", pour rentrer dans les appels r<EFBFBD>cursifs
|
|||
|
successifs de fact). Faites par exemple 5 appels puis lisez le
|
|||
|
commentaire suivant.
|
|||
|
*/
|
|||
|
|
|||
|
/*
|
|||
|
Voil<EFBFBD>, je suppose que vous en <EFBFBD>tes au 5<EFBFBD>me appel r<EFBFBD>cursif. Donc,
|
|||
|
"n" doit valoir 5. V<EFBFBD>rifiez avec "p n".
|
|||
|
|
|||
|
Mais dans la pile, il y a toujours les autres appels, avec les
|
|||
|
valeurs de "n" comprises entre 10 et 5. Faites "where" pour voir
|
|||
|
l'<EFBFBD>tat de la pile.
|
|||
|
|
|||
|
Pour naviguer dans la pile, utilisez les commandes "up" et "down".
|
|||
|
Par exemple, faites deux fois "up", puis "p n". Vous voyez la
|
|||
|
valeur de "n" au 3<EFBFBD>me appel, c'est <EFBFBD> dire 7.
|
|||
|
|
|||
|
Faites deux fois "down", et vous voil<EFBFBD> revenu o<EFBFBD> vous <EFBFBD>tiez.
|
|||
|
Faites maintenant "finish" pour terminer
|
|||
|
l'appel de fonction courant, plusieurs fois, jusqu'<EFBFBD> revenir <EFBFBD> la fonction
|
|||
|
"main" (ligne 167)
|
|||
|
*/
|
|||
|
if (n <= 1) {
|
|||
|
return 1;
|
|||
|
} else {
|
|||
|
return n * fact(n - 1);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void dire_bonjour(char * nom) {
|
|||
|
printf("Bonjour, %s\n", nom);
|
|||
|
}
|