Jakiego procesu zwykle używasz podczas próby debugowania problemu / problemu / błędu w oprogramowaniu? [Zamknięte]


15

Wydaje się, że większość ludzi traktuje debugowanie raczej jako sztukę niż naukę. Dla tych, którzy traktują to jako naukę, a nie sztukę - jakich procesów zwykle używasz w obliczu nowego problemu / błędu / problemu?

Odpowiedzi:


13

Mówiąc bardzo ogólnie, robię to:

  1. Spróbuj wyizolować problem. Pomyśl, co się zmieniło, gdy błąd pojawił się po raz pierwszy. Nad czym pracujesz? Którą część kodu zmieniłeś? 99% moich błędów jest rozwiązanych w ten sposób. Zwykle jest to coś głupiego.

  2. Jeśli zgaduję, gdzie jest problem, przyjrzyj się kodowi, który wydaje się być przyczyną. Przeczytaj to. Przeczytaj to nawet na głos. Zadaj sobie pytanie: „Co próbuję osiągnąć?”. W przypadku niektórych rodzajów problemów: czy może to mieć jakieś skutki uboczne lub może mieć wpływ na kod w innym miejscu w sposób, o którym nie myślałem?

  3. Spróbuj na różne sposoby przeanalizować, co pójdzie nie tak, gdzie i kiedy (patrz poniżej).

  4. Jeśli nadal nie mam pojęcia, sprawdzam, czy starsza wersja mojego źródła ma ten sam problem, spróbuj znaleźć, kiedy na osi czasu programowania pojawił się problem. Aby to zrobić, musisz pracować z dobrym systemem kontroli wersji, takim jak git (git ma funkcję bisect właśnie do tego rodzaju debugowania).

  5. Jeśli nadal nie masz pojęcia, zrób sobie przerwę ... to naprawdę często pomaga.

  6. Wróć do tablicy kreślarskiej - sprawdź, jak powinien działać Twój program i czy ma to sens.

To naprawdę zależy od rodzaju problemu, ale zakładając, że mam ogólne pojęcie o tym, gdzie może być problem, to:

  • Jeśli podejrzewam, że problem dotyczy jakiejś części kodu / ostatniej zmiany, próbuję najpierw usunąć / skomentować / zmienić lub cokolwiek innego, aby błąd zniknął, upraszczając kod, a następnie przywróć problematyczny kod i weź dobrze na to spojrzeć.

  • Uruchom debugger z punktami przerwania (jeśli to w ogóle możliwe) i spójrz na to, jak wyglądają moje dane, które próbują znaleźć, kiedy zaczyna działać źle, aby uzyskać lepszy obraz tego, co się dzieje.


1
+1 za przerwę. Najtrudniejsze problemy stają się coraz trudniejsze tylko wtedy, gdy jesteś sfrustrowany i debugujesz je w szóstej godzinie. Wiedza, kiedy zrobić sobie przerwę, jest jedną z najbardziej przydatnych umiejętności debugowania, jakie zdobyłem.
Brad Gardner

Świetna odpowiedź. Nie mogę nic lepszego.
EricBoersma

1
Podobnie jak moje podejście, ale zapomniałeś o tym, że poprosiłeś kolegę, aby
rzucił

1
Doskonała odpowiedź. Chcę tylko dodać, że uncja zapobiegania jest warta funta lekarstwa. Duża część mojego procesu debugowania polega na tym, że podczas pisania kodu wprowadzam tylko niewielkie, przyrostowe zmiany, a także kompiluję, testuję i lokalnie zatwierdzam między nimi. W ten sposób, jeśli nagle pojawi się błąd, lista podejrzanych jest bardzo mała i łatwo ją zobaczyć za pomocą bzr qdiffpolecenia.
Karl Bielefeldt

8

Staram się używać programowania opartego na testach ( TDD ). Piszę test, który replikuje błąd, a następnie próbuję go zaliczyć. Czasami pisanie testu pomaga znaleźć błąd.

To przez większość czasu utrzymuje mnie z dala od debuggera i zapewnia testy regresji, aby zapobiec ponownemu wprowadzeniu błędu.

Niektóre linki:


4
Myślę, że ta odpowiedź jest niezwykle niepełna. Nie rozumiem tylu głosów pozytywnych.
Alex

1
Otrzymuje tylko tyle głosów poparcia, ponieważ zawiera magiczny akronim: TDD.
Bjarke Freund-Hansen

@Alex - dodałem kilka linków. Przykład „Znajdź BŁĄD, napisz test” ma przykład. Mogę to rozwinąć, ale to naprawdę takie proste.
TrueWill,

7

Istnieje wiele definicji słowa nauka, ale brzmi to tak, jakbyś miał na myśli to, co może być dokładniej nazwane „ metodą naukową ”. Metodę naukową można podsumować jako obserwowanie niektórych zjawisk (prawdopodobnie błąd lub nieoczekiwane zachowanie programu), formułowanie hipotezy lub hipotez wyjaśniających zachowanie oraz najbardziej prawdopodobne eksperymentowanie w celu udowodnienia tego (napisanie testu, który rzetelnie odtwarza problem).

Rodzaje błędów (zjawisk), które mogą wystąpić, są praktycznie nieograniczone, a niektóre niekoniecznie wymagają dobrze zdefiniowanego procesu. Na przykład czasami widzisz błąd i od razu wiesz, co go spowodowało, ponieważ dobrze znasz kod. Innym razem wiesz, że przy pewnych danych wejściowych (akcja, seria kroków itp.) Następuje niepoprawny wynik (awaria, złe wyjście itp.). W takich przypadkach często nie wymaga dużo „naukowego” myślenia. Pewna myśl może pomóc w ograniczeniu przestrzeni wyszukiwania, ale powszechną metodą jest po prostu przejście przez kod w debuggerze i zobaczenie, co poszło nie tak.

Jednak sytuacje, które uważam za najciekawsze i być może godne procesu naukowego, są związane z ostatecznym rezultatem i poproszeniem o wyjaśnienie, jak to się stało. Oczywistym przykładem jest zrzut awaryjny. Możesz załadować zrzut awaryjny i obserwować stan systemu, a Twoim zadaniem jest wyjaśnienie, jak dostał się w tym stanie. Zrzut awarii (lub rdzenia) może pokazywać wyjątek, zakleszczenie, błąd wewnętrzny lub jakiś „niepożądany” stan zdefiniowany przez użytkownika (np. Powolność). W takich sytuacjach na ogół wykonuję następujące kroki:

  • Wąska obserwacja : w razie potrzeby zapoznaj się z informacjami bezpośrednio dotyczącymi konkretnego problemu. Oczywiste są tutaj stos wywołań, lokalne zmienne, jeśli je widać, linie kodu otaczające problem. Ten rodzaj badania konkretnego miejsca nie zawsze ma zastosowanie. Na przykład badanie „powolnego” systemu może nie mieć takiej oczywistej lokalizacji początkowej, ale awaria lub błąd wewnętrzny prawdopodobnie będzie miał natychmiastowy i oczywisty cel. Jednym konkretnym krokiem może być użycie narzędzi takich jak windbg (uruchom! Analizuj -v na załadowanym zrzutu awaryjnym i spójrz na to, co ci mówi).

  • Szeroka obserwacja : przestudiuj inne części systemu. Sprawdź stan wszystkich wątków w systemie, spójrz na dowolne informacje globalne (liczba użytkowników / operacji / pozycji, aktywnych transakcji / procesów / widżetów itp.), Informacje o systemie (OS) itp. Jeśli użytkownik podał jakieś dane zewnętrzne , pomyśl o tych w połączeniu z tym, co zaobserwowałeś. Na przykład, jeśli powiedzieli ci, że problem występuje w każdy wtorek po południu, zadaj sobie pytanie, co to może znaczyć.

  • Hipoteza: To jest naprawdę zabawna część (i nie żartuję z tego, że jest zabawna). Często wymaga to dużo logicznego myślenia w odwrotnej kolejności. Myśl o tym, jak system wszedł w obecny stan, może być bardzo przyjemna. Podejrzewam, że jest to część, którą wielu ludzi uważa za sztukę. I przypuszczam, że może być tak, że programista po prostu zacznie losowo rzucać w nią rzeczami, aby zobaczyć, co się trzyma. Ale z doświadczeniem może to być dość dobrze zdefiniowany proces. Jeśli w tym momencie myślisz bardzo logicznie, często można zdefiniować możliwe zestawy ścieżek, które doprowadziły do ​​danego stanu. Wiem, że jesteśmy w stanie S5. Aby tak się stało, S4a lub S4b musiały wystąpić, a może S3 przed S4a itp. Częściej nie, może istnieć wiele elementów, które mogą prowadzić do danego stanu. Czasami pomocne może być zapisanie na notatniku prostego schematu przepływu lub stanu lub szeregu kroków związanych z czasem. Rzeczywiste procesy tutaj będą się znacznie różnić w zależności od sytuacji, ale poważne przemyślenia (i ponowne zbadanie w poprzednich krokach) w tym czasie często dostarczą jednej lub więcej wiarygodnych odpowiedzi. Zauważ też, że niezwykle ważną częścią tego kroku jest wyeliminowanie rzeczy niemożliwych. Usunięcie niemożliwego może pomóc przyciąć przestrzeń rozwiązania (pamiętaj, co Sherlock Holmes powiedział o tym, co pozostało po wyeliminowaniu niemożliwego). Zauważ też, że niezwykle ważną częścią tego kroku jest wyeliminowanie rzeczy niemożliwych. Usunięcie niemożliwego może pomóc przyciąć przestrzeń rozwiązania (pamiętaj, co Sherlock Holmes powiedział o tym, co pozostało po wyeliminowaniu niemożliwego). Zauważ też, że niezwykle ważną częścią tego kroku jest wyeliminowanie rzeczy niemożliwych. Usunięcie niemożliwego może pomóc przyciąć przestrzeń rozwiązania (pamiętaj, co Sherlock Holmes powiedział o tym, co pozostało po wyeliminowaniu niemożliwego).

  • Eksperyment : na tym etapie spróbuj odtworzyć problem na podstawie hipotez wyprowadzonych w poprzednim kroku. Jeśli poważnie pomyślałeś w poprzednim kroku, powinno to być bardzo proste. Czasami „oszukuję” i modyfikuję bazę kodu, aby pomóc w danym teście. Na przykład ostatnio badałem wypadek, który doszedłem do wniosku, że był spowodowany wyścigiem. Aby to zweryfikować, po prostu umieszczam Sleep (500) między kilkoma liniami kodu, aby pozwolić innym wątkom na wykonanie swoich złych rzeczy we „właściwym” czasie. Nie wiem, czy jest to dozwolone w „prawdziwej” nauce, ale jest to całkowicie uzasadnione w kodzie, który posiadasz.

Jeśli uda ci się go odtworzyć, masz duże szanse, że już prawie skończyłeś (pozostaje ci tylko prosty krok, aby to naprawić ... ale to na kolejny dzień). Pamiętaj, aby sprawdzić nowy test w systemie testów regresji. I powinienem zaznaczyć, że moim zdaniem poprzednie oświadczenie o naprawieniu było proste, by mówić z przymrużeniem oka. Znalezienie rozwiązania i jego wdrożenie może wymagać intensywnej pracy. Moim zdaniem naprawienie błędu nie jest częścią procesu debugowania, ale raczej rozwojem. A jeśli poprawka jest w ogóle zaangażowana, powinna ona wymagać pewnego projektu i przeglądu.


Większość błędów, które widziałem, nie były niezawodnie odtwarzalne, a dla tego podzbioru większość nadal wymagała znacznych prac związanych z debugowaniem po ich odtworzeniu, zanim można by rozpocząć pracę nad ich naprawą. Nawet jeśli zamiast powiedzieć „udaje się to odtworzyć”, mówisz, „udaje się zawęzić test jednostkowy, który wyraźnie ćwiczy błąd”, powiedziałbym, że praca debugowania jeszcze się nie zakończyła. Dla mnie debugowanie kończy się, gdy mam już poprawkę. Mogę udowodnić, że rozwiązuje problem, i mam niezawodny dowód, że moja poprawka jest tym, co naprawia rzeczy.
blueberryfields

Zgadzam się, że naprawienie tego może wymagać sporo pracy. Rzeczywiście używałem sarkazmu w moich słowach „prosty krok to naprawić”, ale to nie wychodzi zbyt dobrze w formie.

4

Spróbuj zmniejszyć przypadek testowy. Gdy jest wystarczająco mały, zwykle łatwiej jest znaleźć odpowiedni kod, który powoduje problem.

Prawdopodobnie przyczyną problemu jest nowe zameldowanie, a poprzednia codzienna kompilacja była w porządku. W takim przypadku Twój dziennik zmian z kontroli źródła powinien pomóc ci zdecydować, kogo złapać.

Ponadto, jeśli jesteś w C / C ++, rozważ uruchomienie valgrind lub oczyść, aby wyodrębnić problemy związane z pamięcią.


2

Najtrudniejszą częścią debugowania jest izolacja problemu, szczególnie gdy problem jest ukryty pod kilkoma warstwami. Na studiach studiowałem nagrywanie muzyki i, o dziwo, istniała klasa Studio Electronics, która bezpośrednio dotyczy tutaj. Wykorzystam debugowanie środowiska studyjnego jako ilustrację systematycznego procesu debugowania.

  1. Sprawdź swoje liczniki. Przy użyciu tonu testowego przy znanym skalibrowanym napięciu miernik powinien odczytać „U” (wzmocnienie jedności). Tłumaczenie: Jeśli twoje narzędzia są zepsute, nie możesz ich użyć, aby dowiedzieć się, co jeszcze jest nie tak.
  2. Przetestuj każdy element / stopień wzmocnienia pracujący wstecz od końca. Używając tego samego tonu testowego zastosowanego na wejściu stopnia, nie powinno być żadnych zmian na wyjściu stopnia. Tłumaczenie: Izolując wstecz każdy obiekt od wyniku, budujemy zaufanie do naszego kodu, dopóki nie znajdziemy miejsca, w którym się psuje. Jeśli narzędzie potrzebuje kilku warstw do zasygnalizowania problemu, musisz wiedzieć, że warstwy pośrednie nie przyczyniają się do tego.

Kod debugujący naprawdę nie jest taki inny. Debugowanie jest znacznie łatwiejsze, gdy kod zgłasza wyjątek. Możesz prześledzić wstecz od śledzenia stosu tego wyjątku i ustawić punkty przerwania w kluczowych pozycjach. Zwykle zaraz po ustawieniu zmiennej lub w linii wywołującej metodę zgłaszającą wyjątek. Może się okazać, że co najmniej jedna wartość jest nieprawidłowa. Jeśli to nie jest poprawne (zero, gdy nie powinno być lub wartość jest poza zakresem), to jest to proces odkrywania, dlaczego to nie jest poprawne. Punkty przerwania w IDE są równoważne elektronicznym punktom testowym (zaprojektowanym dla sondy miernika do sprawdzenia obwodu).

Teraz, kiedy przejdę tę trudną część odkrywania, gdzie jest mój prawdziwy problem, napiszę kilka testów jednostkowych, aby sprawdzić to w przyszłości.


2

Z tymi paskudnymi błędami, które staram się wyśledzić późnym popołudniem, moją najskuteczniejszą strategią jest wstanie i odejść na kilka minut. Zwykle nowe pomysły na temat możliwych źródeł błędów zaczynają napływać już po 30 sekundach.


2

Bardziej praktyczne podejście:

  1. Jeśli błąd jest związany z nieobsługiwanym wyjątkiem - spójrz na ślad stosu. Najczęstsze jest odwołanie zerowe, indeks poza zakresem itp. Oraz własne zdefiniowane wyjątki. Możesz przypisać ten błąd do młodszego programisty, jest to prawdopodobnie łatwe i dobre doświadczenie w nauce.

  2. Jeśli nie zdarza się to na każdej maszynie, jest to prawdopodobnie problem z wyścigiem / problem z wątkami. To super zabawa, aby wyśledzić, umieść na nim swojego znudzonego starszego programistę. Udało się to dzięki wielu logom, dobrej wiedzy i dobrym narzędziom.

  3. Inną dużą klasą błędów jest to, że zespół testowy lub klienci nie lubią określonego zachowania. Na przykład nie podoba im się to, że decydujesz się na wyświetlanie identyfikatorów użytkowników lub że podczas wyszukiwania nie otrzymujesz autouzupełniania. Są to prawdziwe błędy, rozważ lepsze zarządzanie produktem i deweloperów z szerszym spojrzeniem. Naprawienie tego powinno zająć deweloperowi stosunkowo krótki czas, jeśli zbuduje system z myślą o rozbudowie.

  4. 80% wszystkich innych błędów można rozwiązać, mając dobre systemy logowania i gromadząc wystarczającą ilość informacji, aby je rozwiązać. Użyj wbudowanego śledzenia z wieloma poziomami złożonych systemów rejestrowania, takich jak Log4Net / Log4J

  5. błędy wydajności są kategorią samą w sobie, zasada Goldera brzmi: „zmień najpierw, napraw później!”, a zdziwisz się, ilu programistów zgadnie, gdzie jest problem, i od razu zajmie się naprawą, aby zobaczyć później jedynie o 3-4% skrócenie czasu odpowiedzi.


Gdybym mógł dać +1 każdemu z tych 5 osobno, zrobiłbym to!
jmort253

1

Mam dwa podejścia:

  1. Podziel dany problem na mniejsze części, a następnie pokonaj każdą mniejszą część zgodnie z Divide and ConquerParadygmatem.
  2. Ilekroć mam wątpliwości co do jakichkolwiek wartości, po prostu wypisuję wartości zmiennych, aby zobaczyć, co dokładnie wchodzi i wychodzi ze zmiennej.

Takie podejście pomogło mi przez większość czasu.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.