Die Ermittlung der Valgrind-Meldung „Bedingter Sprung oder Bewegung hängt von nicht initialisierten Werten ab“

166

Ich habe also eine mysteriöse Nachricht über nicht initialisierte Werte von Valgrind erhalten, und es war ziemlich rätselhaft, woher der schlechte Wert stammt.

Scheint, dass valgrind den Ort anzeigt, an dem der unitialisierte Wert verwendet wird, aber nicht den Ursprung des nicht initialisierten Werts.

==11366== Conditional jump or move depends on uninitialised value(s)
==11366==    at 0x43CAE4F: __printf_fp (in /lib/tls/i686/cmov/libc-2.7.so)
==11366==    by 0x43C6563: vfprintf (in /lib/tls/i686/cmov/libc-2.7.so)
==11366==    by 0x43EAC03: vsnprintf (in /lib/tls/i686/cmov/libc-2.7.so)
==11366==    by 0x42D475B: (within /usr/lib/libstdc++.so.6.0.9)
==11366==    by 0x42E2C9B: std::ostreambuf_iterator<char, std::char_traits<char> > std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::_M_insert_float<double>(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, char, double) const (in /usr/lib/libstdc++.so.6.0.9)
==11366==    by 0x42E31B4: std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::do_put(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, double) const (in /usr/lib/libstdc++.so.6.0.9)
==11366==    by 0x42EE56F: std::ostream& std::ostream::_M_insert<double>(double) (in /usr/lib/libstdc++.so.6.0.9)
==11366==    by 0x81109ED: Snake::SnakeBody::syncBodyPos() (ostream:221)
==11366==    by 0x810B9F1: Snake::Snake::update() (snake.cpp:257)
==11366==    by 0x81113C1: SnakeApp::updateState() (snakeapp.cpp:224)
==11366==    by 0x8120351: RoenGL::updateState() (roengl.cpp:1180)
==11366==    by 0x81E87D9: Roensachs::update() (rs.cpp:321)

Wie zu sehen ist, wird es ziemlich kryptisch. Besonders, wenn es von Class :: MethodX gesagt wird, zeigt es manchmal direkt auf ostream usw. Vielleicht liegt das an der Optimierung?

==11366==    by 0x81109ED: Snake::SnakeBody::syncBodyPos() (ostream:221)

Genau so. Fehlt mir etwas? Was ist der beste Weg, um schlechte Werte zu erfassen, ohne auf superlange Detektivarbeit zurückgreifen zu müssen?

Aktualisieren:

Ich fand heraus, was los war, aber das Seltsame ist, dass Valgrind es nicht gemeldet hat, als der schlechte Wert zum ersten Mal verwendet wurde. Es wurde in einer Multiplikationsfunktion verwendet:

movespeed = stat.speedfactor * speedfac * currentbendfactor.val;

Wo Speedfac ein unitialisierter Schwimmer war. Zu diesem Zeitpunkt wurde jedoch nicht gemeldet, und erst wenn der Wert gedruckt werden soll, wird der Fehler angezeigt. Gibt es eine Einstellung für valgrind, um dieses Verhalten zu ändern?

Kamziro
quelle

Antworten:

230

Verwenden Sie die Option valgrind --track-origins=yes, um den Ursprung nicht initialisierter Werte zu verfolgen. Dies macht es langsamer und benötigt mehr Speicher, kann jedoch sehr hilfreich sein, wenn Sie den Ursprung eines nicht initialisierten Werts ermitteln müssen.

Update: In Bezug auf den Punkt, an dem der nicht initialisierte Wert gemeldet wird, heißt es im Handbuch von valgrind :

Es ist wichtig zu verstehen, dass Ihr Programm Junk-Daten (nicht initialisiert) beliebig oft kopieren kann. Memcheck beobachtet dies und verfolgt die Daten, beschwert sich aber nicht. Eine Beschwerde wird nur ausgegeben, wenn Ihr Programm versucht, nicht initialisierte Daten so zu verwenden, dass das von außen sichtbare Verhalten Ihres Programms beeinträchtigt wird.

Aus den Valgrind FAQ :

Für die eifrige Meldung von Kopien nicht initialisierter Speicherwerte wurde dies mehrfach vorgeschlagen. Leider kopieren fast alle Programme zu Recht nicht initialisierte Speicherwerte (weil Compiler Strukturen auffüllen, um die Ausrichtung beizubehalten), und eifrige Überprüfungen führen zu Hunderten von Fehlalarmen. Daher unterstützt Memcheck derzeit keine eifrige Überprüfung.

mark4o
quelle
1
Was ist die Mindestversion von valgrind, um diese Funktion zu verwenden? Ich benutze 3.3.0 und es scheint die Option nicht zu mögen.
Robert S. Barnes
7
@ Robert: --track-Ursprünge wurde in Valgrind 3.4.0 hinzugefügt
mark4o
20

Dies bedeutet, dass Sie versuchen, einen Wert auszudrucken / auszugeben, der zumindest teilweise nicht initialisiert ist. Können Sie es eingrenzen, damit Sie genau wissen, welcher Wert das ist? Verfolgen Sie anschließend Ihren Code, um zu sehen, wo er initialisiert wird. Möglicherweise werden Sie feststellen, dass es nicht vollständig initialisiert wird.

Wenn Sie weitere Hilfe benötigen, kann das Posten der relevanten Abschnitte des Quellcodes es jemandem ermöglichen, weitere Anleitungen anzubieten.

BEARBEITEN

Ich sehe, Sie haben das Problem gefunden. Beachten Sie, dass valgrind basierend auf einheitlichen Variablen auf bedingte Sprünge oder Bewegungen achtet. Dies bedeutet, dass nur dann eine Warnung ausgegeben wird, wenn die Ausführung des Programms aufgrund des nicht initialisierten Werts geändert wird (dh das Programm nimmt beispielsweise in einer if-Anweisung einen anderen Zweig). Da die eigentliche Arithmetik keinen bedingten Sprung oder eine bedingte Bewegung beinhaltete, warnte Valgrind Sie nicht davor. Stattdessen wurde der Status "nicht initialisiert" an das Ergebnis der Anweisung weitergegeben, die ihn verwendet hat.

Es mag nicht intuitiv erscheinen, Sie nicht sofort zu warnen, aber wie mark4o hervorhob , geschieht dies, weil in C ständig nicht initialisierte Werte verwendet werden (Beispiele: Auffüllen von Strukturen, realloc()Aufruf usw.), sodass diese Warnungen nicht angezeigt werden sehr nützlich wegen der falsch positiven Frequenz.

RarrRarrRarr
quelle
Vielen Dank. Ich habe gerade herausgefunden, was los war, aber das Seltsame ist, dass Valgrind die Sache mit dem einheitlichen Wert erst gemeldet hat, als sie an anderer Stelle verwendet wurde.
Kamziro
Das ist beabsichtigt. Wenn nur das Kopieren oder Weitergeben nicht initialisierter Werte zu einem Fehlerbericht führen würde, würden Sie diese ständig durch Auffüllen von Strukturen erhalten.
Mark4o