Lesen Sie die Kommentare zu dieser Antwort , insbesondere:
Nur weil Sie keinen Test schreiben können, heißt das noch lange nicht, dass er nicht kaputt ist. Undefiniertes Verhalten, das normalerweise wie erwartet funktioniert (C und C ++ sind voll davon), Rennbedingungen, mögliche Neuordnung aufgrund eines schwachen Speichermodells ... - CodesInChaos vor 7 Stunden
@CodesInChaos Wenn der Code nicht reproduziert werden kann, kann er auch nicht getestet werden. Und ungetesteten Code ins Leben zu rufen ist meiner Meinung nach ein schlimmeres Verbrechen - RhysW vor 5 Stunden
... hat mich gefragt, ob es generell gute Möglichkeiten gibt, konsequent sehr selten auftretende Produktionsprobleme auszulösen, die durch Rennbedingungen im Testfall verursacht werden.
quelle
Antworten:
Nachdem ich seit etwa 1978 in diesem verrückten Geschäft tätig war und fast die ganze Zeit im Bereich Embedded Real-Time Computing verbracht hatte, arbeitete ich mit Multitasking, Multithreading und Multi-Was-Systemen, manchmal mit mehreren physischen Prozessoren, und habe mehr als meinen fairen Anteil an der Konkurrenz gejagt Meine überlegte Meinung ist, dass die Antwort auf Ihre Frage ganz einfach ist.
Nein.
Es gibt keine gute allgemeine Möglichkeit, beim Testen eine Rennbedingung auszulösen.
Ihre einzige Hoffnung ist es, sie vollständig aus Ihrem System heraus zu entwerfen.
Wann und wenn Sie feststellen, dass jemand anderes einen hineingestopft hat, sollten Sie ihn auf einen Ameisenhaufen setzen und ihn dann neu designen, um ihn zu beseitigen. Nachdem Sie seinen Fauxpas (ausgesprochen f *** up) aus Ihrem System heraus entworfen haben, können Sie ihn von den Ameisen befreien. (Wenn die Ameisen ihn bereits verzehrt haben und nur Knochen zurücklassen, setzen Sie ein Schild mit der Aufschrift "Das passiert mit Leuten, die die Rennbedingungen in das XYZ-Projekt einbauen!" Und LASSEN SIE IHN DORT.)
quelle
Wenn Sie sich in der MS-Toolkette befinden. Frau Research hat ein Tool erstellt, das für jeden Lauf neue Zwischenzeiten erzwingt und fehlgeschlagene Läufe, das so genannte Schach, neu erstellen kann .
Hier ist ein Video, das es im Einsatz zeigt.
quelle
Das beste Tool, das ich für diese Art von Problemen kenne, ist eine Erweiterung von Valgrind namens Helgrind .
Grundsätzlich simuliert Valgrind einen virtuellen Prozessor und führt Ihre Binärdatei (unverändert) darüber aus, sodass jeder einzelne Speicherzugriff überprüft werden kann. Unter Verwendung dieses Frameworks ruft das Helgrind-Überwachungssystem den Schluss auf, wenn der Zugriff auf eine gemeinsam genutzte Variable nicht ordnungsgemäß durch einen Mechanismus zum gegenseitigen Ausschluss geschützt ist. Auf diese Weise kann eine theoretische Rassenbedingung erkannt werden, auch wenn sie nicht tatsächlich eingetreten ist.
Intel verkauft ein sehr ähnliches Tool namens Intel Inspector .
Diese Tools liefern großartige Ergebnisse, Ihr Programm wird jedoch während der Analyse erheblich langsamer.
quelle
Um einen Multithreading-Fehler aufzudecken, müssen verschiedene Ausführungsthreads gezwungen werden, ihre Schritte in einer bestimmten verschachtelten Reihenfolge auszuführen. Normalerweise ist dies schwierig, ohne manuelles Debuggen oder Manipulieren des Codes, um eine Art "Handle" zur Steuerung dieser Verschachtelung zu erhalten. Das Ändern von Code, der sich unvorhersehbar verhält, wirkt sich jedoch häufig auf diese Unvorhersehbarkeit aus. Daher ist dies schwer zu automatisieren.
Ein netter Trick wird von Jaroslav Tulach in Practical API Design beschrieben : Wenn Sie im fraglichen Code Protokollierungsanweisungen haben, manipulieren Sie den Verbraucher dieser Protokollierungsanweisungen (z. B. ein injiziertes Pseudoterminal), damit er die einzelnen Protokollnachrichten in einem bestimmten akzeptiert Auftrag basiert auf ihrem Inhalt. Auf diese Weise können Sie die Verschachtelung von Schritten in verschiedenen Threads steuern, ohne dem Produktionscode etwas hinzufügen zu müssen, das noch nicht vorhanden ist.
quelle
Es gibt keine Möglichkeit, absolut sicher zu sein, dass verschiedene Arten von undefiniertem Verhalten (insbesondere Rennbedingungen) nicht existieren.
Es gibt jedoch eine Reihe von Tools, die eine gute Anzahl solcher Situationen aufzeigen. Möglicherweise können Sie nachweisen, dass derzeit ein Problem mit solchen Tools vorliegt, obwohl Sie nicht nachweisen können, dass Ihr Fix gültig ist.
Einige interessante Tools für diesen Zweck:
Valgrind ist ein Gedächtnisprüfer. Es findet Speicherlecks, liest nicht initialisierten Speicher, verwendet baumelnde Zeiger und grenzüberschreitende Zugriffe.
Helgrind ist ein Fadensicherheitsprüfer. Es findet Rennbedingungen.
Beide arbeiten mit dynamischer Instrumentierung, dh sie nehmen Ihr Programm wie es ist und führen es in einer virtualisierten Umgebung aus. Das macht sie unaufdringlich, aber langsam.
UBSan ist ein undefinierter Verhaltensüberprüfer. Es werden verschiedene Fälle von undefiniertem Verhalten in C und C ++ gefunden, z. B. Integer-Überläufe, Verschiebungen außerhalb des Bereichs und ähnliches.
MSan ist ein Speicher-Checker. Es hat ähnliche Ziele wie Valgrind.
TSan ist ein Thread-Sicherheitsprüfer. Es hat ähnliche Ziele wie Helgrind.
Diese drei Funktionen sind in den Clang-Compiler integriert und generieren beim Kompilieren Code. Dies bedeutet, dass Sie sie in Ihren Erstellungsprozess integrieren müssen (insbesondere müssen Sie sie mit Clang kompilieren). Dies macht sie anfangs viel schwieriger einzurichten als * grind, hat aber andererseits einen viel geringeren Laufzeitaufwand.
Alle aufgelisteten Tools funktionieren unter Linux und einige unter MacOS. Ich denke noch keine Arbeit an Windows zuverlässig.
quelle
Es scheint, dass die meisten Antworten hier diese Frage als "Wie erkenne ich Rennbedingungen automatisch?" wenn die Frage wirklich ist "Wie reproduziere ich Rennbedingungen beim Testen, wenn ich sie finde?"
Die Möglichkeit besteht darin, in Ihrem Code eine Synchronisierung einzuführen, die nur zu Testzwecken verwendet wird. Wenn beispielsweise eine Racebedingung eintritt, wenn Ereignis X zwischen Ereignis A und Ereignis B eintritt, schreiben Sie zum Testen Ihrer Anwendung einen Code, der darauf wartet, dass Ereignis X nach Ereignis A eintritt. Sie werden wahrscheinlich eine Möglichkeit benötigen, damit Ihre Tests mit Ihrer Anwendung kommunizieren können, um dies mitzuteilen ("Hey, ich teste dieses Ding, also warten Sie an dieser Stelle auf dieses Ereignis").
Ich verwende node.js und mongo, wobei einige Aktionen das Erstellen konsistenter Daten in mehreren Sammlungen beinhalten. In diesen Fällen rufen meine Komponententests die Anwendung auf und teilen ihr mit, dass sie "ein Warten auf Ereignis X" eingerichtet haben. Sobald die Anwendung es eingerichtet hat, wird der Test für Ereignis X ausgeführt und die Tests geben anschließend Auskunft Die Anwendung ("Ich bin fertig mit dem Warten auf Ereignis X"), damit der Rest der Tests normal ausgeführt wird.
Die Antwort hier erklärt diese Art von Dingen im Zusammenhang mit Python im Detail: https://stackoverflow.com/questions/19602535/how-can-i-reproduce-the-race-conditions-in-this-python-code- zuverlässig
quelle