Debugowanie
Część serii poświęconej |
tworzeniu oprogramowania |
---|
W programowaniu komputerowym i opracowywaniu oprogramowania debugowanie to proces znajdowania i rozwiązywania błędów ( wad lub problemów, które uniemożliwiają prawidłowe działanie) w programach komputerowych , oprogramowaniu lub systemach .
Taktyka debugowania może obejmować debugowanie interaktywne , analizę przepływu sterowania , testy jednostkowe , testy integracyjne , analizę plików dziennika , monitorowanie na poziomie aplikacji lub systemu , zrzuty pamięci i profilowanie . Wiele języków programowania i narzędzi do tworzenia oprogramowania oferuje również programy ułatwiające debugowanie, zwane debuggerami .
Etymologia
Terminy „błąd” i „debugowanie” są powszechnie przypisywane admiral Grace Hopper w latach czterdziestych XX wieku. Kiedy pracowała na Mark II na Uniwersytecie Harvarda, jej współpracownicy odkryli ćmę, która utknęła w przekaźniku, co utrudnia działanie, po czym zauważyła, że „debugowali” system. Jednak termin „błąd” w znaczeniu „błąd techniczny” sięga co najmniej 1878 r. i Thomasa Edisona , który opisuje „małe wady i trudności” inżynierii mechanicznej jako „błędy”.
Podobnie wydaje się, że termin „debugowanie” był używany jako termin w aeronautyce przed wejściem do świata komputerów. W wywiadzie Grace Hopper zauważyła, że nie wymyśliła tego terminu. [ potrzebne źródło ] Ćma pasowała do istniejącej już terminologii, więc została uratowana. List od J. Roberta Oppenheimera (dyrektora projektu bomby atomowej Manhattan z II wojny światowej w Los Alamos w Nowym Meksyku) użył tego terminu w liście do dr Ernesta Lawrence'a z UC Berkeley, datowanym na 27 października 1944 r., dotyczącym rekrutacji dodatkowych pracowników technicznych personel.
Oxford English Dictionary dotyczące „debugowania” cytuje termin „debugowanie” używany w odniesieniu do testowania silników samolotów w artykule z 1945 r. W Journal of the Royal Aeronautical Society. Artykuł w „Airforce” (czerwiec 1945, s. 50) również odnosi się do debugowania, tym razem kamer lotniczych. Błąd Hoppera został znaleziony 9 września 1947 roku. Programiści komputerowi przyjęli ten termin dopiero na początku lat pięćdziesiątych. Przełomowy artykuł Gilla z 1951 roku jest najwcześniejszą dogłębną dyskusją na temat błędów programowania, ale nie używa terminu „błąd” ani „debugowanie”. W ACM termin „debugowanie” został użyty po raz pierwszy w trzech artykułach z krajowych spotkań ACM z 1952 r. Dwóch z trzech używa tego terminu w cudzysłowie. W 1963 roku „debugowanie” było na tyle powszechnym terminem, że można go było wymienić mimochodem bez wyjaśnienia na stronie 1 podręcznika CTSS .
Zakres
Ponieważ oprogramowanie i systemy elektroniczne stały się ogólnie bardziej złożone, różne popularne techniki debugowania rozszerzyły się o więcej metod wykrywania anomalii, oceny wpływu i planowania poprawek oprogramowania lub pełnych aktualizacji systemu. Słowa „anomalia” i „rozbieżność” mogą być używane jako terminy bardziej neutralne , aby uniknąć słów „błąd” i „wada” lub „błąd”, w przypadku gdy może to sugerować, że wszystkie tak zwane błędy , defekty lub usterki musi zostać naprawiony (za wszelką cenę). Zamiast tego można przeprowadzić ocenę wpływu w celu ustalenia, czy zmiany mające na celu usunięcie anomalii (lub rozbieżności ) byłyby opłacalne dla systemu, czy może zaplanowana nowa wersja może sprawić, że zmiana(y) nie będą potrzebne. Nie wszystkie problemy są krytyczne dla bezpieczeństwa lub krytyczne w systemie. Ponadto ważne jest, aby uniknąć sytuacji, w której zmiana może być bardziej niepokojąca dla użytkowników w dłuższej perspektywie niż życie ze znanym problemem (gdzie „lekarstwo byłoby gorsze niż choroba”). Opierając decyzje o akceptowalności niektórych anomalii, można uniknąć kultury mandatu „zera defektów”, w której ludzie mogą ulegać pokusie zaprzeczania istnieniu problemów, tak aby wynik wyglądał na zero defektów . Biorąc pod uwagę kwestie dodatkowe, takie jak ocena wpływu kosztów w stosunku do korzyści, zostaną rozszerzone szersze techniki debugowania w celu określenia częstotliwości anomalii (jak często występują te same „błędy”), aby pomóc ocenić ich wpływ na cały system.
Narzędzia
Debugowanie ma różną złożoność, od naprawiania prostych błędów po wykonywanie długotrwałych i męczących zadań związanych z gromadzeniem danych, analizą i planowaniem aktualizacji. Umiejętność debugowania programisty może być głównym czynnikiem wpływającym na zdolność debugowania problemu, ale trudność debugowania oprogramowania różni się znacznie w zależności od złożoności systemu, a także zależy w pewnym stopniu od używanego języka (języków ) programowania oraz dostępne narzędzia, takie jak debugery . Debugery to narzędzia programowe, które umożliwiają programiście monitorowanie wykonywania programu, zatrzymywanie go, ponowne uruchamianie, ustawianie punktów przerwania i zmianę wartości w pamięci. Termin debuger może również odnosić się do osoby, która przeprowadza debugowanie.
Ogólnie rzecz biorąc, języki programowania wysokiego poziomu , takie jak Java , ułatwiają debugowanie, ponieważ mają takie funkcje, jak obsługa wyjątków i sprawdzanie typu , które ułatwiają wykrywanie rzeczywistych źródeł błędnego zachowania. W językach programowania, takich jak C lub asembler , błędy mogą powodować ciche problemy, takie jak uszkodzenie pamięci , i często trudno jest zobaczyć, gdzie wystąpił początkowy problem. W takich przypadkach mogą być potrzebne narzędzia do debugowania pamięci .
W niektórych sytuacjach bardzo przydatne mogą być narzędzia programowe ogólnego przeznaczenia, które są specyficzne dla danego języka. Przyjmują one formę statycznych narzędzi do analizy kodu . Narzędzia te wyszukują w kodzie źródłowym bardzo specyficzny zestaw znanych problemów, niektóre powszechne, a niektóre rzadkie, koncentrując się bardziej na semantyce (np. przepływie danych), niż na składni, jak robią to kompilatory i interpretery.
Istnieją zarówno komercyjne, jak i bezpłatne narzędzia dla różnych języków; niektórzy twierdzą, że są w stanie wykryć setki różnych problemów. Narzędzia te mogą być niezwykle przydatne podczas sprawdzania bardzo dużych drzew źródłowych, w których niepraktyczne jest przeglądanie kodu. Typowym przykładem wykrytego problemu może być dereferencja zmiennej, która ma miejsce przed przypisaniem zmiennej wartości. Jako inny przykład, niektóre takie narzędzia wykonują silne sprawdzanie typów, gdy język tego nie wymaga. Dzięki temu są lepsi w lokalizowaniu prawdopodobnych błędów w kodzie, który jest poprawny składniowo. Ale te narzędzia mają reputację fałszywych alarmów, gdzie poprawny kod jest oznaczany jako wątpliwy. Stary program Unix lint jest wczesnym przykładem.
Do debugowania sprzętu elektronicznego (np. sprzętu komputerowego ), jak również oprogramowania niskiego poziomu (np. BIOS-y , sterowniki urządzeń ) i oprogramowania sprzętowego , instrumenty takie jak oscyloskopy , analizatory logiczne lub emulatory w obwodzie (ICE) są często używane samodzielnie lub w połączeniu. ICE może wykonywać wiele typowych zadań debugera oprogramowania na oprogramowaniu i oprogramowaniu niskiego poziomu .
Proces debugowania
Proces debugowania zwykle rozpoczyna się od zidentyfikowania kroków prowadzących do odtworzenia problemu. Może to być nietrywialne zadanie, szczególnie w przypadku procesów równoległych i na przykład niektórych błędów Heisenbug . Specyficzne środowisko użytkownika i historia użytkowania mogą również utrudniać odtworzenie problemu.
Po odtworzeniu błędu może być konieczne uproszczenie danych wejściowych programu, aby ułatwić debugowanie. Na przykład błąd w kompilatorze może spowodować jego awarię podczas analizowania dużego pliku źródłowego. Jednak po uproszczeniu przypadku testowego tylko kilka linii z oryginalnego pliku źródłowego może wystarczyć do odtworzenia tej samej awarii. Uproszczenia można dokonać ręcznie, stosując dziel i zwyciężaj , w której programista próbuje usunąć niektóre części oryginalnego przypadku testowego, a następnie sprawdza, czy problem nadal występuje. Podczas debugowania w GUI programista może spróbować pominąć niektóre interakcje użytkownika z oryginalnego opisu problemu, aby sprawdzić, czy pozostałe działania są wystarczające do spowodowania wystąpienia błędu.
Po wystarczającym uproszczeniu przypadku testowego programista może użyć narzędzia do debugowania, aby zbadać stany programu (wartości zmiennych plus stos wywołań ) i wyśledzić źródło problemu (problemów). Alternatywnie można zastosować śledzenie . W prostych przypadkach śledzenie to tylko kilka instrukcji print, które wyświetlają wartości zmiennych w określonych punktach podczas wykonywania programu. [ potrzebne źródło ]
Techniki
- Debugowanie interaktywne wykorzystuje narzędzia do debugowania, które umożliwiają przetwarzanie kodu aplikacji krok po kroku i wstrzymywanie go w celu sprawdzenia lub zmiany stanu aplikacji. Narzędzia te zwykle obsługują punkty obserwacyjne, w których wykonywanie może być kontynuowane do momentu zmiany określonej zmiennej, oraz punkty przechwytujące, które powodują zatrzymanie debugera w przypadku określonych rodzajów zdarzeń programu, takich jak wyjątki lub ładowanie biblioteki współdzielonej.
- Debugowanie lub śledzenie drukowania to czynność oglądania (na żywo lub nagranych) instrukcji śledzenia lub instrukcji drukowania, które wskazują przebieg wykonywania procesu i postęp danych. Śledzenie można wykonać za pomocą specjalistycznych narzędzi (jak w przypadku śledzenia GDB) lub poprzez wstawienie instrukcji śledzenia do kodu źródłowego. Ten ostatni jest czasami nazywany debugowaniem printf , ze względu na użycie funkcji printf w C. Ten rodzaj debugowania został włączony przez polecenie TRON w oryginalnych wersjach języka programowania BASIC zorientowanego na nowicjuszy. TRON oznacza „Trace On”. TRON powodował drukowanie numerów linii każdego wiersza poleceń języka BASIC podczas działania programu.
- Zdalne debugowanie to proces debugowania programu działającego w systemie innym niż debugger. Aby rozpocząć zdalne debugowanie, debugger łączy się ze zdalnym systemem za pośrednictwem łącza komunikacyjnego, takiego jak sieć lokalna. Debuger może wówczas sterować wykonywaniem programu w systemie zdalnym i pobierać informacje o jego stanie.
- Debugowanie pośmiertne to debugowanie programu po jego awarii . Pokrewne techniki często obejmują różne techniki śledzenia, takie jak badanie plików dziennika, wysyłanie stosu wywołań w przypadku awarii i analiza zrzutu pamięci (lub zrzutu rdzenia ) procesu, który uległ awarii. Zrzut procesu może zostać uzyskany automatycznie przez system (na przykład, gdy proces zakończył się z powodu nieobsługiwanego wyjątku), przez instrukcję wprowadzoną przez programistę lub ręcznie przez interaktywnego użytkownika.
- Algorytm „wilczego płotu”: Edward Gauss opisał ten prosty, ale bardzo użyteczny i obecnie słynny algorytm w artykule z 1982 r. dla Communications of the ACM w następujący sposób: „Na Alasce jest jeden wilk; jak go znaleźć? Najpierw zbuduj ogrodzenie na środku stanu, poczekaj, aż wilk zawyje, określ, po której stronie płotu się znajduje. Powtarzaj proces tylko po tej stronie, aż dojdziesz do miejsca, w którym będziesz mógł zobaczyć wilka”. Jest to zaimplementowane np. w systemie kontroli wersji Git jako polecenie git bisect , które wykorzystuje powyższy algorytm do określenia, który commit wprowadził dany błąd.
- Debugowanie w trybie nagrywania i odtwarzania to technika tworzenia nagrania wykonywania programu (np. za pomocą bezpłatnego narzędzia do debugowania rr firmy Mozilla ; umożliwianie odwracalnego debugowania/wykonywania), które można odtwarzać i interaktywnie debugować. Przydatne do zdalnego debugowania i debugowania sporadycznych, niedeterministycznych i innych trudnych do odtworzenia defektów.
- Debugowanie podróży w czasie to proces cofania się w czasie za pomocą kodu źródłowego (np. za pomocą Undo LiveRecorder ) w celu zrozumienia, co dzieje się podczas wykonywania programu komputerowego; aby umożliwić użytkownikom interakcję z programem; aby w razie potrzeby zmienić historię i obserwować reakcję programu.
- Delta Debugging – technika automatyzacji upraszczania przypadków testowych.
- Saff Squeeze – technika izolowania niepowodzenia w teście za pomocą progresywnego wstawiania części nieudanego testu.
- Śledzenie przyczynowości : Istnieją techniki śledzenia łańcuchów przyczynowo-skutkowych w obliczeniach. Techniki te można dostosować do określonych błędów, takich jak dereferencje zerowego wskaźnika.
Debugowanie systemów wbudowanych
W przeciwieństwie do środowiska projektowania oprogramowania komputerowego ogólnego przeznaczenia, podstawową cechą środowisk wbudowanych jest sama liczba różnych platform dostępnych dla programistów (architektury procesorów, dostawcy, systemy operacyjne i ich warianty). Systemy wbudowane z definicji nie są projektami ogólnego przeznaczenia: są zwykle opracowywane dla jednego zadania (lub niewielkiego zakresu zadań), a platforma jest wybierana specjalnie w celu optymalizacji tej aplikacji. Fakt ten nie tylko utrudnia życie programistom systemów wbudowanych, ale także utrudnia debugowanie i testowanie tych systemów, ponieważ dla różnych platform potrzebne są różne narzędzia do debugowania.
Pomimo wspomnianego powyżej wyzwania związanego z heterogenicznością, niektóre debuggery zostały opracowane komercyjnie, a także prototypy badawcze. Przykłady rozwiązań komercyjnych pochodzą od Green Hills Software , Lauterbach GmbH i MPLAB-ICD firmy Microchip (dla debuggera w obwodzie). Dwa przykłady prototypowych narzędzi badawczych to Aveksha i Flocklab. Wszystkie wykorzystują funkcjonalność dostępną w tanich procesorach wbudowanych, On-Chip Debug Module (OCDM), którego sygnały są udostępniane przez standardowy interfejs JTAG . Są one porównywane w oparciu o to, ile zmian w aplikacji jest potrzebnych i tempo zdarzeń, za którymi mogą nadążyć.
Oprócz typowego zadania polegającego na identyfikowaniu błędów w systemie, debugowanie systemu wbudowanego ma również na celu zebranie informacji o stanach operacyjnych systemu, które można następnie wykorzystać do analizy systemu: znalezienia sposobów na zwiększenie jego wydajności lub zoptymalizowanie innych ważnych charakterystyki (np. zużycie energii, niezawodność, reakcja w czasie rzeczywistym itp.).
Antydebugowanie
Anti-debugging to „implementacja jednej lub więcej technik w kodzie komputerowym, która utrudnia próby inżynierii wstecznej lub debugowania docelowego procesu”. Jest aktywnie wykorzystywany przez uznanych wydawców w ochrony przed kopiowaniem , ale jest również wykorzystywany przez złośliwe oprogramowanie do komplikowania jego wykrywania i eliminacji. Techniki stosowane w zapobieganiu debugowaniu obejmują:
- Oparte na interfejsie API: sprawdź istnienie debuggera przy użyciu informacji o systemie
- Oparte na wyjątkach: sprawdź, czy wyjątki są zakłócane
- Bloki procesów i wątków: sprawdź, czy zostały zmanipulowane bloki procesów i wątków
- Zmodyfikowany kod: sprawdź modyfikacje kodu wprowadzone przez debugger obsługujący punkty przerwania oprogramowania
- Oparte na sprzęcie i rejestrach: sprawdź sprzętowe punkty przerwania i rejestry procesora
- Timing i latencja: sprawdź czas wykonania instrukcji
- Wykrywanie i karanie debuggera
Wczesny przykład zapobiegania debugowaniu istniał we wczesnych wersjach programu Microsoft Word , który w przypadku wykrycia debugera generował komunikat o treści: „Drzewo zła rodzi gorzkie owoce. Teraz kasuje dysk z programem.”, po czym powodował dyskietkę dysku, aby emitował alarmujące dźwięki z zamiarem odstraszenia użytkownika od ponownej próby.
Zobacz też
Dalsza lektura
- Agans, David J. (2002). Debugowanie: dziewięć niezbędnych zasad znajdowania nawet najbardziej nieuchwytnych problemów z oprogramowaniem i sprzętem . AMACOM. ISBN 0-8144-7168-4 .
- Blunden, Bill (2003). Egzorcyzm programowy: podręcznik debugowania i optymalizacji starszego kodu . ANaciśnij. ISBN 1-59059-234-4 .
- Ford, Ann R.; Teorey, Toby J. (2002). Praktyczne debugowanie w C++ . Sala Prentice'a. ISBN 0-13-065394-2 .
- Grötker, Thorsten; Holtmann, Ulrich; Keding, Holger; Włoka, Markus (2012). Przewodnik programisty dotyczący debugowania, wydanie drugie . Utwórzprzestrzeń. ISBN 978-1-4701-8552-7 .
- Metzger, Robert C. (2003). Debugowanie przez myślenie: podejście multidyscyplinarne . Prasa cyfrowa. ISBN 1-55558-307-5 .
- Myers, Glenford J (2004). Sztuka testowania oprogramowania . John Wiley & Sons Inc. ISBN 0-471-04328-1 .
- Robbins, John (2000). Aplikacje do debugowania . Microsoft Press. ISBN 0-7356-0886-5 .
- Telles, Matthew A.; Hsieh, Yuan (2001). Nauka debugowania . Grupa Coriolisa. ISBN 1-57610-917-8 .
- Wostokow, Dmitrij (2008). Analiza zrzutu pamięci Antologia Tom 1 . Otwórz zadanie. ISBN 978-0-9558328-0-2 .
- Zeller, Andreas (2009). Dlaczego programy zawodzą, wydanie drugie: przewodnik po systematycznym debugowaniu . Morgana Kaufmanna. ISBN 978-0-1237-4515-6 .
- Peggy Aldrich Kidwell, Śledzenie nieuchwytnego błędu komputerowego , IEEE Annals of the History of Computing, 1998.
Linki zewnętrzne
- Wzorce analizy zrzutów awaryjnych — szczegółowe artykuły na temat analizowania i znajdowania błędów w zrzutach awaryjnych
- Poznaj podstawy debugowania — jak poprawić swoje umiejętności debugowania — dobry artykuł w serwisie IBM developerWorks (zarchiwizowany z oryginału w dniu 18 lutego 2007 r.)
- Debugowanie oparte na wtyczkach dla systemów wbudowanych
- Test i debugowanie systemów wbudowanych - o generowaniu wejść cyfrowych - wyniki ankiety na temat testowania i debugowania systemów wbudowanych, Byte Paradigm (zarchiwizowane z oryginału w dniu 12 stycznia 2012 r.)