1 / 27

Odrobaczanie (debugging) programu

Odrobaczanie (debugging) programu. Zasada van Tassela: Żaden program nie działa od razu. Bądź szczególnie podejrzliwy, jeżeli za pierwszym razem dostałeś poprawną odpowiedź.

kalei
Télécharger la présentation

Odrobaczanie (debugging) programu

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Odrobaczanie (debugging) programu Zasada van Tassela:Żaden program nie działa od razu. Bądź szczególnie podejrzliwy, jeżeli za pierwszym razem dostałeś poprawną odpowiedź. Pojęcie “robaka” (bug) w elektronicznej technice obliczeniowej przypisuje się admirał U.S. Navy, Grace Murray Hopper, twórczyni COBOLu, która opowiadała historię o techniku, który naprawił komputer Harvard II usuwając ćmę z obwodów. Cała historia jest opisana w Annals of the History of Computing, 3 285-286 (1981). Później uogólniono to pojęcie na złe funkcjonowanie programów. Jeszcze wcześniej mianem “bug” określano defekty urządzeń elektrycznych ("Hawkin's New Catechism of Electricity", T. Audel & Co., 1896) albo przyczyny szumu na liniach telefonicznych lub telegraficznych. Słowa “bug” w sensie nagłego rujnującego zdarzenia używano już w czasach Shakespeare’a. Samo słowo w tym znaczeniu prawdopodobnie pochodzi z walijskiego (zanglicyzowanego) “bugbear”, oznaczającego różne mityczne potwory.

  2. Strona z dziennika komputera Harvard II z opisem usunięcia ćmy z obwodów wraz z przyklejonym owadem. Admirał Grace Murray Hopper (1896-1992)

  3. Jeden z “najsławniejszych” błędów w FORTRANie (historia opisana przez Freda Webba na alt.folklore.computers w roku 1990: I worked at Nasa during the summer of 1963. The group I was working in was doing preliminary work on the Mission Control Center computer systems and programs. My office mate had the job of testing out an orbit computation program which had been used during the Mercury flights. Running some test data with known answers through it, he was getting answers that were close, but not accurate enough. So, he started looking for numerical problems in the algorithm, checking to make sure his tests data was really correct, etc. After a couple of weeks with no results, he came across a DO statement, in the form: DO 10 I=1.10 This statement was interpreted by the compiler (correctly) as: DO10I = 1.10 The programmer had clearly intended: DO 10 I = 1, 10

  4. Najczęstsze błędy podczas programowania w FORTRANie i sposoby zapobiegania im

  5. Sposoby odrobaczania programu • Analiza kodu źródłowego • Analiza wyników działania programu • Diagnostyka użytkownika (drukowanie wyników pośrednich) • Użycie opcji kompilatora generujących informację diagnostyczną oraz sprawdzających przekroczenie granic tablic. • Użycie odpowiednego programu narzędziowego, debuggera.

  6. Sześć zasad odrobaczania. • Doprowadź do tego, żeby błąd był powtarzalny. • Zawęż problem do najprostszych przebiegów z najmniejszym zestawem danych, najlepiej takich gdzie wszystkie operacje możesz przeprowadzić na piechotę. • Zlokalizuj najmniejszą jednostkę programu, gdzie błąd występuje. • Przeprowadź odpowiednio zaplanowane eksperymenty (np., jeżeli program ma obliczyć kąt między trzema atomami rozsądnie jest wybrać najpierw współrzędne które dadzą kąt prosty, potem pełny, potem dowolny. • Używaj doświadczenia własnego i innych. • Nigdy nie dopuszczaj myśli, że problemu nie rozwiążesz albo że do jego rozwiązania jest potrzebna wiedza magiczna. Więcej w artykule Terence Parr “Learn the Essentials of Debugging”

  7. Debuggery do kodów FORTRANowskich gdb (Linux, darmowy) dbx (AIX i inne systemy; przychodzi z kompilatorem) xdb (AIX; okienkowy) totalview (AIX, UNICOS; okienkowy)

  8. Praca z debuggerem gdb Kompilacja z opcją –g (dodaje informację diagnostyczną do kodu wynikowego) Przykład: f77 –g program.f –o program Warto dodać sprawdzanie rozmiarów tablic (opcja fbounds-check), np: f77 –g –fbounds-check program.f –o program Tak skompilowany program można uruchomić z poziomu gdb: gdb program

  9. Podstawowe komendy debuggera breakplik.f:nr_linii ustawienie punktu zatrzymania w linii nr_linii pliku źródłowego plik.f clearplik.f:nr_linii usunięcie danego punktu zatrzymania run wykonanie programu do końca lub pierwszego zatrzymania cont wznowienie wykonywania programu po napotkaniu punktu zatrzymania next [liczba_linii] przejście do następnej linii lub wykonanie następnych liczba_linii kodu danego podprogramu bez wliczania linii wywoływanych popdrogramów lub funkcji. step [liczba_linii] przejście do następnej instrukcji lub wykonanie następnych liczba_linii instrukcji programu, włączając linie podprogramów lub funkcji wywoływanych printzmienna drukowanie wartości danej zmiennej. quit wyjście z debuggera.

  10. Trochę bardziej zaawansowane ale użyteczne komendy cdkatalog zmiana katalogu roboczego na katalog pwd pokazuje katalog roboczy file prog ładowanie programu prog do debuggera set args argwprowadzanie argumentów arg do linii polecenia programu list [nr1,nr2]listowanie 10 linii kodu scentrowanych na danej linii (uwaga: kolejne list kontytuuje) albo od linii nr1 do linii nr2. list +[-] listowanie następnych (+) albo poprzednich (-) linii kodu źródłowego. listplik.f:linia listowanie kodu źródłowego z pliku plik.f począwszy od linii linia. info kategoria informacja na temat danej kategorii; np. info break podaje informację o punktach zatrzymania where pokazuje w którym miejscu programu się znajdujemy backtrace where full pokazuje gdzie się znajdujemy oraz drukuje wartości wszystkich backtrace full zmiennych w danym segmencie oraz segmentach wywołujących

  11. set z=wartość nadawanie nowej wartości zmiennej z występującej w aktualnie analizowanym module programu set env zm=wyrNadawanie wartości wyr zmiennej środowiskowej zm unset env zm anulowanie zmiennej środowiskowej zm show env pokazuje zmienne środowiskowe show path pokazuje ścieżki dostępu where full pokazuje gdzie się znajdujemy oraz drukuje wartości wszystkich zmiennych w danym segmencie oraz segmentach wywołujących watchwyrażenie zatrzymuje program jeżeli wartość wyrażenia wyrażenie zmienia się i drukuje nową wartość (wyrażenie może być nazwą zmiennej) awatchwyrażenie zatrzymuje program jeżeli wyrażenie jest pisane lub czytane rwatchwyrażenie zatrzymuje program, jeżeli wyrażenie jest czytane Pełen opis gdb: http://www.delorie.com/gnu/docs/gdb/gdb.html

  12. Przykład użycia debuggera do śledzenia wykonywania programu titr obliczającego krzywą miareczkowania mocnego kwasu mocną zasadą Źródła FORTRANowskie titr.fczyt_dane.foblicz_krzywa.foblicz_ph.fpisz_wyniki.fDIMENSIONS Dane krzywa_dane Wyniki krzywa_wyniki Plik Makefile

  13. Przykład sesji debuggera etoh:~/FORTRAN/DEBUG> gdb titr (gdb) break titr.f:15 Breakpoint 1 at 0x804887b: file titr.f, line 15. (gdb) run Starting program: /big/staff/adam/FORTRAN/DEBUG/titr Breakpoint 1, MAIN__ () at titr.f:15 15 CALL OBLICZ_KRZYWA(IOUT,CKW,CZ,VKW) Current language: auto; currently fortran (gdb) print ckw $1 = 0.10000000000000001 (gdb) next 17 CALL PISZ_WYNIKI(IOUT) (gdb) step pisz_wyniki__ (wyjscie=0x804a050) at pisz_wyniki.f:7 7 OPEN(WYJSCIE,FILE='krzywa.wyniki',STATUS='UNKNOWN') (gdb) step 3 11 WRITE(WYJSCIE,'(I3,2x,F5.2,F8.3)') I,OBJZ(I),PH(I) (gdb) cont Continuing. Program exited normally. (gdb)

  14. Sprawdzanie przekroczenia rozmiarów tablic Zmieniamy w pliku DIMENSIONS maksymalną liczbę punktów ze 100 na 10 PARAMETER (MAXPUNKT=10) Kompilujemy bez debuggingu etoh:~/FORTRAN/DEBUG> make f77 -c titr.f f77 -c czyt_dane.f f77 -c oblicz_krzywa.f f77 -c oblicz_ph.f f77 -c pisz_wyniki.f f77 -o titr titr.o czyt_dane.o oblicz_krzywa.o oblicz_ph.o pisz_wyniki.o etoh:~/FORTRAN/DEBUG> ./titr Program pozornie się wykonał ale plik krzywa.wyniki zawiera tylko nagłówek etoh:~/FORTRAN/DEBUG> more krzywa.wyniki Punkt VZ pH

  15. Teraz kompilujemy z –fbounds-check –g etoh:~/FORTRAN/DEBUG> make -f Makefile-debug f77 -c -fbounds-check -g titr.f f77 -c -fbounds-check -g czyt_dane.f f77 -c -fbounds-check -g oblicz_krzywa.f f77 -c -fbounds-check -g oblicz_ph.f f77 -c -fbounds-check -g pisz_wyniki.f f77 -o titr -fbounds-check -g titr.o czyt_dane.o oblicz_krzywa.o oblicz_ph.o pisz_wyniki.o Po uruchomieniu od razu widać, gdzie jest problem etoh:~/FORTRAN/DEBUG> ./titr Subscript out of range on file line 12, procedure czyt. Attempt to access the 21-th element of variable vz. Abort

  16. SUBROUTINE CZYT_DANE(WEJSCIE,STKW,STZ,OBJKW) C Czytanie danych wejsciowych IMPLICIT NONE INCLUDE 'DIMENSIONS' INTEGER WEJSCIE,I,NPUNKT REAL*8 STKW,STZ,OBJKW REAL*8 VZ(MAXPUNKT),PH(MAXPUNKT) COMMON /KRZYWA/ VZ,PH,NPUNKT OPEN(WEJSCIE,FILE='krzywa.dane',STATUS='OLD') READ(WEJSCIE,*) NPUNKT,STKW,STZ,OBJKW READ(WEJSCIE,*) (VZ(I),I=1,NPUNKT) CLOSE(WEJSCIE) RETURN END Zatrzymanie

  17. Podobne informacje dostajemy uruchamiając program z poziomu debuggera etoh:~/FORTRAN/DEBUG> gdb titr (gdb) run Starting program: /big/staff/adam/FORTRAN/DEBUG/titr Subscript out of range on file line 12, procedure czyt. Attempt to access the 21-th element of variable vz. Program received signal SIGABRT, Aborted. 0x4009283b in raise () from /lib/tls/libc.so.6 Możemy wtedy zapytać dokładnie gdzie błąd nastąpił (gdb) where #0 0x4009283b in raise () from /lib/tls/libc.so.6 #1 0x40093fa2 in abort () from /lib/tls/libc.so.6 #2 0x4002846f in sig_die () from /usr/lib/libg2c.so.0 #3 0x40027c94 in s_rnge () from /usr/lib/libg2c.so.0 #4 0x080489ea in czyt_dane__ (wejscie=0x804a04c, stkw=0xbffffc10, stz=0xbffffc08, objkw=0xbffffc00) at czyt_dane.f:12 #5 0x0804887b in MAIN__ () at titr.f:12 #6 0x08048dd6 in main ()

  18. Można też uzyskać pełną informację, łącznie z wartościami zmiennych (gdb) where full #0 0x4009283b in raise () from /lib/tls/libc.so.6 No symbol table info available. #1 0x40093fa2 in abort () from /lib/tls/libc.so.6 No symbol table info available. #2 0x4002846f in sig_die () from /usr/lib/libg2c.so.0 No symbol table info available. #3 0x40027c94 in s_rnge () from /usr/lib/libg2c.so.0 No symbol table info available. #4 0x080489ea in czyt_dane__ (wejscie=0x804a04c, stkw=0xbffffc10, stz=0xbffffc08, objkw=0xbffffc00) at czyt_dane.f:12 __g77_impdo_0 = 10 __g77_cilist_1 = {err = 0, unit = 1, end = 0, format = 0x0, rec = 0}

  19. i = 21 npunkt = 31 vz = {1, 2, 3, 4, 5, 6, 7, 8, 9, 9.1999999999999993, 9.4000000000000004, 9.5999999999999996, 9.8000000000000007, 9.9000000000000004, 9.9499999999999993, 9.9900000000000002, 10, 10.01, 10.050000000000001, 10.1} ph = {0 <repeats 20 times>} #5 0x0804887b in MAIN__ () at titr.f:12 n = 31 inp = 1 iout = 2 ckw = 0.10000000000000001 cz = 0.10000000000000001 vkw = 10 vz = {1, 2, 3, 4, 5, 6, 7, 8, 9, 9.1999999999999993, 9.4000000000000004, 9.5999999999999996, 9.8000000000000007, 9.9000000000000004, 9.9499999999999993, 9.9900000000000002, 10, 10.01, 10.050000000000001, 10.1} ph = {0 <repeats 20 times>} #6 0x08048dd6 in main () No symbol table info available.

  20. Dlaczego błąd nie był sygnalizowany bez opcji –fbounds-check, natomiast program nie wydrukował wyników ? Kompilujemy tylko z opcją –g etoh:~/FORTRAN/DEBUG> make -f Makefile-debug1 f77 -c -g titr.f f77 -c -g czyt_dane.f f77 -c -g oblicz_krzywa.f f77 -c -g oblicz_ph.f f77 -c -g pisz_wyniki.f f77 -o titr -g titr.o czyt_dane.o oblicz_krzywa.o oblicz_ph.o pisz_wyniki.o

  21. Kompilujemy tylko z opcją –g etoh:~/FORTRAN/DEBUG> make -f Makefile-debug1 f77 -c -g titr.f f77 -c -g czyt_dane.f f77 -c -g oblicz_krzywa.f f77 -c -g oblicz_ph.f f77 -c -g pisz_wyniki.f f77 -o titr -g titr.o czyt_dane.o oblicz_krzywa.o oblicz_ph.o pisz_wyniki.o Uruchamiamy z gdb ustawiając zatrzymanie w pisz_wyniki.f etoh:~/FORTRAN/DEBUG> gdb titr (gdb) break pisz_wyniki.f:1 Breakpoint 1 at 0x8048aec: file pisz_wyniki.f, line 1. (gdb) run Starting program: /big/staff/adam/FORTRAN/DEBUG/titr Breakpoint 1, pisz_wyniki__ (wyjscie=0x804864c) at pisz_wyniki.f:1 1 SUBROUTINE PISZ_WYNIKI(WYJSCIE) Current language: auto; currently fortran

  22. Drukujemy pełną informację (gdb) where full #0 pisz_wyniki__ (wyjscie=0x804864c) at pisz_wyniki.f:1 i = 1069128089 npunkt = -2112311744 objz = (1, 2, 3, 4, 5, 6, 7, 8, 9, 9.1999999999999993, 9.4000000000000004, 9.5999999999999996, 9.8000000000000007, 9.9000000000000004, 9.9499999999999993, 9.9900000000000002, 10, 10.01, 10.050000000000001, 10.1) ph = (1.0871501757189002, 1.1760912590556813, 1.26884531229258, 1.3679767852945943, 1.4771212547196624, 1.6020599913279623, 1.7533276666586115, 1.9542425094393248, 2.2787536009528289, 2.3802112417116055, 2.5096504795465826, 2.6901960800285134, 2.9956351945975515, 3.2988530764097082, 3.6009728956867422, 4.3008127941181264, 7, 9.6987529113637798, 10.396855627379825, 10.696803942579511)

  23. #1 0x08048868 in MAIN__ () at titr.f:16 n = -2112311744 inp = 1 iout = 2 ckw = 0.10000000000000001 cz = 0.10000000000000001 vkw = 10 vz = (1, 2, 3, 4, 5, 6, 7, 8, 9, 9.1999999999999993, 9.4000000000000004, 9.5999999999999996, 9.8000000000000007, 9.9000000000000004, 9.9499999999999993, 9.9900000000000002, 10, 10.01, 10.050000000000001, 10.1) ph = (1.0871501757189002, 1.1760912590556813, 1.26884531229258, 1.3679767852945943, 1.4771212547196624, 1.6020599913279623, 1.7533276666586115, 1.9542425094393248, 2.2787536009528289, 2.3802112417116055, 2.5096504795465826, 2.6901960800285134, 2.9956351945975515, 3.2988530764097082, 3.6009728956867422, 4.3008127941181264, 7, 9.6987529113637798, 10.396855627379825, 10.696803942579511) #2 0x08048c76 in main () No symbol table info available.

  24. Taka wartość zmiennej NPUNKT (w pisz_wyniki.f) lub N (w titr.f) jest wynikiem nadpisania jej przez pierwszy element tablicy PH wykraczający poza zadeklarowany rozmiar tablicy. Zmienna NPUNKT lub N sąsiadje z tablicą PH w obszarze wspólnym KRZYWA: REAL*8 VZ(MAXPUNKT),PH(MAXPUNKT) COMMON /KRZYWA/ VZ,PH,NPUNKT Błąd ten nastąpił w podprogramie oblicz_krzywa.f, gdzie jest wypełniana tablica PH. Jeżeli podejrzewamy, że problem jest ze zmienną NPUNKT, możemy wydrukować jej wartość bez zrzucania całego zestawu zmiennych w danym punkcie programu. (gdb) print npunkt $1 = -2112311744

  25. Zmianę wartości n możemy też śledzić przy pomocy komendy watch (gdb) break titr.f:1 Breakpoint 1 at 0x8048814: file titr.f, line 1. (gdb) run Starting program: /big/staff/adam/FORTRAN/DEBUG/titr Breakpoint 1, MAIN__ () at titr.f:1 1 PROGRAM TITR Current language: auto; currently fortran

  26. (gdb) watch n Hardware watchpoint 2: n (gdb) cont Continuing. Hardware watchpoint 2: n Old value = 0 New value = 31 0x4002ffc7 in l_read () from /usr/lib/libg2c.so.0 (gdb) cont Continuing. Hardware watchpoint 2: n Old value = 31 New value = -2112311744 oblicz_krzywa__ (wyjscie=0x8049010, stkw=0xbffffc10, stz=0xbffffc08, objkw=0xbffffc00) at oblicz_krzywa.f:13 13 ENDDO

  27. Użyteczne warianty wywoływania gdb Analiza zrzutu pamięci (coredump) programu który się “wywalił” Zwykle zrzut jest zapisany w pliku core. Przykładowo po wywołaniu gdb prog core gdb zachowa się tak, jakby program prog był uruchomiony z poziomu debuggera i zatrzymał się wskutek błędu, zatem można prowadzić analizę używając where, print, itp. Kolejność argumentów nie obowiązuje jeżeli prog poprzedzimu –e a core –c. Debugging już uruchomionego programu gdb prognr_procesu Gdzie prog jest nazwą pliku wykonywalnego a nr_procesu numerem procesu odpowiadającego “chodzącemu” programowi. Specyfikowanie katalogów, gdzie znajdują się pliki źródłowe gdb –d katalogprog

More Related