[Atom] [Mail] [Twitter]
Liens : git · hacks · divers · cabale · buzz! · à propos +
Au menu
\/

Détection automatique de bugs

#. Par Cygal dans Réactions. Mis à jour le 01.03 2010 à 23:29. 7 commentaires.
<. Histoire de sexe et d'informatique
>. Cohérence des effets de bord
analyse_statique bugs inférence réaction

L’Ours & Hippy crew vous présente son second billet feat. Cygal. Sous le regard hippie de blueblue aux commandes des platines d’édition — que l’on reconnaît grâce au manque d’originalité de son titre frigido-sérieux — l’artiste nous a concocté en direct de derrière le studio un nouvel article…
Qui nous parle du chant des insectes déviants,
Leurs pesticides heuristiques,
Et ses recettes hygiéniques,
À inculquer absolument aux ptits enfants !

—minh

Cette réaction traite d’un point qui va finir par ressembler à une obsession de ma part : la correction de bugs. Plus précisément, ce papier traite de la détection automatique de bugs. Le but est d’obtenir du programme assez d’informations pour pouvoir remarquer des comportements anormaux.

☃ : Alors que pas du tout, je suis encore à la recherche d’un fétiche.

C’est d’une certaine manière ce qu’apporte le typage à la compilation : les types apportent suffisament d’informations pour pouvoir détecter un certain nombre d’erreurs avant qu’elles n’apparaissent à l’exécution. L’analyse qui est faite ici est aussi statique, et se base sur un principe général qui peut s’appliquer à un nombre étonnament varié d’erreurs possibles. Les auteurs du papier ont ainsi détecté des centaines de bugs dans les noyaux Linux et BSD, principalement dans des drivers pas toujours maintenus.

Des langages comme OCaml sont capables d’inférer les types utilisés dans un programme, c’est-à-dire les deviner à partir du code. Le principe est ici le même, sauf qu’on essaie d’inférer du sens pour nous aider à trouver des erreurs.

Présentation du papier

Les exemples donnés ici sont directement tirés du papier : Bugs as deviant behavior (PDF, 16 pages).

Le principe est donc d’inférer le maximum d’informations possible dans chaque fonction, et d’essayer de détecter les contradictions présentes. L’existence d’une contradiction entre deux lignes montre qu’il y a forcément une erreur, même si le système ne peut pas forcément savoir de quelle ligne il s’agit.

Le montant d’informations qu’il est possible d’obtenir à partir d’un code source automatiquement est non seulement limité, mais on doit parfois se baser sur des informations peut-être fausses. C’est pourquoi j’ai séparé la présentation en deux courtes parties :

  • une sur ce qui est appelé les « MUST beliefs » dans le papier (on est certain qu’il y a une erreur) ;
  • et une autre sur les « MAY beliefs » (il y a très probablement une erreur).

MUST beliefs

Ainsi, prenons un exemple simple, tiré d’une version du noyau Linux aussi vieille que le papier.

/* 2.4.7 drivers/char/mxser.c */
int mxser_write(struct tty_struct *tty, ...) {
  struct mxser_struct *info = tty->driver_data;
  unsigned long flags;

  if (!tty || !info->xmit_buf)
    return(0);
  ...

Le bug ici vient du déréférencement du pointeur tty alors qu’il pourrait valoir NULL. C’est quelque chose qui peut arriver assez vite si l’on ni porte pas attention, par exemple pendant l’évolution d’un code source. Regardons comment notre programme fonctionne dans ce cas précis.

*info = tty->driver_data
Le pointeur tty est déréférencé, ce qui veut dire que le programmeur estime qu’il ne peut pas être nul.
if (!tty || !info->xmit_buf)
La nullité du pointeur tty est testée, ce qui veut dire que le programmeur estime qu’il peut être nul.

On a donc une contradiction : l’une des deux lignes pose problème, ce qui être un bug. Et en effet si le pointeur peut être nul, il est dangereux de le déréférencer.

En pratique, il y a beaucoup trop de faux positifs. Par exemple, les macros vérifient souvent que leur arguments ne sont pas nul, et une fois que leur expansion est faite, on ne fait pas la différence avec le code normal. Les auteurs ont donc modifié leur programme (basé sur GCC apparement) pour annoter les expansions de macros, et ne pas les traiter pendant l’algorithme. D’autres problèmes ont été traités pour pouvoir montrer de réelles erreurs, je vous laisse lire le papier si ça vous intéresse.

MAY beliefs

Dans l’exemple sur les pointeurs donné ci-dessus et quelques autres, le programme est capable de dire avec certitude qu’il y a un problème. Cependant, ce n’est souvent pas possible de l’affirmer avec certitude : les informations inférées lors de la lecture du code ne sont pas forcément exactes. Ce n’est pas une raison de les ignorer, mais il faut les traiter avec prudence. L’idée est alors de rassembler un maximum de cas d’utilisations similaires, et d’en déduire les cas érronés. Par exemple, si une fonction est utilisée 999 fois d’une certaine manière, et autrement ailleurs, on peut supposer que le cas seul est érroné.

Donnons encore une fois un exemple pour expliquer cette pratique.

lock lk;            // Lock
int a, b;           // Variables potentially
                    // protected by l
void foo() {
       lock(lk);    // Enter critical section
       a = a + b;   // MAY: a, b protected by l
       unlock(lk);  // Exit critical section
       b = b + 1;   // MUST: b not protected by l
}
void bar() {
       lock(lk);
       a = a + 1;   // MAY: a protected by l
       unlock(lk);
}
void baz() {
       a = a + 1;   // MAY: a protected by l
       unlock(lk);
       b = b - 1;   // MUST: b not protected by l
       a = a / 5;   // MUST: a not protected by l
}

Supposons que lock() soit un appel système qui existe, et qui bloque l’accès aux données du noyau. On suppose donc que toute variable modifiée entre deux locks peut être concernée par ce lock. Une fois que l’on sait à peu près quelles sont les chances estimées que la variable soit concernée par le lock lk, on peut dire à l’utilisateur à quel point une utilisation en dehors du lock peut être dangereuse.

‽ : Pour information, le Big Kernel Lock a été créé, puis est enlevé petit à petit.

Ici, la fonction foo nous permet de dire que a est sûrement concerné par le lock lk, et que b peut l’être (mais moins de chance, vu que l’accès ne se fait qu’en lecture, non ?). La fonction bar montre que a est la seule variable utilisée dans le lock, donc il commence à y avoir de grandes chances que ce soit effectivement a qui est protégé. Dans baz, a est utilisé en dehors d’un lock, donc c’est certainement un bug qu’il faut corriger !

Cette manière d’essayer de deviner des informations plausibles pour ensuite essayer de détecter des erreurs a elle aussi de nombreuses applications décrites dans le papier. Certaines ne sont que des idées placées en plein milieu sans aucun rapport : d’ailleurs le style du papier laisse à désirer, le tout n’est pas très structuré, soyez avertis si vous comptez le lire (sachez aussi que je ne prétends pas faire mieux :). Une de ces idées consiste en la vérification de la cohérence d’une API proposée d’une version stable à une autre. Si on compare les croyances obtenues par notre système entre deux versions stables, elle doivent être exactement les mêmes. Sinon cela implique une possibilité de casser les applications utilisant l’API en question. Cela peut être aussi trouvé via l’utilisation de tests (potentiellement unitaires), mais c’est plus compliqué à mettre en place : un effort de la part du programmeur est impératif, et un test doit être exécuté, contrairement à l’analyse qui est ici statique.

N’hésitez pas à lire le papier si ces quelques lignes ne vous ont pas endormies : un certain nombre d’autres techniques intéressantes pour par exemple :

  • inférer le maximum d’information d’un code (notamment avec le nom des fonctions) ;
  • vérifier que la gestion d’erreurs d’un programme est efficace ;
  • que les pointeurs sont utilisés correctement dans le kernel (différence entre kernel pointer et user pointer) ;
  • détecter des fuites mémoires ;
  • etc.

Enseignements à tirer ?

Pour m’être amusé pendant tout un été à corriger des bugs, je commence à avoir une idée des pratiques à éviter ou au contraire à encourager pour éviter l’apparition de bugs. Le principe de base de ces pratiques est d’apporter le maximum d’informations à celui qui va lire le code (sans parler des commentaires). Ainsi, si un programme est capable de penser que telle façon de faire quelque chose est une erreur, c’est que certainement un humain qui va passer par là sans savoir comment fonctionne le code va aussi penser à une erreur, ou du moins ne va pas comprendre au premier abord ce qui se passe.

C’est l’origine de plusieurs pratiques couramment encouragées en programmation. Citons ici KISS et la minimisation des effets de bords. La première permet à tout le monde de mieux comprendre un code, de mieux comprendre ce qu’il fait exactement pour pouvoir l’utiliser et le modifier la conscience tranquille.

La seconde permet de pouvoir utiliser une fonction sans avoir peur qu’elle casse autre chose ailleurs. Ça permet aussi d’utiliser de la composition de fonction sans risque. Il y a beaucoup de choses à dire sur cette pratique, mais retenez que l’intérêt principal est de ne pas cacher les canaux par lesquels transite l’information pour rendre le code le plus compréhensible et facile à modifier possible. Moins le programmeur qui lit le code a de chances d’être surpris, moins il y a de chances d’avoir des problèmes par la suite.

Ainsi, certains 'design patterns' ont beau résoudre des problèmes, ils ajoutent souvent une couche d’abstraction qu’il faut pouvoir traverser pour comprendre exactement ce qui se passe. Cette couche empêche l’échange d’informations entre deux parties du code qui intéragissent pourtant directement, ce qui provoquera forcément des bugs, qui auraient pu être détectés autrement. De la même manière, trop de généricité empêche une vision claire du code qu’on a sous les yeux. Cela a causé certains problèmes qui sont devenus tellement fréquents qu’il a fallu inventer d’autres design patterns pour les éviter. L’important est d’en être conscient et d’ajouter d’autres mesures de protection quand le bon sens où le compilateur ne peut plus aider à filtrer les erreurs.

De la même manière, utiliser son langage de manière idiomatique et rester consistant dans son formattage, sa façon de penser et les techniques utilisées est important. Cela permet au lecteur de retirer beaucoup d’informations rapidement, et donc de pouvoir se concentrer sur les points importants.

Si le programme a cru détecter une erreur qui n’était en fait qu’un faux positif, il reste néanmoins important d’essayer de corriger le code : tous les humains qui le liront risqueront de faire la même erreur et de ne pas comprendre ce qui se passe. Dans ce sens là, les exemples présents dans le papier sont instructifs : se sont des erreurs qui peuvent sembler anodines mais qui restent importantes.

[ tag:blog.huoc.org,2009:bluestorm/3 ]
Voir les commentaires · Commenter

/\

Histoire de sexe et d'informatique

#. Par Cygal dans Réactions. Publié le 27.08 2009 à 12:06. 5 commentaires.
<. L'innocence est un mythe : les programmes fonctionnels nécessairement impurs
>. Détection automatique de bugs
femmes histoire informatique réaction

Comme me l’a encore récemment prouvé le Google Summer of Code, et surtout sa hum, superbe, liste de diffusion, où, noyés parmi cinquante messages dont je n’avais aucune utilité (et un petit e-mail vital), on pouvait trouver un vieux troll sur le sexisme, voici un thème qui est plus que jamais sensible dans le monde de l’informatique, et errr, je parie un sucre que quelqu’un va se sentir offensé par mon titre tout meugnon. :]

Bien sûr, cette réaction n’est pas de moi. Et surprise, elle n’est pas de blueblue non plus ! Cygal est dans la place ! Faut que l’on arrête le rap français, ici.

—minh

Cette réaction traite d’un point récurrent dans l’informatique en général et dans le monde du libre en particulier : la minorité parfois écrasante de la gent féminine. Nous ne discuterons pas ici de toutes les raisons qui me font penser que c’est une mauvaise chose qu’il faut enrayer, bien que ça puisse être intéressant, mais nous contenterons d’une analyse de cet article par rapport à la littérature existante sur le sujet.

La présence des femmes dans l’informatique est un sujet qui a déjà été maintes fois traité. Ce papier là est original dans le sens où il n’essaye pas de traiter le problème, et ne le présente pas seulement via des statistiques. Il essaie d’expliquer de manière historique pourquoi les femmes sont sous-représentées dans l’informatique en général. C’est une analyse courte et peut-être simpliste mais les explications données sont crédibles.

Petit résumé du papier

Contrairement aux autres disciplines scientifiques, l’informatique n’existait pas avant la Seconde Guerre Mondiale. C’était donc la porte ouverte aux femmes qui ont été embauchées dans le domaine, et se sont avérées très douées, selon l’auteur. Après la guerre, les hommes ont « récupéré leurs places » dans toutes les disciplines qu’ils occupaient avant la guerre. L’informatique n’existant pas avant, les femmes n’avaient pas de places à laisser.

Cependant, la pression sociale a fait qu’une majeure partie est retournée à des occupations considérées comme acceptables à l’époque. Celles qui sont restées dans de grandes compagnies étaient discriminées et devaient repousser les avances de leur collègues. Ce fut la première vague de déféminisation. De manière générale, le fait que le rôle traditionnel des femmes soit d’élever ses enfants n’a pas aidé, dans aucune de ces vagues.

La deuxième vague s’est effectuée quand l’apprentissage de l’informatique a été couplé à celui de l’ingénierie électronique, domaine presque exclusivement masculin, ce qui n’a pas encouragé les gens à s’intéresser à l’informatique.

La troisième vague s’est déroulée lors de la démocratisation de l’informatique. Les jeux ont toujours été faits pour et par des hommes, ce qui a fini par ancrer dans les esprits que « l’informatique c’est pas pour les filles ».

Un peu plus loin ?

Il n’est pas rare dans les domaines scientifiques de voir les filles traitées différemment, voire rejetées, parfois inconsciemment.

En informatique, c’est particulièrement vrai puisque les femmes et les hommes estiment que l’informatique n’est pas faite pour les femmes. Elles ne se disent pas intéressées et préfèrent choisir d’autres voies. Le fait qu’elles soient sous-représentées à ce point ne leur donne pas envie de venir. Si vous êtes un homme, imaginez ce que ça fait de rentrer dans un salon de manucure rempli de femmes. Vous ne vous sentirez pas à l’aise.

Cette analyse me semble intéressante parce qu’elle est courte mais semble expliquer un certain nombre de choses. Il y a sûrement d’autres points qui expliquent la situation, c’est pourquoi je vous propose un certain nombre de lectures complémentaires en annexe. Elles ne sont pas toutes centrées sur l’informatique en général, mais semblent intéressantes. L’article Wikipédia vous permettra de pousser plus loin.

Liens

[ tag:blog.huoc.org,2009:posts/27 ]
Voir les commentaires · Commenter