NOTE D’INGÉNIERIE

Diagnostiquer l’attache LLDB et le chargement des symboles sur un Mac cloud

Diagnostiquer l’attache LLDB et le chargement des symboles sur un Mac cloud

Xcode indique déjà « Attachement en cours », mais l’utilisation du CPU du Mac cloud reste presque nulle, les points d’arrêt passent du bleu au gris et la fenêtre des variables n’affiche aucun résultat. Dans cette situation, cliquer plusieurs fois sur Arrêter, redémarrer ou supprimer DerivedData ne fait généralement qu’effacer les indices utiles. Une méthode plus efficace consiste à décomposer le problème en trois niveaux : le processus cible peut-il être débogué, la session entre LLDB et la cible est-elle établie, et les symboles correspondant au binaire actuel sont-ils disponibles ?

Déterminer d’abord à quel niveau se situe le blocage

Commencez par noter l’heure de l’incident, le commit du projet, le Scheme, la Configuration, l’appareil cible et le mode de lancement, puis observez l’état de Xcode. Ne confondez pas lenteur de l’attachement, lenteur du démarrage de l’application et lenteur de la résolution des symboles.

Symptôme Vérification prioritaire Conclusion courante
L’application ne démarre pas et LLDB reste en attente Processus cible, arguments de lancement Le processus s’est arrêté ou une autre version du binaire a été lancée
Le PID du processus est affiché, mais l’interface ne répond plus État du processus, service de débogage La cible est suspendue ou bloquée, ou la négociation de débogage n’est pas terminée
Les points d’arrêt sont creux ou gris Modules et symboles Le module n’est pas chargé, le chemin a changé ou les UUID ne correspondent pas
L’exécution s’arrête, mais les variables locales sont invisibles Niveau d’optimisation, informations de débogage L’optimisation Release a fusionné ou supprimé les variables

Enregistrez un instantané des processus dans un autre terminal :

mkdir -p "$HOME/lldb-case"
date -u > "$HOME/lldb-case/time.txt"
ps -axo pid,ppid,state,%cpu,etime,command \
  > "$HOME/lldb-case/processes.txt"
xcrun simctl list devices \
  > "$HOME/lldb-case/simulators.txt"

Dans state, un état U persistant peut indiquer que le processus est en attente non interruptible ; un état T continu signifie qu’il est suspendu. Un seul instantané ne permet pas d’établir une tendance : une seconde capture dix secondes plus tard sera plus utile.

Conservez d’abord les indices, puis procédez au nettoyage. Un redémarrage réussi permet seulement de reprendre le travail ; il ne prouve pas que la cause racine a disparu.

Vérifier le processus et la session de débogage

Confirmer que LLDB est attaché au bon binaire

Une application portant le même nom peut être présente simultanément dans plusieurs simulateurs, répertoires de test ou anciens répertoires de build. Commencez par vérifier le processus et les images chargées dans LLDB :

(lldb) process status
(lldb) target list
(lldb) image list -o -f

target list doit pointer vers l’exécutable produit par le build actuel ; dans image list, le chemin du module cible doit se trouver dans le répertoire Build Products attendu. Si le chemin provient d’un autre ensemble DerivedData, ne supprimez pas immédiatement l’intégralité de son contenu. Notez d’abord l’ancien chemin et vérifiez si le Scheme réutilise un artefact de build incorrect.

Si la ligne de commande LLDB répond toujours, exécutez :

(lldb) thread list
(lldb) thread backtrace all

Si une trace d’appels est disponible pour chaque thread, le canal de débogage est généralement établi. Le problème se situe alors plus probablement dans une attente interne à l’application ou dans la résolution des symboles. Si les commandes restent sans réponse, échantillonnez le processus cible depuis le système :

sample <PID> 10 1 -file "$HOME/lldb-case/app-sample.txt"

N’exécutez pas plusieurs commandes sample à la suite. Un échantillon de dix secondes suffit pour déterminer si le thread principal attend sur un verrou, une opération d’E/S fichier, un appel réseau ou un framework système.

Vérifier le dSYM à l’aide des UUID

Des noms de fichiers identiques ne garantissent pas que les symboles correspondent. Chaque édition de liens peut produire un UUID différent, et LLDB n’utilise que le dSYM associé au Mach-O concerné.

dwarfdump --uuid "/path/to/MyApp.app/MyApp"
dwarfdump --uuid "/path/to/MyApp.app.dSYM"

Pour une même architecture, les UUID doivent être identiques des deux côtés. Si l’application contient arm64, comparez au minimum la ligne arm64. N’utilisez pas à la place le dSYM d’une autre Archive, d’un autre commit ou d’un binaire réédité.

Vérifier si le module est chargé

Recherchez le module cible dans LLDB :

(lldb) image lookup -n AppDelegate
(lldb) image lookup -r -n 'YourModule\..*'
(lldb) breakpoint list

Si image lookup ne trouve aucun symbole alors que le module apparaît dans image list, vérifiez en priorité le format des informations de débogage. Un build de développement doit généralement produire du DWARF ou du DWARF with dSYM. Avec une configuration fortement optimisée, certaines fonctions peuvent être intégrées et les variables locales devenir invisibles. Créez alors une Configuration de diagnostic dédiée au lieu de modifier temporairement la configuration Release partagée par l’équipe.

Un point d’arrêt affiché comme pending n’indique pas nécessairement une erreur. Tant qu’un framework dynamique n’est pas chargé, le point d’arrêt attend que l’image correspondante entre dans le processus. Placez d’abord un point d’arrêt sur un point d’entrée dont le chargement est certain, puis observez les modules suivants au lieu de supprimer et recréer sans cesse le même point d’arrêt.

Rechercher les échecs de négociation dans les journaux système

L’interface de Xcode se contente souvent d’afficher « Échec de l’attachement », tandis que les journaux système conservent des informations plus précises sur l’arrêt du processus, un refus d’autorisation ou l’interruption d’une connexion. Avant de reproduire le problème, lancez une collecte de journaux ciblée :

log stream --style compact --info \
  --predicate 'process == "debugserver" OR process == "lldb-rpc-server"' \
  > "$HOME/lldb-case/debug-session.log"

Après une reproduction, arrêtez la collecte avec Control-C. Les journaux peuvent contenir des noms d’utilisateur, des chemins de projet et des identifiants d’appareil. Anonymisez-les avant de créer un ticket ou de les partager avec l’équipe. Évitez une collecte globale et prolongée avec --debug : elle génère beaucoup de bruit inutile et augmente le coût du filtrage.

Si les journaux montrent que le processus cible s’arrête au moment de l’attachement, lancez d’abord l’application seule, sans LLDB, afin de vérifier qu’elle reste active de manière stable. Si seul le processus de test échoue, contrôlez séparément l’hôte de test, le Bundle de test et l’application testée au lieu de vous concentrer uniquement sur le PID de l’application principale.

Établir une procédure de récupération reproductible

Les actions de récupération doivent commencer par les étapes ayant le moins d’impact :

  1. Mettez fin à la session de débogage actuelle, mais conservez l’application cible et les journaux.
  2. Vérifiez le Scheme, la Configuration, la destination d’exécution et le chemin de l’exécutable.
  3. Comparez les UUID du Mach-O et du dSYM.
  4. Recompilez le Target actuel sans nettoyer l’ensemble du projet.
  5. Supprimez le répertoire DerivedData du projet concerné uniquement après avoir confirmé qu’un ancien artefact est réutilisé.
  6. Si le problème reste reproductible, conservez les traces d’appels des threads, l’échantillon du processus, les journaux système et une procédure minimale de reproduction.

Utilisez d’abord la commande suivante pour lister les répertoires et leurs dates de modification, afin d’éviter de supprimer les données d’autres tâches :

find "$HOME/Library/Developer/Xcode/DerivedData" \
  -maxdepth 1 -mindepth 1 -type d -print

Sur un Mac cloud, des tâches de build, de test et de débogage graphique peuvent s’exécuter simultanément. Avant tout nettoyage, vérifiez qu’aucun autre pipeline n’utilise le même répertoire. Une solution plus robuste consiste à attribuer un -derivedDataPath distinct à chaque tâche afin de séparer les données de diagnostic du cache des builds automatisés.

Transformer les résultats du diagnostic en critères de validation

Après chaque correction, validez au minimum quatre points : l’attachement fonctionne après un démarrage à froid, il fonctionne sur un processus déjà lancé, les points d’arrêt dans le code source sont atteints et les variables importantes restent consultables lors d’une suspension sur exception. Quittez ensuite la session distante, reconnectez-vous et recommencez le test afin de vérifier que le résultat ne dépend ni de la session graphique actuelle ni de variables d’environnement temporaires.

L’équipe peut ajouter la vérification des UUID, le chemin des artefacts et la Configuration à la checklist de build, mais les scripts ne doivent pas contenir de chemin absolu vers le répertoire personnel d’un utilisateur. Une procédure de débogage réellement stable ne consiste pas à « nettoyer puis réessayer » : elle permet à chaque membre de l’équipe d’utiliser le même ensemble d’indices pour déterminer si la défaillance se situe au niveau du processus, de la session ou des symboles, puis de ne corriger que le niveau concerné.

Questions fréquentes

Que vérifier si LLDB s’attache au processus mais n’atteint aucun point d’arrêt ?

Utilisez image list pour confirmer le chargement du module, puis comparez les UUID de l’exécutable et du dSYM. Un module absent laisse le point d’arrêt en attente, tandis qu’un UUID différent empêche la correspondance avec les sources.

Faut-il supprimer DerivedData dès qu’une session de débogage se bloque ?

Non. Enregistrez d’abord la liste des processus, la sortie LLDB, les journaux système et les UUID des artefacts. Ne supprimez que le dossier du projet concerné après avoir confirmé des données de compilation obsolètes.

Le débogage distant impose-t-il d’exposer un port sur Internet ?

Généralement non. Exécutez Xcode et LLDB sur le Mac cloud via un bureau distant contrôlé ou SSH. Si une redirection est indispensable, utilisez un tunnel restreint et limitez l’adresse d’écoute.

VMDebug Mac physiques dans le cloud

Besoin d’un Mac mini physique dédié à une seule commande ?

Comparez les configurations M4 et M4 Pro, les quatre durées de location et les cinq nœuds disponibles, puis choisissez l’appareil adapté à votre workflow.

Choisir une configuration et commander