This repository has been archived on 2019-08-09. You can view files and clone it, but cannot push or open issues or pull requests.
s6-pa-tp/TP6/gdb-tutorial2.c

224 lines
7.1 KiB
C
Raw Normal View History

2017-03-27 15:59:08 +02:00
/*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);
}