Skip to content

Latest commit

 

History

History
1406 lines (961 loc) · 36 KB

File metadata and controls

1406 lines (961 loc) · 36 KB

🔝 Retour au Sommaire

12.6.1 Débogage avancé avec LLDB v12

Introduction

LLDB (Low Level Debugger) est le débogueur moderne et puissant qui a remplacé GDB (GNU Debugger) pour les plateformes Apple et de nombreux systèmes Linux. Delphi 13 Florence intègre la version 12 de LLDB, apportant des améliorations significatives en termes de performances, de stabilité et de fonctionnalités.

Pour un débutant, LLDB peut sembler intimidant avec son interface en ligne de commande et ses nombreuses options. Cependant, une fois que vous comprenez ses concepts de base, LLDB devient un outil extraordinairement puissant qui vous donne un contrôle total sur l'exécution de votre application.

Ce chapitre vous guide à travers les capacités avancées de LLDB v12, en commençant par les bases et en progressant vers des techniques sophistiquées de débogage.

Qu'est-ce que LLDB ?

Définition et contexte

LLDB est un débogueur de nouvelle génération développé par le projet LLVM. Il est utilisé par :

  • Xcode d'Apple (pour macOS et iOS)
  • Android Studio (pour le débogage natif)
  • De nombreux IDE modernes
  • Delphi (depuis la version 10.3 Rio)

LLDB offre une architecture modulaire et extensible, ce qui le rend adapté à de nombreuses plateformes et langages de programmation.

Pourquoi LLDB plutôt que GDB ?

Performances supérieures

  • Démarrage plus rapide
  • Réponse plus réactive lors des commandes
  • Meilleure gestion de la mémoire

Architecture moderne

  • Code plus propre et maintenable
  • Meilleure extensibilité
  • Support natif des nouvelles plateformes

Support Apple

  • Seul débogueur officiellement supporté pour macOS et iOS modernes
  • Intégration optimale avec les outils Apple

Support Delphi amélioré

  • Meilleure compréhension des types Object Pascal
  • Visualisation améliorée des structures Delphi
  • Intégration fluide avec l'IDE

LLDB dans l'écosystème Delphi

Lorsque vous déboguez une application Delphi sur :

  • macOS : LLDB est utilisé automatiquement
  • iOS : LLDB est le seul débogueur disponible
  • Linux : LLDB est utilisé (selon la configuration)
  • Android : LLDB peut être utilisé pour le débogage natif
  • Windows : Le débogueur Windows natif est utilisé par défaut

Nouveautés de LLDB v12

Delphi 13 Florence intègre LLDB version 12, qui apporte de nombreuses améliorations par rapport aux versions précédentes.

Améliorations de performance

Démarrage accéléré

Le temps de démarrage d'une session de débogage a été réduit de manière significative :

  • Chargement plus rapide des symboles de débogage
  • Initialisation optimisée du débogueur
  • Connexion plus rapide aux processus cibles

Temps de réponse amélioré

Les opérations courantes sont plus rapides :

  • Évaluation d'expressions (variables, propriétés)
  • Navigation dans la pile d'appels
  • Inspection de structures complexes
  • Exécution pas à pas

Gestion mémoire optimisée

LLDB v12 utilise moins de mémoire lors du débogage d'applications complexes, permettant :

  • Le débogage d'applications plus volumineuses
  • Des sessions de débogage plus longues sans ralentissement
  • Meilleure stabilité générale

Support amélioré des types Delphi

Chaînes de caractères

LLDB v12 comprend mieux les types de chaînes Delphi :

  • String (UnicodeString)
  • AnsiString
  • ShortString
  • WideString

L'affichage est plus naturel et ne montre plus de données brutes illisibles.

Tableaux dynamiques

Les tableaux dynamiques (TArray<T>) sont maintenant :

  • Correctement reconnus et affichés avec leur taille
  • Navigables élément par élément
  • Affichés avec leur contenu réel

Génériques

Le support des types génériques a été considérablement amélioré :

  • TList<T>, TDictionary<K,V>, etc. sont correctement reconnus
  • Les types paramétrés s'affichent clairement
  • L'inspection des éléments internes fonctionne mieux

Enregistrements (Records)

Les records complexes, notamment ceux avec méthodes et opérateurs, sont mieux gérés :

  • Affichage structuré des champs
  • Support des records génériques
  • Meilleure visualisation des variants records

Stabilité et fiabilité

Moins de crashs

LLDB v12 est plus robuste face à :

  • Des structures de données corrompues
  • Des pointeurs invalides
  • Des conditions de threading complexes
  • Des applications avec beaucoup de modules

Récupération d'erreurs

En cas de problème, LLDB v12 :

  • Tente de récupérer gracieusement
  • Fournit des messages d'erreur plus clairs
  • Maintient la session de débogage active si possible

Débogage multi-thread amélioré

Le support du multi-threading a été renforcé :

  • Meilleure synchronisation entre threads
  • Moins de deadlocks lors du débogage
  • Visualisation plus claire de l'état des threads

Nouvelles fonctionnalités

Watchpoints améliorés

Les watchpoints (surveillance de modification de mémoire) sont :

  • Plus fiables
  • Plus rapides
  • Supportent plus de cas d'usage

Expressions conditionnelles plus puissantes

Les conditions dans les breakpoints peuvent maintenant :

  • Évaluer des expressions plus complexes
  • Appeler des fonctions (avec précaution)
  • Utiliser des opérateurs avancés

Support de scripting Python amélioré

LLDB v12 offre une meilleure intégration avec Python pour :

  • Automatiser des tâches de débogage
  • Créer des commandes personnalisées
  • Analyser des dumps mémoire

Interface LLDB dans Delphi

Accéder à la console LLDB

Lorsque vous déboguez une application sur une plateforme utilisant LLDB, vous pouvez accéder à la console LLDB directement depuis l'IDE.

Ouvrir la console LLDB :

  1. Démarrez une session de débogage (F9)
  2. Mettez l'application en pause (point d'arrêt ou pause manuelle)
  3. Dans Delphi, allez dans View > Debug Windows > LLDB Console
  4. Une fenêtre s'ouvre affichant l'invite LLDB : (lldb)

À quoi ressemble la console :

(lldb) target create "/Users/dev/MyApp/MyApp"
Current executable set to '/Users/dev/MyApp/MyApp' (x86_64).
(lldb) process attach --pid 12345
Process 12345 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
(lldb)

Vous pouvez maintenant taper des commandes LLDB directement.

Commandes de base

Avant d'explorer les commandes avancées, familiarisons-nous avec les commandes de base.

help : Obtenir de l'aide

(lldb) help
(lldb) help breakpoint    # Aide sur les breakpoints
(lldb) help thread        # Aide sur les threads

process : Contrôler le processus

(lldb) process continue    # Reprendre l'exécution (équivalent F9)
(lldb) process interrupt   # Mettre en pause
(lldb) process kill        # Terminer le processus

thread : Travailler avec les threads

(lldb) thread list         # Lister tous les threads
(lldb) thread select 2     # Sélectionner le thread 2
(lldb) thread backtrace    # Voir la pile du thread actuel

frame : Naviguer dans la pile d'appels

(lldb) frame select 0      # Sélectionner le frame 0 (fonction actuelle)
(lldb) frame variable      # Voir toutes les variables locales
(lldb) frame info          # Infos sur le frame actuel

expression (ou expr ou p) : Évaluer une expression

(lldb) expr MonAge
(int) $0 = 25

(lldb) p MonNom
(string) $1 = "Jean Dupont"

(lldb) expr MaListe.Count
(int) $2 = 10

Commandes de navigation

step : Exécution pas à pas

(lldb) step      # Équivalent F7 (Step Into)
(lldb) next      # Équivalent F8 (Step Over)
(lldb) finish    # Équivalent Shift+F8 (Step Out)
(lldb) continue  # Équivalent F9 (Continue)

Raccourcis :

  • s pour step
  • n pour next
  • c pour continue

Affichage de l'état du programme

backtrace (ou bt) : Pile d'appels

(lldb) bt
* thread #1, queue = 'com.apple.main-thread'
  * frame #0: 0x00000001000015a0 MyApp`TForm1.Button1Click at MainForm.pas:42
    frame #1: 0x00000001000023b0 MyApp`TControl.Click at Controls.pas:1234
    frame #2: 0x0000000100003210 MyApp`TButton.Click at Buttons.pas:567
    frame #3: 0x0000000100005678 MyApp`ApplicationRun at Forms.pas:890

frame variable : Variables locales

(lldb) frame variable
(int) Counter = 5
(string) Name = "Test"
(TMyObject *) MyObj = 0x00007f9e8c400000

thread list : État de tous les threads

(lldb) thread list
Process 12345 stopped
* thread #1: tid = 0x1a03, 0x00000001000015a0 MyApp`TForm1.Button1Click
  thread #2: tid = 0x1a04, 0x00007fff204050c0 libsystem_kernel.dylib`__workq_kernreturn
  thread #3: tid = 0x1a05, 0x00007fff204050c0 libsystem_kernel.dylib`__workq_kernreturn

L'astérisque (*) indique le thread actuel.

Breakpoints avancés avec LLDB

Créer des breakpoints en ligne de commande

Bien que vous puissiez créer des breakpoints dans l'IDE, LLDB offre plus de contrôle.

Breakpoint sur une ligne spécifique :

(lldb) breakpoint set --file MainForm.pas --line 42
Breakpoint 1: where = MyApp`TForm1.Button1Click + 20 at MainForm.pas:42

Raccourci :

(lldb) br s -f MainForm.pas -l 42

Breakpoint sur une fonction :

(lldb) breakpoint set --name TForm1.Button1Click
Breakpoint 2: where = MyApp`TForm1.Button1Click at MainForm.pas:40

Raccourci :

(lldb) br s -n TForm1.Button1Click

Breakpoint avec wildcard :

(lldb) breakpoint set --func-regex ".*Click"
# S'arrête sur toutes les fonctions dont le nom se termine par "Click"

Breakpoints conditionnels

Les breakpoints conditionnels ne se déclenchent que si une condition est vraie.

Syntaxe :

(lldb) breakpoint set -f MainForm.pas -l 42 -c "Counter > 100"

L'application ne s'arrêtera à la ligne 42 que si Counter est supérieur à 100.

Exemples de conditions :

# S'arrêter si un pointeur est nil
(lldb) br s -f MyUnit.pas -l 50 -c "MyObject == nullptr"

# S'arrêter si une chaîne contient un texte
(lldb) br s -f MyUnit.pas -l 75 -c "strcmp(Name, \"Test\") == 0"

# S'arrêter sur une valeur spécifique
(lldb) br s -f MyUnit.pas -l 100 -c "ID == 12345"

Modifier une condition existante :

(lldb) breakpoint modify 1 --condition "Age >= 18"

Breakpoints avec compteurs

Déclencher un breakpoint après N passages :

(lldb) breakpoint set -f MyUnit.pas -l 42 --ignore-count 99

Le breakpoint se déclenchera au 100ème passage (ignore les 99 premiers).

Cas d'usage :

Utile dans les boucles où vous voulez examiner un état spécifique :

for i := 0 to 999 do  
begin  
  TraiterElement(i);  // Breakpoint qui se déclenche au 500ème passage
end;

Commandes automatiques sur breakpoint

Exécuter des commandes LLDB automatiquement quand un breakpoint est atteint :

(lldb) breakpoint set -f MyUnit.pas -l 42
Breakpoint 3: where = MyApp`ProcessData...

(lldb) breakpoint command add 3
Enter your debugger command(s). Type 'DONE' to end.
> frame variable Counter
> expr MyList.Count
> continue
> DONE

Maintenant, chaque fois que le breakpoint 3 est atteint, LLDB :

  1. Affiche la valeur de Counter
  2. Affiche MyList.Count
  3. Continue automatiquement l'exécution

Cas d'usage :

Journaliser des valeurs sans arrêter l'exécution :

(lldb) br s -f MyUnit.pas -l 50
(lldb) br command add 1
> expr printf("Counter = %d\n", Counter)
> continue
> DONE

Lister et gérer les breakpoints

Lister tous les breakpoints :

(lldb) breakpoint list
Current breakpoints:
1: file = 'MainForm.pas', line = 42, exact_match = 0, locations = 1
2: name = 'TForm1.Button1Click', locations = 1
3: file = 'DataModule.pas', line = 150, exact_match = 0, locations = 1

Activer/désactiver :

(lldb) breakpoint disable 1    # Désactiver le breakpoint 1
(lldb) breakpoint enable 1     # Réactiver le breakpoint 1

Supprimer :

(lldb) breakpoint delete 1     # Supprimer le breakpoint 1
(lldb) breakpoint delete       # Supprimer TOUS les breakpoints (demande confirmation)

Informations détaillées :

(lldb) breakpoint list 1 -v    # Détails verbeux sur le breakpoint 1

Watchpoints : Surveiller les modifications mémoire

Les watchpoints (points d'observation) sont différents des breakpoints. Ils surveillent une zone mémoire et s'arrêtent quand cette mémoire est lue ou modifiée.

Créer un watchpoint

Surveiller une variable :

(lldb) watchpoint set variable MonCompteur
Watchpoint created: Watchpoint 1: addr = 0x7ffeefbff5a8 size = 4 state = enabled type = w

Le programme s'arrêtera chaque fois que MonCompteur est modifié.

Surveiller une adresse mémoire spécifique :

(lldb) watchpoint set expression -- 0x7ffeefbff5a8

Surveiller en lecture ET écriture :

(lldb) watchpoint set variable MonCompteur --watch read_write

Surveiller uniquement en écriture (défaut) :

(lldb) watchpoint set variable MonCompteur --watch write

Cas d'usage des watchpoints

Trouver qui modifie une variable

Vous avez une variable globale qui change de manière inattendue :

var
  GlobalCounter: Integer = 0;  // Qui modifie cette variable ?
(lldb) watchpoint set variable GlobalCounter
(lldb) continue

Le programme s'arrêtera à la ligne exacte qui modifie GlobalCounter.

Détecter la corruption de données

Si une structure de données se corrompt :

type
  TImportantData = record
    ID: Integer;
    Status: Integer;
    Name: string;
  end;

var
  MyData: TImportantData;
(lldb) watchpoint set expression -- &MyData

Toute modification de n'importe quel champ de MyData déclenchera le watchpoint.

Débogage de pointeurs

Surveiller où un pointeur est modifié :

(lldb) watchpoint set variable MyPointer

Gérer les watchpoints

Lister :

(lldb) watchpoint list
Number of supported hardware watchpoints: 4  
Current watchpoints:  
Watchpoint 1: addr = 0x7ffeefbff5a8 size = 4 state = enabled type = w  
    old value: 5
    new value: 10

Désactiver/activer :

(lldb) watchpoint disable 1
(lldb) watchpoint enable 1

Supprimer :

(lldb) watchpoint delete 1

Limitation importante :

Les watchpoints utilisent des ressources matérielles limitées. La plupart des processeurs supportent seulement 4 watchpoints simultanés. Si vous en créez trop, LLDB vous avertira.

Inspection mémoire avancée

Lire la mémoire

LLDB permet d'inspecter directement la mémoire brute.

Syntaxe de base :

(lldb) memory read <adresse>

Exemples :

# Lire 16 octets à une adresse
(lldb) memory read --size 1 --count 16 0x00007ffeefbff5a8

# Lire sous forme de mots de 4 octets (integers)
(lldb) memory read --size 4 --count 10 0x00007ffeefbff5a8

# Afficher en hexadécimal ET en ASCII
(lldb) memory read --format hex 0x00007ffeefbff5a8

Formats d'affichage :

  • hex : Hexadécimal
  • decimal : Décimal
  • binary : Binaire
  • character : Caractères ASCII
  • float : Nombres à virgule flottante
  • pointer : Adresses pointeur

Exemple de sortie :

(lldb) memory read --size 1 --count 32 --format hex 0x00007ffeefbff5a8
0x7ffeefbff5a8: 0x05 0x00 0x00 0x00 0x48 0x65 0x6c 0x6c
0x7ffeefbff5b0: 0x6f 0x00 0x00 0x00 0x00 0x00 0x00 0x00

Trouver l'adresse d'une variable

Pour utiliser memory read, vous avez besoin de l'adresse. Comment la trouver ?

(lldb) expr &MonCompteur
(int *) $3 = 0x00007ffeefbff5a8

(lldb) memory read 0x00007ffeefbff5a8

Ou directement :

(lldb) memory read `&MonCompteur`

Les backticks ` permettent d'évaluer l'expression et d'utiliser le résultat.

Écrire dans la mémoire

Attention : Modifier la mémoire directement peut corrompre votre application. Utilisez cette fonctionnalité avec précaution.

(lldb) memory write 0x00007ffeefbff5a8 0x0A

Ceci écrit la valeur 0x0A (10 en décimal) à l'adresse spécifiée.

Cas d'usage :

Tester rapidement comment l'application se comporte avec une valeur différente :

if Status = 1 then
  ProcessNormal
else
  ProcessSpecial;

Pendant le débogage, vous pouvez changer Status en mémoire pour forcer un chemin spécifique.

Rechercher dans la mémoire

Rechercher un pattern dans la mémoire :

(lldb) memory find --size 4 --count 1 --expression 0x12345678 0x100000000 0x200000000

Recherche la valeur 0x12345678 dans la plage mémoire de 0x100000000 à 0x200000000.

Rechercher une chaîne :

(lldb) memory find --string "Error" 0x100000000 0x200000000

Cas d'usage :

Trouver où une chaîne spécifique est stockée en mémoire, utile pour déboguer des problèmes de corruption de chaînes.

Évaluation d'expressions avancée

Expressions simples

Nous avons vu les bases avec expr. Allons plus loin.

Opérations arithmétiques :

(lldb) expr 2 + 2
(int) $0 = 4

(lldb) expr MonAge * 2
(int) $1 = 50

(lldb) expr (Prix * Quantite) * 1.20
(double) $2 = 144.0

Comparaisons :

(lldb) expr Age > 18
(bool) $3 = true

(lldb) expr Status == 0
(bool) $4 = false

Appeler des fonctions

LLDB peut appeler des fonctions pendant le débogage. C'est puissant mais potentiellement dangereux.

Exemple :

function CalculerTotal(Prix: Double; Quantite: Integer): Double;  
begin  
  Result := Prix * Quantite;
end;
(lldb) expr CalculerTotal(10.5, 3)
(double) $5 = 31.5

Précautions :

  • La fonction ne doit pas avoir d'effets de bord dangereux
  • Évitez d'appeler des fonctions qui modifient l'état global
  • Ne jamais appeler des fonctions qui pourraient planter
  • Les fonctions peuvent avoir accès aux variables du scope actuel

Cas d'usage utile :

(lldb) expr ShowMessage("Debug: Counter = " + IntToStr(Counter))

Afficher un message depuis le débogueur sans recompiler.

Créer des variables temporaires

LLDB vous permet de créer des variables temporaires pendant le débogage :

(lldb) expr int $mytemp = 42
(lldb) expr $mytemp * 2
(int) $6 = 84

Les variables commençant par $ sont des variables LLDB, pas des variables de votre programme.

Usage :

Stocker un calcul complexe pour y faire référence plus tard :

(lldb) expr double $total = (Prix * Quantite) * 1.20
(lldb) expr $total > 100
(bool) $7 = true

Type casting

Forcer un type lors de l'évaluation :

(lldb) expr (int)MyFloatValue
(lldb) expr (char*)MyPointer
(lldb) expr (TMyClass*)MyObject

Utile quand LLDB ne peut pas déterminer automatiquement le type.

Débogage multi-thread avec LLDB

Les applications modernes utilisent souvent plusieurs threads. LLDB offre des outils puissants pour les déboguer.

Lister les threads

(lldb) thread list
Process 12345 stopped
* thread #1, name = 'MainThread', queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
  thread #2, name = 'WorkerThread1'
  thread #3, name = 'WorkerThread2'
  thread #4, name = 'NetworkThread'

L'astérisque (*) indique le thread actuellement sélectionné.

Sélectionner un thread

(lldb) thread select 2
* thread #2, name = 'WorkerThread1'
  frame #0: 0x0000000100002340 MyApp`WorkerProc at Worker.pas:25

Toutes les commandes suivantes s'appliqueront à ce thread.

Voir la pile d'appels de tous les threads

(lldb) thread backtrace all

Affiche la stack trace de TOUS les threads, utile pour comprendre ce que fait toute l'application.

Exemple de sortie :

* thread #1, name = 'MainThread'
  * frame #0: 0x0000000100001000 MyApp`TForm1.Button1Click at MainForm.pas:42
    frame #1: 0x0000000100002000 MyApp`TControl.Click at Controls.pas:200

  thread #2, name = 'WorkerThread1'
    frame #0: 0x00007fff204050c0 libsystem_kernel.dylib`__psynch_cvwait
    frame #1: 0x0000000100003000 MyApp`TWorkerThread.Execute at Worker.pas:30

  thread #3, name = 'WorkerThread2'
    frame #0: 0x00007fff204050d0 libsystem_kernel.dylib`__select
    frame #1: 0x0000000100004000 MyApp`NetworkWait at Network.pas:50

Continuer un thread spécifique

(lldb) thread continue 2

Reprend l'exécution du thread 2 seulement, les autres restent en pause.

Exécuter une commande sur tous les threads

(lldb) thread apply all bt

Exécute bt (backtrace) sur tous les threads.

Breakpoints spécifiques à un thread

Créer un breakpoint qui ne se déclenche que sur un thread spécifique :

(lldb) breakpoint set -f Worker.pas -l 30 --thread-index 2

Le breakpoint ne se déclenchera que quand le thread 2 atteint cette ligne.

Déboguer les deadlocks

Un deadlock survient quand deux threads s'attendent mutuellement.

Diagnostic :

  1. L'application semble figée
  2. Mettez en pause (Pause button ou Ctrl+C dans la console)
  3. Examinez tous les threads :
(lldb) thread backtrace all
  1. Cherchez les patterns de verrouillage :
    • Thread 1 attend un mutex détenu par Thread 2
    • Thread 2 attend un mutex détenu par Thread 1

Exemple de ce que vous pourriez voir :

thread #1: waiting on mutex at 0x12345678  
thread #2: waiting on mutex at 0x23456789  

En examinant les stack traces, vous pouvez identifier l'ordre des verrouillages qui cause le deadlock.

Déboguer les data races

Les data races surviennent quand plusieurs threads accèdent à la même donnée sans synchronisation.

Utiliser les watchpoints :

(lldb) watchpoint set variable SharedCounter

Maintenant, si plusieurs threads modifient SharedCounter, vous verrez exactement quand et par quel thread.

Scripts et automatisation

LLDB supporte les scripts Python pour automatiser des tâches complexes.

Exécuter une commande Python simple

(lldb) script print("Hello from Python")
Hello from Python

Accéder aux objets LLDB depuis Python

(lldb) script
>>> import lldb
>>> target = lldb.debugger.GetSelectedTarget()
>>> process = target.GetProcess()
>>> print(f"Process ID: {process.GetProcessID()}")
Process ID: 12345
>>>

Tapez Ctrl+D pour sortir du mode script.

Créer des commandes personnalisées

Vous pouvez créer des commandes LLDB personnalisées en Python.

Exemple : Commande pour afficher toutes les variables locales formatées

# Fichier : mycommands.py

import lldb

def show_all_vars(debugger, command, result, internal_dict):
    target = debugger.GetSelectedTarget()
    process = target.GetProcess()
    thread = process.GetSelectedThread()
    frame = thread.GetSelectedFrame()

    print("=== Variables locales ===")
    for var in frame.GetVariables(True, True, True, True):
        print(f"{var.GetName()} = {var.GetValue()}")

def __lldb_init_module(debugger, internal_dict):
    debugger.HandleCommand('command script add -f mycommands.show_all_vars showvars')
    print("Commande 'showvars' chargée")

Charger le script :

(lldb) command script import /path/to/mycommands.py
Commande 'showvars' chargée

Utiliser la commande :

(lldb) showvars
=== Variables locales ===
Counter = 5  
Name = "Test"  
MyObj = 0x00007f9e8c400000  

Scripts pour l'analyse automatique

Exemple : Détecter les fuites mémoire potentielles

def check_nil_pointers(debugger, command, result, internal_dict):
    frame = debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame()

    nil_pointers = []
    for var in frame.GetVariables(True, True, True, True):
        if var.GetTypeName().endswith('*'):  # C'est un pointeur
            if var.GetValueAsUnsigned() == 0:
                nil_pointers.append(var.GetName())

    if nil_pointers:
        print("⚠️  Pointeurs nil détectés:")
        for name in nil_pointers:
            print(f"  - {name}")
    else:
        print("✓ Aucun pointeur nil")

Charger automatiquement des scripts

Créez un fichier .lldbinit dans votre home directory :

# ~/.lldbinit
command script import ~/lldb_scripts/mycommands.py  
command script import ~/lldb_scripts/memory_tools.py  

# Alias utiles
command alias pv frame variable  
command alias bt thread backtrace  

Ces commandes seront exécutées automatiquement au démarrage de LLDB.

Astuces et techniques avancées

Affichage formaté des données

Format personnalisé pour les expressions :

(lldb) type summary add --summary-string "Count: ${var.FCount}" TList

Maintenant, quand vous affichez un TList, LLDB montrera automatiquement son count.

Affichage en hexadécimal :

(lldb) expr --format hex -- MonEntier
(int) $8 = 0x0000007b

Affichage en binaire :

(lldb) expr --format binary -- MonEntier
(int) $9 = 0b01111011

Capture de la sortie

Rediriger la sortie d'une commande vers un fichier :

(lldb) log enable lldb all -f /tmp/lldb.log

Tous les événements LLDB seront journalisés dans /tmp/lldb.log.

Désassemblage du code

Voir le code assembleur :

(lldb) disassemble

Affiche le code assembleur de la fonction actuelle.

Désassembler une fonction spécifique :

(lldb) disassemble --name TForm1.Button1Click

Cas d'usage :

Comprendre exactement ce que fait le compilateur, utile pour :

  • Optimisation de performance
  • Comprendre des bugs subtils
  • Analyse de sécurité

Examiner les registres

Voir les registres du processeur :

(lldb) register read

Exemple de sortie (x64) :

General Purpose Registers:
       rax = 0x0000000000000005
       rbx = 0x00007ffeefbff5a8
       rcx = 0x0000000000000000
       rdx = 0x0000000000000010
       ...

Modifier un registre :

(lldb) register write rax 0x0000000000000042

Attention : Modifier les registres peut rendre votre programme instable. À utiliser seulement si vous savez ce que vous faites.

Informations sur les modules

Lister les modules (DLLs, frameworks) chargés :

(lldb) image list
[  0] 2A1E5B3C-0000-0000-0000-000000000000 0x0000000100000000 /Users/dev/MyApp/MyApp
[  1] 65289842-0000-0000-0000-000000000000 0x00007fff20400000 /usr/lib/libSystem.B.dylib
[  2] 91FD34AB-0000-0000-0000-000000000000 0x00007fff30000000 /System/Library/Frameworks/Foundation.framework/Foundation

Chercher dans quel module se trouve une adresse :

(lldb) image lookup --address 0x0000000100001234
Address: MyApp[0x0000000000001234] (MyApp.__TEXT.__text + 1234)  
Summary: MyApp`TForm1.ProcessData at MainForm.pas:125  

Core dumps et post-mortem debugging

Si votre application a planté et généré un core dump, vous pouvez l'analyser avec LLDB.

Charger un core dump :

lldb --core /path/to/core.dump /path/to/executable

Analyser :

(lldb) bt
(lldb) frame variable
(lldb) thread list

Vous pouvez examiner l'état exact du programme au moment du crash.

Intégration avec Delphi IDE

Synchronisation avec l'interface graphique

Lorsque vous utilisez la console LLDB dans Delphi, certaines commandes peuvent ne pas se synchroniser avec l'interface graphique.

Commandes qui se synchronisent :

  • breakpoint : Les breakpoints apparaîtront dans l'éditeur
  • thread : Le thread actuel se synchronisera
  • frame : La fenêtre de code suivra

Commandes qui ne se synchronisent pas toujours :

  • Modifications de variables via expr
  • Watchpoints créés manuellement
  • Commandes avancées de script

Conseil : Après des opérations complexes dans la console LLDB, rafraîchissez l'IDE en cliquant dans une fenêtre de débogage (Watch List, Call Stack).

Combiner GUI et ligne de commande

Stratégie recommandée :

  1. Utiliser l'IDE pour :

    • Placer des breakpoints simples
    • Navigation de base (Step Into, Step Over)
    • Inspection rapide de variables (hover)
  2. Utiliser la console LLDB pour :

    • Breakpoints conditionnels complexes
    • Watchpoints
    • Inspection mémoire détaillée
    • Analyse multi-thread avancée
    • Scripts et automatisation

Exporter les résultats

Pour partager des informations de débogage avec votre équipe :

(lldb) thread backtrace all > /tmp/all_threads.txt
(lldb) frame variable > /tmp/variables.txt

Les fichiers peuvent être ouverts dans un éditeur de texte et partagés.

Bonnes pratiques et conseils

1. Apprenez progressivement

Ne tentez pas de maîtriser toutes les commandes LLDB d'un coup. Commencez par :

  • bt (backtrace)
  • frame variable
  • expr
  • thread list

Ajoutez progressivement des commandes plus avancées à votre arsenal.

2. Créez des alias

Pour les commandes que vous utilisez souvent :

(lldb) command alias pv frame variable
(lldb) command alias allvars frame variable --show-types

Maintenant pv est un raccourci pour frame variable.

3. Documentez vos sessions

Quand vous trouvez quelque chose d'intéressant :

# Créer un fichier de notes
(lldb) log enable lldb all -f debug_session.log

Vous aurez un enregistrement complet de votre session.

4. Utilisez l'historique

LLDB conserve un historique des commandes. Utilisez les flèches ↑ et ↓ pour naviguer dans l'historique.

5. Tab completion

Tapez le début d'une commande et appuyez sur Tab pour l'auto-complétion :

(lldb) break<Tab>
breakpoint   break

6. Soyez prudent avec les appels de fonction

Appeler des fonctions depuis LLDB peut avoir des effets de bord. Ne le faites que si :

  • Vous comprenez ce que fait la fonction
  • La fonction n'a pas d'effets de bord critiques
  • Vous êtes prêt à redémarrer le débogage si quelque chose tourne mal

7. Sauvegardez vos breakpoints

Si vous avez créé de nombreux breakpoints complexes :

(lldb) breakpoint write -f /tmp/my_breakpoints.txt

Pour les recharger plus tard :

(lldb) breakpoint read -f /tmp/my_breakpoints.txt

8. Explorez l'aide intégrée

LLDB a une documentation complète intégrée :

(lldb) help
(lldb) help breakpoint
(lldb) help breakpoint set

Consultez-la régulièrement pour découvrir de nouvelles fonctionnalités.

9. Pratiquez dans un environnement sûr

Créez une petite application de test pour expérimenter avec les commandes LLDB sans risquer d'endommager un projet important.

10. Partagez vos découvertes

Si vous trouvez une technique utile, documentez-la et partagez-la avec votre équipe.

Dépannage LLDB

LLDB ne répond pas

Symptôme : Les commandes LLDB ne retournent rien ou prennent très longtemps.

Solutions :

  1. Vérifiez la connexion réseau (pour le débogage distant)
  2. Vérifiez que le processus cible est toujours en cours : process status
  3. Si figé, interrompez : Ctrl+C
  4. En dernier recours, arrêtez et relancez la session de débogage

"Error: couldn't resolve this expression"

Cause : LLDB ne peut pas trouver ou évaluer l'expression.

Solutions :

  1. Vérifiez l'orthographe
  2. Assurez-vous que la variable est dans le scope actuel
  3. Vérifiez que les symboles de débogage sont présents
  4. Essayez de spécifier le type explicitement : (int)MaVariable

Symboles de débogage manquants

Symptôme : Les noms de variables n'apparaissent pas, tout est en adresses.

Solution :

  1. Recompilez avec les informations de débogage activées
  2. Vérifiez dans Project > Options > Delphi Compiler > Compiling
  3. Assurez-vous que Debug Information est coché
  4. Redéployez l'application

Breakpoints ignorés

Cause : L'optimisation du compilateur peut réorganiser ou éliminer du code.

Solution :

  1. Désactivez l'optimisation en mode Debug
  2. Project > Options > Delphi Compiler > Compiling > Optimization = Off
  3. Recompilez et redéployez

LLDB crashe

Symptôme : LLDB lui-même plante pendant le débogage.

Solutions :

  1. Vérifiez que vous utilisez la bonne version de LLDB pour votre plateforme
  2. Mettez à jour vers la dernière version de Delphi/LLDB
  3. Simplifiez votre session de débogage (moins de breakpoints, moins de watchpoints)
  4. Redémarrez Delphi

Performance lente

Cause : Trop de breakpoints, watchpoints, ou expressions complexes.

Solutions :

  1. Réduisez le nombre de breakpoints actifs
  2. Supprimez les watchpoints non nécessaires
  3. Simplifiez les expressions dans la Watch List
  4. Utilisez des breakpoints conditionnels au lieu de vérifier manuellement

Checklist de débogage avancé avec LLDB

Utilisez cette checklist pour les sessions de débogage complexes :

□ Préparation

  • Informations de débogage activées dans le projet
  • Optimisation désactivée en mode Debug
  • Application déployée sur la cible
  • LLDB console ouverte dans Delphi

□ Session de débogage

  • Breakpoints placés aux endroits stratégiques
  • Watchpoints configurés si nécessaire
  • Thread approprié sélectionné
  • Variables clés surveillées

□ Analyse

  • Stack trace examinée (bt)
  • Variables locales inspectées (frame variable)
  • État des threads vérifié (thread list)
  • Mémoire examinée si nécessaire

□ Documentation

  • Résultats notés
  • Commandes utiles sauvegardées
  • Breakpoints complexes exportés si nécessaire

Ressources supplémentaires

Documentation officielle

Communauté

  • Stack Overflow : Questions avec le tag [lldb]
  • Forums Embarcadero : Section Debugging
  • LLDB Mailing List : lldb-dev@lists.llvm.org

Cheat sheets

Créez votre propre cheat sheet avec vos commandes préférées. Voici un exemple de départ :

=== LLDB Quick Reference ===

Navigation:
  continue (c)     - Continue execution
  step (s)         - Step into
  next (n)         - Step over
  finish           - Step out

Inspection:
  bt               - Backtrace
  frame variable   - Show local variables
  expr <expr>      - Evaluate expression
  thread list      - List all threads

Breakpoints:
  br s -f file.pas -l 42           - Set breakpoint
  br s -n FunctionName              - Break on function
  br s -f file.pas -l 42 -c "x>10" - Conditional
  br list                           - List all
  br delete <n>                     - Delete breakpoint

Watchpoints:
  watchpoint set variable <var>     - Watch variable
  watchpoint list                   - List all
  watchpoint delete <n>             - Delete watchpoint

Threads:
  thread select <n>      - Select thread
  thread backtrace all   - All stacks
  thread continue <n>    - Continue one thread

Memory:
  memory read <addr>     - Read memory
  memory write <addr> <val> - Write memory

Conclusion

LLDB v12 est un outil de débogage extraordinairement puissant qui vous donne un contrôle total sur l'exécution de vos applications Delphi sur macOS, iOS, Linux et Android. Bien que son interface en ligne de commande puisse sembler intimidante au début, les capacités qu'elle offre en font un atout inestimable pour le débogage de problèmes complexes.

Points clés à retenir :

Nouveautés v12 : LLDB v12 apporte des améliorations significatives en performance, stabilité et support des types Delphi.

Commandes essentielles : Maîtrisez d'abord bt, frame variable, expr, et thread list avant d'explorer les fonctionnalités avancées.

Breakpoints avancés : Les conditions, compteurs et commandes automatiques rendent les breakpoints extrêmement flexibles.

Watchpoints : Surveillez les modifications mémoire pour trouver les bugs insaisissables.

Multi-threading : LLDB offre des outils excellents pour déboguer les applications multi-thread et diagnostiquer les deadlocks.

Scripting : L'intégration Python permet d'automatiser des tâches complexes et de créer des outils personnalisés.

Pratique progressive : Commencez par les bases et ajoutez progressivement des techniques avancées à votre workflow.

Documentation : Utilisez help généreusement et consultez la documentation officielle pour approfondir.

En maîtrisant LLDB, vous rejoignez les rangs des développeurs capables de résoudre même les bugs les plus difficiles et obscurs. C'est un investissement en temps qui rapporte énormément, particulièrement pour le développement multi-plateformes avec Delphi. Pratiquez régulièrement, expérimentez avec les différentes commandes, et vous découvrirez rapidement que LLDB devient un outil indispensable dans votre arsenal de débogage.

⏭️ Tests d'intégration