[lfs-fr] Une astuce à relire

Stéphane Aulery lkppo at free.fr
Jeu 4 Oct 16:48:24 PDT 2012


Bonjour Denis,

Le mercredi 03 octobre 2012 à 01:15:29, Denis MUGNIER a écrit :
>
> je viens de terminer la traduction de l'astuce de Alex Kloss sur "que
> faire en cas d'erreurs".
>
> Pour le moment, j'ai pas fait la mise en page pour avoir maxi 80
> caractères par ligne. Je le ferais après les modifications
des relecteurs ;o))

Et une version relue en pièce jointe, une !

Cordialement,

-- 
Stéphane Aulery
-------------- section suivante --------------
AUTEUR :        Alex Kloss <alex at 22-music.com>

TRADUCTEUR : Denis Mugnier <myou72 at orange point fr>

DATE: 2003-11-10

LICENCE : GNU Free Documentation License Version 1.2

SYNOPSIS : Que faire en cas d'erreurs

DESCRIPTION :
Le livre LFS a un court, mais beau chapitre, à propos des erreurs. Une plus longue dissertation
sur la façon de localiser l'erreur, comment la décrire (sur IRC ou la liste de diffusion),
et peut-être la contourner est le but de cette astuce.

PRÉREQUIS :
Bon sens, LFS, patience, compétences en programmation (facultatif).

ASTUCE :
Presque tous les adeptes de LFS ont vus des lignes comme :

- make[1]: Error
- Segmentation Fault
- ld returned signal 2: ...

Le premier réflexe est d'écrire sur la liste de diffusion ou sur IRC quelque chose comme :

J'ai une erreur dans le programme <remplacer par la mention appropriée> !

Première chose, est ce vraiment une erreur ? Si vous trouver l'option "-Werror" dans les
lignes qui appellent gcc, l'"erreur" à laquelle vous êtes confronté peut aussi bien être un avertissement
(-Werror stipule à gcc de traiter tous les avertissements comme des erreurs). Vous rencontrerez souvent des messages
d'avertissement et d'erreurs mélangés avant le classique "make[1]: Error". Un avertissement est
quelque chose dont gcc se plaint, mais il continue sans erreur, alors qu'une erreur est quelque chose qui
arrête la compilation du paquet que vous êtes en train de construire.
Pour désactiver les messages d'avertissement distrayant, utilisez "export CFLAGS="-w".

Généralement, il n'y a jamais assez d'informations sur les erreurs,
ce qui est nuisible pour celui qui demande et pour celui qui tente de répondre,
en raison du dialogue ennuyeux qui va suivre.
Je dois admettre que la liste de diffusion LFS et IRC n'échouent jamais à résoudre mes problèmes
(et d'une manière assez gaiement), mais j'en suis arrivé à un point où je voulais résoudre
autant de mes problèmes que possible.
J'ai donc dû apprendre beaucoup de choses, ce qui était sans aucun doute plaisant.

QUELLE SORTE D'ERREUR ?

Vous devez savoir distinguer les différentes sortes d'erreurs.
Plus vous pouvez en apprendre à propos de l'erreur,
plus vous pourrez la résoudre facilement.

Cela devrait être une astuce normale,
mais je pense qu'il est plus facile de dessiner un schéma :

Question :

 - Quand est-ce arrivé ?
 - Qu'est-il arrivé ?
 - Où est-ce arrivé ?

                                                        , Compilation (gcc) ...
                                  , ... introuvable ---<- Dépendances (depmod)
           Erreur de compilation < -,                   ` Liaison (ld)
         /                        `. `- gcc-3.4.x*
        /                           \
Erreur <                             Erreur de segmentation
        `.                         ,'
           Erreur d'exécution ----<            , complet
                                   ` plantage <
                                               ` programme seulement
                                               
____
* gcc-3.4 et ses successeurs, n'acceptent ni les étiquettes à la fin des instructions composées 
  ni l'utilisation de fonctions protégées à l'intérieur d'autres fichiers.

Cela semble vraiment simple, non ? Mais c'est seulement le début.
Nous allons examiner de plus près chacun de ces types d'erreur !

1. Erreurs de compilation

En premier, vérifiez si le paquet que vous êtes en train de compiler contient des fichiers comme README
et/ou INSTALL. Vous pouvez contourner la plupart des erreurs en suivant strictement leurs instructions.

Durant la construction de votre paquet, vous rencontrez parfois une erreur qui vous signale que quelque chose est manquant, malformé ou simplement incompilable.

1.1 ... introuvable

1.1.1 Compilation (gcc)

Il y a beaucoup de choses que gcc peut ne pas pouvoir trouver.
S'il y a quelque chose à inclure, c'est peut être le fichier à inclure qui manque.
Les questions ici sont :

 - Que manque-t-il ?
 - Que faire contre cela ?

1.1.1.1 Fichier d'entête manquant

S'il manque seulement un fichier d'entête, vous aurez un message d'erreur ressemblant à :

foo.c:3:31: /usr/include/bar.h: No such file or directory

Si un fichier manque, vous pouvez le chercher sur votre système :

find / -name <nom_du_fichier> ou
locate <nom_du_fichier> (lancer updatedb, si locate le demande)

Si vous ne trouvez pas le fichier, la question suivante sera :

 - D'où ce fichier devrait-il venir ?
 - Est-ce un prérequis que vous avez oublié ?
 - Tous les outils sont-ils disponibles dans la version requise ?

Si le fichier est ailleurs que dans le chemin d'inclusion standard (/usr/include,
/usr/local/include), vous devez ajouter -I<chemin_non_standard_pour_include> dans le CFLAGS,
par exemple "export CFLAGS=-I/usr/X11R6/include". Si l'instruction #include
contient un sous-répertoire, alors que le fichier à inclure est dans 
le répertoire standard, vous devez éditer l'instruction #include.

Dans la plupart des cas le fichier sera dans un répertoire que le développeur n'attendais pas.
La façon la plus simple de contourner cela sera un lien symbolique, mais ce n'est pas une solution propre.
Alors nous cherchons dans les sources les occurrences du fichier "oublié" en premier :

grep -R "<chemin et nom du fichier oublié>" *.*

Maintenant, éditez chaque fichier qui utilise le mauvais chemin dans ces instructions #include.
L'utilisateur paresseux peut utiliser sed :

for i in *.*; do
 mv $i $i.bak
 sed s|'<fichier "oublié">'|'<fichier trouvé>'|g $i.bak > $i
done

Cela doit résoudre le problème ; vous pouvez continuer la construction du paquet.

1.1.1.2 Déclaration oubliée

Un message d'erreur agréable vient d'une déclaration oubliée :

foo:124:4: bla undefined

Si "bla" est une fonction des bibliothèques génériques (comme glibc), ce sera probablement 
documenté avec une page de manuel qui contiendra les informations donnant le ou les fichiers
d'entête qui doivent être inclus :

man bla

Regardez /usr/share/man/man3 pour les appels de fonction documentés :
La page de manuel ressemblera à quelque chose comme :

--snip
FUNC(3)                         Linux Programmer's Manual               FUNC(3)

NAME
        func, ffunc, vfunc - Example function without any use

SYNOPSIS
        #include <stdfunc.h>
        int func(char *format, ...);
        int ffunc(FILE *stream, const char *format, ...);

        #include <vstdfunc.h>
        int vfunc(const char *format, va list ap);

DESCRIPTION
...
--snap

Dans la plupart des cas le fichier d'entête n'est pas inclus là où il est utile,
alors vous devez juste écrire dans le fichier où c'est oublié : "#include <stdfunc.h>".

Si la définition n'est dans aucune bibliothèque standard, vous devez chercher
dans le code du programme que vous êtes en train de compiler la fonction oubliée :

grep "<nom de la fonction>" *.* | less

Maintenant cherchez quelque chose comme "#define bla ( const char * ...". Si vous ne trouvez rien,
la fonction doit probablement être incluse dans d'autres sources, alors vous devriez
revérifier les exigences du paquet que vous êtes en train de compiler, dans le cas
ou quelque chose vous a échappé.

Si le fichier ou la définition est incluse est un fichier d'entête (*.h),
il suffit de l'inclure, autrement copiez et collez la définition
dans le fichier dont gcc se plaint.

Parfois ce n'est pas une définition, mais un argument oublié pour une fonction. Le dernier
exemple vécu de ce gizmo était une erreur due à quelques changements d'API dans le pilote
alsa-1.0-pre ; Quand on compilait n'importe quel paquets qui utilisait au moins une fois
les fonctions snd_pcm_hw_param (par exemple, mplayer ou wine), l'erreur liée était affichée
comme ci-dessous :

audio.c: In function `Alsa_TraceParameters':
audio.c:292: error: too few arguments to function `snd_pcm_hw_params_get_format'
(...)

Dans ce cas vous devez connaître les arguments attendus par la fonction.
Donc, vous cherchez le fichier d'entête qui défini la fonction (comme expliqué à propos
des fonctions oubliées). Pour notre exemple alsa, la ligne dans le fichier
d'entête était dans /usr/include/alsa/pcm.h et ressemblait à :

int snd_pcm_hw_params_get_format(const snd_pcm_hw_params_t *params,
snd_pcm_format_t *val);

Alors que le code où cette fonction était appelée utilisait seulement :

(...) format = snd_pcm_hw_params_get_format(hw_params);

Il faut noter que seul le premier argument est donné, l'autre argument
"snd_pcm_format_t" du type "*val" est oublié. Maintenant vous devez savoir
ce qu'est le type *val, pour ensuite pouvoir l'insérer dans audio.c.

1.1.1.3 fonction bla... redéfinie

Une autre erreur presque identique apparaît si quelque chose est défini en double.
Le compilateur n'est pas capable de dire si les deux définitions sont égales, alors il
enverra souvent une erreur dans ce cas. Vous devez chercher les définitions, vérifier laquelle
est valide dans votre cas et encadrer la fonction "invalide" avec "#ifndef <Nom>"
et "#endif". Certain voudront simplement effacer la définition "invalide", mais si un autre
paquet en a besoin, elle sera absente, alors la solution #ifndef/#endif est clairement
la meilleure.

1.1.2 Édition de lien (ld)

L'édition de lien échoue souvent à cause de bibliothèques manquantes. Soyez certain que 
votre /etc/ld.so.conf énumére tout les répertoires où sont les bibliothèques. Dans le cas
où un autre répertoire est nécessaire, utilisez LDFLAGS : "export LDFLAGS=-L/usr/X11R6/lib"
pour inclure les bibliothèques Xfree86. "/lib" et "/usr/lib" sont toujours inclus par défaut
et ne doivent pas être ajoutés.

Une autre erreur (occasionnelle) peut arriver si les bibliothèques ne sont pas liées
correctement. J'ai vu arriver cela une fois seulement avec un programme lié à 
libpng, mais pas à libz, qui est utilisée par libpng, mais qui doit être liée également.
Alors dans le Makefile où j'ai oublié "LIBS=-lpng", j'ai ajouté aussi "LIBS=-lpng -lz".
La plupart du temps la fonction manquante est indiquée ; vous pouvez essayer de faire un
grep dans la bibliothèque (motifs binaires).

1.1.3 Vérification de la dépendance d'un module (depmod)

Une autre erreur qui arrive seulement si le noyau exécuté est différent de celui
avec lesquelles les sources ont été compilée (ce qui peut être le cas quand la compilation
est faite en mode chroot) est l'erreur "unresolved dependency in module". Pour contourner ce bogue,
lancez depmod avec l'option "-F /usr/src/linux/System.map". Et assurez-vous de compiler
les modules avec le même compilateur que celui utilisé pour le noyau.

1.2 gcc-3.4.x

La version 3.4.x introduit quelques nouvelles erreurs, qui compilait correctement avec
une version antérieure du même compilateur. Au lieu de ressortir une ancienne version, il peut
être plus facile de les corriger.

1.2.1 gcc-3.4.x : étiquette à la fin des instructions composées

Depuis gcc-3.4.x, les étiquettes à la fin des instructions composées sont traitées comme
des erreurs, mais elles sont largement utilisées en dépit de leur impropriété. Sans aucun doute
ce problème peut être facilement corrigé, il suffit de remplacer les occurrences de 

  goto [label];

par

  return;

et effacer l'étiquette du code source ou la commenter. En règle général, bannissez les instructions goto
de votre code C.

1.2.2 gcc-3.4.x : fonctions protégées

Le message

  Error: `foo::bar function(pointer*)' is protected

montre que quelque part dans le code il y a une fonction préfixée par 

  protected:

Bien que ceci ait un sens, cela arrête la compilation de notre application, alors nous
pouvons facilement le commenter :

  // protected:

et continuer la compilation.

1.3 Erreur de segmentation

C'est la plus ennuyeuse. Cela signifie qu'une application tente d'obtenir quelque chose
depuis un fichier/tunnel/périphérique/variable d'environnement qui n'est pas initialisé et n'a
pas d'autre solution s'il est nul qu'un plantage et un arrêt immédiat. Si les informations
suivantes ne sont pas suffisantes pour vous, vous pouvez regarder la FAQ de SIG11 disponible à l'adresse
http://www.bitwizard.nl/sig11 - mais regardez cette section en premier.

1.3.1 Erreur de segmentation pendant la compilation

Les erreurs de segmentation pendant la compilation sont rares. Vous pouvez seulement avoir un SIG11
si la mémoire est pleine pendant la construction d'un paquet - cela arrivera seulement
sur les systèmes avec peu de mémoire. Vous pouvez ajouter un périphérique loop de swap pour augmenter
votre mémoire ; cela ralentira la compilation, mais au moins cela fonctionnera
sur les périphériques qui ont une mémoire insuffisante :

dd if=/dev/zero of=/tmp/swapspace bs=1M count=128
losetup /dev/loop0 /tmp/swapspace
mkswap /dev/loop0
swapon /dev/loop0

initialisera 128 Mo d'espace de swap (ou mémoire virtuelle). Si cela continue à échouer,
augmentez la taille de l'espace disque utilisé (count=256; count=512; count=XXX).
Si vous avez terminé la compilation ou voulez augmenter la taille, effacez l'espace de swap
ajouté avec :

swapoff /dev/loop0
losetup -d /dev/loop0
rm /tmp/swapspace

1.3.2 Erreur de segmentation pendant l'exécution

Si un programme a une erreur de segmentation, il n'y a rien que vous puissiez
facilement faire pour éliminer l'erreur sans que vous ayez des connaissances en 
programmation. Contacter le développeur et donnez lui une vue détaillée de votre
système ; peut-être que dans /var/log il y a quelque chose à propos de l'erreur ?
Si vous voulez éliminer le bogue par vous même, lisez la FAQ de SIG11 et utilisez
strace que vous pouvez trouver à l'adresse http://www.liacs.nl/~wichert/strace/
et qui est facile à installé dans le programme ; cela pourra vous aider à trouver quel
fichier/tunnel/chaîne d'environnement/etc le programme attend. Ensuite
essayez de faire un grep sur les sources du programme qui est en erreur de segmentation après
le fichier/tunnel/etc qui échoue. Ajoutez une routine de secours. Un bel exemple
est gsview-4.4-patch. gsview 4.4 essaye d'obtenir la variable d'environnement LANG,
mais n'a pas de solution de secours dans le cas ou elle n'est pas initialisée.
La partie du code source en cause ressemble à

   strncpy(lang, getenv("LANG"), sizeof(lang)-1);

... qui aurait copié une partie de la variable d'environnement LANG(uage) sans le dernier caractère(
Si LANG était vide, il aurait tenterait de copier -1 caractère, ce qui donne une erreur
de segmentation). La solution facile serait d'initialiser LANG avec quelque chose, mais
la meilleure solution est de fournir une solution de secours en modifiant le code comme suit :

   strncpy(lang, (getenv("LANG") == 0) ? "C" : getenv("LANG"),sizeof(lang)-1);

Ce qui est un peu flou pour ceux qui ne connaissent par le C, mais qui signifie "si LANG est 0, alors
utiliser 'C' à la place de la variable d'environnement LANG (qui est standard),
sinon utiliser la variable d'environnement LANG moins un caractère". Maintenant c'est votre tour,
si vous voulez résoudre ce bogue par vous même !

1.4 Plantage

Les plantages sont les plus ennuyeuses erreurs qui y aient. Heureusement avec linux
ils sont aussi rare qu'ennuyeux (sauf si vous utilisez les dernières versions des sources).
Les plantages sont le plus souvent causés par des boucles sans fin,
des problèmes de pilote qui conduisent à un verrouillage du bus,
et des problèmes matériels (comme un condensateur défectueux dans l'alimentation du CPU :
vérifiez ceux qui sont éclatés). Les boucles infinies sont facilement détectées
par la plupart des compilateurs, le dernier est plus difficile à trouver.
Essayez de rétrograder le pilote que vous pensez responsable du plantage
et envoyez un rapport sur sa liste de diffusion.

1.4.1 Plantage complet

Vous reconnaissez un plantage complet en pressant la touche [CAPS LOCK]. Si la led s'allume,
le clavier est toujours lié à la console, donc pas de plantage totale. Sinon, essayez de presser
différentes touches. Si rien ne fonctionne, utilisez un redémarrage matériel 
(ce qui est toujours le dernier recours pour reprendre la main). Si le clavier semble actif,
mais que l'écran reste blanc, essayez de redémarrer avec [ALT][CTRL][DEL]. 
Si même cela ne fonctionne pas, vous pouvez être chanceux et avoir la fonction touche sysrq
de compilé dans votre noyau. Pour plus d'informations, lire
[/usr/src/linux/Documentation/sysrq.txt].

1.4.2 Plantage d'un programme seulement

Si un programme plante en laissant le reste du système intact, vous pouvez
utiliser la commande appropriée dans kill/killall/xkill pour le tuer. Le plantage
isolé d'un programme se produit le plus souvent en raison d'une boucle infinie,
par exemple en essayant de lire depuis un tunnel bloqué,
dans la plupart des cas la charge augmentera visiblement.

1.5 Autres erreurs

Si vous rencontrez un message d'erreur absent de cette astuce,
vérifiez les listes de diffusion appropriées, entrez le message d'erreur dans Google
et regardez s'il y a une nouvelle version ou si la version cvs )si elle est disponible) a la même erreur.
Si rien ne vous aide, demandez sur IRC ou par mail sur la liste de diffusion des développeurs, ou
envoyez un rapport de bogue. Pensez à décrire précisément l'erreur et donnez toutes les
informations sur le système sur lequel vous êtes en train d'essayer de construire le paquet
(logs, versions, sortie de strace, sortie de dmesg, messages de débogage et ainsi de suite).

1.6 Quelques liens utiles

À propos de l'erreur SIG11 (erreur de segmentation) :       http://www.bitwizard.nl/sig11
Cette page a quelques informations générales à propos de l'erreur SIG11.

Acquérir des connaissances en programmation :            http://ibiblio.org/obp/thinkCS
Cette page présente un livre appelé "How to think like a computer scientist",
qui peut être téléchargée librement dans les variantes Java, C++ et Python.
La variante C++ sera la plus utile pour les adeptes de LFS, depuis que la plupart des projets GNU
utilisent soit C soit C++.


Puisse les sources être avec vous !

MODIFICATIONS :
  [2002-01-04]
    * Démarrage de l'écriture de cette astuce.

  [2003-10-07]
    * Version initiale, de petits ajouts.

  [2003-10-08]
    * Oubli de mentionner Tushar dans les remerciements, de petits changements et ajouts.
    * De petits changements et corrections suggérés par Bill Maltby

  [2003-10-26]
    * Ajout d'un lien vers la FAQ SIG11, quelques trucs en plus sur les erreurs de segmentation et quelques mots
      à propos des problèmes de depmod avec des noyaux différents.

  [2003-11-10]
    * Ajout d'un chapitre liens avec un lien vers un livre qui aide à acquérir les connaissances en C++.

  [2004-01-20]
    * Ajout d'une petite partie sur les fonctions redéfinies.

  [2004-09-07]
    * Des corrections mineures, mention de l'incapacité de gcc-3.4 à accepter les étiquettes à la fin 
      des instructions composées et de sa solution.

  [2004-09-08]
    * D'autres corrections mineures et des choses de gcc-3.4 à propos des fonctions protégées.

REMERCIEMENTS :
Merci à teemu pour m'avoir rappeler "-I" et "-l" autant qu'à Tushar pour l'avertissement
sur les avertissements et pour avoir sonner la cloche pour l'option "-w", sans oublier Bill
pour ses corrections. Merci à Gérard pour la partie de LFS sur les erreurs qui m'a inspirée !
Merci à Allen B. Downey pour son livre brillant qui est distribué librement sous licence GPL ! :-)


More information about the lfs-traducfr mailing list