🔝 Retour au Sommaire
Le profiling sous Linux dispose d'un écosystème d'outils puissants et gratuits qui permettent d'analyser en profondeur les performances de vos applications FreePascal. Contrairement à Windows, Linux offre nativement plusieurs outils complémentaires.
- Outils gratuits et open source : gprof, Valgrind, perf sont libres et très performants
- Intégration système : accès direct aux métriques du noyau Linux
- Précision : mesures très détaillées des performances CPU et mémoire
- Débogage avancé : détection de fuites mémoire, erreurs d'accès, problèmes de cache
Linux propose principalement deux outils complémentaires :
| Outil | Usage Principal | Ce qu'il mesure |
|---|---|---|
| gprof | Profiling CPU | Temps d'exécution des fonctions |
| Valgrind | Profiling mémoire et CPU | Utilisation mémoire, fuites, accès invalides, cache |
Ces outils sont complémentaires : gprof pour le temps CPU, Valgrind pour la mémoire et les problèmes subtils.
gprof (GNU Profiler) est l'outil standard GNU pour le profiling de temps d'exécution. Il fonctionne exactement de la même manière sous Linux que sous Windows, mais avec une meilleure intégration système.
Sur Ubuntu/Debian :
sudo apt update
sudo apt install binutils gcc gdbSur Fedora/RedHat :
sudo dnf install binutils gcc gdbgprof est généralement déjà installé avec le compilateur GCC.
Pour utiliser gprof, compilez votre programme avec l'option -pg :
fpc -pg -gl MonProgramme.pasOptions importantes :
-pg: Active l'instrumentation pour gprof-gl: Inclut les informations de débogage avec numéros de ligne-O2: (Optionnel) Active les optimisations pour profiler du code réaliste
# Compilation
fpc -pg -gl -O2 MonProgramme.pas
# Vérification
ls -lh MonProgramme
# Le fichier doit être légèrement plus gros (code d'instrumentation)./MonProgrammeLe programme génère automatiquement un fichier gmon.out dans le répertoire courant.
Note : Le fichier gmon.out est écrasé à chaque exécution.
gprof MonProgramme gmon.out > rapport.txtOu pour visualiser directement :
gprof MonProgramme gmon.out | lessOuvrez rapport.txt avec votre éditeur préféré :
nano rapport.txt
# ou
gedit rapport.txt
# ou
kate rapport.txtLe rapport contient deux sections principales :
Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ms/call ms/call name
33.34 0.35 0.35 500000 0.00 0.00 CalculerDistance
28.57 0.65 0.30 100000 0.00 0.01 TrierDonnees
19.05 0.85 0.20 1000000 0.00 0.00 ValiderEntree
14.29 1.00 0.15 1 150.00 1000.00 FonctionPrincipale
Lecture du tableau :
- % time : Pourcentage du temps total dans cette fonction (33.34% pour CalculerDistance)
- cumulative seconds : Temps cumulé (0.35s pour la première fonction)
- self seconds : Temps passé uniquement dans cette fonction
- calls : Nombre d'appels (500 000 fois pour CalculerDistance)
- self ms/call : Temps moyen par appel
- name : Nom de la fonction
Interprétation : CalculerDistance est la fonction la plus coûteuse (33% du temps) et est appelée très souvent (500 000 fois). C'est la priorité pour l'optimisation.
index % time self children called name
<spontaneous>
[1] 100.0 0.15 0.85 FonctionPrincipale [1]
0.35 0.00 500000/500000 CalculerDistance [2]
0.30 0.00 100000/100000 TrierDonnees [3]
0.20 0.00 1000000/1000000 ValiderEntree [4]
-----------------------------------------------
0.35 0.00 500000/500000 FonctionPrincipale [1]
[2] 33.3 0.35 0.00 500000 CalculerDistance [2]
-----------------------------------------------
Ce que ça montre :
FonctionPrincipale[1] est le point d'entrée (100% du temps)- Elle appelle
CalculerDistance500 000 fois - Chaque fonction est liée à ses appelants et appelés
gprof -b MonProgramme gmon.out | head -n 50 > top50.txtgprof -e FonctionAIgnorer MonProgramme gmon.outgprof -A MonProgramme gmon.out > rapport_annote.txtASLR (Address Space Layout Randomization) peut perturber le profiling :
# Désactiver temporairement
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
# Exécuter le profiling
./MonProgramme
gprof MonProgramme gmon.out > rapport.txt
# Réactiver (important pour la sécurité)
echo 2 | sudo tee /proc/sys/kernel/randomize_va_spacePour des résultats plus stables :
taskset -c 0 ./MonProgrammeCela force l'exécution sur le CPU 0 uniquement.
Créez un script profile.sh :
#!/bin/bash
# Script de profiling automatique
PROGRAM=$1
if [ -z "$PROGRAM" ]; then
echo "Usage: ./profile.sh MonProgramme"
exit 1
fi
# Compilation
echo "Compilation avec profiling..."
fpc -pg -gl -O2 $PROGRAM.pas
# Exécution
echo "Exécution du programme..."
./$PROGRAM
# Génération du rapport
echo "Génération du rapport..."
gprof $PROGRAM gmon.out > profiling_$(date +%Y%m%d_%H%M%S).txt
echo "Rapport généré : profiling_$(date +%Y%m%d_%H%M%S).txt"Utilisation :
chmod +x profile.sh
./profile.sh MonProgrammeValgrind est une suite d'outils de débogage et de profiling extrêmement puissante. Elle fonctionne en exécutant votre programme dans une machine virtuelle qui surveille chaque accès mémoire et instruction CPU.
Valgrind contient plusieurs outils :
| Outil | Fonction |
|---|---|
| Memcheck | Détection de fuites mémoire et erreurs d'accès |
| Cachegrind | Profiling du cache CPU (L1, L2, L3) |
| Callgrind | Profiling détaillé des appels de fonction |
| Massif | Profiling de l'utilisation du heap (tas) |
| Helgrind | Détection de problèmes de threads |
# Ubuntu/Debian
sudo apt update
sudo apt install valgrind
# Fedora/RedHat
sudo dnf install valgrind
# Vérification
valgrind --version- Fuites mémoire : mémoire allouée mais jamais libérée
- Accès invalides : lecture/écriture hors des limites
- Utilisation de mémoire non initialisée
- Double libération : libérer deux fois la même mémoire
- Débordements de pile (stack overflow)
Pas besoin de -pg, mais ajoutez des informations de débogage :
fpc -gl -gh MonProgramme.pasOptions :
-gl: Informations de débogage avec numéros de ligne-gh: Active le heap manager pour tracer les allocations
Commande de base :
valgrind --leak-check=full ./MonProgrammeCommande détaillée avec toutes les options utiles :
valgrind \
--leak-check=full \
--show-leak-kinds=all \
--track-origins=yes \
--verbose \
--log-file=memcheck_rapport.txt \
./MonProgrammeOptions expliquées :
--leak-check=full: Recherche exhaustive des fuites--show-leak-kinds=all: Affiche tous les types de fuites--track-origins=yes: Trace l'origine des valeurs non initialisées--verbose: Mode verbeux pour plus de détails--log-file=...: Sauvegarde le rapport dans un fichier
Exemple de sortie :
==12345== Memcheck, a memory error detector
==12345== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==12345== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info
==12345== Command: ./MonProgramme
==12345==
==12345== Invalid write of size 4
==12345== at 0x401234: EcrireDansTableau (MonProgramme.pas:42)
==12345== by 0x401156: FonctionPrincipale (MonProgramme.pas:28)
==12345== by 0x401089: main (MonProgramme.pas:15)
==12345== Address 0x5204050 is 0 bytes after a block of size 40 alloc'd
==12345== at 0x4C2DB8F: malloc (vg_replace_malloc.c:299)
==12345== by 0x401123: AllouerTableau (MonProgramme.pas:25)
==12345== LEAK SUMMARY:
==12345== definitely lost: 120 bytes in 3 blocks
==12345== indirectly lost: 0 bytes in 0 blocks
==12345== possibly lost: 40 bytes in 1 blocks
==12345== still reachable: 0 bytes in 0 blocks
==12345== suppressed: 0 bytes in 0 blocks
Interprétation :
- Invalid write : Écriture en dehors des limites à la ligne 42
- definitely lost: 120 bytes : Fuite mémoire confirmée (3 allocations jamais libérées)
- possibly lost: 40 bytes : Fuite possible (à vérifier)
- Les numéros de ligne permettent de localiser exactement le problème
definitely lost → Fuite confirmée, plus aucune référence
indirectly lost → Fuite indirecte (contenue dans une fuite principale)
possibly lost → Peut-être une fuite (pointeur vers le milieu du bloc)
still reachable → Mémoire non libérée mais toujours accessible (pas grave)
suppressed → Fuites ignorées (bibliothèques système)
Cachegrind simule le fonctionnement des caches CPU (L1, L2, L3) et compte :
- Les instructions exécutées
- Les accès mémoire
- Les défauts de cache (cache miss) qui ralentissent l'exécution
valgrind --tool=cachegrind ./MonProgrammeCela génère un fichier cachegrind.out.<pid>.
Utilisez cg_annotate pour lire le rapport :
cg_annotate cachegrind.out.12345Exemple de sortie :
--------------------------------------------------------------------------------
Ir I1mr ILmr Dr D1mr DLmr Dw D1mw DLmw file:function
--------------------------------------------------------------------------------
1,234,567,890 456 123 345,678,901 12,345 2,345 123,456,789 3,456 890 MonProgramme.pas:CalculerDistance
987,654,321 234 89 234,567,890 10,234 1,890 100,000,000 2,100 567 MonProgramme.pas:TrierDonnees
Colonnes expliquées :
- Ir : Instructions Read (nombre d'instructions exécutées)
- I1mr : Instruction cache L1 miss rate (défauts L1)
- Dr : Data Read (lectures de données)
- D1mr : Data cache L1 miss rate (défauts lecture L1)
- Dw : Data Write (écritures de données)
- D1mw : Data cache L1 write miss (défauts écriture L1)
Plus le nombre de "miss" est élevé, plus le code est lent car les données ne sont pas dans le cache rapide.
Pour voir ligne par ligne les statistiques :
cg_annotate --auto=yes cachegrind.out.12345 MonProgramme.pas > rapport_annote.txt- Plus précis : compte exact des instructions
- Pas besoin de
-pg: pas d'instrumentation du code - Visualisation graphique : avec KCachegrind
valgrind --tool=callgrind ./MonProgrammeGénère un fichier callgrind.out.<pid>.
Installation :
sudo apt install kcachegrindUtilisation :
kcachegrind callgrind.out.12345KCachegrind affiche :
- Un graphique visuel des appels de fonction
- Le temps passé dans chaque fonction (avec couleurs)
- Le graphe d'appels interactif
- Les statistiques de cache
- Le code source annoté
C'est l'outil le plus visuel et intuitif pour le profiling !
callgrind_annotate callgrind.out.12345Affiche un rapport similaire à gprof mais plus détaillé.
Massif trace l'utilisation du heap (tas) dans le temps :
- Combien de mémoire est allouée
- Quand les allocations ont lieu
- Quelles fonctions allouent le plus de mémoire
valgrind --tool=massif ./MonProgrammeGénère un fichier massif.out.<pid>.
ms_print massif.out.12345Exemple de sortie (graphique ASCII) :
MB
120 | :#
| @@@:#
| @@@@@@:#
100 | @@@@@@@@@:#
| @@@@@@@@@@@@:#
| @@@@@@@@@@@@@@@:#
80 | @@@@@@@@@@@@@@@@@@:#
| @@@@@@@@@@@@@@@@@@@@@:#
| @@@@@@@@@@@@@@@@@@@@@@@@:#
60 | @@@@@@@@@@@@@@@@@@@@@@@@@@@:#
| @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@:#
| @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@:#
40 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@:#
| @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@:#
| @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@:#
20 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@:#
| @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@:#
| @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@:#
0 +----------------------------------------------------------------------->s
0 10.00
Chaque @ ou : représente une allocation. On voit clairement :
- Le pic de mémoire à 120 MB
- La croissance progressive de l'utilisation
- Les moments d'allocation intense
Installation :
sudo apt install massif-visualizerUtilisation :
massif-visualizer massif.out.12345Affiche un graphique moderne avec :
- Courbe de l'utilisation mémoire dans le temps
- Liste des fonctions qui allouent le plus
- Détails de chaque snapshot
| Critère | gprof | Valgrind |
|---|---|---|
| Vitesse | Rapide (10-20% overhead) | Lent (10-50x plus lent) |
| Précision | Sampling (échantillonnage) | Exacte (instruction par instruction) |
| Mémoire | Non | Oui (Memcheck, Massif) |
| Cache | Non | Oui (Cachegrind) |
| Setup | Compilation avec -pg |
Aucune compilation spéciale |
| Threads | Limité | Excellent (Helgrind) |
| Usage | Profiling rapide temps CPU | Analyse approfondie et débogage |
Règle générale :
- gprof pour identifier rapidement les fonctions lentes
- Valgrind pour comprendre en détail les problèmes de mémoire et performance
# 1. Profiling rapide avec gprof
fpc -pg -O2 MonProgramme.pas
./MonProgramme
gprof MonProgramme gmon.out > gprof_rapport.txt
# 2. Identifier les fonctions lentes dans gprof_rapport.txt
# 3. Analyse détaillée avec Callgrind
fpc -gl -O2 MonProgramme.pas
valgrind --tool=callgrind ./MonProgramme
kcachegrind callgrind.out.*
# 4. Optimiser le code
# 5. Recommencer pour vérifier l'amélioration# 1. Compilation avec débogage
fpc -gl -gh MonProgramme.pas
# 2. Détection de fuites avec Memcheck
valgrind --leak-check=full \
--show-leak-kinds=all \
--track-origins=yes \
--log-file=memcheck.txt \
./MonProgramme
# 3. Analyser memcheck.txt pour trouver les fuites
# 4. Corriger le code
# 5. Profiler l'utilisation heap avec Massif
valgrind --tool=massif ./MonProgramme
massif-visualizer massif.out.*
# 6. Vérifier que l'utilisation mémoire est normale# 1. Profiler le cache
valgrind --tool=cachegrind ./MonProgramme
# 2. Analyser les défauts de cache
cg_annotate --auto=yes cachegrind.out.* MonProgramme.pas
# 3. Identifier les structures de données avec beaucoup de cache miss
# 4. Réorganiser les données pour améliorer la localité
# (ex: structure of arrays au lieu de array of structures)
# 5. Re-profiler pour confirmer l'améliorationAvec Callgrind, vous pouvez activer/désactiver le profiling dynamiquement :
valgrind --tool=callgrind --instr-atstart=no ./MonProgrammeDans votre code Pascal, utilisez les fonctions de contrôle :
{$IFDEF UNIX}
uses
BaseUnix;
procedure DemarrerProfiling;
begin
// Démarre l'instrumentation Callgrind
FpSystem('callgrind_control -i on');
end;
procedure ArreterProfiling;
begin
// Arrête l'instrumentation
FpSystem('callgrind_control -i off');
end;
{$ENDIF}
// Utilisation
DemarrerProfiling;
CodeACritiqueAProfler;
ArreterProfiling;Utilisez cg_diff pour comparer deux sessions Callgrind :
# Profiling avant optimisation
valgrind --tool=callgrind -o callgrind.avant ./MonProgramme
# Optimisation du code...
# Profiling après optimisation
valgrind --tool=callgrind -o callgrind.apres ./MonProgramme
# Comparaison
cg_diff callgrind.avant callgrind.apresPour profiler les applications multi-threadées :
# Vérifier les race conditions
valgrind --tool=helgrind ./MonProgramme
# Profiler avec support threads
valgrind --tool=callgrind --separate-threads=yes ./MonProgrammeCréez un fichier suppressions.txt pour ignorer les fausses alertes :
{
<nom_suppression>
Memcheck:Leak
fun:malloc
fun:InitializeRTL
}
Utilisez-le :
valgrind --suppressions=suppressions.txt ./MonProgrammeCréez profile_complet.sh :
#!/bin/bash
PROGRAM=$1
echo "=== Compilation ==="
fpc -gl -gh -O2 $PROGRAM.pas
echo "=== Memcheck - Fuites mémoire ==="
valgrind --leak-check=full \
--log-file=memcheck.log \
./$PROGRAM
echo "=== Callgrind - Performance ==="
valgrind --tool=callgrind \
--callgrind-out-file=callgrind.out \
./$PROGRAM
echo "=== Massif - Heap ==="
valgrind --tool=massif \
--massif-out-file=massif.out \
./$PROGRAM
echo "=== Génération des rapports ==="
callgrind_annotate callgrind.out > callgrind_rapport.txt
ms_print massif.out > massif_rapport.txt
echo "=== Terminé ==="
echo "Rapports disponibles:"
echo " - memcheck.log"
echo " - callgrind_rapport.txt"
echo " - massif_rapport.txt"program ExempleProfilageBuggy;
{$mode objfpc}{$H+}
uses
SysUtils, Classes;
type
TPersonne = class
Nom: string;
Age: Integer;
end;
var
Liste: TList;
i: Integer;
p: TPersonne;
begin
Liste := TList.Create;
// PROBLÈME 1: Fuite mémoire - objets jamais libérés
for i := 1 to 10000 do
begin
p := TPersonne.Create;
p.Nom := 'Personne' + IntToStr(i);
p.Age := Random(100);
Liste.Add(p);
end;
// PROBLÈME 2: Accès hors limites
WriteLn('Accès à l''élément 10001:', TPersonne(Liste[10001]).Nom);
// PROBLÈME 3: Liste libérée mais pas son contenu
Liste.Free;
end.# Compilation
fpc -gl -gh ExempleProfilageBuggy.pas
# Détection des problèmes
valgrind --leak-check=full ./ExempleProfilageBuggy==12345== Invalid read of size 8
==12345== at 0x401234: main (ExempleProfilageBuggy.pas:28)
==12345== Address 0x5204050 is not stack'd, malloc'd or (recently) free'd
==12345== LEAK SUMMARY:
==12345== definitely lost: 480,000 bytes in 10,000 blocks
Diagnostic :
- Accès invalide ligne 28 (hors limites)
- Fuite de 480 Ko (10 000 objets TPersonne jamais libérés)
program ExempleProfilageCorrige;
{$mode objfpc}{$H+}
uses
SysUtils, Classes;
type
TPersonne = class
Nom: string;
Age: Integer;
end;
var
Liste: TList;
i: Integer;
p: TPersonne;
begin
Liste := TList.Create;
try
// Création des objets
for i := 1 to 10000 do
begin
p := TPersonne.Create;
p.Nom := 'Personne' + IntToStr(i);
p.Age := Random(100);
Liste.Add(p);
end;
// Accès VALIDE (dans les limites)
if Liste.Count > 0 then
WriteLn('Première personne: ', TPersonne(Liste[0]).Nom);
finally
// Libération correcte de tous les objets
for i := 0 to Liste.Count - 1 do
TPersonne(Liste[i]).Free;
Liste.Free;
end;
end.valgrind --leak-check=full ./ExempleProfilageCorrigeRésultat :
==12346== HEAP SUMMARY:
==12346== in use at exit: 0 bytes in 0 blocks
==12346== total heap usage: 10,001 allocs, 10,001 frees
==12346==
==12346== All heap blocks were freed -- no leaks are possible
✅ Aucune fuite, tous les objets libérés correctement !
Le profiling sous Linux offre des outils exceptionnels :
✅ Rapide et simple pour identifier les fonctions lentes
✅ Intégré nativement
✅ Parfait pour une première analyse
✅ Détection exhaustive des problèmes mémoire
✅ Profiling très précis du CPU et du cache
✅ Visualisation graphique avec KCachegrind
✅ Indispensable pour du code robuste
Recommandation : Utilisez les deux ! gprof pour la vitesse, Valgrind pour la qualité et la rigueur.
Règle d'or du profiling :
Mesurez, optimisez, mesurez à nouveau. Jamais dans l'autre sens !