Les outils pour débugger sous Linux

Les programmeurs qui développent en C ou C++ ont tous passé de longs moments à débugger leurs programmes. Cet article présente les principaux outils disponibles sous Linux pour les aider dans cette tâche fastidieuse. En plus des outils "classiques" comme GNU gdb ou DDD, des outils plus spécialisés comme Electric Fence sont également présentés. Bien que cet article s'adresse en priorité aux programmeurs débutants, les vieux routards pouront également y trouver leur bonheur.

    GNU gdb

GNU gdb est LE programme de débuggage sous Linux (ainsi que sur bon nombre d'autres plate-formes). En dépis de son interface en mode texte, bien qu'il existe des surcouches graphiques, comme DDD ou KDevelop que nous verrons plus loin, il est extrêmement puissant et permet de venir à bout des bugs les plus corriaces et les plus incrustés dans vos programmes.

Présentation générale :

Un débugueur comme gdb permet de visualiser ce qui se passe dans votre programme, soit au cours de son exécution, soit au moment où il a crashé dans le cas où gdb est utilisé pour faire une analyse post-mortem (nous reviendrons sur ce terme plus loin).
gdb permet quatre principaux types d'actions pour déceler les problèmes :
- Lancer un programme en spécifiant les paramètres appropriés.
- Demander au programme de s'arrêter dans des circonstances bien précises.
- Examiner ce qui s'est passé, une fois le programme terminé.
- Changer l'état d'exécution du programme (comme la valeur d'une variable) pour corriger les résultats.
La version actuelle de gdb (5.0) permet de débugger les programmes écrits en C, C++, Modula-2, Chill (un "dialecte" du LISP), Fortran et Java. Il existe également des patchs pour supporter l'ADA et l'Objective-C.
Notons au passage que pour être pleinement exploitable par gdb, un programme doit être compilé avec l'option '-g', voire '-g3'; d'autre part les optimisations (-O/-O2/etc) doivent être désactivées dans la mesure du possible.

Pour lancer gdb, il suffit de tapper 'gdb' au prompt shell, mais le plus souvent on lance gdb avec quelques paramètres : soit 'gdb ./monprog' pour spécifier directement le programme cible (ce qui évite de taper 'file ./monprog' dès l'apparition du prompt gdb), soit 'gdb ./monprog core' pour faire une analyse post-mortem à partir d'un fichier core. Il est également possible d'utiliser gdb sur un programme qui est déjà en cours d'exécution : 'gdb ./monprog 2345' pour attacher gdb au processus dont le PID est 2345. Attention, gdb recherche les fichiers core avant les PID, dans ce dernier cas, vous ne devez pas avoir de fichier core ayant le même nom que le PID du processus dans le répertoire courant.

Voici la liste des commandes gdb les plus utilisées :

break [fichier_source:]fonction
    Met un point d'arrêt au début de la fonction spécifiée. Lorsque l'exécution du programme rencontre un de ces points d'arrêt, le programme est stoppé et le programmeur peut entrer d'autres commandes au prompt du débogueur. L'exécution pourra ensuite être reprise grâce à la commande 'cont'. A la place d'un nom de fonction, on peut directement passer un numéro de ligne dans le code source à la commande 'break'.

condition numéro test
    Permet de poser une condition sur un point d'arrêt précédement défini par la commande 'break'. Par exemple, 'condition 1 i==10' n'activera le point d'arrêt numéro 1 que dans le cas où la variable i vaut 10.

run [liste de paramètres]
    Démarre l'exécution du programme avec les éventuels paramètres spécifiés.

bt
    Backtrace : demande à gdb d'afficher le contenu de la pile du programme. Synonyme de la commande 'where'.

print expression
    Affiche la valeur d'une expression. En particulier, cette commande peut être utilisée pour afficher la valeur d'une variable.

cont
    Demande la reprise de l'exécution d'un programme stoppé.

next
    Exécute la ligne suivante dans un programme suspendu.  Si cette ligne contient des appels de fonctions, elles seront exécutées sans être elles-mêmes tracées.

nexti
    Idem que la commande 'next', mais le pas de progression n'est plus la ligne de programme, mais l'opcode assembleur (commande à réserver pour les cas les plus désespérés).

set [option] [paramètres]
    Cette option permet de changer la plus grande partie des paramètres de gdb ; parmi les principales options disponibles :
    environment : permet de changer une variable d'environnement du programme exécuté.
    language : permet de changer le langage source par défaut.
    variable : permet de changer à la volée le contenu d'une variable.

step
    Idem que la commande 'next', mais les éventuelles fonctions appelées seront elles aussi tracées.

stepi
    Idem que la commande 'step', mais le pas de progression n'est plus la ligne de programme, mais l'opcode assembleur.

help [nom]
    Donne des informations sur la commande donnée en paramètre, ou une page d'aide générique si aucun paramètre n'est mentionné.

quit
    Quitte gdb.

Il est important de noter que la ligne de commande de gdb utilise la bibliothèque GNU readline et que par conséquent tous les raccourcis clavier courrants (^A, ^E, ^K, etc...) ainsi que le mécanisme d'historique (flèches curseur, ^R) sont disponibles.
 

En cas de gros problème, voire de doute sur le code généré par le compilateur, gdb permet également de désassembler le code d'une fonction. Bien évidemment, il est rare d'en arriver à une telle extrémité ;-)
(gdb) disassemble Double
Dump of assembler code for function Double:
0x8048480 <Double>:     push   %ebp
0x8048481 <Double+1>:   mov    %esp,%ebp
0x8048483 <Double+3>:   mov    0x8(%ebp),%eax
0x8048486 <Double+6>:   add    %eax,%eax
0x8048488 <Double+8>:   leave
0x8048489 <Double+9>:   ret
End of assembler dump.
 

    DDD & KDevelop

DDD et KDevelop sont les 2 interfaces graphiques les plus pratiques de gdb. Par rapport à KDevelop, DDD fait figure d'ancétre, mais celui-ci est très convivial, donc très simple à utiliser et peu gourmand en ressources.


                                                 DDD : Fenêtre principale
 

Cependant, DDD ne reste qu'une interface graphique au dessus de gdb, ce qui pourra laisser les plus exigeants sur leur faim. Heureusement, KDevelop comble un manque important : c'est un véritable environnement de développement intégrant un éditeur avec colorisation du code, un système de gestion de projets et enfin une interface de débugage.
Bien que KDevelop soit une application KDE, elle fonctionne très bien avec simplement les bibliothèques Qt et KDElibs, c'est à dire que vous n'aurez pas toutes les applications KDE à installer si vous désirez utiliser KDevelop; seules les bibliothèques de base sont suffisantes. D'autre part, KDevelop permet sans aucun problème de développer et débugguer des applications GNOME ;-)
Bref, si la ligne de commande de gdb vous rebute, si vous ne maîtrisez pas totalement Emacs et que vous ne supportez pas vi, alors KDevelop est certainement fait pour vous. KDevelop intéressera également les développeurs qui découvrent le monde Linux alors qu'ils étaient habitués aux environnements de développement intégrés du monde Windows.


 

    Les problèmes de plomberie

Vous avez déjà probablement eu a retrouver des "fuites" de mémoires, c'est-à-dire à trouver des zones mémoires qui ont été allouées, mais qui ne sont pas désallouées par la suite. Dans cette partie, nous allons faire un tour d'horizon des divers outils disponibles sous Linux qui peuvent aider dans cette recherche.

Vous savez déjà probablement qu'une zone de mémoire allouée est automatiquement restituée au système d'exploitation lors de la terminaison du processus. Dans ce cas, pourquoi s'obstiner à rechercher ces fuites ? Celles qui sont situées dans les programmes qui n'allouent dynamiquement que peu de mémoire et dont le temps de fonctionnement est très cours (comme par exemple, les commandes ls ou ps) ne prettent pas à conséquences bien que ce soit un style de programmation relativement peu esthétique. Par contre, si des fuites surviennent dans des programmes qui utilisent beaucoup de mémoire ou qui sont sensés tourner pendant longtemps, alors ce problème devient réelement génant. Lequel d'entre vous n'a pas déjà eu à redémarrer Netscape ou son serveur X (voire les deux) parce qu'il occupait plus de 100 Mo en mémoire ?

Les fuites ne sont pas les seuls problèmes liés à l'allocation mémoire. Les langages C et C++ n'effectuant pas de tests pour vérifier si l'on accéde bien toujours à des zones mémoires correctes, qu'elles aient été allouées dynamiquement (par un appel à la fonction malloc()) ou non (tableau statique). Dans ce cas, cela se terminera dans la plupart des cas par un résultat erroné ou par une erreur de segmentation (voire l'un suivi de l'autre).
Heureusement, il existe sous Linux quelques outils qui permettent de grandement simplifier la chasse aux bugs.

    Electric Fence

Electric Fence (litéralement "cloture électrique") est un outil très simple, mais il permet néanmoins d'intercepter un grand nombre de problèmes grâce à un minimum d'efforts. Plus particulièrement, Electric Fence rend possible la détection des erreurs suivantes :
- accès en dehors des zones allouées par la fonction malloc() ("buffer overruns" et "buffer underruns")
- accès à une zone mémoire retournée au système par un appel à la fonction free().
- détection des problèmes d'alignement.
A la différence des autres outils similaires, Electric Fence détecte non seulement les tentatives d'écritures en dehors des zones allouées, mais également les tentatives de lecture. C'est, en grande partie, ce qui fait l'intérêt de cet outil.

Electric Fence se présente sous la forme d'une bibliothèque avec laquelle vous devrez "linker" votre programme. Vous pouvez choisir d'utiliser la bibliothèque libefence explicitement lors de l'édition de liens finale en ajoutant l'option '-lefence' lors de l'appel à la commande gcc, cependant il est beaucoup plus pratique de forcer l'édition de liens au moment de l'exécution grâce à la variable d'environnement LD_PRELOAD. Cela peut se faire simplement à partir de la ligne de commande :
# LD_PRELOAD=libefence.so.0.0 ./monprog
ou bien depuis gdb grace à la directive 'set environment LD_PRELOAD=libefence.so.0.0'.

Voyons concrétement avec un exemple comment s'utilise Electric Fence. Considérons le petit programme suivant :
 

eftest1.c
#include  <stdio.h>
#include  <errno.h>

int main (int argc, char **argv) {
    int  *p, i;

    if (! (p = (int *) malloc(10*sizeof(int)))) {
       perror("echec de malloc:");
       exit(-1);
    }
    for (i=0; i<=10; i++) {
        p[i]=i;
    }
    printf("Fin du test\n");
    exit(0);
}

(Les lecteurs attentifs auront certainement déjà vu qu'il y à une erreur dans le code, mais merci de ne rien dire pour le moment afin de ne pas me gacher mon effet de surprise que je réserve pour plus tard ;-)

On compile ce programme grâce à la commande :
$ gcc -g eftest1.c -o eftest1
puis on l'exécute :
$ ./eftest1
Fin du test
$

Tout s'est donc passé comme prévu ; du moins en apparence, car cela ne veut pas dire que le programme fonctionne correctement. Pour nous en convaincre relançons ce même programme, mais cette fois en utilisant Electric Fence :
$ LD_PRELOAD=libefence.so.0.0 ./eftest1

  Electric Fence 2.0.5 Copyright (C) 1987-1998 Bruce Perens.
Segmentation fault.
$

On note au passage qu'Electric Fence a bien été trouvé comme l'indique le message (Ne vous laissez pas impressionner par le fait que la dernière version d'Electric Fence date de 1998; cela est tout simplement dû au fait que cet utilitaire remplit parfaitement son rôle et qu'aucun bug n'y a été découvert depuis cette date). Mais on voit surtout que le programme a planté avant la fin à cause d'une erreur de segmentation. Pour y voir plus clair revoyons la scène avec gdb (à défaut de ralenti).

$ gdb -q ./eftest1
(gdb) set environment LD_PRELOAD=libefence.so.0.0
(gdb) run
Starting program: /home/vincent/articles/./eftest1

  Electric Fence 2.0.5 Copyright (C) 1987-1998 Bruce Perens.

Program received signal SIGSEGV, Segmentation fault.
0x8048520 in main (argc=1, argv=0xbffffd44) at eftest1.c:12
12         p[i]=i;
(gdb) print i
$1 = 10

A ce stade, on en sait déjà un peu plus. D'une part, on sait précisément quelle ligne du programme a provoquée l'erreur ; d'autre part, on a pu déterminer que la variable i valait 10 au moment du plantage. A partir de là, l'erreur est évidente : l'appel de malloc() a alloué un segment de 10 mots machine, alors que la boucle for essaye d'en utiliser 11 (de 0 à 10, cela fait bien 11, pas 10).

Pour cet exemple, on a utilisé le mode de fonctionnement par défaut de Electric Fence, à savoir les problèmes de dépassements par le haut (buffer overruns). Du fait de sa conception, Electric Fence ne permet pas de détecter en une seule exécution les dépassements par le haut et par le bas.
En fait Electric Fence utilise le hardware gérant la mémoire virtuelle (la MMU) pour placer une page inaccessible après (ou avant selon l'option utilisée) chaque plage de mémoire allouée. De la sorte, lorsque le programme tente de lire ou d'écrire cette page inaccessible, une erreur de segmentation est déclenchée. A partir de ce moment, il est trival de trouver l'instruction fautive grâce à gdb, comme l'a montré l'exemple précédent. De même une zone mémoire qui a été libérée par un appel de la fonction free() sera rendue inaccessible, de sorte qu'une tentative d'accès après libération provoque un plantage du programme.
En ce qui concerne son utilisation, Electric Fence peut être utilisé pour simplement générer un fichier core qui sera utilisé pour une analyse post-mortem; cette méthode est cependant déconseillée, car du fait qu'Electric Fence alloue systèmatiquement 2 pages mémoire (soit 8 Ko sur les architectures i386), le programme débugué nécessitera une quantité de mémoire considérablement plus importante.
 
Figure 1 - Système d'allocation mémoire par défaut

A titre de référence, voici la liste des variables d'environnement succeptibles d'être utilisées pour modifier le comportement d'Electric Fence :

EF_DISABLE_BANNER
    Si cette variable a une valeur différente de zéro, alors Electric Fence n'affichera pas de message lors de son démarrage. L'utilisation de cette fonctionnalité est extrêmement déconseillée si vous linkez votre programme statiquement, car vous risquer d'oublier d'enlever Electric Fence dans la version en production de votre programme, avec les problèmes de performances que cela comporte.

EF_PROTECT_BELOW
    Par défaut (comme le montre la figure 1 qui ne devrait pas être bien loin si elle ne s'est pas perdue au département PAO), Electric Fence bloque l'accès à la page qui suit la zone allouée. Par conséquent si l'on tente d'accéder en dehors de la zone allouée par le bas, le problème ne sera pas détecté. Pour palier à ce problème, lorsque la variable EF_PROTECT_BELOW a une valeur non nulle, le mécanisme inverse est utilisé, c'est-à-dire que c'est la première page qui est bloquée, et la zone mémoire allouée est placée au début de la deuxième page (voir la Figure 2).
 
Figure 2 - Allocation mémoire avec EF_PROTECT_BELOW

EF_PROTECT_FREE
    Si cette variable est positionnée, Electric Fence ne retournera pas la mémoire désallouée avec free() au système, mais au contraire, il la rendra totalement inaccessible au programme. Cette fonctionnalité est très utile si vous suspectez que le programme que vous tentez de débugger essaye d'accéder à des zones mémoire précédemment désallouées. Attention, cette fonctionnalité décuple les besoins en mémoire de l'application, vu que la mémoire allouée est rendue inutilisable.

EF_ALLOW_MALLOC_0
    Par défaut, Electric Fence intercepte les appels à la fonction malloc() avec une taille nulle, car ils sont le plus souvent dus à un bug (quel pourrait bien être l'intérêt d'allouer une zone de taille nulle?!). Cependant cette fonctionnalité peut être désactivée en donnant une valeur non-nulle à la variable d'environnement EF_ALLOW_MALLOC_0.

EF_ALIGNMENT
    Ce paramètre permet de changer la valeur de l'alignement des zones mémoire retournées par Electric Fence. En quoi est-ce important? Tout simplement parce que par défault, les tailles des zones mémoires sont des multiples de la taille des mots mémoire, soit 4 octets (32 bits) sur la plupart des architectures ou 8 octets (64 bits) sur les Alpha et UltraSparc. Etant donné que chaque zone mémoire est un multiple de 4 octets, un dépassement de moins de 3 octets ne sera pas intercepté par Electric Fence.
Pour vous convaincre (si si, je vois bien à votre air dubitatif que vous n'étes pas totalement convaincu), modifions l'exemple eftest1.c précédent de la manière suivante :
 

eftest2.c
#include  <stdio.h>
#include  <errno.h>

int main (int argc, char **argv) {
    char *p;
    int  i;

    if (! (p = (char *) malloc(10*sizeof(char)))) {
       perror("echec de malloc:");
       exit(-1);
    }
    for (i=0; i<=10; i++) {
        p[i]=i;
    }
    printf("Fin du test\n");
    exit(0);
}

Comme vous pouvez le voir, la seule modification a consisté à remplacer le tableau d'"int" par un tableau de "char".
Maintenant, compilons et exécutons ce programme :

# gdb -q ./eftest2
(gdb) set environment LD_PRELOAD=libefence.so.0.0
(gdb) run
Starting program: /home/vincent/articles/./eftest2

  Electric Fence 2.0.5 Copyright (C) 1987-1998 Bruce Perens.
Fin du test

Program exited normally.

Surprise... Cette fois l'erreur de dépassement n'a pas été trouvée par Electric Fence, car lorsque l'allocation de 10 octets a été demandé, c'est en fait un nombre entier de mots machine de 4 octets qui ont été alloués (3 dans notre exemple), ce qui nous fait 12 caractères. Le code de l'exemple n'accédant qu'aux 11 premiers, aucune erreur n'a été détectée. Par contre, si l'on tente d'accéder au delà de ces 12 octets, alors l'erreur sera interceptée; vous pouvez le tester par vous-même en remplaçant le "10" de la boucle "for" par "12".
Alors comment faire pour détecter les problèmes d'allocation mémoire dont la taille est inférieure à la taille d'un mot machine? Heureusement, Electric Fence fourni un mécanisme pour contourner ce problème : la variable EF_ALIGNMENT peut être utilisée pour définir sur combien d'octets l'alignement des zones allouées sera fait. Pour détecter les dépassements d'un seul octet, il suffit donc de positionner cette variable à 1. Pourquoi n'est ce pas fait par défaut ? Tout simplement à cause du fait que la fonction malloc() est sensée retourner une zone mémoire alignée sur un mot machine et que tous les processeurs ne sont pas capables d'accéder à des données qui ne respectent pas scrupuleusement cette contrainte d'alignement (les processeurs Alpha par exemple).

(gdb) set environment EF_ALIGNMENT=1
(gdb) run
Starting program: /home/vincent/articles/./eftest2

  Electric Fence 2.0.5 Copyright (C) 1987-1998 Bruce Perens.

Program received signal SIGSEGV, Segmentation fault.
0x804857c in main (argc=1, argv=0xbffffcc4) at eftest2.c:13
13              p[i]=i;
(gdb)

Cette fois, en enlevant la contrainte d'alignement, le problème est bien intercepté.
 
 
Astuce
Lorsqu'Electric Fence est utilisé pour débugger un programme, les besoins en mémoire de celui-ci sont décuplés, et il se peut que vous atteignez rapidement les limites de la mémoire virtuelle de votre machine. Si vous rencontrez ce problème, il est facile d'y remédier temporairement grâce à un fichier de swap supplémentaire (oui : un fichier, pas une partition)
Supposons que vous estimiez à 80 Mo votre besoin de mémoire supplémentaire. Il vous suffit de :
1 - Créer un fichier vide de 80 Mo :
# dd if=/dev/zero of=/tmp/extraswap count=80 bs=1M
80+0 records in
80+0 records out
2 - Le "formatter" avec la commande mkswap :
# mkswap /tmp/extraswap
Setting up swapspace version 1, size = 83881984 bytes
3 - Monter le fichier créé :
# swapon /tmp/extraswap

A ce stade, vous pouvez utiliser la commande 'free' pour vérifier que la taille de votre espace de swap a bien augmenté de 80 Mo. Lorsque vous n'aurez plus besoin de ce swap supplémentaire, vous pourrez le désactiver grâce à la commande 'swapoff /tmp/extraswap', puis effacer le fichier.
Attention : si vous effacez le fichier avant d'utiliser la commande swapoff, vous avez de fortes chances (99%) de planter votre système.

    strace & ltrace

    Ces 2 outils ne sont pas aussi génériques que ceux que nous venons de présenter, et leur intérêt est souvent plus limité pour débugger un programme dont vous êtes l'auteur. Cependant, ils ont pour particularité (à la différence des outils présentés jusqu'à présent) d'être extrêmement utiles pour déterminer ce qui se passe dans un programme pour lequel vous ne disposez pas du code source.

        strace

    Cet outil permet de visualiser les appels systèmes effectués par le programme tracé. Notons au passage que le système Solaris comporte également un outil similaire, nommé 'truss' dans les anciennes versions et 'trace' dans les versions plus récentes.
Essayons strace sur le programme d'exemple eftest2 dont nous avons donné le code source un peu plus haut :
 
# strace ./eftest2
execve("./eftest2", ["./eftest2"], [/* 25 vars */]) = 0
brk(0)                                  = 0x8049840
old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40016000
open("/etc/ld.so.preload", O_RDONLY)    = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=38822, ...}) = 0
old_mmap(NULL, 38822, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40017000
close(3)                                = 0
open("/lib/libc.so.6", O_RDONLY)        = 3
fstat(3, {st_mode=S_IFREG|0755, st_size=1057576, ...}) = 0
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\224\314"..., 4096) = 4096
[...]
brk(0x8049cb8)                          = 0x8049cb8
brk(0x804a000)                          = 0x804a000
[...]
ioctl(1, TCGETS, {B9600 opost isig icanon echo ...}) = 0
write(1, "Fin du test\n", 12)           = 12
[...]
close(3)                                = 0
munmap(0x40017000, 4096)                = 0
_exit(0)                                = ?
#

Outre les premières lignes qui correspondent à l'initialisation du processus et des bibliothèques dynamiques qu'il utilise, on voit les appels à brk (appel système utilisé par la fonction malloc) et à write (correspondant à la fonction printf).

Ce type d'outil est par exemple très utile si l'on cherche à connaître les fichiers d'initialisation lus par un programme, auquel cas il suffira de rechercher les occurences de l'appel système 'read'.

        ltrace

Le fonctionnement de ltrace est relativement similaire à celui de strace, à la différence près que ltrace montre non pas les appels systèmes, mais les appels aux fonctions de bibliothèques. Voici par exemple les informations fournies par ltrace sur le même programme d'exemple.
 
# ltrace ./eftest2
__libc_start_main(0x08048610, 1, 0xbffffd14, 0x080483ec, 0x080486dc <unfinished ...>
__monstartup(0x080484f0, 0x080486f8, 0xbffffcb8, 0x4004f4a8, 0x401221e8) = 0
atexit(0x08048470)                                = 0
__register_frame_info(0x08049730, 0x08049828, 0xbffffcb8, 0x4004f4a8, 0x401221e8) = 0x40122e60
mcount(0x08049744, 0xbffffcc8, 0xbffffce8, 0x4003dbcc, 1) = 0x40120a58
malloc(10)                                        = 0x08049cb0
printf("Fin du test\n"Fin du test
)                           = 12
exit(0)                                           = <void>
_mcleanup(0x401221e8, 0x40015ec0, 1, 0, 0x4004f360) = 0x08049ca8
__deregister_frame_info(0x08049730, 0x400163f8, 0xbffffc6c, 0x400e8121, 0x08049848) = 0x08049828
+++ exited (status 0) +++
#

La plupart des appels mis en évidence sont en fait internes au fonctionnement de la libc, mais l'on voit tout de même les appels des 2 fonctions de bibliothèques faits par notre programme : malloc() et printf().

Maintenant, non seulement, vous n'avez plus aucune excuse pour avoir des fuites mémoire dans vos programmes, mais vous étes également armés pour corriger les bugs de tout autre programme que vous pourrez être amené à utiliser.
 
 
Références
GNU gdb :
    http://sources.redhat.com/gdb/
Electric Fence : 
    Disponible en paquet Debian sous le nom 'electric-fence'. 
    Sources : ftp://ftp.perens.com/pub/ElectricFence/
KDevelop :
    http://www.kdevelop.org/
gcc-checker : 
    http://www.gnu.org/software/checker/checker.html
glib : 
    ftp://ftp.gtk.org/pub/gtk/v1.2/
strace :
    http://www.wi.leidenuniv.nl/~wichert/strace/
ltrace :
    ftp://ftp.debian.org/debian/dists/unstable/main/source/utils/ltrace_0.3.10.tar.gz

 
 
Utilisateur de GNU/Linux depuis 1993, Vincent Renardias est activement impliqué dans son développement depuis 1996 : Développeur de la distribution Debian, auteur de la traduction Française de l'environnement GNOME, créateur du groupe d'utilisateurs Linux de Marseille (PLUG). Il continue aujourd'hui activement a promouvoir le système GNU/Linux en tant que responsable technique de la société Echo, créatrice du portail voila.fr.
Vincent Renardias <vincent@echo.fr>