Eine verzwickte Situation: Der Anwender beschwert sich, dass sein Programm etwa einmal pro Woche abstürzt. Leider lässt sich das Problem bei den Entwicklern nicht nachstellen und der User ist ein paar hundert Kilometer entfernt. Der hat natürlich keine Lust, sich selbst an einen Debugger zu setzen.
Kleine Ursache, späte Wirkung
Schuld sind häufig Fehler in der Speicherverwaltung. Sie zu entdecken ist besonders schwer, da die Symptome nicht eindeutig auf die Ursache hindeuten. Wenn eine Routine Daten an der falschen Adresse ablegt, wirkt sich das oft erst sehr viel später aus - es kann den Ablauf verändern, das Ergebnis verfälschen oder das Programm abstürzen lassen. Wie es dazu kam, lässt sich dann meist nicht mehr ermitteln.
Zum Glück gibt es mittlerweile etliche Tools, die dem Entwickler aus der Patsche helfen. Zwei sehr leistungsfähige Vertreter sind Mpatrol[1] und Valgrind[2]. Beide Libraries werden vor dem Programmstart mit Hilfe der »LD_PRELOAD«-Umgebungsvariablen gestartet und übernehmen zur Laufzeit die Kontrolle über die Applikation. Valgrind und Mpatrol prüfen und protokollieren die Speicheranforderung und Memory-Zugriffe. Außerdem können sie das Programm anhalten, wenn es fehlerhafte Speicherzugriffe ausführt.
Diese Tools erzeugen detaillierte Logfiles, die ungewöhnliche Vorgänge in der Speicherverwaltung aufzeichnen. Sie helfen damit dem Entwickler, jedes Problem nachzuvollziehen. Beide Libraries erledigen ihre Aufgabe allerdings auf höchst unterschiedliche Weise.
Ein kleines Testprogramm namens »error.c« ist in Listing 1 zu sehen, es demonstriert die Leistungsfähigkeit der Malloc-Debugger. Das Programm wertet den ersten Aufrufparameter aus - eine Ziffer zwischen 1 und 8 - und simuliert verschiedene Arten von Fehlern. In Tabelle 1 ist dargestellt, welches Argument zu welchem Bug führt. Wichtig ist, das Programm mit »gcc -g« zu kompilieren: Nur so enthält das Binary Debuginformationen. In den Logfiles steht dann statt der Speicheradresse die Zeilennummer des Quellcodes. Das erleichtert die Fehlersuche erheblich.
01 #include <stdio.h>
02 #include <stdlib.h>
03
04 char* up () {
05 char a='5';
06 return &a;
07 }
08
09 void access(void* ptr) {
10 *(char*)ptr=0;
11 }
12
13 int main(int argc, char **argv) {
14 char* c1_ptr;
15 char* c2_ptr;
16 int c;
17 c1_ptr = (char *)malloc(8);
18
19 if (argc!=2) return 0;
20 c = *argv[1];
21 switch (c) {
22 case '1':
23 c1_ptr=0;
24 break;
25 case '2':
26 free (c1_ptr);
27 free (c1_ptr);
28 break;
29 case '3':
30 free (c1_ptr);
31 access(c1_ptr);
32 break;
33 case '4':
34 free((void *)c2_ptr);
35 break;
36 case '5':
37 access(c2_ptr);
38 break;
39 case '6':
40 free (c1_ptr);
41 c2_ptr = up();
42 printf("%cn",*c2_ptr);
43 break;
44 case '7':
45 access(c1_ptr+9);
46 break;
47 case '8':
48 free (c1_ptr);
49 break;
50 }
51 printf("Enden");
52 return 0;
53 }
|
|
|
|
Parameter
|
Fehler
|
|
1
|
Gibt Speicher nicht frei (Speicherleck)
|
|
2
|
Gibt Speicher mehrfach frei
|
|
3
|
Schreibt auf bereits freigegebenen Speicher
|
|
4
|
Gibt Speicher frei, der gar nicht alloziert wurde
|
|
5
|
Schreibt auf beliebigen Speicher
|
|
6
|
Benutzt den Stack eines bereits verlassenen Unterprogramms
|
|
7
|
Schreibt über den allozierten Speicher hinaus
|
|
8
|
Kein Fehler
|
Mpatrol, der Memory-Wächter
Zunächst soll Mpatrol[1] zeigen, welche Fehler es findet. Die Installation dieses Tools gestaltet sich leider nicht ganz problemlos, die automatischen Installationsroutinen sind fehlerhaft und brechen mit einem Versionskonflikt im mitgelieferten Libtool ab. Aber es geht auch per Hand - der kleine Zusatzaufwand lohnt die Mühe allemal.
Auch für die manuelle Installation sind noch zwei Korrekturen nötig. Mpatrol geht davon aus, dass die Liberty-Bibliothek als Shared Library vorliegt. Unter Linux ist das in der Regel aber nicht der Fall. Die Lösung: »libiberty.a« fest zur Mpatrol-Bibliothek linken. Dazu ist eine Änderung am Makefile im Verzeichnis »mpatrol/build/unix« nötig. In dessen Zeile 127 ist als Ergänzung zusätzlich »-liberty« einzutragen:
$(LD) $(LDFLAGS) -o $@ $(SHARED_MPTOBJS)
-liberty
Auch ein Headerfile ist noch anzupassen: In »mpatrol/src/config.h« muss aus der Define-Anweisung in Zeile 686 der Eintrag »MP_LIBNAME(iberty)« verschwinden. Danach sollte das Make-Kommando problemlos durchlaufen:
cd mpatrol/build/unix/
make all
Je nach Linux-System bereitet auch die BFD-Library Probleme. Sie greift ebenfalls auf Funktionen der Liberty-Bibliothek zurück, womit das »LD_PROLOAD« scheitert. Auch hier ist die Lösung: Im Makefile »-lbfd« hinzufügen und aus dem Config-Header »MP_LIBNAME(bfd)« entfernen. Wer den GUI-Support im »mptrace«-Kommando von Mpatrol nutzen will, muss in Zeile 39 noch das Makro »GUISUP« von »false« auf »true« ändern. Das setzt aber voraus, dass die X11-Entwicklerpakete installiert sind.