Ponieważ narzędzie Valgrinda do debugowania pamięci p.t. Memcheck sprawdza nie tylko kod aplikacji, ale też wszystkich dynamicznych bibiotek z których ona korzysta, dobrze jest wyłączyć wyświetlanie błędów, których i tak nie możemy naprawić (bo należą do cudzego kodu). O tym jest dzisiejszy wpis.
Tworzenie filtrów
Do filtrowania wyników memchecka pomocne są dwie opcje:
- –suppressions=plik_z_filtrami – pozwala wskazać plik, z którego Valgrind ma wczytać filtry. Plik ten trzeba umieścić w katalogu, z którego odpalamy Valgrinda – zwykle jest to katalog z naszym skompilowanym programem. Dzięki temu każdy projekt może mieć swój własny zestaw filtrów
- –gen-suppressions=all – dla każdego błędu generuje sygnaturkę, którą można przekopiować sobie do pliku z filtrami. Następnym razem błąd nie będzie już raportowany
Przykładowa sygnaturka może wyglądać tak:
{ <FILTR MEMCZEKA> Memcheck:Cond fun:_Z5test1Ri fun:main }
Pewnie niewiele Ci ona mówi, ale nie przejmuj się tym. Raz w życiu będziesz ją oglądał – kiedy będziesz kopiował ją do pliku z filtrami ;)
Droga na skróty
Zeby nie podawać pliku z filtrami za każdym razem, gdy odpalamy memchecka, warto stworzyć sobie plik konfiguracyjny ~/.valgrindrc, i w nim umieścić linię –suppressions=plik_z_filtrami. Za każdym razem, gdy odpalamy Valgrinda, wczytuje on ten plik i stosuje opcje w nim zapisane.
Przykład
Mamy prosty programik z problemem pamięciowym – wykonujemy instrukcję warunkową w oparciu o niezainicjalizowaną zmienną;
// program.cpp int main() { int x; if (x == 5) return 1; else return 0; }
Co oczywiście skutkuje protestem memchecka. I bardzo dobrze – takie jest jego zadanie.
My jednak chcemy wyfiltrować ten błąd.
W tym celu, dla wygody, tworzymy sobie plik konfiguracyjny ~/.valgrindrc, i umieszczamy w nim linię –suppressions=plik_z_filtrami.
Następnie w katalogu z programem tworzymy plik o nazwie plik_z_filtrami. W nim będziemy umieszczali nasze filtry.
Dalej, kompilujemy program w trybie debugowania i odpalamy memchecka z opcją generowania sygnatur błędów:
> g++ -g -o program program.cpp > valgrind --gen-suppressions=all ./program
W wyniku tego otrzymujemy sygnaturkę błędu:
==5626== Conditional jump or move depends on uninitialised value(s) ==5626== at 0x8048409: main (program.cpp:5) { <insert a suppression name here> Memcheck:Cond fun:main } ==5626== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 15 from 1) ==5626== malloc/free: in use at exit: 0 bytes in 0 blocks. ==5626== malloc/free: 0 allocs, 0 frees, 0 bytes allocated. ==5626== For counts of detected errors, rerun with: -v ==5626== All heap blocks were freed -- no leaks are possible.
Wklejamy ją do naszego pliku z filtrami plik_z_filtrami, i odpalamy memczeka jeszcze raz. Tym razem dostajemy taki output:
==5663== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 16 from 2) ==5663== malloc/free: in use at exit: 0 bytes in 0 blocks. ==5663== malloc/free: 0 allocs, 0 frees, 0 bytes allocated. ==5663== For counts of detected errors, rerun with: -v ==5663== All heap blocks were freed -- no leaks are possible.
Czyli udało nam się wyfiltrować nasz błąd :)
Podsumowanie
Dzięk plikowi konfiguracyjnemu ~/.valgrindrc przypisaliśmy Valgrindowi na stałe plik z filtrami błędów.
Tworząc plik z filtrami błędów i wklejając do niego sygnaturkę naszecho niechcianego błędu, na stałe pozbyliśmy się go z raportów memchecka :)