git bisect

06 marzec 2014

Dużym wyzwaniem związanym z problemami zgłoszonymi przez użytkowników jest powtórzenie ich na własnym komputerze. Zdecydowanie częściej się to nie udaje, więc możemy poradzić jedynie zgłoszenie tego samego błędu na bugtrackerze danego programu. Jeżeli jednak bug występuje też u nas i użytkownik nie wygląda na osobę uczoną w debugowaniu, sami kontaktujemy się z autorami kodu. FS#38321 okazało się świetnym pretekstem do odkurzenia kilku płyt Audio-CD oraz nauczenia się obsługi git bisect.

Narzędzie to jest szokująco genialne w swojej prostocie. Znając chociaż jeden commit na którym program działał poprawnie, używa wyszukiwania binarnego, aby przy jak najmniejszym nakładzie pracy znaleźć wadliwe zmiany. Wiemy zatem, że 0.2.4 działa jak powinno, a 0.3.0 i HEAD są zepsute. Aby zacząć debugowanie, należy wykonać git bisect start. Polecenie git bisect good v0.2.4 oznaczy tą wersję jako sprawną, natomiast git bisect bad oznaczy najnowszy commit jako błędny. Będzie to jednak niepotrzebne dokładanie sobie pracy – skoro odtwarzanie Audio-CD nie działało już na 0.3.0, właśnie ten tag obierzemy jako "górną" granicę: git bisect bad v0.3.0.

% git bisect start
% git bisect good v0.2.4
% git bisect bad v0.3.0
Bisecting: a merge base must be tested
[f572d22863849ad08746ce9a221403aa58b5e086] docs: osc: add short info about lua and how to disable it

(Całość można uprościć do pojedynczego polecenia w postaci git bisect start błędny poprawny.)

Należy jeszcze przetestować czy 0.2.4 rzeczywiście jest sprawne. Jeśli tak:

% git bisect good
Bisecting: 387 revisions left to test after this (roughly 9 steps)
[b5b16925938fd37a604f5235afc7257f0b9bd6c7] video: disable PTS sorting fallback by default

Tutaj na scenę wchodzi algorytm wyszukiwania binarnego. Zamiast przeglądać każdy z 776 commitów pomiędzy wydaniem 0.2.4 i 0.3.0, Git wybiera środkowy z nich. Jeżeli błąd na nim nie występuje (git bisect good), zmiana musiała zostać wprowadzona po tym commicie – w innym przypadku (git bisect bad) przed nim.

Commit również jest sprawny b5b1692593, ale 1974c9b49d już nie:

% git bisect good
Bisecting: 193 revisions left to test after this (roughly 8 steps)
[b0bd93cbc18032dde6abb42cbc3068e906622e48] dvdnav: support mouse interaction
% ./waf configure; ./waf build -j2; ./mpv cdda://% git bisect good
Bisecting: 96 revisions left to test after this (roughly 7 steps)
[1974c9b49d7bf09469362ef6ba1214f625ecb40a] audio: mp_msg conversions
% ./waf configure; ./waf build -j2; ./mpv cdda://Playing: cdda://
Found audio CD with 12 tracks.
Detected file format: Uncompressed audio
[stream] Audio (+) --aid=1 (mp-pcm)
Video: no video
initial decode failed
Audio decoder init failed for lavc:mp-pcm
Failed to initialize an audio decoder for codec 'mp-pcm'.
Audio: no audio
No video or audio streams selected.

Exiting... (No files played)

Po kolejnych cyklach z good i bad zostaje coraz mniej zmian do przetestowania:

% git bisect bad
Bisecting: 0 revisions left to test after this (roughly 1 step)
[1948131a2a7372ffd310f71edf12c25632a05403] cache: try harder on EOF

Bardzo łatwo ulec wrażeniu, że to właśnie 1948131a2 spowodował błędy. Oczywiście tak nie jest: w puli zostały zaledwie 3 commity, z których bisect ustawił nas na środkowym. W zależności od tego czy działa, problematycznie zmiany zostały wprowadzone w następnym lub wcześniejszym commicie.

% git bisect bad
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[9a723fc03b5b8114abca2bfa7aa8b54f111d6702] stream: don't seek when seeking to the same position

Po wszystkim warto wykonać git bisect reset, aby HEAD ponownie wskazywało najnowszy commit w repozytorium. Z tą wiedzą można spróbować naprawić problem samodzielnie albo zgłosić go autorom – z całą pewnością będą z tego bardziej zadowoleni niż ze zgłoszenia typu "XYZ nie działa". Wyżej wspomniany błąd został naprawiony 25 minut po zgłoszeniu go na kanale IRC.

Jest to na tyle potężne narzędzie, że jeden z developerów Archa przeszukał zmiany w Linuksie od 3.9 do 3.11 z powodu problemów z NFS; zajęło mu to "aż" dzień przez względnie długi proces kompilacji. We wpisie opisałem zaledwie podstawy – więcej informacji (dotyczących między innymi automatyzacji) można znaleźć w dokumentacji Gita.

Post scriptum (13.04.2014): Ciekawym przypadkiem jest potrzeba znalezienia pierwszego działającego commita. Chociaż brzmi to jak zupełnie inne zagadnienie niż to opisane powyżej, tak naprawdę niczym się nie różni: git bisect bad oznacza sprawny commit, a git bisect good niedziałający. Fakt, w efekcie można skończyć z rozdwojeniem jaźni, ale… zadanie wykonane, prawda?