|

Bughunting mit git-bisect

In der Softwareentwicklung kann bei nicht ausreichenden Unit-, Integration- oder Visual-Regression-Tests eine Code-Änderung einen Fehler verursachen, der in den davor durchgeführten Tests noch nicht auftrat. Dies sind Regressionen, also Fehler die in bereits getesteten Code auftreten.

Falls man keine automatisierten Tests hat, kann die Suche nach der Ursache sehr aufwändig werden. Die meisten würden in dem Fall manuell Commits auschecken, die möglicherweise die Ursache waren und sich so langsam an die Ursache des Fehlers herantasten.

Fehler schneller finden

Um dies zu vereinfachen und zu beschleunigen, gibt es in der Softwareentwicklung Bisecting. Dabei wird ein Bereich von Änderungen zwischen dem zuletzt bekannten guten und dem aktuellen schlechten Zustand genommen. Anschließend wird automatisch in der Mitte davon die Änderung ausgecheckt und manuell überprüft.

Wenn der Fehler dort noch nicht besteht, existiert er mit hoher Wahrscheinlichkeit erst in einer Änderung danach.
In dem Fall wird die zweite Hälfte genommen und dort wieder in der Mitte aufgeteilt und die Änderung in der Mitte überprüft.
Der zu testende Bereich wird immer weiter in der Mitte aufgeteilt bis am Ende die Ursache des Fehlers gefunden wurde.

Dieses Verfahren kann sehr hilfreich sein und ist bereits in vielen Werkzeugen verfügbar.

Beispiel

Wir wollen dies anhand von git bisect demonstrieren. Folgende Commits liegen in unserem Beispiel-Projekt.

(1) 6f6053d5828b5667a02aa4a564301f3d18549d54 add template file
(2) 67e6c1d2e881b53528be26bbdcf9e0664f429c6a add lorem ipsum text and reference stylesheet
(3) 8890f2299daa99de216517a3ffc9dcf7bf3c7a42 add some basic styles
(4) 4059af3b17ab6605d2b31c6c24d30bf7e59aa31e style p tags
(5) e533f83ac1500ae7d025061a7c411399257a837d add some images
(6) e0edfd78d9c36b51c6eef44d6dfa78a03b87fd4a change image size
(7) f76d24c386e2911fc7459f30e898b61b00640fdf enable zooming
(8) 067431f40f9e722c83fa7ae76b789fe2d13df29a remove viewport zoom constraint, remove IE tag
(9) cfc432b1b6abe49146558b5c56d8d16e34841792 use blockquote and change background
(10) 09e0660f870979f3cab4b70c853f692098392966 wrap content in main tag
(11) 95e2727943867d756bfae8956c74d150bb477939 add article tags and level 2 headings
(12) fea7e6d185fc8fe4dc9646c2db7e6c39e2a68539 move images into articles
(13) d6256a0e351c3aaa654dbe9b77ce43002cc96f03 make the images unique
(14) 159006165326a293b69f2054635143b475754b9b enable scrolling

Dabei ist (1) der älteste und (14) der aktuelle Stand.

Wir beginnen das Bisecten. Dazu müssen wir auf der obersten Ebene sein, wo der .git Ordner des Projekts liegt.

.
..
.git
some_dir/

Hier führen wir dann git bisect start aus. Wir befinden uns nun im "bisect mode". Wir markieren den aktuellen Commit bzw. Zustand als schlecht (14), denn hier ist uns nun mal zuerst aufgefallen, dass ein bestimmtes Feature nicht mehr so funktioniert wie es soll. Den Zustand markieren wir via git bisect bad als schlecht.

Mit git bisect good e0edfd78d9c36b51c6eef44d6dfa78a03b87fd4a markieren wir den uns als letzten bekannten funktionierenden Commit (6). Nun erhalten wir Infos, wie viele Schritte nötig und Änderungen nach dem ersten Test noch zu testen sein werden.

Bisecting: 3 revisions left to test after this (roughly 2 steps)
[cfc432b1b6abe49146558b5c56d8d16e34841792] use blockquote and change background

Hier wurde nun bei (10) aufgeteilt. Zwischen (6) und (10) liegen 3 Änderungen und wenn man die Anzahl immer durch 2 teilt erhält man ca 2 Schritte.

Es wurde nun automatisch (9) ausgecheckt. Wenn der Fehler an der Stelle nicht besteht, markieren wir den aktuellen Zustand als gut, ansonsten als schlecht.
In unserem Fall besteht bereits hier der Fehler. Entsprechend führen wir nun git bisect bad aus, was den Commit als schlecht markiert.

project ((cfc432b...)|BISECTING)
git bisect bad
Bisecting: 0 revisions left to test after this (roughly 1 step)
[067431f40f9e722c83fa7ae76b789fe2d13df29a] remove viewport zoom constraint, remove IE tag

Nun ist (8) ausgecheckt. Hier bestand der Fehler noch nicht. Also markieren wir den Commit als gut. Da wir hier nicht viel zu testen hatten, erhalten wir direkt den Commit, der den Fehler verursacht.

project ((067431f...)|BISECTING)
git bisect good
cfc432b1b6abe49146558b5c56d8d16e34841792 is the first bad commit
commit cfc432b1b6abe49146558b5c56d8d16e34841792
Author: Daniel Ruf <daniel@daniel-ruf.de>
Date:   Sun Feb 25 22:16:00 2018 +0100

    use blockquote and change background

:040000 040000 1c806b5db1dfb33a709a7d78769e6fe5f382324f 35e94ba8beba9af3c03ec3d00254909608e24248 M      css
:100644 100644 cc9ac9f68b08f299989797889a0b47aee067de16 8530044061516542530ae8b04753ab13881c271d M      index.html

Somit ist eine Änderung in (9) die Ursache.
cfc432b1b6abe49146558b5c56d8d16e34841792 is the first bad commit

Wenn wir hier fertig sind, müssen wir nur noch das Bisecting mit git bisect reset beenden.

Um Fehler zukünftig genau zu lokalisieren lohnt es sich also möglichst kleine Commits zu machen. Commit early and often.

Auch "squashed" Merges sollten vermieden werden da diese aus mehreren Commits einen Commit machen, was das Testen erschwert. Den getesteten Beispielcode findet ihr bei https://github.com/DanielRuf/bisect-regression-testing

Alternatives Tool - mozregression

Dieses Testverfahren wird auch bei sehr großen opensource Projekten genutzt, um schnell die Ursache einer Regression zu finden. Ein Beispiel für eine Implementierung als spezielles Tool ist zum Beispiel mozregression. Damit kann man auf dem selben Weg über eine grafische Oberfläche verschiedene Versionen des Firefox-Browsers testen. Darüber haben wir zwischen Firefox 57 und 58 einen Fehler lokalisiert.

[...]
(18) 2017-10-18
(19) 2017-10-19
(20) 2017-10-20
(21) 2017-10-21
(22) 2017-10-22
(23) 2017-10-23
(24) 2017-10-24
(25) 2017-10-25
(26) 2017-10-26
(27) 2017-10-27
(28) 2017-10-28
(29) 2017-10-29
(30) 2017-10-30
(31) 2017-10-31

Dort testen wir die Änderungen zwischen dem zuletzt bekannten daily Release (18) und dem ersten bekannten Release mit dem Fehler (31). Es werden (25), (28) und (30) als gut befunden. Somit müssen dann die Änderungen von (31) einzeln getestet werden. Diese werden so lange getestet und als gut oder schlecht markiert, bis die genaue Änderung gefunden wurde, die für den Fehler verantwortlich ist. Das ist in unserem Fall fa94f7205173d34f23975c6af9cb95237b28c8b8.

36060708-1b8b5c6a-0e4f-11e8-9b8d-d28766d01f73

Der Fehler wurde in #1437272 gemeldet und in 0f0f9354d50f571f73d5411aa643632dd4fd25ca behoben.
Der Bugfix wird spätestens in Firefox 60, optional auch bereits in 59, verfügbar sein.