Dieser Beitrag bezieht sich auf ein technisches Problem, das in einem aktuellen Softwareprojekt aufgetreten ist, und ermöglicht es dem Leser, von der schwer verdienten Lösung des Problems zu profitieren.
Hintergrund
In meiner Firma bin ich der Implementierer und Betreuer einer internen Bibliothek, die Boost asio ("Asynchronous I / O") Socket-Framework , um plattformübergreifende Datenübertragung über Sockets zu erreichen. Eine Kollegin kam kürzlich zu mir mit dem folgenden Problem: Ihre Blackberry 10-Anwendung, die meine Bibliothek verlinkte und benutzte, stürzte innerhalb weniger Sekunden ab, wenn der Wi-Fi-Router während eines Dateiübertragungsvorgangs kurzerhand ausgeschaltet wurde.
Die integrierte Ablaufverfolgung innerhalb der Bibliothek hat uns gezeigt, dass der Absturz stattfand, als die Bibliothek boost :: asio :: write (boost :: asio :: ip :: tcp :: socket *, boost :: asio) aufgerufen hat :: buffer) funktioniert mit einem Socket, das nicht 'valid' ist (dh der Socket kann unbrauchbar sein). Ein Versuch / Fang (boost :: system :: system_error) Block um das write () herum zu legen, konnte nichts fangen - der Absturz geschah offensichtlich innerhalb von Boost.
Da der Absturz nur in Release-Builds auftrat, konnten wir den Debugger nicht verwenden.
Technische Information
Hier ist ein typischer Befehlszeilenaufruf des Compilers:
%Vor%Schritte zum Lokalisieren der Problemquelle
Wir haben eine leichtgewichtige Minimal-App geschrieben, um das Problem mit viel weniger Code zu reproduzieren, zuerst mit rohen Sockets und dann mit Boost's ASIO. Wäre der Absturz aufgetreten, hätten wir annehmen können, dass das Problem nicht durch unsere proprietäre Bibliothek verursacht wurde. Leider war der Absturz nicht reproduzierbar, was uns zu der Vermutung führte, dass unsere Bibliothek fehlerhaft war.
Wir haben ein leichtgewichtiges Tracing-Framework geschrieben, das in Boosts ASIO-Header-Dateien verwendet werden kann, um Funktionen für das Problem zu instrumentieren. Das Framework gab beim Eingang und beim Verlassen dieser Funktionen eine Zeichenfolge aus, die es uns ermöglichte, auch Werte von Variablen zu verfolgen.
Mit dem Tracing-Framework konnten wir beweisen, dass der Absturz in der boost :: throw_exception () - Template-Funktion aufgetreten ist (nicht relevanter # ifdef-Code entfernt). Boost ruft diese Funktion auf, wenn die Schreiboperation auf Systemebene bei einer "unterbrochenen Leitung" fehlschlägt:
%Vor%Das Einfügen von Spuren und das Aufteilen der 'throw'-Anweisung in separate Anweisungen haben uns gezeigt, dass der Absturz beim Werfen des Ausnahmeobjekts auftrat. Aller Wahrscheinlichkeit nach lief etwas sehr falsch mit dem Abwickeln des Stapels, als die Ausnahme geworfen wurde.
Die Lösung
Nachdem wir festgestellt hatten, dass es sich höchstwahrscheinlich um einen Compiler-Fehler handelte, und nicht um einen auf Anwendungsebene, untersuchten wir die Compiler-Optionen, die zum Erstellen der Bibliothek verwendet wurden. Wir haben eine Speicherbeschädigung ausgeschlossen, weil der interne Boost-Code wahrscheinlich gehärtet und robust genug ist. Sobald wir dachten, dass die Optimierung des Release-Modus die Ursache sein könnte, war die Entfernung zur Lösung kurz: Lassen Sie die Optimierungsstufe von -O3 auf -O2 fallen.
Sobald wir das getan haben, ist der Crash verdampft.
Wir haben seither die Blackberry.cmake-Datei in der QNX-Toolchain so modifiziert, dass sie "-O2" anstelle des ursprünglichen "-O3" verwendet:
%Vor%Angesichts dieses Absturzes könnte es ratsam sein, "-O3" mit Vorsicht zu verwenden. Der Grund, warum unsere Minimal-App das Problem nicht reproduziert hat, war, dass sie mit der Optimierungsstufe 2 und nicht mit der 3 kompiliert wurde.
Wir suchen nach einem SSCCE zum Einreichen bei den QNX- und / oder GNU-Teams.
Die vollständige Lösungsbeschreibung finden Sie oben in der Frage. Laut Dales Zeiger gebe ich hier eine kurze Erklärung der Lösung.
Der Absturz war darauf zurückzuführen, dass Boost für Blackberry mit der Standard-Optimierungsstufe 3 kompiliert wurde. Nachdem wir die Optimierungsstufe von -O3 auf -O2 reduziert hatten, verdampfte der Crash.
Wir haben seither die Blackberry.cmake-Datei in der QNX-Toolchain modifiziert, um "-O2" anstelle des ursprünglichen "-O3" zu verwenden. Angesichts dieses Absturzes könnte es ratsam sein, "-O3" mit Vorsicht zu verwenden.
Tags und Links c++ sockets boost compiler-optimization blackberry-10