kurzer Schauer

kurzer Schauer

Camera Model: Nikon D90
Lens: Nikon AF-S DX Nikkor 18-105mm 1:3.5-5.6G ED VR
Focal Length: 105.00 mm
Focal Length (35mm Equiv): 157 mm
Exposure Time: 1/25 sec
F-Number: f/5,6
Shooting mode: Aerture priority
Exposure bias: -1/3 EV
Flash: No
ISO: 100
Image at flickr, large version

An der schönen Isar

An der schönen Isar

Camera Model: Nikon D90
Lens: Nikon AF-S DX Nikkor 18-105mm 1:3.5-5.6G ED VR
Focal Length: 58.00 mm
Focal Length (35mm Equiv): 87 mm
Exposure Time: 1/640 sec
F-Number: f/6,3
Shooting mode: Program automatic
Exposure bias: 0 EV
Flash: No
ISO: 100
Image at flickr, large version

Sol

Sol

Camera Model: Nikon D70
Lens: Nikon Nikkor 50mm 1:1.8
Focal Length: 50.00 mm
Focal Length (35mm Equiv): 75 mm
Exposure Time: 1/50 sec
F-Number: f/1.8
Shooting mode: Aperture priority
Exposure bias: -2/3 EV
Flash: No
ISO: 200
Image at flickr, large version

Stacktrace gefällig?

Gelegentlich braucht man einen Stacktrace. Ich bin heute über ein Problem gestolpert, bei dessen Lösung mir ein Stacktrace sehr helfen würde.

20090907_dddlogoEine unserer Bibliotheken kümmert sich um Speichermanagement. Das sind kleine Wrapper um malloc / free, die Buchhaltung über den Speicher führen und bei Fehlern entsprechende Meldungen ausgeben. Also der übliche Weg für das Speichermanagementproblem in C, es gibt duzende Lösungen die ähnlich funktionieren.

Das Problem heute war, dass Speicher freigegeben werden sollte, der nie allokiert war oder schon einmal freigegeben wurde. Er war auf jeden Fall nicht mehr als belegt gekennzeichnet und konnte somit nicht freigegeben werden. Programme ohne solch ein Speichermanagement würden an dieser Stelle abstürzen, bei uns taucht nur eine Meldung im Log auf.

Diese Meldung war mein Startpunkt für die Suche. Die Aufrufe der free-Wrapperfunktion sind jedoch in vielen Sourcefiles auf tausenden Zeilen Code verstreut. Wie soll man dort den einen Aufruf finden, der die Ursache für die Meldung ist?

Genau: ein Stacktrace muss her. Der schmutzige Weg wäre, das Programm mutwillig zum Absturz zu bringen. Dann kann man sich aus dem core-File den Stack rauspopeln und weiss, woher der Aufruf kommt.

Es gibt jedoch etwas viel schöneres: backtrace() aus der glibc. Mit ein paar Zeilen Code kann man an jeder beliebigen Stelle des Programms einen Stacktrace erzeugen:

void *array[10];
size_t size;
char **strings;
size_t i;
 
size = backtrace (array, 10);
strings = backtrace_symbols (array, size);
 
printf ("Obtained %zd stack frames.\n", size);
 
for (i = 0; i < size; i++)
   printf ("%s\n", strings[i]);
 
free (strings);

Ich habe also in der free-Wrapperfunktion backtrace() eingebaut und konnte mir den Stacktrace für den Fehlerfall ausgeben. Im Log erhält man dann etwa diese Ausgabe:

yourbinary [0x80b9e0c]
yourbinary [0x80b9f02]
yourbinary [0x8054abd]
yourbinary [0x805124f]
yourbinary [0x8050078]
yourbinary [0x808d6f3]
yourbinary [0x804bd42]
yourbinary [0x804b04e]
libc.so.6(__libc_start_main+0xe5) [0xb7d0f775]
yourbinary [0x804a701]

Ok, das sieht schon mal gut aus 😉 Jetzt muss man nur noch yourbinary in den ddd laden um die Speicheradressen in Codezeilen umzuwandeln – et voilà: schon ist die bösartige Zeile Code identifiziert.