Utilitaires de développement

valgrind

Cet utilitaire fonctionnant uniquement sous linux permet de détecter les fuites mémoires ainsi que les accès invalides à la mémoire. Il fonctionne avec les dll, les threads, le C et le C++, ce qui n'est pas souvent le cas des autres utilitaires (memprof).

C'est un must que tout developpeur qui se respecte doit utiliser. Ce n'est sans doute pas un hasard si cet utilitaire est intégré à kdevelop.

Soit le programme suivant :


#include <stdio.h>
#include <stdlib.h>

int main( int argc, char * argv[] )
{
    char * p; char c;

    p = (char *)malloc(1);

    p[0] = 'a'; /* Pas de probleme */
    p[1] = 'b'; /* Access a de la memoire non allouee (l.11)*/
    free(p); /* Liberation de la memoire - Pas de probleme (l.12)*/
    free(p); /* Liberation de la memoire une 2 fois (=> Probleme) (l.13)*/
    c = p[0]; /* Acces en lecture a de la memoire desallouee (l.14)*/
    p[0] = 'c' ; /* Acces en ecriture a de la memoire desallouee (l.15)*/

    p = (char *)malloc(1234); /* Allocation sans liberation (l.17)*/
    return 0;
}

On le compile en debug (-g) afin que valgrind nous indique les erreurs avec le numéro de ligne et le nom du fichier :

$ gcc -g testvalgrind.c -o testvalgrind

On obtient la sortie suivante (un peu nettoyée) qui nous indique toutes les erreurs de ce programme, avec les numéros de ligne :

$ valgrind --leak-check=yes ./testvalgrind
==15101== Memcheck, a.k.a. Valgrind, a memory error detector for x86-linux.
==15101== Copyright (C) 2002-2003, and GNU GPL'd, by Julian Seward.
==15101== Using valgrind-2.0.0, a program supervision framework for x86-linux.
==15101== Copyright (C) 2000-2003, and GNU GPL'd, by Julian Seward.
==15101== Estimated CPU clock rate is 3020 MHz
==15101== For more details, rerun with: -v
==15101==
==15101== Invalid write of size 1
==15101==    at 0x80483B6: main (test.c:11)
==15101==
==15101== Invalid free() / delete / delete[]
==15101==    at 0x4002AF19: free (vg_replace_malloc.c:231)
==15101==    by 0x80483D1: main (test.c:13)
==15101==    Address 0x411D0024 is 0 bytes inside a block of size 1 free'd
==15101==    at 0x4002AF19: free (vg_replace_malloc.c:231)
==15101==    by 0x80483C3: main (test.c:12)
==15101==
==15101== Invalid read of size 1
==15101==    at 0x80483D8: main (test.c:14)
==15101==    Address 0x411D0024 is 0 bytes inside a block of size 1 free'd
==15101==    at 0x4002AF19: free (vg_replace_malloc.c:231)
==15101==    by 0x80483C3: main (test.c:12)
==15101==
==15101== Invalid write of size 1
==15101==    at 0x80483E0: main (test.c:15)
==15101==    Address 0x411D0024 is 0 bytes inside a block of size 1 free'd
==15101==    at 0x4002AF19: free (vg_replace_malloc.c:231)
==15101==    by 0x80483C3: main (test.c:12)
==15101==
==15101== ERROR SUMMARY: 4 errors from 4 contexts (suppressed: 0 from 0)
==15101== malloc/free: in use at exit: 1234 bytes in 1 blocks.
==15101== malloc/free: 2 allocs, 2 frees, 1235 bytes allocated.
==15101== For counts of detected errors, rerun with: -v
==15101== searching for pointers to 1 not-freed blocks.
==15101== checked 3611120 bytes.
==15101==
==15101==
==15101== 1234 bytes in 1 blocks are definitely lost in loss record 1 of 1
==15101==    at 0x4002AC64: malloc (vg_replace_malloc.c:153)
==15101==    by 0x80483EF: main (test.c:17)
==15101==
==15101== LEAK SUMMARY:
==15101==    definitely lost: 1234 bytes in 1 blocks.
==15101==    possibly lost:   0 bytes in 0 blocks.
==15101==    still reachable: 0 bytes in 0 blocks.
==15101==         suppressed: 0 bytes in 0 blocks.
==15101== Reachable blocks (those to which a pointer was found) are not shown.
==15101== To see them, rerun with: --show-reachable=yes
==15101==

gprof et sprof

Ces utilitaires permettent de mesurer le temps passé dans chaque fonction d'un programme. Cela permet de voir quelles parties d'un programme il faut optimiser.

L'utilitaire le plus connu et le mieux documenté est gprof.

Pour utiler gprof, il faut penser à compiler et lier son programme avec l'option -pg.

Malheureusement, celui-ci ne fonctionne qu'avec un seul thread et il ne montre pas les appels dans les librairies partagées.

Ici, une solution pour faire fonctionner gprof avec plusieurs threads.

Pour profiler des librairies partagées, la seule solution avec gprof consiste à lier celles-ci de manière statique avec l'exécutable, ce qui n'est pas toujours possible.

L'alternative à gprof est sprof. Malheureusement, sprof est pauvrement documenté. Voici le man :

SPROF(1)                                                              SPROF(1)

NAME
       sprof - Read and display shared object profiling data

SYNOPSIS
       sprof -p|-c [-q]

DESCRIPTION
       --call-pairs, -c

               print list of count paths and their number of use

       --flat-profile, -p

               generate flat profile with counts and ticks

       --graph, -q

               generate call graph

AUTHOR
       sprof is written by Ulrich Drepper for the GNU C Library

       This man page is written by Joel Klecker  for the
       Debian GNU/Linux system.

Ce que le man ne dit pas est comment faire pour profiler une librairie partagée. Cela repose principalement sur la mise en place de 3 variables d'environnements, LD_PRELOAD, LD_PROFILE et LD_PROFILE_OUTPUT.

Le fichier créé a le même nom que la dll avec l'extension .profile. Dans l'exemple ci-dessous, il s'agit de libshared.so.profile.

Le script suivant permet de profiler la librairie partagée libshared.so chargée avec ldopen() par main :

rm -f libshared.so.profile
LD_PRELOAD=./libshared.so
LD_PROFILE=./libshared.so
LD_PROFILE_OUTPUT=./
./main
LD_PRELOAD=
LD_PROFILE=
LD_PROFILE_OUTPUT=
sprof libshared.so libshared.so.profile

Valid XHTML 1.0!