[Powered by Google Translate] [Valgrind] [Nate Hardison, Harvard University] To CS50, CS50.TV] Niektóre z najtrudniejszych błędów w programach C pochodzić od złego zarządzania pamięcią. Istnieje ogromna ilość sposobów zepsuć rzeczy, w tym przydziału złą ilość pamięci, zapominając zainicjować zmienne, zapisu przed lub po zakończeniu bufora i uwolnienie zachować pamięci wiele razy. Objawy wahają się od nagłych załamań do wartości tajemniczo nadpisany, często w miejscach i czasach odległych od pierwotnego błędu. Śledzenia obserwowanego problemu z powrotem do przyczyny głównej może być trudne, ale na szczęście jest to bardzo pomocny program o nazwie Valgrind że można zrobić wiele, aby pomóc. Uruchomić program pod Valgrind aby umożliwić rozległe sprawdzenie alokacji pamięci sterty i dostępów. Kiedy Valgrind wykryje problem, daje natychmiastowe, bezpośrednie informacje, które pozwala na łatwiej znaleźć i rozwiązać problem. Valgrind również raporty na temat śmiertelnych mniej problemów z pamięcią, takie jak wycieki pamięci, przydział pamięci sterty, i zapominając, aby uwolnić ją. Jak nasz kompilator, dzyń, w naszym debugger, GDB Valgrind jest wolne oprogramowanie i jest zainstalowany na urządzeniu. Valgrind działa na pliku binarnym, nie twój. c lub pliki. h kod źródłowy, więc upewnij się, że sporządziła up-to-date kopii programu używając dzyń lub Make. Następnie uruchomić program pod Valgrind może być tak proste, jak tylko poprzedzania standardowe polecenie programu z Valgrind słownego, który zaczyna się Valgrind oraz uruchamia program w środku. Przy uruchamianiu Valgrind robi jakiś kompleks jiggering skonfigurować plik wykonywalny do kontroli pamięci, więc może to zająć trochę się i działa. Program następnie wykonywać normalnie, czy to o wiele wolniej, i kiedy to się zakończy, Valgrind wydrukuje podsumowanie jego użycia pamięci. Jeśli wszystko pójdzie dobrze, będzie to wyglądać mniej więcej tak: W tym przypadku,. / Clean_program to ścieżka do programu chcę uruchomić. A gdy ten nie podejmuje żadnych argumentów, Jeśli to nie tylko, że że ich przyczepność do końca, jak zwykle polecenia. Clean program jest tylko głupi mały program stworzyłem który przydziela miejsca na bloku wskazówki na stercie, umieścić kilka wartości w nich, i uwalnia cały blok. To jest to, co fotografujesz za, bez błędów i bez wycieków. Innym ważnym metryka jest całkowita liczba bajtów przydzielone. W zależności od programu, jeśli przydziały są w megabajtach lub wyższym, pewnie robi coś złego. Czy niepotrzebnie przechowywania duplikatów? Używasz sterty do przechowywania, kiedy lepiej byłoby użyć stos? Tak, błędy pamięci mogą być naprawdę zła. Im bardziej jawne te powodują spektakularne awarie, ale nawet wtedy może być nadal trudno wskazać co dokładnie doprowadziło do katastrofy. Bardziej podstępnie program z błędem pamięci może jeszcze skompilować czysto i wciąż wydaje się działać poprawnie ponieważ udało się na szczęście większość czasu. Po kilku udanych rezultatów "," może po prostu uważam, że katastrofa jest fuks z komputera, ale komputer nigdy nie jest źle. Bieganie Valgrind pomaga wyśledzić przyczynę widocznych błędów pamięci jak również znaleźć czai błędy nawet nie jeszcze wiedzieć. Każdorazowo Valgrind wykryje problem, drukuje informacje o tym co to przestrzegane. Każda pozycja jest dość lakoniczny - linia źródło instrukcji nagannego, co kwestią jest i trochę informacji o danej pamięci - ale często jest to wystarczająco dużo informacji, aby skierować uwagę na właściwym miejscu. Oto przykład z Valgrind uruchomiony na buggy programu które wykonuje nieprawidłową odczytu pamięci sterty. Nie widzimy żadnych błędów ani ostrzeżeń w kompilacji. Uh-oh, summary błąd mówi, że są dwa błędy - dwa nieważne odsłon wielkości 4 - bajty, że jest. Zarówno źle odczytuje wystąpił w głównej funkcji invalid_read.c, najpierw na linii 16 i sekund na linii 19. Spójrzmy na kod. Wygląda tak, jak na pierwsze zaproszenie do printf próbuje czytać int przeszłość koniec naszego bloku pamięci. Jeśli spojrzymy na wyjściu Valgrind jest, widzimy, że Valgrind powiedział nam dokładnie to. Adres próbujemy czytać zaczyna 0 bajtów poza końcem bloku wielkości - 16 bajtów cztery 32-bitowe ints że przydzielone. Oznacza to, że adres staraliśmy odczytu rozpoczyna się na końcu naszego bloku jak widzimy w naszym złym rozmowy printf. Teraz nieważne odsłon nie wydaje się, że nic wielkiego, ale jeśli używasz tych danych do sterowania przepływem programu - na przykład, jako część instrukcji if lub pętli - następnie rzeczy można dyskretnie iść źle. Zobacz, jak mogę uruchomić invalid_read programu i nic niezwykłego się nie dzieje. Straszne, co? Teraz spójrzmy na trochę więcej rodzajów błędów, które mogą wystąpić w kodzie, i zobaczymy jak Valgrind je wykrywa. Właśnie zobaczyłem przykładem invalid_read, więc teraz niech wyewidencjonować invalid_write. Ponownie, żadne błędy ani ostrzeżenia w kompilacji. Okay, Valgrind mówi, że są dwa błędy w tym programie - i invalid_write i invalid_read. Sprawdźmy ten kod. Wygląda na to, że mamy wystąpienie klasycznego strlen plus jeden bug. Kod nie malloc dodatkowy bajt miejsca do / 0 charakteru, więc kiedy str kopia poszła do napisania go w ssubstrlen "CS50 skały!" napisał, 1 bajt poza końcem naszego bloku. Invalid_read przychodzi, gdy wykonujemy nasze wezwanie do printf. Printf kończy czytanie nieprawidłowy pamięci, gdy czyta / 0 znaków jak wygląda na końcu łańcucha E jest drukowanie. Ale nic z tego uciekł Valgrind. Widzimy, że złapał invalid_write jako części egzemplarza ul na linii 11 z głównym i invalid_read jest częścią printf. Rock On, Valgrind. Ponownie, to nie wydaje się to nic wielkiego. Możemy uruchomić ten program w kółko poza Valgrind i nie widać żadnych objawów błędach. Jednak spójrzmy na niewielką zmienność to zobaczyć jak rzeczy mogą się naprawdę źle. Więc, przyznane nam nadużywają rzeczy więcej niż tylko nieco w tym kodzie. My tylko przydzielanie przestrzeni na stercie dla dwóch ciągów długość CS50 skał, tym razem, pamiętając / 0 charakter. Ale potem rzucamy w ciąg super-długie do bloku pamięci że S jest skierowany do. Jaki wpływ będzie, że mają na bloku pamięci, który wskazuje na T? Cóż, jeśli T wskazuje na pamięci, że jest po prostu obok S, przychodzi tylko po to, wówczas może pisaliśmy na części T. Przyjrzyjmy się ten kod. Spójrz na to, co się stało. Struny mamy zapisane w naszych blokach sterty obu pojawił się wydrukowane prawidłowo. Nic nie wydaje się w porządku w ogóle. Jednak wróćmy do naszego kodu i skomentuj linię gdzie skopiować CS50 skały do drugiego bloku pamięci, wskazywany przez t. Teraz, kiedy mamy ten kod powinniśmy zobaczyć tylko treść pierwszego bloku pamięci wydrukować. Whoa, choć nie str kopia żadnych znaków w drugim bloku sterty, wskazywany przez T, otrzymujemy wydruk. Istotnie, ciąg nadziewane do naszego pierwszego bloku zajęli pierwszy blok i do drugiego bloku, co wszystko wydaje się normalne. Valgrind, choć opowiada prawdziwą historię. Proszę bardzo. Wszystkie te nieważne czyta i pisze. Spójrzmy na przykład inny rodzaj błędu. Tutaj mamy coś zrobić raczej niefortunne. Łapiemy przestrzeń dla int na stercie, i zainicjować int wskaźnik - P - aby wskazywał na tej przestrzeni. Jednakże, podczas gdy nasz wskaźnik jest inicjowany, dane wskazujące, że jest to po prostu nie jest, co śmieci w tej części hałdy. Więc kiedy załadować te dane do int i, my technicznie zainicjować i, ale robimy to z danych śmieci. Wezwanie do dochodzenia, które jest przydatne makro debugowanie zdefiniowane w trafnie nazwanej dochodzić bibliotece przerwie programu, jeśli jego stan test nie powiedzie się. To jest, jeśli nie jest 0. W zależności od tego, co było w przestrzeni sterty, wskazywany przez p, ten program może działać, a czasami nie w innym czasie. Jeśli to działa, po prostu się szczęście. Kompilator nie będzie złapać ten błąd, ale Valgrind pewno. Widzimy tam błąd wynikający z naszego użycia tych danych śmieci. Podczas przydzielania pamięci sterty, ale nie zwalnianie go lub zwolnić go, że nazywa się wyciek. Na małym, krótko programie uruchamiającym i natychmiast wyjść, przecieki są dość nieszkodliwe, ale w przypadku projektu o większym rozmiarze i / lub długowieczności, nawet niewielka nieszczelność może spotęgować się w coś poważnego. Dla CS50, mamy oczekiwać na dbać o uwolnienie wszystkich pamięci sterty, które rozdzieli, ponieważ chcemy budować umiejętności prawidłowo obsługiwać proces ręcznej wymagane przez C. Aby to zrobić, program powinien mieć dokładny jeden do jednego między malloc i wzywa darmo. Na szczęście, Valgrind może pomóc wycieków pamięci też. Tutaj jest dziurawy program o nazwie leak.c przydzielaną Przestrzeń na stosie, to zapisuje się, ale nie zwalnia go. Tworzymy go z Marka i uruchomić pod Valgrind, i widzimy, że o ile nie mamy żadnych błędów związanych z pamięcią, to mamy jeden wyciek. Istnieje 16 bajtów na pewno zwycięstwo, co oznacza, że ​​w tym wskaźnik pamięci nie w zakresie, gdy program wyjściu. Teraz Valgrind nie daje nam mnóstwo informacji na temat wycieku, ale jeśli będziemy śledzić ten liścik, że daje w dół w kierunku dna raporcie ponownie uruchomić z - wyciek sprawdzić = full , aby zobaczyć pełne dane wyciekły pamięci będziemy mieć więcej informacji. Teraz, w podsumowaniu sterty Valgrind mówi nam, gdzie pamięć, która została utracona została wstępnie przydzielone. Tak jak wiemy, od patrzenia w kodzie źródłowym, Valgrind informuje nas, że wyciekły z pamięci przyznane z zaproszeniem do malloc na linii 8 z leak.c w głównej funkcji. Całkiem sprytne. Valgrind kategoryzuje nieszczelności za pomocą tych terminów: Zdecydowanie porażka - to kupa przydzielonej pamięci Program do którego nie ma wskaźnika. Valgrind wie, że kiedyś miał wskaźnik ale od tego czasu straciłem go. Ta pamięć jest na pewno przeciekał. Pośrednio porażka - to kupa przydzielonej pamięci , do którego tylko do niego również wskaźniki są tracone. Na przykład, jeśli nie pamiętasz wskaźnik do pierwszego węzła połączonej listy, następnie pierwszy węzeł sam byłby zdecydowanie utracone, natomiast wszelkie kolejne węzły będą pośrednio utracone. Prawdopodobnie stracił - to kupa przydzielonej pamięci do których nie może być Valgrind się, czy jest to wskaźnik, czy nie. Wciąż dostępny jest kupa przydzielonej pamięci do którego program ma jeszcze wskaźnik na wyjściu, co zazwyczaj oznacza, że ​​zmienna globalna na niego wskazuje. Aby sprawdzić te nieszczelności, będziesz mieć również o możliwość - Wciąż osiągalny = yes w pw Valgrind. Te różne przypadki mogą wymagać różnych strategii do czyszczenia ich, ale nieszczelności powinny być wyeliminowane. Niestety, ustalenie wycieki mogą być trudne do zrobienia, ponieważ błędne połączenia darmo można wysadzić swój program. Na przykład, jeśli spojrzymy na invalid_free.c, widzimy przykład złej dezalokacji pamięci. , Co powinno być pojedyncze wywołanie uwolnienia cały blok pamięci wskazywanego przez int_block, zamiast tego staje się próba zwolnienia każdy int wielkości punkt z pamięci indywidualnie. To nie katastrofalnie. Boom! Jaki błąd. To zdecydowanie nie jest dobry. Jeśli nie wiecie, z tego rodzaju błędów, choć i nie wiesz, gdzie szukać, spadnie z powrotem na swoim nowym najlepszym przyjacielem. Zgadłeś - Valgrind. Valgrind, jak zawsze, nie wie dokładnie, co się dzieje. W Alloc i wolne liczy nie pasują. Mamy 1 alloc i 4 zwalnia. I Valgrind mówi nam także, gdzie pierwsze złe wolne połączenie - który wywołał Blowup - pochodzi - Linia 16. Jak widać, złe połączenia na darmo są naprawdę złe, dlatego zalecamy pozwoleniem wyciek programu podczas pracy na uzyskanie funkcjonalności prawidłowe. Zacząć szukać przecieków tylko po program działa prawidłowo, bez żadnych innych błędów. I to wszystko, co mamy do tego filmu. Teraz co czekasz? Idź uruchomić Valgrind na programach teraz. Nazywam się Nate Hardison. To CS50. [CS50.TV]