V
VikingGe
Gast
Guten Morgen. Ich muss mal eine Geschichte erzählen.
Wem fällt an diesem Code hier etwas auf:
Niemandem? Gut. Ich habe nämlich auch den ganzen Tag dafür gebraucht, herauszufinden, wo der Fehler steckt.
Aber der Reihe nach.
Was soll das Stück Code tun? Nun, es präpariert ein Array und kopiert dessen Inhalt dann asynchron in einen OpenGL-Buffer, um damit ein ansonsten auf der CPU gerendertes Bild auf den Bildschirm zu malen. Das sollte dann ungefähr so aussehen:
Das tat es bis einschließlich heute morgen auch, aber dann bin ich auf die glorreiche Idee gekommen, das ganze mal mit g++ zu compilieren und zu testen. Normalerweise verwende ich clang zum Entwickeln.
Ergebnis:
...ups
Nunja, relativ ratlos habe ich dann erstmal angefangen, alles mit expliziten Memory Barriers vollzuknallen, um festzustellen, dass es wohl nicht daran lag, dass der Compiler irgendwelche Speicherzugriffe durcheinanderoptimiert. Auch eine klassische Race-Condition war schnell ausgeschlossen, die Queue, in die das Funktionsobjekt geschrieben wird, wird ordnungsgemäß gelockt. Und eine Speicherkorruption war es auch nicht, die hätte man spätestens mit valgrind bemerken müssen.
Als nächstes dann probiert, ob es denn mit einer anderen Variante der upload-Methode funktioniert, die zwar im Endeffekt dasselbe macht, vorher aber noch eine Kopie der Daten anlegt. Ja, ging, das Wissen brachte mich aber nicht weiter.
Dann wurde dank printf-Debugging klar, dass die Lambda-Funktion in dem GCC-Build gar nicht ausgeführt wird, die aufrufende Funktion jedoch schon.
Dass die angezeigten Aufrufe allerdings von einer anderen Stelle kamen, habe ich trotz eindeutiger Zahlen auf dem Bildschirm nicht erkannt und und ich dachte, möglicherweise funktioniert meine Klasse für Funktionsobjekte nicht richtig, obwohl diese seit ~9 Monaten existiert und nichts weiter war als ein Mini-Wrapper für std::function. Hab ich also die Klasse neu geschrieben und quasi so eine Art eigenes std::function gebaut - aber auch das löste das Problem nicht. Dafür hat das Projekt jetzt 500 Zeilen Code mehr und einen Todo-Eintrag weniger
Naja. Der upload-Aufruf mündet nach einem kurzen Umweg hier:
Ein Haufen Aufrufe ist tatsächlich an der if-Abfrage gescheitert, obwohl das eigentlich gar nicht hätte passieren dürfen.
Aber immerhin war dadurch sofort die Ursache des Fehlers klar:
Joa. Ganz grob das, was passiert:
Also klassisches undefiniertes Verhalten, weil der Move Seiteneffekte hat. Warnungen vom Compiler aber in beiden Fällen Fehlanzeige, trotz -Wall.
Ich meine, das hier war längst nicht der erste seltsame und zugleich tierisch nervige Fehler, den ich erlebt habe, aber auf jeden Fall einer der merkwürdigsten. Jetzt ist nur die Frage... wie vermeidet man sowas in Zukunft am besten?
Compiler sagt nichts, cppcheck sagt(e) nichts... irgendwelche Ideen?
Wem fällt an diesem Code hier etwas auf:
Code:
void GUIVertexData::updateDataBuffer(const Vector2D<uint32> blockSize, const Vector2D<uint32> blockCount) {
Array<BlockData> blockData(blockCount.x * blockCount.y);
...
this->_context->bufferTransferManager()->upload(
this->_dataBuffer, 0, blockData.size() * sizeof(BlockData),
Lambda([blockData = std::move(blockData)]
(void* dst, const size_t size) {
Functions::copyNT(
reinterpret_cast< char*>(dst),
reinterpret_cast<const char*>(blockData.data()),
size);
}));
}
Aber der Reihe nach.
Was soll das Stück Code tun? Nun, es präpariert ein Array und kopiert dessen Inhalt dann asynchron in einen OpenGL-Buffer, um damit ein ansonsten auf der CPU gerendertes Bild auf den Bildschirm zu malen. Das sollte dann ungefähr so aussehen:
Das tat es bis einschließlich heute morgen auch, aber dann bin ich auf die glorreiche Idee gekommen, das ganze mal mit g++ zu compilieren und zu testen. Normalerweise verwende ich clang zum Entwickeln.
Ergebnis:
...ups
Nunja, relativ ratlos habe ich dann erstmal angefangen, alles mit expliziten Memory Barriers vollzuknallen, um festzustellen, dass es wohl nicht daran lag, dass der Compiler irgendwelche Speicherzugriffe durcheinanderoptimiert. Auch eine klassische Race-Condition war schnell ausgeschlossen, die Queue, in die das Funktionsobjekt geschrieben wird, wird ordnungsgemäß gelockt. Und eine Speicherkorruption war es auch nicht, die hätte man spätestens mit valgrind bemerken müssen.
Als nächstes dann probiert, ob es denn mit einer anderen Variante der upload-Methode funktioniert, die zwar im Endeffekt dasselbe macht, vorher aber noch eine Kopie der Daten anlegt. Ja, ging, das Wissen brachte mich aber nicht weiter.
Dann wurde dank printf-Debugging klar, dass die Lambda-Funktion in dem GCC-Build gar nicht ausgeführt wird, die aufrufende Funktion jedoch schon.
Dass die angezeigten Aufrufe allerdings von einer anderen Stelle kamen, habe ich trotz eindeutiger Zahlen auf dem Bildschirm nicht erkannt und und ich dachte, möglicherweise funktioniert meine Klasse für Funktionsobjekte nicht richtig, obwohl diese seit ~9 Monaten existiert und nichts weiter war als ein Mini-Wrapper für std::function. Hab ich also die Klasse neu geschrieben und quasi so eine Art eigenes std::function gebaut - aber auch das löste das Problem nicht. Dafür hat das Projekt jetzt 500 Zeilen Code mehr und einen Todo-Eintrag weniger
Naja. Der upload-Aufruf mündet nach einem kurzen Umweg hier:
Code:
void BufferTransferManager::upload(const size_t size, UploadFunction&& upload, ExplicitTransferFunction&& execute) {
if (size == 0)
return;
// bisschen Code
}
Ein Haufen Aufrufe ist tatsächlich an der if-Abfrage gescheitert, obwohl das eigentlich gar nicht hätte passieren dürfen.
Aber immerhin war dadurch sofort die Ursache des Fehlers klar:
Code:
this->_dataBuffer, 0, blockData.size() * sizeof(BlockData),
Lambda([blockData = std::move(blockData)]
Joa. Ganz grob das, was passiert:
Code:
clang-Build:
1. blockData.size(), gibt 640 Bytes zurück
2. std::move(blockData)
GCC-Build:
1. std::move(blockData)
2. blockData.size(), ist jetzt natürlich 0
Also klassisches undefiniertes Verhalten, weil der Move Seiteneffekte hat. Warnungen vom Compiler aber in beiden Fällen Fehlanzeige, trotz -Wall.
Ich meine, das hier war längst nicht der erste seltsame und zugleich tierisch nervige Fehler, den ich erlebt habe, aber auf jeden Fall einer der merkwürdigsten. Jetzt ist nur die Frage... wie vermeidet man sowas in Zukunft am besten?
Compiler sagt nichts, cppcheck sagt(e) nichts... irgendwelche Ideen?
Zuletzt bearbeitet: