Czy kod debugowania powinien pozostać na miejscu, czy zawsze dodawany tylko podczas debugowania, i usuwany po znalezieniu błędu?

35

Ja, na przykład, dodaję kod debugowania (taki jak instrukcje drukowania) tylko wtedy, gdy próbuję zlokalizować błąd. A kiedy go znajdę, usuwam kod debugowania (i dodam przypadek testowy, który konkretnie testuje ten błąd). Czuję, że zaśmieca prawdziwy kod i dlatego nie ma tam miejsca, chyba że debuguję.

Jak ty to robisz? Czy pozostawiasz kod debugowania na miejscu, czy usuwasz go, gdy jest przestarzały (co może być trudne do oceny, kiedy to jest)?

gablin
źródło

Odpowiedzi:

30

Należy wyciągnąć instrukcje drukowania debugowania; Jeśli jednak chcesz je dodać, aby debugować problem z produkcją, warto rozważyć, czy masz wystarczającą ilość informacji w swoim systemie rejestrowania. Informacje o parametrach, stanach błędów i tak dalej mogą być przydatne później, gdy pojawi się następny błąd. Korzystanie z dobrego środowiska rejestrowania, które może dynamicznie włączać komunikaty dziennika debugowania lub śledzenia, może być bardzo przydatne na wolności.

Alaryczny
źródło
5
+1 Głównie za wzmiankę o posiadaniu rozsądnej struktury debugowania. Jeśli jest to początkowo na miejscu i istnieją różne poziomy debugowania, wówczas kod produkcyjny może być uruchamiany bez wywoływania drogich procedur debugowania, a kod programistyczny może działać z dowolnym wymaganym poziomem kontroli, miejmy nadzieję, że jest logowany w dowolny sposób.
Orbling
1
Zgadzam się z Orbling. I dodatkowo, w przypadku kodu debugowania innego niż wyświetlanie wyłącznie informacji, które miały wpływ na wydajność lub z innego powodu nieodpowiedniego do produkcji. (np. Potwierdź wynik funkcji, np. sprawdzając wynik sortowania), możesz rozważyć dwa tryby budowania celu. tryb debugowania i tryb zwolnienia.
Zekta Chan
16

Kod dodany specjalnie do debugowania powinien zostać usunięty z oprogramowania produkcyjnego.

To, czy jest to całkowite usunięcie, czy umieszczenie w sekcjach kompilacji warunkowej (jak w C / C ++ / C #), zależy od Ciebie i Twojego standardu kodowania.

Istnieje wiele powodów:

  1. Może wystąpić wpływ na bezpieczeństwo, jeśli ten kod jest wykonywany lub ktoś może uzyskać dostęp do jego wyniku.
  2. Może to spowolnić działanie aplikacji.
  3. Może to być mylące dla innych programistów, którzy patrzą na kod (a nawet na ciebie) 6 miesięcy później.
ChrisF
źródło
+1 dla kompilacji warunkowej, ale bloki komentarzy będą działać w językach, które ich nie obsługują. Nigdy nie powinieneś pozostawiać go skompilowanego do wydania produktu, ale czasami zbyt mało wydajne jest ciągłe usuwanie go za każdym razem, gdy chcesz upuścić kompilację wydania.
Bill
1
Pracowałem w środowiskach, w których kod C / C ++ był zawsze kompilowany z opcjami debugowania na wypadek, gdyby kod produkcyjny wymagał debugowania lub zbadania zrzutu rdzeniowego. Czasami ten zawsze gotowy do debugowania mandat wymagał pozostawienia instrukcji debugowania, aby można je było włączyć flagą bez ponownej kompilacji kodu. Java zawsze pozwala na debugowanie, jeśli ustawione są dla niego opcje JVM, więc do debugowania rzeczy później potrzeba stosunkowo mniej pracy przygotowawczej.
Michael Shopsin
16

ChrisF i Alaric mają ważne punkty; +1 dla nich. Potrafię zidentyfikować co najmniej 5 różnych typów kodu debugowania, którego używam.

  1. Używanie dzienników do zrzucania stanu systemu w określonym momencie.

    void function(...)
    {
        ...dump everything i know about.
    }
    
  2. Używanie dzienników do wykonywania punktów kontrolnych.

    void function(...)
    {
        ...got here 1
        ...got here 2
    }
    
  3. Kod, który faktycznie wymusza spełnienie określonego warunku, ale psuje normalne zachowanie. Przykład:

    • Załóżmy, że masz jakieś dzienniki dotyczące błędu, ale nie możesz odtworzyć problemu. Możesz spróbować napisać kod, który wymusza, aby pewne zmienne miały określone wartości pasujące do informacji w dzienniku.
  4. Rejestrowanie weryfikacji - sklasyfikowałbym to jako rejestrowanie pełne, które może być użyte do sprawdzania poprawności oprogramowania, które nie powinno być uwzględniane w produkcji, jak na przykład sprawdzanie poszczególnych kroków algorytmu.

  5. Rejestrowanie operacji - zapoznaj się z postem Alarica . To właściwie to, co rozumiem przez „rejestrowanie operacji”.

1, 2 i 3 powinny być całkowicie usuwane. Coś w rodzaju 4 Prawdopodobnie skompilowałbym warunkowo z kodu. W przypadku 5 Alaric miał świetną okazję do dynamicznego wyłączania dzienników. W większości przypadków może to dotyczyć punktu, w którym ChrisF ma swoją drugą kulę.

Pemda
źródło
1
To dobre podsumowanie. Byłoby jednak lepiej, gdyby można go poprawnie sformatować, zastępując 1)… przez 1.… (tak, że formatowanie Markdown rozpoznaje go jako listę) i wcina przykładowy kod o 8 spacji (ponownie, aby Markdown podniósł go jako przykład kod na liście).
Konrad Rudolph
@Konrad Rudolph: Gotowe.
gablin
3

To zależy od tego, co robi kod. Część kodu używanego do debugowania można pozostawić bez zmian, a niektóre należy usunąć.

Kod weryfikujący poczytalność parametrów w metodzie nie zawsze jest przydatny, gdy kod działa poprawnie, ale często jest utrzymywany, aby upewnić się, że kod nadal działa poprawnie.

Czasami piszesz kod inaczej, aby ułatwić debugowanie kodu, na przykład obliczanie wartości i umieszczanie jej w zmiennej lokalnej, a następnie użycie zmiennej w następnym wierszu, co ułatwia sprawdzenie wyniku obliczenia podczas pojedynczego kroku przez kod. Możesz przepisać kod, aby bezpośrednio użyć obliczonej wartości, ale koszt użycia zmiennej lokalnej jest tak mały (jeśli w ogóle), że nie ma powodu, aby przepisywać kod. Poza tym po przetestowaniu kodu nie ma sensu, zawsze istnieje niewielkie ryzyko, że wprowadzisz błąd podczas jego zmiany.

Kod, który dodajesz tylko w celu wyśledzenia określonego błędu, często można usunąć po jego znalezieniu.

Guffa
źródło
2

Dawno, dawno temu używałem dużo kodu do debugowania. Niemal całkowicie celowałem w system Windows, więc było wiele funkcji wyjściowych łańcucha debugowania, których nie pamiętam, jak przeliterować, więc mogłem przechwycić ślad za pomocą konkretnego programu.

Część kodu debugowania pozostała na miejscu, szczególne rzeczy, które miały zagnieżdżać wywołania. Jednak pomimo tego, że ciąg znaków debugowania w większości nie byłby widoczny w systemie produkcyjnym, wszystko to zostało wykonane w ramach kompilacji warunkowej.

Rzeczywistość jest jednak taka, że ​​cały ten kod debugowania wymagał dużego wysiłku w przypadku czegoś, co idealnie byłoby obsługiwane w inny sposób - oczywiście przy użyciu debugera. W tym czasie nie byłem pod wrażeniem debuggera Borland C ++. Narzędzia były tam, ale zbyt często dawały mylące wyniki, a użycie debuggera innego niż IDE (często konieczne) oznaczało zapamiętanie skrótów klawiszowych, co oznaczało odwrócenie uwagi od wykonywanego zadania.

Jedynym doświadczeniem debugowania, które jest gorsze, jest GDB z wiersza poleceń.

Bycie ekspertem w zakresie narzędzi, których używasz na co dzień, jest oczywiście ważne - ale debugowanie naprawdę nie powinno być czymś, co robisz na co dzień. Jeśli używasz debugera tak często, możesz nauczyć się dziesiątek poleceń i / lub skrótów klawiaturowych, wydaje mi się to nieco czerwoną flagą.

Kiedy pracowałem w Visual Studio 7, było jednak jasne, że debugowanie może być bardzo praktyczne i skuteczne. Jeśli możesz przeprowadzić debugowanie w programie Visual Studio (w tym wersje ekspresowe), debugowanie jest bardzo proste. Bez wątpienia, jeśli możesz znaleźć odpowiedni interfejs GUI / IDE, GDB jest również łatwy i skuteczny, chociaż nie przeprowadziłem jeszcze tego wyszukiwania.

Można również powiedzieć coś o testowaniu jednostkowym, z analizą zasięgu za pomocą gcov. Im bardziej pewne jest zachowanie bibliotek, tym mniej potrzeba głębokiego debugowania - i tym rzadziej potrzebujesz debuggera. Pisanie testów jednostkowych jest dość rozsądnym rozwiązaniem, które powinieneś robić przez większość dni.

Nieoczekiwanie ważne narzędzie = cmake, narzędzie do budowania, które pozwala mi łatwo przełączać się między budowaniem dla GCC i dla VC ++, między innymi. Mogę więc przeprowadzić testy jednostkowe i zasięg oparty na gcov za pomocą GCC, ale łatwo przejść do VC ++, aby użyć debuggera.

Steve314
źródło
Debuger może być całkiem bezużyteczny, jeśli nie niebezpieczny w aplikacjach wielowątkowych, ale podoba mi się twój komentarz z czerwoną flagą.
Pemdas
@Pemdas - Nie miałem jeszcze poważnego problemu w tym zakresie, chociaż wielowątkowość oczywiście nie jest przyjazna dla debuggera. Mimo to uważam, że odpowiednie narzędzia są prawdopodobnie lepszym rozwiązaniem niż kod debugowania w zasadzie. Przydatne byłoby narzędzie do analizy statycznej, które może wykryć warunki wyścigu, impasy, warunki, w których dwa wątki mogą walczyć o tę samą pamięć / zasoby w tym samym czasie i tak dalej. Nie mam pojęcia, co jest dostępne w tych kierunkach, choć wiem, że istnieją pewne sprytne narzędzia. na przykład klee - nie rozumiem tego, ale podstawowy opis brzmi bardzo imponująco.
Steve314,
„Myślę, że właściwe narzędzia są prawdopodobnie lepszym rozwiązaniem niż kod debugowania w zasadzie” To niebezpieczne stwierdzenie;). Posiadanie narzędzi, które wykonują część analizy, może być fajne, ale martwię się, że programiści, szczególnie nowi, zaczęliby zbytnio polegać na tych narzędziach, jak ktoś, kto musi użyć kalkulatora, aby dowiedzieć się, co to jest 15% ze 100.
Pemdas
Zbytnie uzależnianie się od narzędzi, których nawet nie szukałem, wydaje się mało prawdopodobne. Na twoim przykładzie kalkulatora tak, ale nadal będę korzystać z OpenOffice Calc zamiast pisać własny prosty arkusz kalkulacyjny. Jest czas na kod debugowania (nawet z debuggerem - np. Twoją dziwną skrzynką tworzącą warunki), ale jeśli wykracza poza pewien punkt, istniejące narzędzia wygrywają. A jeśli chodzi o odejmowanie 15% od 115, użyję również tego kalkulatora - to, co wielu ludzi dałoby jako oczywistą odpowiedź (100), jest błędne. W wielowątkowości, oczywiście prawidłowe odpowiedzi są niesławne, ponieważ czasami okazują się błędne.
Steve314,
1

Moje zdanie: kod debugowania używany do zabicia błędu w danym kodzie, który zazwyczaj usuwam całkowicie. Kod debugowania użyty do zabicia błędu wynikającego z sił zewnętrznych, które zazwyczaj po prostu komentuję.

Loren Pechtel
źródło
-1

Jeśli błąd pochodzi z testowania jednostkowego lub wewnętrznego, kod debugowania można całkowicie usunąć. Ale jeśli błąd pochodzi z produkcji, kod debugowania powinien znajdować się wewnątrz znaczników kompilacji. Umieszczenie go w znacznikach kompilacji pomoże innym programistom zrozumieć, że ten kod służy wyłącznie do debugowania.

Manoj R.
źródło
-1

Użyj TDD , aby Twój kod testowy zawsze zawierał miejsce, w którym jest utrzymywany.

dukeofgaming
źródło
Jak to odpowiada na zadane pytanie?
komara
Ponieważ TDD prowadzi do „debugowania” lub testowania kodu, którego nie trzeba usuwać.
dukeofgaming,
Przepraszam, nie podążam. W jaki sposób?
komar