wskazanie „warunkowy skok lub ruch zależy od niezainicjowanej wartości (wartości)” komunikat valgrind

166

Tak więc otrzymałem od valgrind jakąś tajemniczą wiadomość o niezainicjowanych wartościach i pozostaje tajemnicą, skąd się wzięła zła wartość.

Wydaje się, że valgrind pokazuje miejsce, w którym ostatecznie używana jest wartość zjednostkowana, ale nie źródło niezainicjowanej wartości.

==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)

Jak widać, robi się to dość tajemnicze… zwłaszcza, że ​​kiedy mówi Class :: MethodX, czasami wskazuje prosto na ostream itp. Być może jest to spowodowane optymalizacją?

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

Właśnie tak. Czy jest coś, czego mi brakuje? Jaki jest najlepszy sposób na wyłapanie złych wartości bez konieczności uciekania się do bardzo długiej pracy detektywa printf?

Aktualizacja:

Dowiedziałem się, co było nie tak, ale dziwne jest to, że Valgrind nie zgłosił tego, kiedy po raz pierwszy użyto złej wartości. Został użyty w funkcji mnożenia:

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

Gdzie speedfac był zjednoczonym spławikiem. Jednak w tym czasie nie zostało to zgłoszone i dopiero gdy wartość ma być wydrukowana, że ​​pojawia się błąd .. Czy istnieje ustawienie dla valgrind, aby zmienić to zachowanie?

kamziro
źródło

Odpowiedzi:

230

Użyj opcji valgrind, --track-origins=yesaby śledzić pochodzenie niezainicjowanych wartości. Spowoduje to spowolnienie i zajmie więcej pamięci, ale może być bardzo pomocne, jeśli chcesz wyśledzić pochodzenie niezainicjowanej wartości.

Aktualizacja: Jeśli chodzi o punkt, w którym zgłaszana jest niezainicjowana wartość, instrukcja valgrind stwierdza :

Ważne jest, aby zrozumieć, że Twój program może kopiować niepotrzebne (niezainicjowane) dane tyle, ile chce. Memcheck obserwuje to i śledzi dane, ale nie narzeka. Skarga jest zgłaszana tylko wtedy, gdy program próbuje wykorzystać niezainicjowane dane w sposób, który może wpłynąć na zachowanie programu widoczne z zewnątrz.

Z FAQ Valgrind :

Co do gorliwego raportowania kopii niezainicjowanych wartości pamięci, było to wielokrotnie sugerowane. Niestety, prawie wszystkie programy legalnie kopiują niezainicjowane wartości pamięci (ponieważ kompilatory wykorzystują struktury, aby zachować wyrównanie), a gorliwe sprawdzanie prowadzi do setek fałszywych alarmów. Dlatego Memcheck nie obsługuje obecnie gorliwego sprawdzania.

mark4o
źródło
1
Jaka jest minimalna wersja Valgrind do korzystania z tej funkcji? Używam wersji 3.3.0 i wydaje mi się, że ta opcja nie odpowiada.
Robert S. Barnes
8
@Robert: --track-origins zostało dodane w valgrind 3.4.0
mark4o
20

Oznacza to, że próbujesz wydrukować / wyprowadzić wartość, która jest przynajmniej częściowo niezainicjalizowana. Czy możesz to zawęzić, aby dokładnie wiedzieć, jaka to wartość? Następnie prześledź swój kod, aby zobaczyć, gdzie jest inicjowany. Są szanse, że zobaczysz, że nie jest w pełni inicjalizowany.

Jeśli potrzebujesz więcej pomocy, opublikowanie odpowiednich sekcji kodu źródłowego może pozwolić komuś zaoferować więcej wskazówek.

EDYTOWAĆ

Widzę, że znalazłeś problem. Zwróć uwagę, że valgrind obserwuje skok warunkowy lub ruch w oparciu o zjednostkowane zmienne. Oznacza to, że wygeneruje ostrzeżenie tylko wtedy, gdy wykonanie programu zostanie zmienione z powodu niezainicjowanej wartości (np. Program przyjmuje inną gałąź w instrukcji if). Ponieważ faktyczna arytmetyka nie obejmowała warunkowego skoku ani ruchu, valgrind nie ostrzegł cię o tym. Zamiast tego propagował stan „niezainicjowany” do wyniku instrukcji, która go użyła.

Może się wydawać sprzeczne z intuicją, że nie ostrzega od razu, ale jak zauważył mark4o , robi to, ponieważ niezainicjalizowane wartości są używane w C przez cały czas (przykłady: wypełnienie struktur, realloc()wywołanie itp.), Więc te ostrzeżenia nie byłyby bardzo przydatne ze względu na fałszywie dodatnią częstotliwość.

RarrRarrRarr
źródło
Dzięki. Właśnie dowiedziałem się, co jest nie tak, ale dziwne jest to, że Valgrind nie zgłosił wartości zjednoczonej, dopóki nie został użyty w innym miejscu ..
kamziro
To jest zamierzone. Gdyby samo skopiowanie lub przekazanie niezainicjowanych wartości spowodowało raport o błędzie, otrzymywałbyś je cały czas z wypełniania struktur.
mark4o