Code : stratégies de débogguage

Parfois, lorsqu’un sketch tourne, c’est utile d’afficher des informations complémentaires afin de montrer si des parties de notre code tourne comme prévu. Le cas c’est produit récemment avec un étudiant de 4e année qui faisait du tracking avec un Kinect, la visualisation de son sketch ne donnait pas le résultat escompté lors des interactions. Mais où se trouvait le problème ?

Pour les besoins de ce code, on va supposer que kinet.getHand() invoque une librairie externe qui récupère les informations formattées venant du Kinect 60 fois par seconde. Que la classe kinect peut détecter une main et retourner la forme détecter, ainsi que le rectangle qui enferme cette forme de main. Et que la fonction rectInRect() retourne true s’il y a recouvrement entre les deux rectangles, et false sinon. Mais en fin de compte, ces details ne sont pas importantes.

Si les collisions ne semblent pas fonctionner, nous avons besoin, tout d’abord de vérifier que les informations arrivent de la part du Kinect.

Dans ce cas, on devrait voir un restangle rouge translucide à l’écran et qui devrait suivre la position de notre main. Si nous ne voyons pas ceci, soit le Kinect, soit la librairie ne fonctionne pas prévu, et nous rechercher ailleurs.

Pendant que nous travaillons sur notre sketch, nous allons rajouter et enlever ce bout de code à plusieurs reprises, probablement en utilisant des commentaires pour le rendre inactif.

Or, il existe une manière plus simple.

Il y a plusieurs choses à voir dans ce code. Le premier est l’emploi d’un constant debug. Différents langages de programmation gèrent les constants de la même manière, mais le principe reste pareil — une fois que ce genre particulier de variable a été défini, il ne peut plus être changé par la suite, le variable reste constant… Ceci possède au moins deux avantages. Primo, on ne peut changer sa valeur par accident ailleurs dans notre code, ce qui peut se produre quand le code devient plus complexe. Deuxio, il y a généralement un petit avantage de vitesse, car lors de l’exécution du programme [ou sketch] l’environnement n’a pas besoin de vérifier si le variable a changé de valeur.

Dans Processing, le mot clé final permet d’indiquer que ceci est la valeur finale, et donc ne peut pas changer. Tout ce qu’on peut demander d’un constant.

Lors de l’exécution de notre code, on utilise une condition — si debug est vrai, alors exécute le code suivant, sinon on le saute. Ceci permet de garder des bouts de code de débogguage en place dans notre code, puis de les activer/désactiver en changeant la valeur de notre constant — soit true, soit false.

À noter aussi, comme ceci est du code temporaire, je n’applique pas l’indentation habituelle du code, je reviens sur la marge gauche. Ceci me permet, quand je regarde le code dans sa version finale, de savoir ce qui relève du code fonctionnel — ça suit l’indentation habituelle — ou du code temporaire, typiquement du débogguage — l’indentation revient sur la marge gauche. Et je peux l’éliminer sans souci du code final.

Allant plus loin avec des drapeaux…

J’ai commencé la programmation il y a très longtemps, où les ordinateurs avaient beaucoup moins de mémoire — mon premier avait 1K — 1024 octets! Mon préféré, un Apple //c, avait 128K soit 128 x 1024 octets. Autant dire rien. Non seulement je codais en Assembleur — une réprésentation symbolique des instructions du microprocesseur — mais je devais apprendre des astuces pour gagner de la place en mémoire. En, comme en C, nous devons gérer et, plus important, libérer l’espace mémoire manuellement.

Une des techniques est d’utiliser des drapeaux — aujourd’hui on dirait un boolean — un simple variable qui peut avoir deux états, et dans le fil du programme, comme pour la gestion du debug ci-haut, on sélectionne tel ou tel chemin dans le code. Mais chaque drapeau devait normalement prendre un octet en mémoire. Ce qui est très gaspilleur quand on pense qu’un octet peut encoder 8 bits, et que, fondamentalement, un boolean n’est qu’un bit. Ainsi, la logique nous disait que sur un octet on pouvait encoder 8 bits, donc 8 drapeaux.

Si on regarde un octet en mémoire, on peut le répresenter comme un tiroir à 8 cases. Chaque case peut contenir soit zéro, soit un. SI c’est un, le drapeau est mis.

Mais comment les mettre en place, et comment les lire par la suite ?

La réponse se passe par ce qu’on appèle des opérateurs bitwise, c’est-à-dire qu’au lieu de regarder la logique boolean [vrai ou faux], ces opérateurs regardent l’état des bits…

Exemple :

Résultat : "pas de correspondance"

Mais si nous changeons la comparaison…

Résultat : "correspondance"

La valeur 1 se lit en bits comme 00000001

La valeur 2 se lit en bits comme 00000010

La valeur 3 se lit en bits comme 00000011

Ainsi on peut voir que dans la réprésentation binaire de 1 et 2 il n’y a pas de bit à ’1’ qui soit placé en même endroit. D’où le résultat « pas de corresdonce ». Mais pour la réprésentation binaire de 1 et de 3 l’emplacement le plus à droite est égale à 1 pour les deux. On peut donc affirmer que
un & trois égale un
dans la logique des bits.

Et ce que nous venons de voir ici l’emploi d’un drapeau…

Et comment ça nous aide ?

Supposons que nous travaillons sur un sketch avec trois composants principaux — audio, une librairie externe et le rendu du kinect sur l’écran — et que, par moments, nous devons activer ou désactiver le code de débogguage correspondant à chaque.

ensuite, il suffit de décider à chaque fois ce que avons besoin de débogguer

ou

ou

et, si on veut afficher le code de débogguage de plus d’une partie à la fois, on peut cumuler les drapeau au moment de l’initialisation de notre constant en utilisant l’opérateur OR.

dans ce cas, les codes de débogguage pour debug_ecran et debug_son seront activés.

Et ensuite ?

Ce qui pourrait être intéressant serait de lire le clavier pendant le déroulement d’un sketch, et selon la touche enfoncée — S pour son, K pour Kinect, et E pour écran — d’activer ou désactiver les parties correspondants du code.

Allez-y, proposer du code pour le faire et poster-le ici..

2 réflexions au sujet de « Code : stratégies de débogguage »

  1. On peut utiliser des char au lieu des int, ainsi que les fonctions keyPressed & keyReleased pour récupérer l’event « presser une touche du clavier » :

    char debug_son = ‘s’;
    char debug_ecran = ‘e’;
    char debug_kinect = ‘k’;
    char debug = ‘0’; // debug desactivé

    void keyPressed(){
    debug=key;
    }
    void keyReleased() {
    debug=’0′;
    }

    Le skecth processing pour tester :
    lab.victorien.net/debug.zip

  2. Je ne comprends pas vraiment l’intérêt de la méthode des drapeaux car le premier sketch ci-dessous n’utilise pas cette méthode et pèse 274 octets, alors que le second qui l’utilise pèse 463 octets, sachant que les deux font exactement la même chose.
    De plus, je ne comprends pas pourquoi (1 & 3) == 1 renvoi « correspondance » mais (1 & 3) == 3 renvoi « pas de correspondance ».

    Premier sketch :

    final char debug_son = ‘s’;
    final char debug_ecran = ‘e’;
    final char debug_kinect = ‘k’;
    int debug = 0;

    void draw() {
    if(keyPressed) {
    if(key == ‘s’) {
    println(« son »);
    }
    if(key == ‘e’) {
    println(« ecran »);
    }
    if(key == ‘k’) {
    println(« kinect »);
    }
    }
    }

    Deuxième sketch :

    final int debug_son = 1;
    final int debug_ecran = 2;
    final int debug_kinect = 4;
    int debug = 0;

    void draw() {
    if((debug & debug_son ) == debug_son) {
    println(« son »);
    }
    if((debug & debug_ecran ) == debug_ecran) {
    println(« ecran »);
    }
    if((debug & debug_kinect ) == debug_kinect) {
    println(« kinect »);
    }

    if(keyPressed) {
    if(key == ‘s’) {
    debug = 1;
    }
    if(key == ‘e’) {
    debug = 2;
    }
    if(key == ‘k’) {
    debug = 4;
    }
    }
    }

Les commentaires sont fermés.