Jak debugowali błędy segmentacji przed chronioną pamięcią?

20

Teraz, kiedy popełniam błąd programistyczny ze wskaźnikami w C, dostaję ładny błąd segmentacji, mój program ulega awarii, a debugger może nawet powiedzieć mi, gdzie poszło źle.

Jak to zrobili, skoro ochrona pamięci nie była dostępna? Widzę, jak programiści DOS bawią się i psują cały system operacyjny, gdy popełnił błąd. Wirtualizacja nie była dostępna, więc mógł jedynie uruchomić się ponownie i spróbować ponownie. Czy to naprawdę tak wyglądało?

Bart Friederichs
źródło
4
Tak, tak to poszło. Losowy restart komputera miał miejsce i zdarzał się często. Ochrona pamięci to wspaniała rzecz :)
Rocklan
7
Gdy nie masz chronionej pamięci, nie ma czegoś takiego jak błąd segmentacji. Oczywiście, nawet jeśli masz chronioną pamięć, nadal możesz sypać własną przestrzeń; system operacyjny po prostu się tym nie przejmuje.
Blrfl
3
Nawet teraz wiele błędów wskaźnika nie powoduje miłej segregacji.
CodesInChaos
1
W czasach DOS chroniona pamięć istniała już w innych systemach operacyjnych.
mouviciel

Odpowiedzi:

36

Widzę, jak programiści DOS bawią się i psują cały system operacyjny, gdy popełnił błąd.

Tak, prawie tak się stało. W większości systemów, które miały mapy pamięci, lokalizacja 0 była oznaczona jako niepoprawna, dzięki czemu wskaźniki zerowe mogły być łatwo wykryte, ponieważ był to najczęstszy przypadek. Ale było wiele innych przypadków, które spowodowały spustoszenie.

Ryzykując, że zabrzmi to jak geezer, powinienem zauważyć, że obecnie skupianie się na debugowaniu nie należy do przeszłości. Wcześniej dużo starano się pisać poprawne programy, zamiast usuwać błędy z niewłaściwych programów. Po części dlatego, że taki był nasz cel, ale w dużej mierze dlatego, że narzędzia utrudniały pracę. Spróbuj pisać swoje programy na papierze lub na perforowanych kartach, a nie w IDE i bez korzyści interaktywnego debuggera. Daje smak poprawności.

Ross Patterson
źródło
3
W rzeczywistości miałem nadzieję, że „stare geezery” odpowiedzą na moje pytanie. Nic nie przebije doświadczenia z pierwszej ręki. Dzięki.
Bart Friederichs
6
Spróbuj napisać kod, gdy sprzęt jest dostępny do debugowania co noc od 02:00 do 06:00, oczywiście zakładając, że twój kolega nie zarezerwował go na sesję debugowania.
MSalters
@MSalters Rzeczywiście! Przy mojej pierwszej pracy mogliśmy również zarezerwować automaty w niedzielę od 0700 do 1900 - prawdziwa uczta, daj mi powiedzieć :-)
Ross Patterson
2
Pamiętam, jak pisałem swój pierwszy program na papierze, podróżując do domu z uniwersytetu. Następnego dnia, kiedy mogłem go
wbić
1
@ JanDoggen, to samo dla mnie. Gdy masz tylko jedną próbę, sprawiasz, że ta próba naprawdę się liczy.
dokładnie
23

Kiedyś nie mieliśmy ochrony pamięci i całego tego cholernego biznesu! Użyliśmy printf, aby określić, gdzie jesteśmy w programie, i podobało nam się !

Choć z całą powagą, zwykle oznaczało to, że byliśmy bardziej ostrożni. Tam, gdzie nazywa się malloc, gdzieś w programie musiał być darmowy, a takie sprawdzanie było rygorystyczne, ponieważ w przypadku problemu, jak wyraźnie zauważyłeś, błędy segmentacji nie są pomocnymi błędami.

W przypadku takich błędów najlepiej jest spróbować zrozumieć, kiedy występują takie błędy segmentacji (za pomocą printf) i, patrząc na kod, ustalić, dlaczego dostęp do pamięci w tym momencie nie był prawidłowy i stamtąd działać wstecz.

Zasadniczo to samo dzieje się dzisiaj, z wyjątkiem tego, że używamy debugerów do ustalenia, kiedy wystąpią błędy, ale nadal musisz zrozumieć, dlaczego tak się stało, i nie zawsze jest to tak proste, jak znalezienie linii, w której wystąpił błąd. Błędy powodują błędy, takie jak reakcja łańcuchowa, a jeśli byłeś programistą C w tamtych czasach, spędziłeś 20% czasu na kodowaniu, a resztę czasu wyciągałeś włosy, naprawiając błędy.

Neil
źródło
2
Uwolnij Malloc!
Chris
1
Od czasu do czasu, nawet dzisiaj, nawet stos wywołań i stan zmiennej są całkowicie bezużyteczne do określania, co do diabła poszło nie tak i jak to naprawić. Dzieje się tak zwłaszcza w przypadku złożonego oprogramowania z dużą liczbą możliwych stanów, z których niektóre są współzależne, a niektóre wzajemnie się wykluczają. Jedno zbłąkane pismo w dowolnym miejscu i pominięte twierdzenie o gwarantowanych warunkach wstępnych może doprowadzić cię właśnie tam.
CVn
1
@ MichaelKjörling, myślę, że jeśli chodzi o wykrywanie błędów w programach, zrobiliśmy postępy tylko w kwestii znalezienia wyzwalacza błędów, ale wciąż mamy wiele kilometrów do odkrycia przyczyny tych błędów. Asercje na pewno utrzymują mnie przy zdrowych zmysłach. :)
Neil
6

dobrze ..

segfault to naprawdę fajny wskaźnik, że coś jest nie tak, ale nadal musisz znaleźć podstawową przyczynę. Więc jeśli zadajesz pytanie, jak znaleźć podstawową przyczynę, odpowiedź nie różni się dzisiaj tak bardzo jak wtedy. Oczywiście języki i narzędzia stały się łatwiejsze w obsłudze, ale ogólny taktik jest taki sam:

  • rejestrowanie pomaga znaleźć obszar, w którym występuje problem. Wyszukiwanie binarne printf jest jego formą.
  • debugowanie, krok po kroku, punkty przerwania i zegarki
  • refaktoryzacja w celu lepszego zrozumienia
  • wpatrując się mocno w kod
  • spójrz na zrzut pamięci / rdzenia
  • karmienie go różnymi danymi
  • pokazując to innym ludziom
  • przełącz się na język bez wskaźników (i nowy zestaw problemów) ...

Na bardziej abstrakcyjnym poziomie masz trzy podejścia: 1. praca z kodem 2. spójrz na program podczas działania 3. spójrz na wyniki po tym, jak zrobił coś głupiego

przy okazji błąd wskaźnika nie musi powodować awarii.

Jako programista Amigi wykorzystałem prawie wszystko. I tak rozpoczyna się tam, gdzie powszechna praktyka.

openCage
źródło
4

Na IBM 360 z uruchomionymi zadaniami wsadowymi Fortran otrzymywaliśmy zrzuty heksadecymalne. Taki zrzut może wynosić nawet cal grubości zielono-białego papieru do drukarek składanego na wachlarz. Informuje o tym, jakie są rejestry, i stamtąd możemy cofnąć się i dowiedzieć się, co robi program. Możemy znaleźć każdy podprogram i dowiedzieć się, gdzie zachował swój adres zwrotny, abyśmy mogli zobaczyć kontekst. Pomogłoby to mieć listę asemblerów programu.

Mike Dunlavey
źródło
2

Kiedyś pracowałem nad naprawą błędów w znanym wówczas oprogramowaniu do prezentacji Windows 3.1.

Miałem błąd, który, gdy się pojawił, spowodował niebieski ekran śmierci.

Błąd wystąpił tylko wtedy, gdy pewna pętla została wykonana ponad 1000 razy. Użyłem zaawansowanych funkcji debuggera, aby pozwolić punktowi przejścia przejść 1000 razy, a następnie ostrożnie przeszedłem program. Za każdym razem, gdy szedłem za daleko lub pomijałem wywołanie funkcji, które zawierało błąd Windows Blue Screened.

W końcu po kilku dniach pracy zawęziłem ją do funkcji, której kończy się pamięć i zamiast wyświetlać komunikat o błędzie, dołączyłem komunikat o błędzie do bufora. Z każdą kolejną iteracją tracił więcej pamięci, aż coś kluczowego zostało nadpisane i system Windows został zniszczony.

Rozwiązaniem były umiejętności debugowania i wytrwałość.

Stuart Woodward
źródło