vendredi 17 octobre 2014

This is the end ...

C'est la fin de 10 ans d'enseignement et de recherche à l'ESIEA... mais c'est aussi un nouveau début.

Merci à tous et toutes, j'ai appris tant de choses, y compris de la part des étudiants.

Chers étudiants je vous dis au revoir, bon courage et bonne chance. Je sais que vous allez "assurer" ;) .

Cher lecteurs je vous dit à bientôt, ce blog continue d'exister, et je continuerai de l'alimenter avec les sujets qui me passionnent (algorithmique, machine learning, traitement du langage, traitement du signal, complexités etc..) comme je le faisais avant.

Vous pouvez aussi me suivre sur Twitter :       sur LinkedIN ou google+ (voir à  droite de cette page.).

La suite professionnelle c'est chez A/B tasty, et j'ai hâte de découvrir de nouveaux challenges.

jeudi 9 octobre 2014

Dernier cours de mise à niveau

Débugger mémoire


Aujourd'hui, pour notre dernier cours, je vais vous montrer pourquoi et comment utiliser un debugger mémoire. Nous utiliserons valgrind.

Pourquoi ?

Le comportement d'un programme qui fait des erreurs mémoire est imprévisible, il est donc très très difficile à débugger. Principalement parce que les erreurs ne seront visible que rarement, et que le message d'erreur sera uniquement "segmentation fault" (sous entendu "erreur de segment mémoire") avec un arrêt prématuré de l'exécution du programme. Tout cela ne vous aidera pas trouver le problème et encore moins à le corriger...
Si vous voulez produire un programme sûr et fiable, vous devez utilisez un debugger mémoire qui surveillera l'utilisation de la mémoire pour identifier les erreurs dès la première exécution du programme.

Comment ?

Valgrind va surveiller l'utilisation de la mémoire faite par votre programme lors de son exécution (ce que ne fait pas le compilateur!).

Pour cela nous allons d'abord devoir ajouter une option lors de la compilation, l'option -g :

       gcc monCode.c -o monExecutable -g

Ceci aura pour effet de garder l'information des numéros de ligne du code source dans l'exécutable.
Ensuite pour l'exécution du programme nous allons préfixer son appel par la commande valgrind :

       valgrind ./monExecutable
     
Le programme se déroule alors exactement comme normalement, sauf que toute utilisation de la mémoire sera pistée, et en cas d'erreur, un rapport sera fait dans le terminal. Ce qui fait que le programme s'exécutera un peu plus lentement. En cas d'erreur, valgrind sera capable de retrouver les numéros de ligne du code source correspondant aux instruction qui posent problème.
Voyez le texte sur cette page : http://valgrind.org/docs/manual/quick-start.html#quick-start.intro
pour comprendre comment interpréter ce rapport.

L'interprétation de ces message n'est pas chose simple au départ, il faut un peu d'exercice.
Je vous propose donc de faire un petit programme dans lequel vous ferez successivement les différents type d'erreurs mémoire que peut détecter valgrind.
À chaque fois vous compilerez le programme, vous constatez que la nature de l'analyse statique faite par le compilateur est incapable de déceler les erreurs. Puis vous l'exécuterez avec valgrind, et vous chercherez à faire le lien entre le rapport produit par valgrind et l'erreur que vous connaissez (puisque vous l'avez faites exprès.). Voici les différents cas  à tester :
  • Utilisation d'une zone mémoire non-initialisée. Faites un malloc puis affichez le contenu de la zone mémoire en question (sans jamais y avoir mi quelque chose au préalable).
  • Lecture d'une zone mémoire non allouée : utilisez un pointeur jamais alloué et affichez son contenu.
  • Même chose que la question précédente mais en écriture. 
  • Dépassement de tableau : allouez un tableau avec malloc, puis utilisez une case qui est au delà de la zone allouée. Exemple créez un tableau de 10 cases et écrivez dans la case No 12.
    Note : valgrind ne surveille pas les allocations statiques (avec les []), encore une raison supplémentaire pour ne plus les utiliser,et faire des malloc.
  • Mettez l'une des erreurs précédentes dans une boucle. Vous constaterez le coté dynamique de l'analyse proposée par valgrind.
  • Après avoir corrigé l'erreur de la question précédente, essayez de voir où est spécifié dans le rapport que vous n'avez pas fait la désallocation du tableau. (Si vous aviez mis un free, je vous félicite ;) , mais , pour l'expérience, enlevez le.). Cela s'appelle une fuite mémoire, si votre programme en fait trop il peut sérieusement ralentir la machine sur laquelle il tourne. (souvenez vous de l'expérience faites lors d'un cours précédent où nous avions occupé toute la mémoire d'un ordinateur...)

Mise en pratique en situation réelle

Voilà maintenant vous êtes en capacité de tenter un vrai débuggage avec valgrind. Prenez votre programme de jeu de pendu. Je vous rappelle que vous deviez l'avoir finalisé et avoir replacé toutes les allocations [] par des malloc (si ce n'est pas fait il très est urgent de le faire). Testez le avec valgrind, il est assez vraisemblable qu'il y ait des erreurs même si votre programme n'a jamais planté...  Débuggez le !
Pensez aussi à vérifier que vous désallouez bien la mémoire en fin de programme.

Si votre programme ne comporte aucune erreur ni aucune fuite mémoire, demandez à ce qu'un de vos camarades vous passez le sien (comportant des erreurs) et débuggez le.

Il est très important que vous vous soyez entraîné à utiliser cet outil dans un contexte simple, avant de vous retrouver dans la situation de devoir débugger un programme complexe.

Conclusion

Nous n'avons pas pu tout voir dans ce cours de mise à niveau, mais nous aurons vu quelques bases qui vous permettrons d'aborder la suite du programme d'informatique.

Voilà j'espère que vous avez apprécié ces cours. Je vous souhaite bon courage pour la suite.

mercredi 1 octobre 2014

Mise à niveau informatique : cours 5

Preambule

Il nous resterait encore bien des choses à voir ensemble mais on arrive à la fin de cette série de cours. À partir de maintenant nous allons à l'essentiel dans le contexte du projet sur lequel vous allez être noté (LAB3040). Le reste des points théoriques vu en deuxième année seront à travailler en autonomie à partir des support de cours du semestre 1 (que nous avons déjà pas mal utilisé) et semestre 2 (qui est totalement nouveau pour vous!).

Pour ce projet vous aurez besoin de savoir gérer des matrices et lire le contenu d'un fichier, c'est ce que nous allons voir aujourd'hui (mardi 7 octobre).

Les matrices

Les matrices, ou plus généralement les tableaux multidimensionnels, sont une extension de la notion de tableau. Nous allons voir  dans le support de cour du semestre 1 comment les allouer, les utiliser et les désallouer.
Pour vous entraîner faites un (nouveau) programme qui :
  • allouez un tableau à 2 dimensions (10x10 cases) contenant des nombres entiers. (via la fonction malloc bien sûr.)
  • initialisez son contenu, chaque case [i][j] devra contenir la somme i+j. C'est juste histoire d'avoir un contenu pour vérifier que tout marche bien. En partique, dans votre TP par exemple vous y stockerez des informations relative à votre labyrinthe.
  • affichez son contenu
  • désallouez cette matrice

Les fichiers

Vous ne trouverez pas d'information sur les fichiers dans les deux support de cours car ce sujet est vu en TP. Nous allons donc le voir ensemble aujourd'hui.

La première chose à comprendre sur les fichiers c'est qu'il y a 3 étapes principales lors de l'utilisation d'un fichier, un peu comme pour l'utilisation de la mémoire.
  • l'ouverture (c'est l'équivalent de l'allocation pour la mémoire). Cela se fait via l'instruction fopen. Tapez "man fopen" dans un terminal, et voyons cela ensemble.
  • l'utilisation, c'est à dire lire ou écrire dans le fichier
    • en mode texte : fprintf et fscanf
    • en mode binaire : fwrite et fread
  • la fermeture (c'est l'équivalent de la désallocation pour la mémoire), grâce à l'instruction fclose.
Notez que tout programme dispose de deux pointeurs de fichier , sans avoir ni à les déclarer ni à les ouvrir : stdin et stdout. Il s'agit des entrées et sorties standard du programme. Par défaut l'entrée standard d'un programme est le clavier , et la sortie standard est l'affichage dans le terminal.

Ces entrées/sorties peuvent être redirigés vers des fichiers lors de l'exécution du programme.
Par exemple : ./monProgramme < mesDonnes.txt > monResultat.txt
Dans ce cas les appels à scanf lirons les données dans le fichier mesDonnes.txt (au lieu de les attendre sur le clavier), et les appels à printf écriront les données dans le fichier monResultat.txt (au lieu de les afficher dans le terminal).

Mettons cela en pratique :
  • Modifiez votre programme de création de matrice de manière à ce qu'il écrive la matrice dans un fichier au lieu de l'afficher dans le terminal. Pour cela utilisez la fonction fprintf. Regardez le fichier crée.
  • Maintenant modifiez votre programme de manière à enregistrer les données non plus en mode texte mais en binaire via l'instruction fwrite (au lieu de fprintf).  Regardez le fichier crée, pourquoi ne pouvez vous pas le lire?
  • Faites maintenant un autre programme qui ira lire (avec fread) le fichier binaire crée à la question précédente. Faites un affichage des valeurs pour vous assurer que tout fonctionne.
Le prochain cours le 14 octobre , sera notre dernier cours de mise à niveau!
(et accessoirement aussi mon derniers cours à l'ESIEA ! après 10 ans de bons et loyaux services.)

Nous apprendrons à utiliser un debugger mémoire : valgrind. Cet outil est installé sur les machines de TP (et je vous conseille fortement de l'installer sur votre machine). Nous testerons alors tous les programmes que vous aurez écrit jusqu'ici. Vous comprendrez alors qu'il est illusoire de croire qu'on peut écrire un programme sans erreur, si on utilise pas un tel outil...