Debugging²

in den letzten Tagen war die Arbeit recht mühsam, denn ich musste die Ursache für einen SEGFAULT suchen. Das ist so ähnlich wie wenn der Gerichtsmediziner die Todesursache bei einem Toten ermitteln muss, nur dass in meinem Fall der Tote eben ein Programm ist und die zu untersuchende Leiche ein sogenannter Coredump.

Um so einen Core-Dump sinnvoll analysieren zu können empfiehlt es sich, das Programm welches da unerwartet stirbt mit allen Debug-Flags zu übersetzen, so dass man im Debugger dann sieht, an welcher Stelle das Ding gestorben ist und sich auch mal die im Coredump verewigten Variablen ansehen kann. Als Debugger kann man gdb nehmen oder das graphische Frontend DDD (Data Display Debugger). Für letzteres hatte ich mich auf der Testmaschine entschieden und wollte dann auch das Programm bis zur Absturzsstelle verfolgen. Lästig dabei war, dass DDD nach dem Start gelegentlich nix mehr tat außer darauf zu warten, das gdb endlich „ready“ wäre.

Eine sichere Methode diesen Hänger zu vermeiden ist, im Home-Directory das Verzeichnis „.ddd“ zu löschen, damit sind aber dann auch alle vom Anwender in DDD vorgenommenen Settings wie z.B. Fenstergröße etc. weg. Also recht unbefriedigend.

Nach einer Weile fiel mir dann auf, dass das Problem wohl nur auftritt wenn ich innerhalb von DDD den Dialog mit den gdb Einstellungen aufgerufen habe. Das hatte dann zur Folge, dass in der Datei „init“ im Verzeichnis „.ddd“ nicht nur die X-Ressourcen definiert wurden, sondern auch eine ganze Menge an Kommandos für den gdb. Und in diesem Rattenschwanz von Kommandos war wohl eines dabei das den DDD sauer aufstößt.

Was macht man also? Den Rattenschwanz verkleinern, sprich die Datei in den Editor laden und die Hälfte dieser Zeilen wegwerfen, dann die Datei wieder speichern. Dann wieder DDD aufrufen und schauen, ob das Problem noch da ist. Ist es noch da, dann ist der Fehler weiterhin in den Kommandos die jetzt in der „init“-Datei stehen, ist der Fehler weg, dann war die „offending line“ wohl in dem Teil den man gerade ins Nulldevice entsorgt hat.

Nach ein paar Intervallhalbierungen habe ich dann die böse Zeile gefunden die den gdb aufhängt. Sie war:

Das kann man ganz einfach ausprobieren indem man gdb von Hand startet und dort als Kommando „set extended-prompt not set“ eingibt. Dann hängt sich der gdb auf und der Prozess muss gekillt werden. Dieser Wert „not set“ kommt wohl daher, dass das Dialogfeld „GDB settings“ innerhalb von DDD seine Werte erfragen tut und auf „show extended-prompt“ meldet der gdb natürlich „not set“.

Ich habe dann ein wenig rumprobiert und festgestellt, dass der gdb sich nicht aufhängt, wenn der Text beim „extended-prompt“ am ende ein „(gdb)“ enthält. Also habe ich im Editor die Zeile so angepasst:

Mit dieser Einstellung tritt das Problem in DDD nicht mehr auf, DDD startet normal und kann problemlos benutzt werden. Der Fehler ist wohl auch eher gdb anzulasten, denn dass sich das Ding bei Setzen eines „falschen“ Prompts einfach aufhängt ist alles andere als normal.

Bei gnu.org gab es einen Bug Report dazu, den habe ich mal kommentiert. Und auf Stack Overflow habe ich den gleichen Kommentar bei der passenden Frage hinkopiert. Damit dürften viele Betroffene jetzt die Lösung finden.

P.S.: Ja, und die Ursache für den SEGFAULT habe ich heute auch gefunden. Problem ist, dass an eine BIOS-Funktion Datenstrukturen übergeben wurden, was funktioniert wenn man das Programm für 32bit übersetzt, bei 64bit sind die „unsigned long“ in den Strukturen aber plötzlich doppelt so lang und das BIOS macht dann nur noch Unsinn und gibt entpsrechend Unsinn zurück. Unsinn der dann in dazu führt dass mit dem Rückgabewert und fast unendlich großen Längenangaben Pointer durchinkrementiert werden bis eben der Pointer total „out of bounds“ ist und das den SEGFAULT verursacht. Liest sich jetzt einfach wenn man weiß warum, aber es zu finden waren drei Tage harte Arbeit, denn das betroffene Programm war „fremd“ und musste daher erst mal mehr oder weniger reverse engineered werden.

[ratings]