*Title: Outils utiles pour coder *Author: Nicolas Haller *TOC * Introduction Pour coder au Robot et en général, il existe certain outils, certain sont indispensables. Je vais ici montrer comment fonctionnent quelques outils que j'utilise personnellement, le but n'étant pas de faire un guide complet mais plutôt une présentation assez détaillée pour les utiliser. * Editeurs ** Vim Vim est un éditeur de texte très puissant qui peut faire beaucoup de chose dont du C. *** Fonctionnement Taper vim dans un bash. Attention, pour coder en C il est nécessaire de modifier quelques options dans le .vimrc, notamment : * _syntax on_, pour avoir de la couleurs en codant ; * _set shiftwidth=4_, pour une indentation de 4 caractères (voir les standards du robot) ; * _set cinoptions={.5s:.5sg.5sh.5st0(0=.5s_, pour le style Efrei Robotique. *** Quelques commandes Pour vim et pour coder, quelques commandes : * :q quitter * :x sauvegarder et quitter * :w enregistrer * :e ouvrir * == pour indenter une ligne * =G indente du curseur à la fin du fichier * :make exécute le makefile et affiche les erreurs éventuelles * :cc aller à l'erreur actuelle * :cn aller à l'erreur suivante * :copen ouvre la liste des erreurs * :cclose la ferme Y'en a plein d'autre mais si vous utilisez vim, elles viendront vite. * Compilateurs ** Pour le C Pour coder en C, gcc, le GNU C Compiler. C'est un compilateur très puissant avec un beaucoup d'options. ** Pour le C++ Pour coder en C il faut juste remplacer gcc par g++. ** Comment ça marche Il suffit de taper gcc (ou g++) et le nom du fichier source (on ne met pas les headers). Si il n'y a qu'un seul fichier source, ça marche, et il y aura comme résultat un fichier a.out, bon a part pour faire un Hello World, ça sert à rien, pour des projets plus conséquent, le Makefile est tout indiqué. Si la compilation foire, le compilateur fera en sorte d'afficher les erreurs avec le nom des fichiers et les lignes où se trouve à 3 lignes près l'erreur. Il se peut aussi qu'il annonce des warnings, c'est pas bon !! Les warnings portent bien leurs noms, et généralement, laisser des warnings dans un programme est suicidaire, donc, à éviter. ** Quelques options Dans la ligne de commandes gcc, il y a quelques options et librairies indispensables : * -o permet de nommer le fichier exécutable, sinon il s'appelle a.out ; * -W et -Wall pour voir tout les warnings qui pourrait passer ; * -g permet d'ajouter les info de debuggage ; * -Olevel Permet d'optimiser le programme, plus level est grand plus c'est optimiser (level 3 max); * -Os permet d'optimiser le code pour obtenir un exécutable plus petit ; * -lm permet d'inclure la librairie mathématique ; * make La commande make est une commande très puissante qui fait ce qu'on lui dit de faire. Elle est intelligente pour certaine action mais pour des choses un peu poussé, elle regarde dans un Makefile ce quel doit faire. C'est avec elle que sont compiler la plupart des programmes. ** Comment ça marche Taper make et si il y a un makefile qui va bien ça s'exécute. make tente de faire de la divination, pour un programme composé d'un code source, il suffit de taper make nomdufichiersansextension. Sinon, l'écriture d'un makefile est obligatoire. ** Le makefile Le makefile s'appelle Makefile (n'oublier pas la majuscule, c'est mieux). Dans le fichier ont trouve des variables et des règles. Les variables sont définies et utilisées comme dans les scripts bash, mais certaines variables sont standards et nous serviront plus tard. Pour faire simple, un makefile pour compiler un programme en C ressemble à ça. ^<< barbecue: main.o merguez.o thermostat.o gcc -o barbecue main.o merguez.o thermostat.o ^>> barbecue est le nom de la cible, main.o merguez.o thermostat.o les "dépendances", la ligne du dessous qui commence obligatoirement par une tabulation est la ou les actions à exécuter. make étant très intelligent, il va essayer de voir si il peut résoudre les dépendances, ici, il va compiler les sources pour produire les .o requis. Cela dit pour un projet plus important, le Makefile peut contenir plusieurs cibles et plusieurs actions, pour éviter de réécrire les choses plusieurs fois, il y a les variables. Les variables fonctionnent comme pour les scripts bash à peu près. Pour reprendre l'exemple au dessus on pourrait marquer ^<< OBJECTS=main.o merguez.o thermostat.o barbecue: ${OBJECTS} gcc -o $@ $^ ^>> Les fichiers object sont dans la variable OBJECTS qui est utilisé dans la ligne de commande gcc, $@ est une variable spéciale qui correspond au nom de la cible et $^ en est une autre indiquant les dépendances. Avec ça, make peut compiler un projet entier, comme il est très intelligent, il regardera ce qu'il a besoin de faire, c'est-à-dire qu'il ne recompilera ce qui est nécessaire, faite le test, compiler quelque chose avec make et refaite le. ^<< nicolas@hermes:~/testmake$ ls casertarien.c nicolas@hermes:~/testmake$ make casertarien cc casertarien.c -o casertarien nicolas@hermes:~/testmake$ make casertarien make: « casertarien » est à jour. ^>> L'exemple précédent montre également une chose, c'est ce qu'exécute make lorsqu'on ne lui dit rien. On voit qu'il utilise cc comme compilateur et qu'il n'utilise rien. Vous allez me dire, m'en fiche je fais un Makefile, certes, mais les dépendances en .o, il les faits tout seul donc, il faut lui dire quoi faire, et pour cela, il existe les variables spéciales. ** Quelques variables utiles Pour dire à make comment faire ses trucs intelligents, certaines variables sont bien utiles, en voici quelques une : * CC permet de définir le compilateur C (exemple: CC=gcc) ; * LDLIBS permet de définir les librairies à utiliser (exemple:LDLIBS=-lm); * CFLAGS qui permet de définir les flags (exemple CFLAGS=-g -W -Wall). Pour le C++ il y a les équivalents * CXX pour le compilo C++ ; * CXXFLAGS pour les flags C++. Mais il en existe bien d'autre. En définitive, notre Makefile pour un programme en C pourrait ressembler à ça: ^<< CC=gcc LDLIBS=-lm CFLAGS=-g -W -Wall -pipe barbecue: main.o merguez.o thermostat.o ${CC} ${LDLIBS} ${CFLAGS} -o $@ $^ ^>> Et on aura à l'écran: ^<< nicolas@hermes:~/testmake/barbecue$ make gcc -g -W -Wall -pipe -c -o main.o main.c gcc -g -W -Wall -pipe -c -o thermostat.o thermostat.c gcc -g -W -Wall -pipe -c -o merguez.o merguez.c gcc -lm -g -W -Wall -pipe -o barbecue main.o thermostat.o merguez.o ^>> Voila un beau Makefile qui comme le dirais Ni, permet de briller en société. * Le débugueur Pour debuguer le jolie code qui marche pas comme prévu, il y a le débuggueur GDB (pour GNU Debbuger, ouais ils ont vachement d'imagination). Pour l'utiliser, il est utile de sortir le -g lors de la compilation du programme foireux. GDB permet plusieurs chose comme examiner les cadavre (les fichiers core par exemple) ou de s'attacher à un processus. Mais je vais juste expliquer le debuggage d'un programme lancée par gdb. Pour lancer gdb, il faut taper gdb , et ensuite, le programme est prêt à être espionné. Il suffit de taper les commandes qui vont bien pour voir le fonctionnement du programme. ** Quelques commandes Voici quelques fonctions sympa * b ou breakpoint permet de mettre un point d'arrêt, c'est un endroit où le débuggueur va s'arrêter. On peut lui donner le nom d'une fonction ou d'un numéro de ligne. * r ou run lance le programme à debugguer * n ou next permet de passer à l'instruction suivante au même niveau dans la pile(reste dans l'instruction) * s ou step permet de passer à l'instruction suivante à un niveau plus profond de la pile le cas échéant(typiquement les instructions d'une fonction appelé) * c ou continue exécute le programme jusqu'au prochain point d'arrêt * l \[numéro de ligne] ou list \[NDL] permet de lister dix lignes du code source. * p ou print \[variable] permet d'obtenir la valeur d'une variable. la variable est à rentrer sous la forme d'une instruction, donc si il est possible d'appelé une variable temp, il est possible de marquer thermostat->temp ou *temp ou encore temp\[0]. * d ou display marche comme print sauf que l'état de la variable est indiqué à chaque instruction * up qui permet de remonter d'un niveau dans la pile * down qui permet de descendre d'un niveau de la pile * info \[truc], donne des infos sur beaucoup de chose. En tapant * address \[fonction] les adresses des fonctions * stack, on a les info sur la pile * breakpoints qui indique les points d'arrêt définis * q ou quit qui permet de quitter * Programmes contre les fuites de mémoire ** Eletric fence Electric fence est une librairie qui permet d'envoyer un SIGSEGV (erreur de segmentation) dès que l'on lit ou que l'on écrit dans l'espace. *** Comment ça marche Electric fence est une librairie qui a l'avantage de ne pas toucher au code, par contre il faut rajouter la librairie lefence sur la ligne du compilateur. Un désavantage également est qu'une allocation de mémoire prend beaucoup plus de place en mémoire et de temps avec que sans electric fence. Donc en cas de grosse allocation ou d'allocation répété, ça risque de trancher chérie. Electric Fence est à utiliser avec gdb pour connaître la position du segfault et de l'instruction incriminé, car le terminal n'est pas très loquace et renverra juste un message "erreur de segmentation". Personnellement, je n'utilise plus Electric fence que j'ai remplacé par valgrind. ** Valgrind Valgrind est un programme très utiles pour tout ce qui touche à la mémoire. Il a également d'autre fonctions comme le profiling du cache que je ne détaillerais pas. *** Comment ça marche Il suffit d'exécuter le programme en rajoutant valgrind et les options qui vont bien avant le nom du programme. Par exemple ^<< nicolas@hermes:~\valgrind --tool=memcheck --leak-check=yes -v barbecue ^>> La j'utilise l'outil memcheck pour vérifier la mémoire (mais c'est l'option par défaut), j'aurais pu mettre cachegrind pour profiler le cache. leak-check permet d'afficher les fuites de mémoire et v c'est verbose. Il n'est pas obligatoire mais hautement conseillé de compiler le programme avec les infos de debbugage (-g) pour avoir toutes les infos sur la localisation d'un problème. Valgrind note les erreurs mais ne forcera pas l'envoie d'un segfault contrairement à electric fence, par conséquent, ce n'est pas parce que y'a pas segfault qu'il n'y a pas d'erreur grave. *** Qu'elle erreurs Valgrind indique Pratiquement toutes les erreurs liées à la mémoire, c'est à dire: * les tests sur des variables qui ne sont pas initialisés exemple ^<< int main (void) { int p, t; if (p == 5) /* Ho une erreur. */ t = p + 1; return 0; } ^>> * Les lecture/écriture dans l'espace En gros, les tentatives de lecture/écriture sur des espaces de la mémoires qui ne sont pas allouée pour ça. exemple ^<< #include int main (void) { int *p, i, a; p = malloc (10 * sizeof (int)); p[11] = 1; /* Ecriture dans l'espace. */ a = p[11]; /* Lecture dans l'espace. */ free (p); return 0; } ^>> * Les libérations invalides Vous essayez de libérer un blocs qui n'est pas alloué. Exemple: ^<< #include int main (void) { int *p, i; p = malloc (10 * sizeof (int)); for (i = 0; i < 10; i++) p[i] = i; free (p); free (p); /* Oui mais non, p est déjà libéré. */ return 0; } ^>> * Les mauvaises utilisations de fonctions Typiquement, les allocations par malloc qui sont libérées par delete. Exemple: ^<< #include int main (void) { int *p, i; p = (int*) malloc (10 * sizeof (int)); for (i = 0; i < 10; i++) p[i] = i; delete p; /* C'est pas la bonne fonction (non, c'est un opérateur !). */ return 0; } ^>> * Les mauvais paramètres sur des fonctions utilisant la mémoire * Les pertes de mémoires dans le cosmos Typiquement, vous avez perdu le pointeur qui avait l'adresse du bloc alloué. Cela donne lieu à une fuite de mémoire. * Autres documents Ce petit descriptif sur les outils ne sert pas à grand chose seul par conséquent, il est conseillé d'aller voir d'autre page pour s'informer et notamment: * Le tutorial pour "Programmation en C sous Unix" fait par Ni à l'adresse: www.linux.efrei.fr/datadoc/progc/progc.pdf * Pour le robot, les standards de codage disponible dans le cvs et sur http://www.linux.efrei.fr/robot/doc/d/dev/standards/coding.html * Conclusion Voilà quelques outils qui sont très utiles pour développer, si vous en voyez d'autre ou si j'ai écris une bêtise, n'hésitez pas à me le dire à nicolas@popple.dyndns.org.