16.10.2016

Argh! Clang failed with exit code 254

Die Änderungen der letzten Wochen in Argh! Earthlings! beschränkten sich auf die Vervollständigung von Szenen-Details, fehlenden Zwischensequenzen oder den Übersetzungen. Es sah aus, als würde einem ersten, externen Test nichts im Wege stehen. Tja. Pech gehabt. Die Analyse des Codes durch X-Code trieb mich die letzten Tage fast in den Wahnsinn.

Hin uns wieder sollte man während der Entwicklung von größeren Projekten in XCode eine Analyse seines Codes durchführen lassen. XCode kann so auf potentielle Memory Leaks aufmerksam machen, noch bevor diese bei Nutzern bzw. Spielern auftreten. Noch besser als das manuelle Ausführen ist natürlich ein Continous Integration-Setup. Dies ist aber mit einwenig mehr Aufwand verbunden. Hätte ich doch von Beginn an sowas Vorbereitet, wäre meine letzte Woche nicht so nervenzerreibend.

Hätte, hätte, Fahrradkette… Nun ist das Kind, bzw. der Code in den Brunnen gefallen und muss untersucht werden.

Der Versuch den Code von Classes/engine/controllers/AEActions.m zu analysieren scheiterte reglmäßig mit einer dubiosen Meldung: clang failed with exit code 254, genauer mit clang: error: unable to execute command: Segmentation fault: 11.

Output:

Showing All Messages
Analyze Classes/engine/controllers/AEActions.m
    cd /Users/damian/Projekte/GameOverTale
    export LANG=en_US.US-ASCII
    export PATH="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/ \
        Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/local/ \
        bin:/usr/bin:/bin:/usr/sbin:/sbin"
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin \
        /clang -x objective-c -arch arm64 ..... ArghEarthlings/normal/arm64/AEActions.plist

clang: error: unable to execute command: Segmentation fault: 11
clang: error: clang frontend command failed due to signal (use -v to see invocation)
Apple LLVM version 8.0.0 (clang-800.0.38)
Target: aarch64-apple-darwin15.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
clang: note: diagnostic msg: PLEASE submit a bug report to http://developer.apple.com/bugreporter/ \
    and include the crash backtrace, preprocessed source, and associated run script.
clang: note: diagnostic msg:
********************

PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT:
Preprocessed source(s) and associated run script(s) are located at:
clang: note: diagnostic msg: /var/folders/j2/h85kccjj2079347__6hwrtgm0000gn/T/AEActions-86e350.m
clang: note: diagnostic msg: /var/folders/j2/h85kccjj2079347__6hwrtgm0000gn/T/AEActions-86e350.sh
clang: note: diagnostic msg:

********************
Command /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ \
    clang failed with exit code 254

Weder eine Codezeile, noch ein konkreter Hinweis auf das Problem waren zu erkennen. Stattdessen die Bitte den Code /var/folders/j2/h85kccjj2079347__6hwrtgm0000gn/T/AEActions-86e350.* an Apple zu senden. Naja. Ich bin skeptisch, ob mir das Einsenden des Bugs eine schnelle Hilfe sein wird. Also, Strategiewechsel.

Dr. Google und einige fleißige Coder haben immer wieder darauf hingewiesen, dass ein subtiler Typo oder eine falsche Nutzung einer C Methode ein Problem darstellen würde… nur welche?

Um das Problem einzukreisen, hatte ich die betroffene Datei und dessen abhängige Dateien in ein neues Projekt kopiert und Stück für Stück soweit rekonstruiert, dass sich das Projekt auch bauen ließ. Die Analyse schlug nun sehr schnell bei einer überschaubaren Menge Code fehl. Also hatte ich begonnen alle abhängigen Dateien zu entfernen und hatte dabei immer wieder eine Analyse durchgeführt. Exit code 254-Deja Vu…

Nach einem Haufen entfernter Methoden hatte ich die Stelle isoliert. Nun war es Zeit den Code der Implementierung zu untersuchen. Eine Stelle, die mir sofort ins Auge fiel, war ein Callback.

[_idleCountdown countDownIdle:aTimeDeltaInMillis
                      trigger:^(AEActionItem *aAction) {
                          aCallback(self);
                      }
];

Nach einigen Versuchen den Code zu entfernen oder verändern, fokussierte ich mich auf die Parameter des Callbacks ^(AEActionItem *aAction). Irgendwie schmeckten sie Clang nicht.

Der Code beschriebt einen Pointer auf eine Methode, einen sogenannten Callback. Diese Methode bietet einen Parameter vom Typ AEActionItem.

Als ich mir nun das Interface an der Stelle countDownIdle:trigger: genauer angeschaut hatte, bin ich fast vom Stuhl gefallen…

-(void) countDownIdle:(NSTimeInterval)aTimeDeltaInMillis
              trigger:(void(^)())aCallback{
    ...
}

Hier ist der Callback ohne Parameter definiert! Subtiler Fehler, ganz wie von der Coder-Gemeinde vorhergesagt.

Nachdem ich die Stelle korrigiert hatte, lief der Analyzer wieder wie erwartet durch.

[_idleCountdown countDownIdle:aTimeDeltaInMillis
                      trigger:^{
                          aCallback(self);
                      }
];

Argh! Der ganze Aufwand nur wegen einem Parameter, der zu viel deklariert wurde? Schade, dass clang, das nicht selbst erkennt. Es zeigt mir wieder einmal, wie wertvoll Typensicherheit höherer Programmiersprachen ist…

Nun ist es an der Zeit den Code von solchen Stellen zu befreien. Auf ein Weiteres!


16.10.2016