ENGINEERING-NOTIZ

LLDB-Anhang und Symbolauflösung auf einem Cloud Mac diagnostizieren

LLDB-Anhang und Symbolauflösung auf einem Cloud Mac diagnostizieren

Xcode zeigt bereits „Wird angehängt“ an, doch die CPU-Auslastung des Cloud Mac liegt nahezu im Leerlauf, Haltepunkte wechseln von Blau zu Grau und das Variablenfenster bleibt leer. In dieser Situation vernichten wiederholtes Stoppen, Neustarten oder Löschen von DerivedData häufig nur wichtige Spuren. Effektiver ist es, das Problem in drei Ebenen aufzuteilen: Ist der Zielprozess debugfähig, wurde die Sitzung zwischen LLDB und dem Ziel aufgebaut, und lassen sich für das aktuelle Build-Artefakt passende Symbole finden?

Zuerst die betroffene Ebene bestimmen

Notieren Sie zunächst den Zeitpunkt, den Commit des Projekts, das Scheme, die Configuration, das Zielgerät und die Startmethode. Beobachten Sie anschließend den Status in Xcode. Langsames Anhängen, ein langsamer Anwendungsstart und eine langsame Symbolauflösung dürfen nicht als dasselbe Problem behandelt werden.

Symptom Zuerst prüfen Häufige Ursache
Anwendung startet nicht, LLDB wartet dauerhaft Zielprozess, Startparameter Der Prozess wurde beendet oder ein anderes Build-Artefakt wurde gestartet
Prozess-PID wird angezeigt, Oberfläche reagiert nicht Prozessstatus, Debug-Dienst Das Ziel ist angehalten, blockiert oder der Debug-Handshake wurde nicht abgeschlossen
Haltepunkte sind hohl oder grau Module und Symbole Das Modul wurde nicht geladen, der Pfad hat sich geändert oder die UUID stimmt nicht überein
Prozess hält an, lokale Variablen sind jedoch nicht sichtbar Optimierungsstufe, Debug-Informationen Durch die Release-Optimierung wurden Variablen zusammengeführt oder entfernt

Speichern Sie in einem zweiten Terminal einen Snapshot der Prozesse:

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"

Ein dauerhaftes U unter state kann bedeuten, dass sich der Prozess in einem nicht unterbrechbaren Wartezustand befindet. Ein anhaltendes T zeigt dagegen an, dass der Prozess angehalten wurde. Ein einzelner Snapshot belegt keinen Trend; eine zweite Erfassung nach zehn Sekunden ist aussagekräftiger.

Sichern Sie zuerst die Belege und räumen Sie erst danach auf. Ein erfolgreicher Neustart stellt lediglich die Arbeitsfähigkeit wieder her, beweist aber nicht, dass die Ursache beseitigt wurde.

Prozess und Debug-Sitzung überprüfen

Sicherstellen, dass das richtige Artefakt angehängt ist

Anwendungen mit demselben Namen können gleichzeitig in mehreren Simulatoren, Testverzeichnissen oder alten Build-Verzeichnissen vorhanden sein. Prüfen Sie deshalb zuerst in LLDB den Prozess und die geladenen Images:

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

target list muss auf die ausführbare Datei des aktuellen Builds verweisen. Der Pfad des Zielmoduls unter image list muss im erwarteten Build-Products-Verzeichnis liegen. Stammt der Pfad aus einer anderen DerivedData-Struktur, löschen Sie nicht sofort den gesamten Bestand. Notieren Sie zuerst den alten Pfad und prüfen Sie, ob das Scheme versehentlich ein falsches Build-Artefakt wiederverwendet.

Falls die LLDB-Befehlszeile weiterhin reagiert, führen Sie Folgendes aus:

(lldb) thread list
(lldb) thread backtrace all

Wenn für alle Threads Backtraces verfügbar sind, wurde der Debug-Kanal in der Regel bereits aufgebaut. Das Problem liegt dann eher in einem Wartezustand der Anwendung selbst oder in der Symbolauflösung. Bleiben die Befehle dauerhaft ohne Antwort, erstellen Sie auf Systemebene ein Sample des Zielprozesses:

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

Führen Sie nicht mehrere sample-Befehle direkt hintereinander aus. Eine zehnsekündige Stichprobe reicht aus, um festzustellen, ob der Hauptthread auf eine Sperre, Datei-I/O, einen Netzwerkaufruf oder ein System-Framework wartet.

dSYM anhand der UUID überprüfen

Identische Dateinamen bedeuten nicht, dass die Symbole übereinstimmen. Jeder Link-Vorgang kann eine andere UUID erzeugen, und LLDB verwendet ausschließlich ein dSYM, das zum jeweiligen Mach-O passt.

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

Die UUIDs derselben Architektur müssen auf beiden Seiten übereinstimmen. Enthält die Anwendung arm64, muss mindestens die arm64-Zeile verglichen werden. Verwenden Sie kein dSYM aus einem anderen Archive, einem anderen Commit oder einem später neu gelinkten Build als Ersatz.

Prüfen, ob das Modul geladen wurde

Suchen Sie in LLDB nach dem Zielmodul:

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

Wenn image lookup keine Symbole findet, das Modul aber bereits unter image list erscheint, prüfen Sie zuerst das Format der Debug-Informationen. Entwicklungs-Builds sollten normalerweise DWARF oder DWARF with dSYM erzeugen. Bei starker Optimierung können Funktionen inline eingebunden werden und lokale Variablen unsichtbar sein. Richten Sie dafür eine eigene Diagnose-Configuration ein, statt vorübergehend die gemeinsam vom Team verwendete Release-Configuration zu ändern.

Ein Haltepunkt mit dem Status pending ist nicht zwangsläufig fehlerhaft. Solange ein dynamisches Framework noch nicht geladen wurde, wartet der Haltepunkt darauf, dass das zugehörige Image in den Prozess geladen wird. Setzen Sie den Haltepunkt zunächst an einem sicher geladenen Einstiegspunkt und beobachten Sie anschließend die weiteren Module, statt denselben Haltepunkt wiederholt zu löschen.

Handshake-Fehler in Systemprotokollen suchen

Die Xcode-Oberfläche zeigt häufig nur an, dass das Anhängen fehlgeschlagen ist. Die Systemprotokolle enthalten dagegen meist genauere Angaben zu Prozessabbrüchen, verweigerten Berechtigungen oder unterbrochenen Verbindungen. Starten Sie vor der Reproduktion eine eingeschränkte Protokollerfassung:

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

Beenden Sie die Erfassung nach einer Reproduktion mit Control-C. Das Protokoll kann Benutzernamen, Projektpfade und Gerätekennungen enthalten. Anonymisieren Sie diese Angaben, bevor Sie ein Ticket einreichen oder die Datei im Team weitergeben. Vermeiden Sie eine unnötige, langfristige systemweite Erfassung mit --debug: Sie erzeugt große Mengen an Stördaten und erhöht den Aufwand für die Auswertung.

Wenn das Protokoll zeigt, dass der Zielprozess genau beim Anhängen beendet wird, starten Sie die Anwendung zunächst ohne LLDB und prüfen Sie, ob sie stabil weiterläuft. Schlägt ausschließlich der Testprozess fehl, untersuchen Sie Test-Host, Test-Bundle und getestete Anwendung getrennt, statt sich nur auf die PID der Hauptanwendung zu konzentrieren.

Eine reproduzierbare Wiederherstellungsreihenfolge festlegen

Beginnen Sie die Wiederherstellung mit den Schritten, die den geringsten Einfluss haben:

  1. Beenden Sie die aktuelle Debug-Sitzung, behalten Sie jedoch die Zielanwendung und die Protokolle bei.
  2. Prüfen Sie Scheme, Configuration, Ausführungsziel und Pfad der ausführbaren Datei.
  3. Vergleichen Sie die UUIDs von Mach-O und dSYM.
  4. Erstellen Sie das aktuelle Target neu, ohne das gesamte Projekt zu bereinigen.
  5. Löschen Sie das DerivedData-Verzeichnis des betreffenden Projekts nur, wenn die Wiederverwendung alter Artefakte bestätigt wurde.
  6. Falls das Problem weiterhin reproduzierbar ist, speichern Sie Thread-Backtraces, Prozess-Samples, Systemprotokolle und eine minimale Reproduktionsanleitung.

Mit dem folgenden Befehl können Sie zunächst die Verzeichnisse und ihre Aktualisierungszeiten auflisten, damit nicht versehentlich Daten anderer Aufgaben gelöscht werden:

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

Auf einem Cloud Mac können Build-, Test- und grafische Debug-Aufgaben gleichzeitig ausgeführt werden. Vergewissern Sie sich vor dem Bereinigen, dass keine andere Pipeline dasselbe Verzeichnis verwendet. Noch zuverlässiger ist es, für verschiedene Aufgaben jeweils einen eigenen -derivedDataPath zu verwenden und den Debug-Zustand vom Cache automatisierter Builds zu trennen.

Diagnoseergebnisse in Abnahmekriterien überführen

Nach jeder Fehlerbehebung sollten mindestens vier Punkte überprüft werden: Anhängen bei einem Kaltstart, Anhängen an einen bereits laufenden Prozess, Auslösen von Quellcode-Haltepunkten und Anzeige wichtiger Variablen bei einer Ausnahmeunterbrechung. Beenden Sie anschließend die Remote-Sitzung, stellen Sie sie erneut her und wiederholen Sie den Test. So lässt sich ausschließen, dass das Ergebnis von der aktuellen grafischen Sitzung oder temporären Umgebungsvariablen abhängt.

Das Team kann UUID-Prüfung, Artefaktpfad und Configuration in eine Build-Checkliste aufnehmen, sollte jedoch keine persönlichen Verzeichnisse fest in Skripten hinterlegen. Ein wirklich stabiler Debug-Ablauf besteht nicht darin, nach einer Bereinigung erneut zu testen. Entscheidend ist, dass jedes Teammitglied anhand derselben Belege feststellen kann, ob der Fehler auf Prozess-, Sitzungs- oder Symbolebene liegt, und ausschließlich die Ebene korrigiert, auf der die Abweichung tatsächlich auftritt.

Häufig gestellte Fragen

Was prüfe ich zuerst, wenn LLDB angehängt ist, aber keinen Haltepunkt trifft?

Prüfen Sie mit image list, ob das Zielmodul geladen wurde, und vergleichen Sie danach die UUIDs von Programmdatei und dSYM. Ein fehlendes Modul lässt den Haltepunkt offen, eine abweichende UUID verhindert die Quellcodezuordnung.

Sollte ich bei einer blockierten Debug-Sitzung sofort DerivedData löschen?

Nein. Sichern Sie zuerst Prozessliste, LLDB-Ausgabe, Systemprotokolle und Artefakt-UUIDs. Löschen Sie nur das Verzeichnis des betroffenen Projekts, nachdem veraltete Build-Daten als Ursache bestätigt wurden.

Muss für Remote-Debugging ein Port im Internet freigegeben werden?

Normalerweise nicht. Führen Sie Xcode und LLDB auf dem Cloud Mac aus und bedienen Sie sie über eine kontrollierte Desktop- oder SSH-Sitzung. Begrenzen Sie unvermeidbare Tunnel auf die erforderliche Schnittstelle.

VMDebug physische Macs in der Cloud

Sie benötigen einen physischen Mac mini zur exklusiven Nutzung in einem Einzelauftrag?

Prüfen Sie die Konfigurationen M4 und M4 Pro, vier Mietlaufzeiten und fünf verfügbare Knoten und wählen Sie anschließend das passende Gerät für Ihren Workflow.

Konfiguration auswählen und bestellen