Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00187 007343 11068427 na godz. na dobę w sumie
Niezawodność oprogramowania - książka
Niezawodność oprogramowania - książka
Autor: Liczba stron: 228
Wydawca: Helion Język publikacji: polski
ISBN: 83-7197-429-9 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> programowanie >> inne - programowanie
Porównaj ceny (książka, ebook, audiobook).
To właśnie programista może w znacznym stopniu przyczynić się do tego, iż wykrywanie błędów i walka z nimi staną się zadaniami łatwiejszymi i bardziej skutecznymi -- tę właśnie tezę Autor stara się udowodnić w niniejszej książce, ilustrując swe wywody konkretnymi przykładami.

Niektóre ze wskazówek i zaleceń zawartych w treści niniejszej książki sprzeciwiają się wielu powszechnie przyjętym praktykom programowania i jako takie prowokować mogą do stwierdzeń w rodzaju 'nikt tak nie pisze' lub 'wszyscy łamią tę regułę'. Warto wówczas zastanowić się nad przyczyną -- jeżeli 'nikt tak nie pisze', to dlaczego? Czy przypadkiem stare nawyki nie okazują się silniejsze od racjonalności?

Odpowiedź na te i inne pytania Czytelnik znajdzie w tej książce.

Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

Niezawodno(cid:156)(cid:230) oprogramowania Autor: Steve Maguire T‡umaczenie: Andrzej Gra¿yæski ISBN: 83-7197-429-9 Tytu‡ orygina‡u: Writing solid code: Microsoft’s techniques for developing bug-free C programs Format: B5, stron: oko‡o 400 To w‡a(cid:156)nie programista mo¿e w znacznym stopniu przyczyni(cid:230) siŒ do tego, i¿(cid:160) wykrywanie b‡Œd(cid:243)w i walka z nimi stan„ siŒ zadaniami ‡atwiejszymi i bardziej skutecznymi -- tŒ w‡a(cid:156)nie tezŒ Autor stara siŒ udowodni(cid:230) w niniejszej ksi„¿ce, ilustruj„c(cid:160) swe wywody konkretnymi przyk‡adami. Niekt(cid:243)re ze wskaz(cid:243)wek i zaleceæ zawartych w tre(cid:156)ci niniejszej ksi„¿ki sprzeciwiaj„ siŒ wielu powszechnie przyjŒtym praktykom programowania i jako takie prowokowa(cid:230) mog„ do stwierdzeæ w rodzaju (cid:132)nikt tak nie pisze(cid:148) lub (cid:132)wszyscy ‡ami„ tŒ regu‡Œ(cid:148). Warto w(cid:243)wczas zastanowi(cid:230) siŒ nad przyczyn„ -- je¿eli (cid:132)nikt tak nie pisze(cid:148), to dlaczego? Czy przypadkiem stare nawyki nie okazuj„ siŒ silniejsze od racjonalno(cid:156)ci? Odpowied(cid:159) na te i inne pytania Czytelnik znajdzie w tej ksi„¿ce. IDZ DO IDZ DO PRZYK£ADOWY ROZDZIA£ PRZYK£ADOWY ROZDZIA£ SPIS TRE(cid:140)CI SPIS TRE(cid:140)CI KATALOG KSI¥flEK KATALOG KSI¥flEK KATALOG ONLINE KATALOG ONLINE ZAM(cid:211)W DRUKOWANY KATALOG ZAM(cid:211)W DRUKOWANY KATALOG TW(cid:211)J KOSZYK TW(cid:211)J KOSZYK DODAJ DO KOSZYKA DODAJ DO KOSZYKA CENNIK I INFORMACJE CENNIK I INFORMACJE ZAM(cid:211)W INFORMACJE ZAM(cid:211)W INFORMACJE O NOWO(cid:140)CIACH O NOWO(cid:140)CIACH ZAM(cid:211)W CENNIK ZAM(cid:211)W CENNIK CZYTELNIA CZYTELNIA FRAGMENTY KSI¥flEK ONLINE FRAGMENTY KSI¥flEK ONLINE Wydawnictwo Helion ul. Chopina 6 44-100 Gliwice tel. (32)230-98-63 e-mail: helion@helion.pl SPIS TREŚCI 5 Przedmowa do wydania polskiego................................................... ................9 Wstęp ................................................... ................................................... .........15 Dwa najważniejsze pytania...................................................s...................................................s......16 Nazewnictwo ...................................................s...................................................s...........................17 Rozdział 1. Hipotetyczny kompilator ................................................... ........21 Poznaj swój język programowania ...................................................s.............................................23 Pożyteczne Narzędzie — Lint ...................................................s....................................................27 To tylko kosmetyczne zmiany ...................................................s....................................................27 Nigdy więcej błędów ...................................................s...................................................s...............28 Rozdział 2. Sprawdzaj samego siebie ................................................... ........31 Przypowieść o dwóch wersjach ...................................................s..................................................32 Asercje ...................................................s...................................................s.....................................33 „Niezdefiniowane” oznacza „nieprzewidywalne”...................................................s......................36 Zagadkowe asercje...................................................s...................................................s...................37 Kompatybilność kontrolowana ...................................................s...................................................39 Gdy niemożliwe staje się możliwe ...................................................s.............................................43 Nic o nas bez nas ...................................................s...................................................s.....................45 Co dwa algorytmy, to nie jeden ...................................................s..................................................48 Usuwaj błędy jak najwcześniej...................................................s...................................................52 Rozdział 3. Ufortyfikuj swoje podsystemy ................................................... 59 Jest błąd, nie ma błędu...................................................s...................................................s.............60 Zutylizuj swoje śmieci ...................................................s...................................................s.............62 Jestem już gdzie indziej ...................................................s...................................................s...........66 Kontroluj wykorzystanie pamięci...................................................s...............................................69 Spójrz na to, czego nie widać ...................................................s.....................................................72 Wybieraj rozsądnie ...................................................s...................................................s..................76 Szybki czy bezbłędny ...................................................s...................................................s..............77 Teraz lub później ...................................................s...................................................s.....................77 6 NIEZAWODNOŚĆ OPROGRAMOWANIA Rozdział 4. Jak wykonuje się Twój kod ................................................... ....81 Uwiarygodnij swój kod...................................................s...................................................s............82 Przetestuj wszystkie rozgałęzienia...................................................s..............................................83 Żywotne znaczenie przepływu danych ...................................................s.......................................85 Czy czegoś nie przeoczyłeś ...................................................s........................................................87 Spróbuj, a polubisz ...................................................s...................................................s..................88 Rozdział 5. Niekomunikatywne interfejsy ................................................... 91 getchar() zwraca liczbę, nie znak...................................................s................................................92 realloc() a gospodarka pamięcią ...................................................s.................................................94 Uniwersalny menedżer pamięci...................................................s..................................................96 Nieprecyzyjne parametry...................................................s...................................................s.........98 Fałszywy alarm ...................................................s...................................................s......................101 Czytanie pomiędzy wierszami ...................................................s..................................................103 Ostrzegaj przed niebezpieczeństwem ...................................................s.......................................105 Diabeł tkwi w szczegółach ...................................................s...................................................s....108 Rozdział 6. Ryzykowny biznes ................................................... .................111 int intowi nierówny...................................................s...................................................s................112 Nadmiar i niedomiar ...................................................s...................................................s..............116 „Projekt” czy „prawie projekt” ...................................................s.................................................118 Po prostu robią, co do nich należy...................................................s............................................120 Przecież to to samo ...................................................s...................................................s................124 ?: to także if...................................................s...................................................s............................125 Precz z redundancją ...................................................s...................................................s...............128 Wysokie ryzyko, bez odwrotu ...................................................s..................................................129 Przeklęta niespójność...................................................s...................................................s.............133 Nie przypisuj zmiennym informacji diagnostycznych ...................................................s.............135 Nie warto ryzykować ...................................................s...................................................s.............137 Rozdział 7. Dramaturgia rzemiosła ................................................... .........141 Szybkość, szybkość ...................................................s...................................................s...............142 Złodziej otwierający zamek kluczem nie przestaje być złodziejem ............................................144 Każdemu według potrzeb ...................................................s...................................................s......146 Nie uzewnętrzniaj prywatnych informacji...................................................s................................148 Funkcje-pasożyty ...................................................s...................................................s...................150 Programistyczne śrubokręty ...................................................s.....................................................153 Syndrom APL ...................................................s...................................................s........................155 Bez udziwnień, proszę ...................................................s...................................................s...........156 Na śmietnik z tymi wszystkimi trikami ...................................................s....................................158 Rozdział 8. Reszta jest kwestią nawyków................................................... 163 Hokus-pokus, nie ma błędu ...................................................s...................................................s...163 Zrób dziś, co masz zrobić jutro...................................................s.................................................165 Doktora!!! ...................................................s...................................................s..............................166 Jeśli działa, nie poprawiaj...................................................s.........................................................167 Funkcja z wozu, koniom lżej ...................................................s....................................................169 Elastyczność rodzi błędy ...................................................s...................................................s.......169 Spróbuj...................................................s...................................................s...................................171 Święty Harmonogram ...................................................s...................................................s............172 „Tester” — nazwa w sam raz dla testera ...................................................s..................................173 Programista zawinił, testera powiesili ...................................................s......................................175 Zdefiniuj swe priorytety...................................................s...................................................s.........176 SPIS TREŚCI 7 Epilog................................................... ................................................... .......181 Dodatek A Lista kontrolna kodowania ................................................... ...183 Dodatek B Podprogramy zarządzania pamięcią .......................................189 Dodatek C Odpowiedzi ................................................... .............................197 Skorowidz................................................... ................................................... 225 JAK WYKONUJE SIĘ TWÓJ KOD 81 Omawiane w poprzednich rozdziałach metody „automatycznego” wykrywania błędów — asercje, testy integralności podsystemów, itp. — stanowią narzędzia niezwykle użyteczne i znaczenie ich naprawdę trudno przecenić, jednakże w nie- których przypadkach okazują się one zupełnie „nieczułe” na błędy występujące w testowanym kodzie. Przyczyna tego stanu rzeczy jest tyleż oczywista, co banalna; wyjaśnijmy ją na (bliskim każdemu z nas) przykładzie zabezpieczenia domu czy mieszkania. Otóż najbardziej nawet wymyślne zabezpieczenie drzwi i okien okaże się zu- pełnie nieprzydatne w sytuacji, gdy złodziej dostanie się do domu np. przez klapę w dachu, czy też otworzy sobie drzwi dorobionym kluczem. Podobnie, najwraż- liwszy nawet czujnik wstrząsowy zamontowany skrycie w magnetowidzie czy komputerze nie uchroni przez kradzieżą np. drogocennej kolekcji obrazów. W obydwu tych przypadkach zagrożenie pojawia się bowiem poza obszarami, na mo- nitorowanie których zorientowane są urządzenia alarmoowe. Na identycznej zasadzie, najbardziej nawet wymyślne asercje, czy jeszcze bar- dziej zaawansowane fragmenty kodu testujące występowanie spodziewanych wa- runków, są coś warte jedynie wtedy, gdy w ogóle zostają wykonane! Brak alarmu ze strony określonej asercji niekoniecznie świadczy o spełnieniu testowanego przez tę asercję warunku, ale może być także wynikiem jej pominięcia; podobnie punkt przerwania spowoduje zatrzymanie wykonywania programu jedynie wtedy, gdy wykonana zostanie instrukcja, na której punkt ten uostawiono. Wyjaśnia to poniekąd, dlaczego niektóre błędy potrafią skutecznie wymykać się (niczym sprytne szczury) najgęstszej nawet sieci asercji czy punktów przerwań, które tym samym stanowią tylko dodatkowy kłopot dla programisty, a także powo- dują dodatkową komplikację i tak przeważnie już złożonoego kodu. 81 82 NIEZAWODNOŚĆ OPROGRAMOWANIA Uciekając się do małej metafory — skoro nie potrafimy schwytać grubego zwierza w pułapkę, warto podążyć jego śladem; skoro sterowanie w naszym pro- gramie omija ustanowione punkty przerwań i asercje, spróbujmy prześledzić jego przebieg. Praca krokowa na poziomie zarówno kodu źródłowego, jak i instrukcji maszynowych jest jedną z podstawowych funkcji każdego debuggera, jest też wbu- dowana w znakomitą większość współczesnych środowisk porojektowych. UWIARYGODNIJ SWÓJ KOD Opracowywałem kiedyś podprogram wykonujący specyficzną funkcję na potrzeby większego projektu (środowiska programistycznego na Macintoshu). Podczas jego rutynowego testowania znalazłem pewien błąd; jego konsekwencje dla innego fragmentu wspomnianego projektu były tak poważne, iż pozostawało dla mnie za- gadką, dlaczego nie został on dotąd wykryty, skoro powinien zamanifestować się w sposób oczywisty. Spotkałem się więc z autorem wspomnianego fragmentu i pokazałem mu błędny fragment swojego kodu. Gdy także wyraził swe zdziwienie z powodu nie- wykrycia widocznego jak na dłoni błędu, postanowiliśmy ustawić punkt przerwa- nia w krytycznym miejscu kodu, a po zatrzymaniu — które naszym zdaniem mu- siało nastąpić — kontynuować wykonywanie w sposób krookowy. Załadowaliśmy nasz projekt, kliknęliśmy przycisk „Run” i... ku naszemu zdu- mieniu program wykonał się w całości, bez zatrzymania! Wyjaśniało to skądinąd, dlaczego błąd nie został zauważony, lecz samo w sobie nadal pozostawało rzeczą zagadkową. Ostatecznie przyczyna całego zamieszania okazała się być prozaiczna: po pro- stu optymalizujący kompilator wyeliminował z kodu źródłowego instrukcje, które uznał za zbędne; instrukcja, na której ustawiliśmy punkt przerwania miała nieszczę- ście należeć do tego zestawu. „Wykonanie” kodu źródłowego krok po kroku (czy raczej — próba takiego wykonania) uwidoczniłoby ten fakt w sposób nie budzący wątpliwości. Jako kierownik projektu, nalegam na programistów, by „krokowe” wykony- wanie tworzonego przez nich kodu stanowiło integralny element jego testowania — i, niestety, nazbyt często spotykam się ze stwierdzeniem, że przecież jest to czynność czasochłonna i jako taka spowoduje wydłużenioe pracy nad projektem. To jednak tylko mała część prawdy: po pierwsze — dodatkowy czas przezna- czony na krokowe testowanie kodu jest tylko drobnym ułamkiem czasu przezna- czonego na stworzenie tegoż kodu; po drugie — uruchomienie programu w trybie pracy krokowej nie jest w niczym trudniejsze od „normalnego” uruchomienia, bo- wiem różnica tkwi zazwyczaj jedynie w... naciśniętych klawiszach; po trzecie (i najważniejsze) — czas spędzony nad testowaniem programu stanowi swego ro- dzaju inwestycję — w przeciwieństwie do czasu spędzonego na walkę z trudnymi do wykrycia błędami, stanowiącego przykrą konieczność. W jednym z poprzed- nich rozdziałów, pisząc o testowaniu metodą „czarnej skrzynki”, wyjaśniałem nie- bagatelną rolę programowania defensywnego w walce z błędami — możliwość obserwacji zachowania się własnego kodu dodatkowo zwiększa przewagę programi- sty nad testerem obserwującym jedynie przetwarzanie danych przez „czarną skrzyn- kę”. Śledzenie stworzonego (lub zmienionego) przez programistę kodu powinno za- JAK WYKONUJE SIĘ TWÓJ KOD 83 tem stać się nieodłącznym elementem jego pracy i — choć może początkowo uciąż- liwe — z czasem będzie po prostu pożytecznym nawykiem. Nie odkładaj testowania krokowego do czasu, gdy pojjawią się błędy. PRZETESTUJ WSZYSTKIE ROZGAŁĘZIENIA Praca krokowa, jak wszelkie inne narzędzia, może wykazywać zróżnicowaną skutecz- ność w zależności od tego, jak umiejętnie jest stosowana. W szczególności — testo- wanie kodu zwiększa prawdopodobieństwo uniknięcia błędów tylko wtedy, jeżeli przetestuje się cały kod; niestety, w przypadku pracy krokowej sterowanie podąża ścieżką wyznaczoną przez zachodzące aktualnie warunki — mowa tu oczywiście o instrukcjach warunkowych, instrukcjach wyboru i wszelkiego rodzaju pętlach. Aby więc przetestować wszystkie możliwe rozgałęzienia, należy przeprowadzić testowa- nie przy np. różnych wartościach warunków instrukcji 0, czy selektorów instruk- cji 7;8-. Notabene pierwszymi ofiarami niedostatecznego testowania padają te frag- menty kodu, które wykonywane są bardzo rzadko lub wcale — do tej ostatniej ka- tegorii należą m.in. wszelkiego rodzaju procedury obsługujące błędy. Przyjrzyjmy się poniższemu fragmentowi: 4,3-(cid:10),8/(cid:14)(cid:15)+3-(cid:10)(cid:15) 0(cid:10)4,3- (cid:15)  3,7o91+,o(cid:9).9     Każda zmiana jest niebezpieczna Programiści często pytają, jaki jest sens testowania każdej zmiany kodu spowodo- wanej wzbogaceniem programu w nowe możliwości. Na tak postawione pytanie można odpowiedzieć jedynie innym pytaniem — czy wprowadzone zmiany na pewno, bez żadnych wątpliwości, wolne są od jakichkolwiek błędów? To prawda, iż prześledzenie każdego nowego (lub zmodyfikowanego) fragmentu kodu wyma- ga trochę czasu, lecz jednocześnie fakt ten staje się nieoczekiwanie przyczyną inte- resującego sprzężenia zwrotnego — mianowicie programiści przywykli do konse- kwentnego śledzenia własnego kodu wykazują tendencję do pisania krótkich i przemyślanych funkcji, bowiem doskonale wiedzą, jak kłopotliwe jest śledzenie funkcji rozwlekłych, pisanych bez zastanowienia. Nie należy także zapominać o tym, by przy wprowadzaniu zmian do kodu już przetestowanego zmiany te należycie wyróżniać. Wyróżniamy w ten sposób te fragmenty, które istotnie wymagają testowania; w przeciwnym razie każda zmiana kodu może pozbawić istniejący kod wiarygodności uzyskanej drogą czasochłonne- go testowania — niczym odrobina żółci zdolnej zepsuoć beczkę miodu. 83 84 NIEZAWODNOŚĆ OPROGRAMOWANIA W prawidłowo działającym programie wywołanie funkcji +3- powoduje przydzielenie tu 32-bajtowego bloku pamięci i zwrócenie niezerowego wskaźnika, zatem blok uwarunkowany instrukcją 0 nie zostaje wykonany. Aby go naprawdę przetestować, należy zasymulować błędną sytuację, czyli zastąpić wartością   dopiero co przypisany wskaźnik: 4,3-(cid:10),8/(cid:14)(cid:15)+3-(cid:10)(cid:15) 4,3-  0(cid:10)4,3- (cid:15)   3,7o91+,o(cid:9).9  Spowoduje to co prawda wyciek pamięci wywołany utratą wskazania na przy- dzielony blok, jednakże na etapie testowania zazwyczaj można sobie na to pozwolić; w ostateczności można wykonać wyzerowanie wskaźnika zamiast wywoływania funkcji +3-: (cid:14)4,3-(cid:10),8/(cid:14)(cid:15)+3-(cid:10)(cid:15)(cid:14) 4,3-  0(cid:10)4,3- (cid:15)   3,7o91+,o(cid:9).9  Na podobnej zasadzie należy przetestować każdą ze ścieżek wyznaczonych przez instrukcje 0 z frazą /7/, instrukcje 7;8-, jak również operatory ,  i . Pamiętaj o przetestowaniu każdego rozgałęzienia w pjrogramie. ŻYWOTNE ZNACZENIE PRZEPŁYWU DANYCH Pierwotna wersja stworzonej przeze mnie funkcji /7/8, prezentowanej w rozdziale 2., wyglądała następująco: :3.(cid:14)/7/8(cid:10):3.(cid:14)4:!,8/,!7/)87/(cid:15)  ,8/(cid:14)4,(cid:10),8/(cid:14)(cid:15)4: 0(cid:10)7/7/$6/73.(cid:15)  92712/.321 (cid:10),(cid:15)(cid:10), (cid:15)(cid:10),(cid:15), JAK WYKONUJE SIĘ TWÓJ KOD 85 4,(cid:10),8/(cid:14)(cid:15)3210(cid:10)(cid:10)321(cid:14)(cid:15)4,!!7/ (cid:15) 7/7/  ;/(cid:10)7/33 (cid:15) (cid:14)4,55, 6/8962(cid:10)4:(cid:15)  Sprawdziłem jej działanie w tworzonej aplikacji wyzerowując fragmenty pa- mięci o różnej wielkości, zarówno większej, jak i mniejszej od założonego progu 7/$6/73.. Wszystko przebiegało zgodnie z oczekiwaniami; wiedząc jednak o tym, iż zero jest wartością w pewnym sensie wyjątkową, dla nadania testowi więk- szej wiarygodności użyłem w charakterze „wypełniacza” innego wzorca — arbi- tralnie wybranej wartości . Dla bloków mniejszych niż 7/$6/73.wszyst- ko było nadal w należytym porządku, jednak dla większych bloków wartość nadana zmiennej l w linii (cid:10),(cid:15)(cid:10), (cid:15)(cid:10),(cid:15), równa była   zamiast spodziewanej . Rzut oka na asemblerową postać wygenerowanego kodu natychmiast ujawnił rzeczywistą przyczynę takiego stanu rzeczy — otóż kompilator, którego używa- łem, prowadził obliczenia wyrażeń całkowitoliczbowych w arytmetyce 16-bitowej, uwzględniając jedynie 16 najmniej znaczących bitów wyrażeń (cid:28), ! i (cid:28),!, czyli po prostu wartość zero. W zmiennej  zapisywała się jedynie bito- wa alternatywa wyrażeń , i (cid:28),!. A co z czujnością kompilatora ? No właśnie. Kod prezentowany w niniejszej książce przetestowałem osobiście używając pięciu różnych kompilatorów; żaden z nich, mimo ustawienia diagnosty- ki na najwyższym możliwym poziomie, nie ostrzegł mnie, iż wspomniane instruk- cje przesuwające 16-bitową wartość o 16, czy 24 bity powodują utratę wszystkich znaczących bitów. Co prawda kompilowany kod zgodny był w zupełności ze stan- dardem ANSI C, jednakże wynik wspomnianych konstrukcji niemal zawsze odbie- ga od oczekiwań programisty — dlaczego więc brak jaokichkolwiek ostrzeżeń? Prezentowany przypadek wykazuje jednoznacznie konieczność nacisku na pro- ducentów kompilatorów, by tego rodzaju opcje pojawiały się w przyszłych wersjach ich produktów. Zbyt często my, jako użytkownicy, nie doceniamy siły swej argu- mentacji w tym względzie... Ten subtelny błąd zostałby niewątpliwie szybko wykryty przez testerów, cho- ciażby ze względu na widoczne konsekwencje (czyli wypełnianie dużych bloków „deseniem”  zamiast ), jednakże poświęcenie zaledwie kilku minut na prześledzenie kodu pozwoliło wykryć ów błąd już na etapie tworzenia funkcji. Jak pokazuje powyższy przykład, krokowe wykonywanie kodu źródłowego może nie tylko wskazać przepływ sterowania, lecz także uwidocznić inny, niesa- mowicie ważny czynnik, mianowicie zmianę wartości poszczególnych zmiennych 85 86 NIEZAWODNOŚĆ OPROGRAMOWANIA programu w rezultacie wykonywania poszczególnych instrukcji — co nazywane bywa skrótowo przepływem danych. Możliwość spojrzenia na stworzony kod pod kątem przepływu danych stanowi dodatkowy oręż dla programisty — zastanów się, które z poniższych błędów mogą zostać dzięki temu wykryte i nie są możliwe do wykrycia w inny sposób:  nadmiar lub niedomiar;  błąd konwersji danych;  błąd „pomyłki o jedynkę” (patrz rozdział 1.);  adresowanie za pomocą zerowych wskaźników;  odwołanie do nieprzydzielonych lub zwolnionych obszoarów pamięci („błąd A3”, patrz rozdział 3.);  pomyłkowe użycie operatora „” zamiast „”;  błąd pierwszeństwa operatorów;  błędy logiczne. Przywołajmy raz jeszcze błędną instrukcję z rozdziałou 1.: 0(cid:10)- 8 (cid:15) 4+2.$+,(cid:10)(cid:15) pomyłkowe użycie operatora  może być tu łatwo przeoczone, jednak zaobserwo- wana zmiana wartości zmiennej - wskutek wykonania instrukcji błąd ten natych- miast demaskuje. Podczas pracy krokowej programu zwracaj szczególną uwagę na przepływ danych. CZY CZEGOŚ NIE PRZEOCZYŁEŚ Obserwując przepływ danych podczas pracy krokowej, nie jesteś jednak w stanie wykryć wszystkich błędów. Przyjrzyjmy się poniższemu foragmentowi: (cid:14)/ /782//; /o6/46//289-7,3 (cid:14)98;3632378+oo+q-9-+;/6+-/132+; ! (cid:14);328/2o+q-9- (cid:14) 0(cid:10)47 473786+/ (cid:15)  6///36(cid:10)473786+/(cid:15) 473786+/   JAK WYKONUJE SIĘ TWÓJ KOD 87 Odwołanie się do pola 47’786+/ ma sens jedynie wtedy, gdy wskaźnik 47 jest niezerowy. W instrukcji 0 powinniśmy więc użyć operatora  powodu- jącego częściowe wartościowanie koniunkcji (gdy pierwszy jej argument ma war- tość 0+7/, drugi nie jest już wartościowany), tymczasem użyty operator  powo- duje wartościowanie kompletne, co przy zerowej wartości wskaźnika 47 może spowodować (a w trybie chronionym — spowoduje na pewno) błąd adresowania. Pomyłkowe użycie operatora  nie może jednak zostać wykryte w warunkach kro- kowego śledzenia kodu źródłowego, postrzegającego ów kod w rozbiciu na kom- pletne instrukcje lub linie, nie zaś na poszczególne wyrażenia. Podobnie rzecz się ma z operatorami  oraz . A co z optymalizacją przekładu? Wzajemne „dopasowanie” instrukcji kodu źródłowego i rozkazów asemblerowych może być znacznie utrudnione, gdy kompilator dokonuje optymalizacji przekładu. Główne przejawy optymalizacji to eliminacja zbędnego kodu oraz łączne tłumacze- nie kilku sąsiadujących instrukcji kodu źródłowego — czyli zjawiska jak najmniej pożądane w procesie śledzenia kodu. Znakomitą większość kompilatorów można zmusić do poniechania optymalizacji na etapie testowania programu poprzez od- powiednie ustawienie opcji kompilacji, wielu programistów dostrzega w tym jed- nak przejaw nadmiernego różnicowania wersji testoweoj i handlowej produktu. Sko- ro jednak podstawowym zadaniem testowania jest wyłapanie błędów, warto zastosować każdy zabieg, który może się przyczynić do pomyślnego wykonania te- go zadania. W każdym razie warto zawsze przekonać się, czy dla konkretnego programu optymalizacja istotnie utrudnia śledzenie jego kodu — być może w ogóle nie trzeba będzie jej wyłączać. Poza wyjątkowo dokładnym sprawdzaniem składni wspomnianych instrukcji lub wyświetlaniem wartości poszczególnych wyrażeń (podczas pracy krokowej) jedynym sposobem wykrycia opisanego błędu jest prześledzenie wykonania pro- gramu na poziomie instrukcji asemblera. Wymaga to niewątpliwie kwalifikacji po- trzebnych do skojarzenia rozkazów maszynowych z odpowiadającymi im instruk- cjami kodu źródłowego, jak adresowanie z użyciem wyzerowanego rejestru segmentowego stają się natychomiast widoczne. takie błędy jednak Śledzenie kodu źródłowego musi być niekiedy uzupełniojne śledzeniem wygenerowanego przekładu. SPRÓBUJ, A POLUBISZ Niektórych programistów naprawdę trudno skłonić do systematycznego śledzenia własnych programów lub przynajmniej do spróbowania tego przez np. miesiąc. Wymawiają się brakiem czasu, bądź też niechęcią do jego tracenia. Uważam, iż 87 88 NIEZAWODNOŚĆ OPROGRAMOWANIA obowiązkiem każdego kierownika projektu jest przekonanie takich „opornych” pro- gramistów, iż taka oszczędność jest oszczędnością zdecydowanie źle pojętą, gdyż oznacza ryzyko tracenia (w przyszłości) znacznie większej ilości czasu na tropienie błędów w gotowym kodzie, przede wszystkim właśnie za pomocą śledzenia kroko- wego. Myślę, że treść niniejszego rozdziału (jak i pozostałych rozdziałów niniejszej książki) dostarcza niezbędnych ku temu argumentów. Zresztą — jeśli po przełamaniu początkowej niechęci podejmie się próbę śle- dzenia stworzonego właśnie kodu i skonstatuje, że wiele tkwiących w nim błędów można z łatwością wyłapać i usunąć już w ciągu dziesięciu minut, dalsze argu- menty okazują się zbyteczne. W taki oto sposób początkowa nieufność ustępuje miejsca pożytecznym nawykom... PODSUMOWANIE  Błędy nie pojawiają się znikąd, lecz są przyrodzonym elementem każdego no- wo tworzonego kodu, bądź efektem wprowadzanych do tego kodu modyfikacji. Wyjaśnia to, dlaczego śledzenie wykonania takich nowych „kawałków” stanowi najskuteczniejszą broń w walce z błędami.  Obawy, iż śledzenie kodu wymaga jakichś ogromnych nakładów czasu, są cał- kowicie nieuzasadnione. Śledzenie kodu trwa na pewno znacznie krócej od je- go tworzenia, poza tym z zasady pozwala uniknąć niepotrzebnej straty czasu związanej z późniejszym wyszukiwaniem błędów w gotowoym programie.  Śledzenie kodu jest operacją w pełni skuteczną jedynie wtedy, gdy śledzeniu podlegają wszystkie rozgałęzienia w kodzie. Należy o tym pamiętać przy śle- dzeniu instrukcji warunkowych, pętli oraz wyrażeń zawierających operatory ,  i .  W niektórych przypadkach śledzenie kodu źródłowego musi być uzupełnione śledzeniem wygenerowanego przekładu w celu zorientowania się, jak w rze- czywistości przebiega wykonanie konkretnej instrukcji. Konieczność taka nie zdarza się co prawda zbyt często, lecz jeżeli faktycznie zaistnieje, nie należy jej lekceważyć. PROJEKT: W rozdziale 1. przedstawiłem przykłady najczęściej występujących błędów programistycznych oraz pytanie o możliwość automatycznego wykrywania ich przez kompilatory. Zastanów się, w jakim stopniu krokowe śledzenie kodu może zwiększyć szansę wykrywania ich przez programistę. PROJEKT: Sporządź listę błędów programistycznych popełnionych przez Ciebie w ciągu ostatnich sześciu miesięcy i zastanów się, ilu z nich można było zapobiec przez systematyczne śledzenie stworzonego kodu.
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

Niezawodność oprogramowania
Autor:

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: